diff --git a/.github/workflows/benchmark_models.yml b/.github/workflows/benchmark_models.yml index 9a60c4fdd..85d0005ab 100644 --- a/.github/workflows/benchmark_models.yml +++ b/.github/workflows/benchmark_models.yml @@ -9,6 +9,9 @@ jobs: name: benchmark models steps: - uses: actions/checkout@master + - name: chown checkout directory + # https://github.com/actions/runner/issues/2033 + run: chown -R $(id -u):$(id -g) $PWD - run: echo "PARPE_BASE=$(pwd)" >> $GITHUB_ENV - run: echo "PARPE_BUILD=${PARPE_BASE}/build" >> $GITHUB_ENV @@ -68,6 +71,6 @@ jobs: - name: Benchmark models --- tests run: | cd $PARPE_BASE/benchmark_collection \ - && AMICI_PARALLEL_COMPILE=4 \ + && AMICI_PARALLEL_COMPILE="" \ BENCHMARK_COLLECTION="$(pwd)/Benchmark-Models-PEtab/Benchmark-Models/" \ $PARPE_BASE/misc/run_in_venv.sh $PARPE_BASE/build/venv ./all.sh diff --git a/.github/workflows/deploy_dockerhub.yml b/.github/workflows/deploy_dockerhub.yml index 2891a70cb..2cf6805d6 100644 --- a/.github/workflows/deploy_dockerhub.yml +++ b/.github/workflows/deploy_dockerhub.yml @@ -9,7 +9,7 @@ jobs: - uses: actions/checkout@master - run: git archive -v -o container/charliecloud/parpe_base/parpe.tar.gz --format=tar.gz HEAD - name: Publish to Registry - uses: elgohr/Publish-Docker-Github-Action@2.8 + uses: elgohr/Publish-Docker-Github-Action@v5 with: name: dweindl/parpe username: ${{ secrets.DOCKER_USERNAME }} diff --git a/.github/workflows/parpe_tests.yml b/.github/workflows/parpe_tests.yml index c795970f8..b223dcdbb 100644 --- a/.github/workflows/parpe_tests.yml +++ b/.github/workflows/parpe_tests.yml @@ -1,7 +1,13 @@ # https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions # https://help.github.com/en/actions/configuring-and-managing-workflows/using-environment-variables name: parPE tests -on: [push, pull_request, workflow_dispatch] +on: + push: + pull_request: + workflow_dispatch: + schedule: + - cron: '48 4 * * *' + jobs: container: runs-on: ubuntu-latest @@ -15,6 +21,9 @@ jobs: steps: - uses: actions/checkout@master + - name: chown checkout directory + # https://github.com/actions/runner/issues/2033 + run: chown -R $(id -u):$(id -g) $PWD - run: git fetch --prune --unshallow - run: echo "PARPE_BASE=$(pwd)" >> $GITHUB_ENV @@ -22,7 +31,7 @@ jobs: - run: echo "AMICI_PATH=${PARPE_BASE}/deps/AMICI/" >> $GITHUB_ENV # sonar cloud - - run: echo "SONAR_SCANNER_VERSION=4.6.1.2450" >> $GITHUB_ENV + - run: echo "SONAR_SCANNER_VERSION=5.0.1.3006" >> $GITHUB_ENV - run: echo "SONAR_SCANNER_HOME=/root/.sonar/sonar-scanner-$SONAR_SCANNER_VERSION-linux" >> $GITHUB_ENV - run: echo "SONAR_SCANNER_OPTS=-server" >> $GITHUB_ENV - run: echo "${SONAR_SCANNER_HOME}/bin" >> $GITHUB_PATH @@ -51,7 +60,8 @@ jobs: - name: Install parPE Python deps run: | - pip install -r ${PARPE_BASE}/python/requirements.txt + pip install -r ${PARPE_BASE}/python/requirements.txt \ + && sudo apt install lcov - name: "Install parPE deps: fides" run: | @@ -97,14 +107,16 @@ jobs: - name: Cache sonar files id: cache-sonar - uses: actions/cache@v1 + uses: actions/cache@v3 with: path: sonar_cache key: ${{ runner.os }}-sonar_cache - name: Run sonar-scanner + if: ${{ env.SONAR_TOKEN != '' }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} run: | sonar-scanner \ -Dsonar.cfamily.build-wrapper-output=bw-output \ @@ -112,7 +124,9 @@ jobs: - name: Run example notebooks run: | - misc/run_notebook.sh examples/parpeamici/steadystate/parpeExampleSteadystateBasic.ipynb examples/parpeamici/steadystate/parpeExampleSteadystateHierarchical.ipynb + misc/run_notebook.sh \ + examples/parpeamici/steadystate/parpeExampleSteadystateBasic.ipynb \ + examples/parpeamici/steadystate/parpeExampleSteadystateHierarchical.ipynb - name: Run valgrind run: | diff --git a/.github/workflows/petab_testsuite.yml b/.github/workflows/petab_testsuite.yml index 0cf36659d..445d6aa93 100644 --- a/.github/workflows/petab_testsuite.yml +++ b/.github/workflows/petab_testsuite.yml @@ -10,6 +10,9 @@ jobs: name: PEtab test suite steps: - uses: actions/checkout@master + - name: chown checkout directory + # https://github.com/actions/runner/issues/2033 + run: chown -R $(id -u):$(id -g) $PWD - run: echo "PARPE_BASE=$(pwd)" >> $GITHUB_ENV - run: echo "PARPE_BUILD=${PARPE_BASE}/build" >> $GITHUB_ENV @@ -63,7 +66,7 @@ jobs: PETAB_TEST_URL: https://github.com/PEtab-dev/petab_test_suite.git run: | cd $PARPE_BASE/ \ - && git clone --depth 1 --branch develop $PETAB_TEST_URL \ + && git clone --depth 1 --branch main $PETAB_TEST_URL \ && $PARPE_BASE/misc/run_in_venv.sh $PARPE_BASE/build/venv \ pip3 install -e petab_test_suite diff --git a/.readthedocs.yml b/.readthedocs.yml index fabbf4626..aad25170d 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -19,9 +19,12 @@ sphinx: # - pdf python: - version: 3.8 install: - requirements: doc/requirements_doc.txt - build: - image: latest + os: "ubuntu-22.04" +# apt_packages: +# - libatlas-base-dev +# - swig + tools: + python: "3.11" diff --git a/CITATION.cff b/CITATION.cff new file mode 100644 index 000000000..334a37ebc --- /dev/null +++ b/CITATION.cff @@ -0,0 +1,58 @@ +# This CITATION.cff file was generated with cffinit. +# Visit https://bit.ly/cffinit to generate yours today! + +cff-version: 1.2.0 +title: parPE +message: >- + If you use this software, please cite it using the + metadata from this file. +type: software +authors: + - given-names: Daniel + family-names: Weindl + email: sci@danielweindl.de + orcid: 'https://orcid.org/0000-0001-9963-6057' + - + family-names: "Stapor" + given-names: "Paul" + orcid: "https://orcid.org/0000-0002-7567-3985" + - + family-names: "Schmiester" + given-names: "Leonard" + orcid: "https://orcid.org/0000-0001-7946-3232" + +repository-code: 'https://github.com/ICB-DCM/parPE/' +license: MIT +doi: 10.5281/zenodo.3478612 +preferred-citation: + type: article + title: "Efficient parameterization of large-scale dynamic models based on relative measurements" + doi: 10.1093/bioinformatics/btz581 + journal: "Bioinformatics" + year: 2020 + month: 1 + start: 594 + end: 602 + issue: 2 + volume: 36 + authors: + - + family-names: "Schmiester" + given-names: "Leonard" + orcid: "https://orcid.org/0000-0001-7946-3232" + - + family-names: "Schälte" + given-names: "Yannik" + orcid: "https://orcid.org/0000-0003-1293-820X" + - + family-names: "Fröhlich" + given-names: "Fabian" + orcid: "https://orcid.org/0000-0002-5360-4292" + - + family-names: "Hasenauer" + given-names: "Jan" + orcid: "https://orcid.org/0000-0002-4935-3312" + - + family-names: "Weindl" + given-names: "Daniel" + orcid: "https://orcid.org/0000-0001-9963-6057" diff --git a/CMakeLists.txt b/CMakeLists.txt index 5211a6896..efad43293 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ -cmake_minimum_required(VERSION 3.13) # pkg_search_module GLOBAL -cmake_policy(VERSION 3.13) +cmake_minimum_required(VERSION 3.15) # pkg_search_module GLOBAL +cmake_policy(VERSION 3.15...3.28) if(POLICY CMP0074) # Use package_ROOT environment variables @@ -17,11 +17,8 @@ include(BuildType) # Ensure CMAKE_BUILD_TYPE is always set include(CTest) set(CMAKE_DEBUG_POSTFIX "-dbg") -# -D_GNU_SOURCE for pthread recursive mutex -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} \ - -std=c99 -Wall -Wno-unused-function -D_GNU_SOURCE") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} \ - -Wall -Wno-unused-function -D_GNU_SOURCE") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -Wall -Wno-unused-function") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-unused-function") if(BUILD_PYTHON_MODULE) # Build PIC code to be used for swig/python module @@ -86,11 +83,6 @@ if(${PARPE_ENABLE_DLIB}) CACHE PATH "DLIB base directory") endif(${PARPE_ENABLE_DLIB}) -# PThreads -set(THREADS_PREFER_PTHREAD_FLAG ON) -find_package(Threads REQUIRED) - - # HDF5 #set(HDF5_PREFER_PARALLEL TRUE) find_package(HDF5 COMPONENTS CXX C HL REQUIRED) @@ -207,7 +199,7 @@ endif() if(BUILD_PYTHON_MODULE AND CMAKE_BUILD_TYPE MATCHES Debug) # Only build swig/python module when we are in debug mode # This requires PIC, but we don't want PIC for release build at the moment - add_subdirectory(python/swig) + add_subdirectory(swig) endif() diff --git a/README.md b/README.md index df616a4a9..d0803e301 100644 --- a/README.md +++ b/README.md @@ -48,10 +48,9 @@ described in [doc/petab_model_import.md](doc/petab_model_import.md). For full functionality, parPE requires the following libraries: -* CMAKE (>=3.10) +* CMAKE (>=3.15) * MPI ([OpenMPI](https://www.open-mpi.org/), [MPICH](https://www.mpich.org/), ...) -* PTHREADS * IPOPT (>= 1.2.7) (requires coinhsl) * CERES (>=1.13) ([requires Eigen](http://ceres-solver.org/installation.html#dependencies)) @@ -61,7 +60,7 @@ For full functionality, parPE requires the following libraries: * [AMICI](https://github.com/AMICI-dev/AMICI) (included in this repository) (uses SuiteSparse, Sundials) * C++17 compiler -* Python >= 3.7, including header files +* Python >= 3.9, including header files On Debian-based systems, dependencies can be installed via: ```shell @@ -73,6 +72,7 @@ sudo apt-get install \ curl \ gfortran \ libblas-dev \ + libboost-chrono-dev \ libboost-serialization-dev \ libboost-thread-dev \ libceres-dev \ @@ -121,7 +121,7 @@ can be found on [dockerhub](https://hub.docker.com/r/dweindl/parpe/). Some high-level documentation is available at [https://parpe.readthedocs.io/en/latest/](https://parpe.readthedocs.io/en/latest/) -and among [Github issues](https://github.com/ICB-DCM/parPE/issues). No extensive +and among [GitHub issues](https://github.com/ICB-DCM/parPE/issues). No extensive full-text documentation is available for the C++ interface yet. For usage of the C++ interface see [`examples/`](examples/) and `*/tests`. @@ -141,6 +141,11 @@ parPE is being used or has been used in the following projects: (2022). [doi:10.1038/s41467-021-27374-6](https://doi.org/10.1038/s41467-021-27374-6) (preprint: [doi:10.1101/859884](https://doi.org/10.1101/859884)). +- Paul F. Lang, David R. Penas, Julio R. Banga, Daniel Weindl, Bela Novak. + *Reusable rule-based cell cycle model explains compartment-resolved dynamics* + *of 16 observables in RPE-1 cells*. bioRxiv (2023). + [doi:10.1101/2023.05.04.539349](https://doi.org/10.1101/2023.05.04.539349) + - [CanPathPro](http://canpathpro.eu/) diff --git a/container/charliecloud/parpe_base/Dockerfile b/container/charliecloud/parpe_base/Dockerfile index 8465ca3d4..c024ffd1e 100644 --- a/container/charliecloud/parpe_base/Dockerfile +++ b/container/charliecloud/parpe_base/Dockerfile @@ -1,12 +1,16 @@ -FROM ubuntu:20.04 +FROM ubuntu:22.04 -COPY . /u18 +COPY . /container-files -RUN /u18/install.sh && rm -rf /tmp && mkdir /tmp +RUN /container-files/install.sh && rm -rf /tmp && mkdir /tmp ENV BASH_ENV "/etc/drydock/.env" +ENV CC clang +ENV CXX clang++ +ENV OMPI_CC clang +ENV OMPI_CXX clang++ -RUN /u18/install_parpe.sh +RUN /container-files/install_parpe.sh ENV PARPE_DIR "/parPE" diff --git a/container/charliecloud/parpe_base/install.sh b/container/charliecloud/parpe_base/install.sh index 48428d1fe..7223e0d5d 100755 --- a/container/charliecloud/parpe_base/install.sh +++ b/container/charliecloud/parpe_base/install.sh @@ -13,11 +13,11 @@ locale-gen en_US en_US.UTF-8 dpkg-reconfigure locales echo "HOME=$HOME" -cd /u18 echo "================= parPE requirements ============" # using openmpi coming with libboost-all-dev instead of libmpich-dev apt-get install -q -y \ + clang \ cmake \ curl \ coinor-libipopt-dev \ @@ -28,16 +28,18 @@ apt-get install -q -y \ libboost-all-dev \ libceres-dev \ libhdf5-dev \ + libomp-dev \ + nano \ python3-dev \ + python3-numpy \ python3-pip \ python3-venv \ libspdlog-dev \ - swig3.0 \ + swig \ unzip \ wget # for setuptools to find: -ln -s /usr/bin/swig3.0 /usr/bin/swig python3 -m pip install --upgrade pip pip3 install -U setuptools pkgconfig wheel diff --git a/container/charliecloud/parpe_base/install_parpe.sh b/container/charliecloud/parpe_base/install_parpe.sh index 0ce5b4037..e37e320c0 100755 --- a/container/charliecloud/parpe_base/install_parpe.sh +++ b/container/charliecloud/parpe_base/install_parpe.sh @@ -7,7 +7,7 @@ export PARPE_BASE="${PARPE_DIR:-/parPE}" # unpack git archive mkdir "$PARPE_BASE" && cd "$PARPE_BASE" -tar -xzf /u18/parpe.tar.gz +tar -xzf /container-files/parpe.tar.gz # Build dependencies @@ -18,10 +18,10 @@ cd "${AMICI_PATH}" \ && scripts/buildSundials.sh mkdir -p "${AMICI_PATH}/build" && cd "${AMICI_PATH}/build" cmake \ - -DCMAKE_BUILD_TYPE=Debug \ + -DCMAKE_BUILD_TYPE=RelWithDebInfo \ -DENABLE_PYTHON=ON \ -DBUILD_TESTS=OFF \ - .. && make -j12 + .. && make -j # install fides optimizer cd "$PARPE_BASE/ThirdParty" && ./installFides.sh @@ -55,7 +55,7 @@ CC=mpicc CXX=mpiCC cmake \ -DTESTS_MPIEXEC_COMMAND="$mpi_cmd" \ .. -make -j12 VERBOSE=1 +make -j VERBOSE=1 # MPI settings for python tests export PARPE_TESTS_MPIEXEC="mpiexec -n 5 --oversubscribe --allow-run-as-root --mca btl_vader_single_copy_mechanism none --mca btl ^openib --mca oob_tcp_if_include lo --mca btl_tcp_if_include lo --mca orte_base_help_aggregate 0" diff --git a/deps/AMICI/.clang-format b/deps/AMICI/.clang-format index 3d4a7682c..22f4c4dd1 100644 --- a/deps/AMICI/.clang-format +++ b/deps/AMICI/.clang-format @@ -1,96 +1,13 @@ --- +BasedOnStyle: LLVM +IndentWidth: 4 +--- Language: Cpp -# BasedOnStyle: LLVM -AccessModifierOffset: -2 -AlignAfterOpenBracket: Align -AlignConsecutiveAssignments: false -AlignConsecutiveDeclarations: false -AlignEscapedNewlinesLeft: false -AlignOperands: true -AlignTrailingComments: true -AllowAllParametersOfDeclarationOnNextLine: true -AllowShortBlocksOnASingleLine: false -AllowShortCaseLabelsOnASingleLine: false -AllowShortFunctionsOnASingleLine: All -AllowShortIfStatementsOnASingleLine: false -AllowShortLoopsOnASingleLine: false -AlwaysBreakAfterDefinitionReturnType: None -AlwaysBreakAfterReturnType: None -AlwaysBreakBeforeMultilineStrings: false -AlwaysBreakTemplateDeclarations: false -BinPackArguments: true -BinPackParameters: true -BraceWrapping: - AfterClass: false - AfterControlStatement: false - AfterEnum: false - AfterFunction: false - AfterNamespace: false - AfterObjCDeclaration: false - AfterStruct: false - AfterUnion: false - BeforeCatch: false - BeforeElse: false - IndentBraces: false -BreakBeforeBinaryOperators: None -BreakBeforeBraces: Attach -BreakBeforeTernaryOperators: true -BreakConstructorInitializersBeforeComma: false -BreakAfterJavaFieldAnnotations: false -BreakStringLiterals: true -ColumnLimit: 80 -CommentPragmas: '^ IWYU pragma:' -ConstructorInitializerAllOnOneLineOrOnePerLine: false -ConstructorInitializerIndentWidth: 4 -ContinuationIndentWidth: 4 -Cpp11BracedListStyle: true -DerivePointerAlignment: false -DisableFormat: false -ExperimentalAutoDetectBinPacking: false -ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] -IncludeCategories: - - Regex: '^"(llvm|llvm-c|clang|clang-c)/' - Priority: 2 - - Regex: '^(<|"(gtest|isl|json)/)' - Priority: 3 - - Regex: '.*' - Priority: 1 -IncludeIsMainRegex: '$' -IndentCaseLabels: false -IndentWidth: 4 -IndentWrappedFunctionNames: false -JavaScriptQuotes: Leave -JavaScriptWrapImports: true -KeepEmptyLinesAtTheStartOfBlocks: true -MacroBlockBegin: '' -MacroBlockEnd: '' -MaxEmptyLinesToKeep: 1 -NamespaceIndentation: None -ObjCBlockIndentWidth: 2 -ObjCSpaceAfterProperty: false -ObjCSpaceBeforeProtocolList: true -PenaltyBreakBeforeFirstCallParameter: 19 -PenaltyBreakComment: 300 -PenaltyBreakFirstLessLess: 120 -PenaltyBreakString: 1000 -PenaltyExcessCharacter: 1000000 -PenaltyReturnTypeOnItsOwnLine: 60 -PointerAlignment: Right -ReflowComments: true -SortIncludes: true -SpaceAfterCStyleCast: false -SpaceAfterTemplateKeyword: true -SpaceBeforeAssignmentOperators: true -SpaceBeforeParens: ControlStatements -SpaceInEmptyParentheses: false -SpacesBeforeTrailingComments: 1 -SpacesInAngles: false -SpacesInContainerLiterals: true -SpacesInCStyleCastParentheses: false -SpacesInParentheses: false -SpacesInSquareBrackets: false -Standard: Cpp11 -TabWidth: 8 -UseTab: Never -... - +AlignAfterOpenBracket: BlockIndent +BreakBeforeBinaryOperators: All +BreakConstructorInitializers: BeforeComma +ColumnLimit: 80 +PointerAlignment: Left +QualifierAlignment: Custom +QualifierOrder: ['inline', 'static', 'type', 'const'] +ReferenceAlignment: Left diff --git a/deps/AMICI/.coveragerc b/deps/AMICI/.coveragerc index cf7406e8b..d5f1748d1 100644 --- a/deps/AMICI/.coveragerc +++ b/deps/AMICI/.coveragerc @@ -6,6 +6,7 @@ omit = */amici_without_hdf5.py */ThirdParty/* *.template.* + /tmp/* parallel = true [report] @@ -15,4 +16,3 @@ exclude_lines = raise except: import - diff --git a/deps/AMICI/.github/CODEOWNERS b/deps/AMICI/.github/CODEOWNERS new file mode 100644 index 000000000..6b2aece45 --- /dev/null +++ b/deps/AMICI/.github/CODEOWNERS @@ -0,0 +1,4 @@ +# see https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners + +# default owners +* @AMICI-dev/amici-maintainers diff --git a/deps/AMICI/.github/actions/install-apt-dependencies/action.yml b/deps/AMICI/.github/actions/install-apt-dependencies/action.yml new file mode 100644 index 000000000..0474e5bef --- /dev/null +++ b/deps/AMICI/.github/actions/install-apt-dependencies/action.yml @@ -0,0 +1,16 @@ +name: Install apt dependencies +description: Install apt dependencies for the AMICI Python package +runs: + using: "composite" + steps: + - run: | + sudo apt-get update \ + && sudo apt-get install -y \ + g++ \ + libatlas-base-dev \ + libboost-chrono-dev \ + libboost-math-dev \ + libboost-serialization-dev \ + libhdf5-serial-dev \ + swig + shell: bash diff --git a/deps/AMICI/.github/actions/setup-amici-cpp/action.yml b/deps/AMICI/.github/actions/setup-amici-cpp/action.yml new file mode 100644 index 000000000..09ec9311b --- /dev/null +++ b/deps/AMICI/.github/actions/setup-amici-cpp/action.yml @@ -0,0 +1,44 @@ +name: Set up AMICI C++ +description: | + Build the AMICI C++ interface and set things for for coverage analysis. + (Currently ubuntu-only). + +runs: + using: "composite" + steps: + # BioNetGen Path + - run: echo "BNGPATH=${GITHUB_WORKSPACE}/ThirdParty/BioNetGen-2.7.0" >> $GITHUB_ENV + shell: bash + + # use all available cores + - run: echo "AMICI_PARALLEL_COMPILE=" >> $GITHUB_ENV + shell: bash + + # enable coverage + - run: echo "ENABLE_GCOV_COVERAGE=TRUE" >> $GITHUB_ENV + shell: bash + + - name: Set up Sonar tools + uses: ./.github/actions/setup-sonar-tools + + - name: Install apt dependencies + uses: ./.github/actions/install-apt-dependencies + + - name: Install additional apt dependencies + run: | + sudo apt-get update \ + && sudo apt-get install -y \ + cmake \ + python3-venv \ + lcov + shell: bash + + - name: Build AMICI dependencies + run: scripts/buildDependencies.sh + shell: bash + + - name: Build AMICI + run: scripts/buildAmici.sh + shell: bash + env: + CI_SONARCLOUD: "TRUE" diff --git a/deps/AMICI/.github/actions/setup-doxygen/action.yml b/deps/AMICI/.github/actions/setup-doxygen/action.yml new file mode 100644 index 000000000..f6a62181c --- /dev/null +++ b/deps/AMICI/.github/actions/setup-doxygen/action.yml @@ -0,0 +1,20 @@ +name: Set up doxygen +description: | + Download, build, and install doxygen. + +runs: + using: "composite" + steps: + - name: Install apt dependencies for doxygen + run: | + sudo apt-get update \ + && sudo apt-get install -y \ + bison \ + ragel \ + graphviz \ + texlive-latex-extra + shell: bash + + - name: Download and build doxygen + run: sudo scripts/downloadAndBuildDoxygen.sh + shell: bash diff --git a/deps/AMICI/.github/actions/setup-sonar-tools/action.yml b/deps/AMICI/.github/actions/setup-sonar-tools/action.yml new file mode 100644 index 000000000..d791c120b --- /dev/null +++ b/deps/AMICI/.github/actions/setup-sonar-tools/action.yml @@ -0,0 +1,26 @@ +name: Set up Sonar tools +description: Download and install sonar-scanner and build-wrapper +runs: + using: "composite" + steps: + - run: echo "SONAR_SCANNER_VERSION=5.0.1.3006" >> $GITHUB_ENV + shell: bash + - run: echo "SONAR_SCANNER_HOME=${HOME}/.sonar/sonar-scanner-$SONAR_SCANNER_VERSION-linux" >> $GITHUB_ENV + shell: bash + - run: echo "SONAR_SCANNER_OPTS=-server" >> $GITHUB_ENV + shell: bash + - run: echo "${SONAR_SCANNER_HOME}/bin" >> $GITHUB_PATH + shell: bash + - run: echo "${HOME}/.sonar/build-wrapper-linux-x86" >> $GITHUB_PATH + shell: bash + + - name: Install sonarcloud tools + run: | + 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/ \ + && curl --create-dirs -sSLo $HOME/.sonar/build-wrapper-linux-x86.zip \ + https://sonarcloud.io/static/cpp/build-wrapper-linux-x86.zip \ + && unzip -o $HOME/.sonar/build-wrapper-linux-x86.zip -d $HOME/.sonar/ \ + shell: bash diff --git a/deps/AMICI/.github/actions/setup-swig/action.yml b/deps/AMICI/.github/actions/setup-swig/action.yml new file mode 100644 index 000000000..0eb8a0447 --- /dev/null +++ b/deps/AMICI/.github/actions/setup-swig/action.yml @@ -0,0 +1,20 @@ +name: Set up SWIG +description: | + Download and build SWIG and set the SWIG environment variable to the path of + the SWIG executable. + +inputs: + swig_version: + description: 'Swig version to build' + required: false + default: '4.1.1' + +runs: + using: "composite" + steps: + - name: Download and build SWIG + run: scripts/downloadAndBuildSwig.sh + shell: bash + + - run: echo "SWIG=${AMICI_DIR}/ThirdParty/swig-${{ inputs.swig_version }}/install/bin/swig" >> $GITHUB_ENV + shell: bash diff --git a/deps/AMICI/.github/workflows/deploy_branch.yml b/deps/AMICI/.github/workflows/deploy_branch.yml index b42410997..73294286a 100644 --- a/deps/AMICI/.github/workflows/deploy_branch.yml +++ b/deps/AMICI/.github/workflows/deploy_branch.yml @@ -1,30 +1,37 @@ name: Deploy Branch -on: [push, pull_request, workflow_dispatch] +on: [push, merge_group, workflow_dispatch] jobs: sdist: name: Deploy Python Source Distribution - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 + + strategy: + matrix: + python-version: [3.9] steps: - - uses: actions/checkout@master + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + + - uses: actions/checkout@v3 with: fetch-depth: 20 - - run: echo "AMICI_DIR=$(pwd)" >> $GITHUB_ENV - - run: echo "SWIG=${AMICI_DIR}/ThirdParty/swig-4.0.1/install/bin/swig" >> $GITHUB_ENV + - name: Set up SWIG + uses: ./.github/actions/setup-swig - - name: Build swig4 - run: | - sudo scripts/downloadAndBuildSwig.sh + - run: echo "AMICI_DIR=$(pwd)" >> $GITHUB_ENV - name: Create AMICI sdist run: | scripts/buildSdist.sh - name: "Upload artifact: sdist" - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v3 with: name: sdist - path: python/sdist/dist + 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 9f1253795..cd9257818 100644 --- a/deps/AMICI/.github/workflows/deploy_protected.yml +++ b/deps/AMICI/.github/workflows/deploy_protected.yml @@ -4,9 +4,12 @@ on: branches: - master - develop + - release** pull_request: - branches: - - master + paths: + - container/Dockerfile + # ensure all relevant files are still included in sdist + - python/sdist/MANIFEST.in workflow_dispatch: jobs: @@ -14,17 +17,30 @@ jobs: # https://github.com/marketplace/actions/publish-docker name: Deploy Dockerhub - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 + + strategy: + matrix: + python-version: [3.9] steps: - - uses: actions/checkout@master - - run: git archive -o docker/amici.tar.gz --format=tar.gz HEAD + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + - uses: actions/checkout@v3 + - run: git archive -o container/amici.tar.gz --format=tar.gz HEAD + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 - name: Publish to Registry - uses: elgohr/Publish-Docker-Github-Action@2.8 + uses: elgohr/Publish-Docker-Github-Action@v4 with: name: dweindl/amici username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - workdir: docker/ + workdir: container/ dockerfile: Dockerfile tag_names: true + platforms: linux/amd64,linux/arm64 diff --git a/deps/AMICI/.github/workflows/deploy_release.yml b/deps/AMICI/.github/workflows/deploy_release.yml index 44f51057a..8fe931763 100644 --- a/deps/AMICI/.github/workflows/deploy_release.yml +++ b/deps/AMICI/.github/workflows/deploy_release.yml @@ -8,35 +8,41 @@ jobs: pypi: name: Deploy PyPI - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 + + strategy: + matrix: + python-version: [3.9] steps: - - uses: actions/checkout@master + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + + - uses: actions/checkout@v3 with: fetch-depth: 20 - - run: echo "AMICI_DIR=$(pwd)" >> $GITHUB_ENV - - run: echo "SWIG=${AMICI_DIR}/ThirdParty/swig-4.0.1/install/bin/swig" >> $GITHUB_ENV + - name: Set up SWIG + uses: ./.github/actions/setup-swig - - name: Build swig4 - run: | - sudo scripts/downloadAndBuildSwig.sh + - run: echo "AMICI_DIR=$(pwd)" >> $GITHUB_ENV - name: sdist - run: | - scripts/buildSdist.sh + run: scripts/buildSdist.sh - name: Publish a Python distribution to PyPI - uses: pypa/gh-action-pypi-publish@master + uses: pypa/gh-action-pypi-publish@release/v1 with: user: __token__ password: ${{ secrets.pypi_password }} - packages_dir: python/sdist/dist + packages-dir: python/sdist/dist bioSimulatorsUpdateCliAndDockerImage: name: Release to BioSimulators needs: pypi - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 env: # Owner/repository-id for the GitHub repository for the downstream command-line interface and Docker image DOWNSTREAM_REPOSITORY: biosimulators/Biosimulators_AMICI diff --git a/deps/AMICI/.github/workflows/test_benchmark_collection_models.yml b/deps/AMICI/.github/workflows/test_benchmark_collection_models.yml index fe946ba1c..bf9509e5c 100644 --- a/deps/AMICI/.github/workflows/test_benchmark_collection_models.yml +++ b/deps/AMICI/.github/workflows/test_benchmark_collection_models.yml @@ -4,11 +4,11 @@ on: branches: - develop - master - pull_request: branches: - master - develop + merge_group: workflow_dispatch: schedule: - cron: '48 4 * * *' @@ -17,32 +17,40 @@ jobs: build: name: Benchmark Collection - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 + + strategy: + fail-fast: false + matrix: + python-version: [ "3.9" ] + extract_subexpressions: ["true", "false"] + env: + AMICI_EXTRACT_CSE: ${{ matrix.extract_subexpressions }} steps: - - uses: actions/checkout@master + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 with: - fetch-depth: 20 + python-version: ${{ matrix.python-version }} - # install dependencies - - name: apt - run: | - sudo apt-get update \ - && sudo apt-get install -y swig libatlas-base-dev + - uses: actions/checkout@v3 + with: + fetch-depth: 20 - - run: pip3 install pytest shyaml + - name: Install apt dependencies + uses: ./.github/actions/install-apt-dependencies - run: echo "${HOME}/.local/bin/" >> $GITHUB_PATH - - run: echo "${GITHUB_WORKSPACE}/tests/performance/" >> $GITHUB_PATH # install AMICI - name: Create AMICI sdist - run: | - pip3 install build && cd python/sdist && python3 -m build --sdist + run: pip3 install build && cd python/sdist && python3 -m build --sdist + - name: Install AMICI sdist run: | + pip3 install --user petab[vis] && \ AMICI_PARALLEL_COMPILE=2 pip3 install -v --user \ - $(ls -t python/sdist/dist/amici-*.tar.gz | head -1)[petab] + $(ls -t python/sdist/dist/amici-*.tar.gz | head -1)[petab,test,vis] # retrieve test models - name: Download and test benchmark collection @@ -50,3 +58,17 @@ jobs: 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 + + # run gradient checks + - name: Run Gradient Checks + run: | + pip install git+https://github.com/ICB-DCM/fiddy.git \ + && cd tests/benchmark-models && pytest ./test_petab_benchmark.py + + # upload results + - uses: actions/upload-artifact@v3 + with: + name: computation times + 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 f2fc8ab6c..07e0afdcc 100644 --- a/deps/AMICI/.github/workflows/test_doc.yml +++ b/deps/AMICI/.github/workflows/test_doc.yml @@ -8,6 +8,7 @@ on: branches: - develop - master + merge_group: workflow_dispatch: schedule: - cron: '48 4 * * *' @@ -15,67 +16,61 @@ on: jobs: doxygen: name: Test Doxygen + runs-on: ubuntu-22.04 - runs-on: ubuntu-20.04 + strategy: + matrix: + python-version: [3.9] steps: - - uses: actions/checkout@master - - run: git fetch --prune --unshallow + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} - - name: apt - run: | - sudo apt-get update \ - && sudo apt-get install -y \ - bison \ - ragel \ - graphviz \ - texlive-latex-extra + - uses: actions/checkout@v3 + - run: git fetch --prune --unshallow - - name: Build doxygen - run: | - sudo scripts/downloadAndBuildDoxygen.sh + - name: Set up doxygen + uses: ./.github/actions/setup-doxygen - name: Run doxygen - run: | - scripts/run-doxygen.sh + run: scripts/run-doxygen.sh sphinx: name: Test Sphinx + runs-on: ubuntu-22.04 - runs-on: ubuntu-20.04 + strategy: + matrix: + python-version: [ "3.10" ] steps: - - uses: actions/checkout@master + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + + - uses: actions/checkout@v3 - run: git fetch --prune --unshallow - run: echo "AMICI_DIR=$(pwd)" >> $GITHUB_ENV - - run: echo "SWIG=${AMICI_DIR}/ThirdParty/swig-4.0.2/install/bin/swig" >> $GITHUB_ENV - - name: Build doxygen - run: | - sudo scripts/downloadAndBuildDoxygen.sh + - name: Set up doxygen + uses: ./.github/actions/setup-doxygen - - name: Set up Python 3.8 - uses: actions/setup-python@v2 - with: - # Semantic version range syntax or exact version of a Python version - python-version: '3.8' + - name: Install apt dependencies + uses: ./.github/actions/install-apt-dependencies - # install amici dependencies - - name: apt + - name: Install further dependencies run: | sudo apt-get update \ && sudo apt-get install -y \ - g++ \ - libatlas-base-dev \ - libboost-serialization-dev \ pandoc \ - python3-venv \ + python3-venv - - name: Build swig - run: | - sudo scripts/downloadAndBuildSwig.sh + - name: Set up SWIG + uses: ./.github/actions/setup-swig - - name: sphinx - run: | - scripts/run-sphinx.sh + - name: Run sphinx + run: scripts/run-sphinx.sh diff --git a/deps/AMICI/.github/workflows/test_install.yml b/deps/AMICI/.github/workflows/test_install.yml index b47ae6b09..be74cfa4c 100644 --- a/deps/AMICI/.github/workflows/test_install.yml +++ b/deps/AMICI/.github/workflows/test_install.yml @@ -1,76 +1,115 @@ name: Installation -on: [push, pull_request, workflow_dispatch] +on: [push, merge_group, workflow_dispatch] jobs: archive: name: Archive Install - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 + + strategy: + matrix: + python-version: [3.9] steps: - - uses: actions/checkout@master + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + + - uses: actions/checkout@v3 - run: git fetch --prune --unshallow - run: echo "AMICI_DIR=$(pwd)" >> $GITHUB_ENV - # install amici dependencies + - name: Install apt dependencies + uses: ./.github/actions/install-apt-dependencies + - name: apt run: | sudo apt-get update \ && sudo apt-get install -y \ - cmake \ - g++ \ - libatlas-base-dev \ - libboost-serialization-dev \ - libhdf5-serial-dev \ - swig + cmake - name: Build suitesparse - run: | - scripts/buildSuiteSparse.sh + run: scripts/buildSuiteSparse.sh - name: Build sundials - run: | - scripts/buildSundials.sh + run: scripts/buildSundials.sh - name: Build AMICI - run: | - scripts/buildAmici.sh + run: scripts/buildAmici.sh - name: Install python archive - run: | - scripts/installAmiciArchive.sh + run: scripts/installAmiciArchive.sh + + sdist_ubuntu: + name: sdist Install Ubuntu - sdist: - name: sdist Install + runs-on: ubuntu-22.04 - runs-on: ubuntu-20.04 + strategy: + matrix: + python-version: [3.9] steps: - - uses: actions/checkout@master + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + + - uses: actions/checkout@v3 + - run: git fetch --prune --unshallow + + - run: echo "AMICI_DIR=$(pwd)" >> $GITHUB_ENV + + - name: Install apt dependencies + uses: ./.github/actions/install-apt-dependencies + + - name: Create AMICI sdist + run: scripts/buildSdist.sh + + - name: Install python sdist + run: pip3 install -v --user $(ls -t python/sdist/dist/amici-*.tar.gz | head -1) + + - name: Test import + run: python -m amici + + + sdist_macos: + name: sdist Install macos + + runs-on: macos-latest + + strategy: + matrix: + python-version: [3.9] + + steps: + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + + - uses: actions/checkout@v3 - run: git fetch --prune --unshallow - run: echo "AMICI_DIR=$(pwd)" >> $GITHUB_ENV # install amici dependencies - - name: apt + - name: homebrew run: | - sudo apt-get update \ - && sudo apt-get install -y \ - g++ \ - libatlas-base-dev \ - libboost-serialization-dev \ - libhdf5-serial-dev \ - swig + brew install hdf5 swig gcc cppcheck libomp boost \ + && brew ls -v boost \ + && brew ls -v libomp \ + && echo LDFLAGS="-L/usr/local/lib/ -L/usr/local/Cellar/boost/1.81.0_1/lib/" >> $GITHUB_ENV \ + && echo CPPFLAGS="-I /usr/local/Cellar/boost/1.81.0_1/include/" >> $GITHUB_ENV - name: Create AMICI sdist - run: | - scripts/buildSdist.sh + run: scripts/buildSdist.sh - name: Install python sdist - run: | - pip3 install -v --user $(ls -t python/sdist/dist/amici-*.tar.gz | head -1) + run: pip3 install -v --user $(ls -t python/sdist/dist/amici-*.tar.gz | head -1) - name: Test import - run: | - python -m amici + run: python -m amici diff --git a/deps/AMICI/.github/workflows/test_matlab.yml b/deps/AMICI/.github/workflows/test_matlab.yml index 90757643d..d51cc3fbf 100644 --- a/deps/AMICI/.github/workflows/test_matlab.yml +++ b/deps/AMICI/.github/workflows/test_matlab.yml @@ -1,5 +1,5 @@ name: Matlab -on: [push, pull_request, workflow_dispatch] +on: [push, merge_group, workflow_dispatch] jobs: matlab: @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@master + - uses: actions/checkout@v3 - run: git fetch --prune --unshallow - run: echo "AMICI_DIR=$(pwd)" >> $GITHUB_ENV diff --git a/deps/AMICI/.github/workflows/test_performance.yml b/deps/AMICI/.github/workflows/test_performance.yml index b5c92449a..de9dd686d 100644 --- a/deps/AMICI/.github/workflows/test_performance.yml +++ b/deps/AMICI/.github/workflows/test_performance.yml @@ -4,7 +4,7 @@ on: branches: - develop - master - - compile_without_optimization + - fix_perftest pull_request: branches: @@ -19,19 +19,26 @@ jobs: build: name: Performance Test - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 + + strategy: + matrix: + python-version: [3.9] steps: - - uses: actions/checkout@master + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + + - uses: actions/checkout@v3 with: fetch-depth: 20 - # install dependencies - - name: apt - run: | - sudo apt-get update \ - && sudo apt-get install -y swig libatlas-base-dev - - run: pip3 install petab shyaml + - name: Install apt dependencies + uses: ./.github/actions/install-apt-dependencies + + - run: pip3 install petab shyaml build - run: echo "${HOME}/.local/bin/" >> $GITHUB_PATH - run: echo "${GITHUB_WORKSPACE}/tests/performance/" >> $GITHUB_PATH @@ -40,7 +47,8 @@ jobs: - name: Create AMICI sdist run: | cd python/sdist \ - && check_time.sh create_sdist /usr/bin/python3 setup.py sdist + && check_time.sh create_sdist python3 -m build --sdist + - name: Install AMICI sdist run: | AMICI_PARALLEL_COMPILE=2 check_time.sh \ @@ -53,13 +61,13 @@ jobs: # import test model - name: Import test model run: | - check_time.sh petab_import python tests/performance/test.py import + 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@v1 + uses: actions/upload-artifact@v3 with: - name: CS_Signalling_ERBB_RAS_AKT - path: CS_Signalling_ERBB_RAS_AKT/CS_Signalling_ERBB_RAS_AKT_petab + name: model_performance_test + path: model_performance_test # install model package - name: Install test model diff --git a/deps/AMICI/.github/workflows/test_petab_test_suite.yml b/deps/AMICI/.github/workflows/test_petab_test_suite.yml index 26148154a..d5c4dc4fe 100644 --- a/deps/AMICI/.github/workflows/test_petab_test_suite.yml +++ b/deps/AMICI/.github/workflows/test_petab_test_suite.yml @@ -4,42 +4,47 @@ on: branches: - develop - master - pull_request: branches: - master - develop - + merge_group: workflow_dispatch: jobs: build: name: PEtab Testsuite - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 env: ENABLE_GCOV_COVERAGE: TRUE + strategy: + matrix: + python-version: [3.9] + steps: - - uses: actions/checkout@master + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + + - uses: actions/checkout@v3 with: fetch-depth: 20 + - name: Install apt dependencies + uses: ./.github/actions/install-apt-dependencies + # install dependencies - name: apt run: | sudo apt-get update \ - && sudo apt-get install -y \ - swig \ - libatlas-base-dev \ - python3-venv - - - run: pip3 install pytest shyaml pytest-cov pysb petab + && sudo apt-get install -y python3-venv - name: Build BNGL - run: | - scripts/buildBNGL.sh + run: scripts/buildBNGL.sh - run: | echo "${HOME}/.local/bin/" >> $GITHUB_PATH @@ -48,33 +53,41 @@ jobs: # install AMICI - name: Install python package + run: scripts/installAmiciSource.sh + + - name: Install petab run: | - scripts/installAmiciSource.sh + source ./build/venv/bin/activate \ + && pip3 install wheel pytest shyaml pytest-cov pysb # retrieve test models - name: Download and install PEtab test suite run: | - git clone --depth 1 --branch develop https://github.com/PEtab-dev/petab_test_suite \ + git clone --depth 1 --branch main \ + https://github.com/PEtab-dev/petab_test_suite \ && source ./build/venv/bin/activate \ - && cd petab_test_suite && pip3 install -e . && cd .. + && cd petab_test_suite && pip3 install -e . - name: Run PEtab-related unit tests run: | source ./build/venv/bin/activate \ - && pytest --cov-report=xml --cov=./ python/tests/test_*petab*.py + && pytest --cov-report=xml:coverage.xml \ + --cov=./ python/tests/test_*petab*.py # run test models - name: Run PEtab test suite - # git clone --depth 1 https://github.com/petab-dev/petab_test_suite run: | source ./build/venv/bin/activate \ && AMICI_PARALLEL_COMPILE=2 pytest -v \ - --cov-report=xml --cov-append --cov=amici tests/petab_test_suite/ + --cov-report=xml:coverage.xml \ + --cov-append \ + --cov=amici \ + tests/petab_test_suite/ - name: Codecov - uses: codecov/codecov-action@v1 + uses: codecov/codecov-action@v3.1.0 with: token: ${{ secrets.CODECOV_TOKEN }} - file: ./coverage.xml + file: coverage.xml flags: petab fail_ci_if_error: true diff --git a/deps/AMICI/.github/workflows/test_pypi.yml b/deps/AMICI/.github/workflows/test_pypi.yml index 138a93395..4f3533850 100644 --- a/deps/AMICI/.github/workflows/test_pypi.yml +++ b/deps/AMICI/.github/workflows/test_pypi.yml @@ -11,8 +11,8 @@ jobs: strategy: fail-fast: false matrix: - python-version: [3.7, 3.8, 3.9] - os: [ubuntu-20.04, macos-latest] + python-version: ["3.9", "3.10", "3.11"] + os: [ubuntu-22.04, macos-latest] runs-on: ${{ matrix.os }} @@ -31,11 +31,12 @@ jobs: - name: homebrew run: | if [[ ${{ matrix.os }} == macos* ]] ; then \ - brew install hdf5 swig gcc libomp + brew install hdf5 swig gcc libomp \ + && echo LDFLAGS="-L/usr/local/lib/" >> $GITHUB_ENV fi - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} diff --git a/deps/AMICI/.github/workflows/test_python_cplusplus.yml b/deps/AMICI/.github/workflows/test_python_cplusplus.yml index a5c8cc782..a531d2db2 100644 --- a/deps/AMICI/.github/workflows/test_python_cplusplus.yml +++ b/deps/AMICI/.github/workflows/test_python_cplusplus.yml @@ -1,166 +1,254 @@ name: C++/Python Tests -on: [push, pull_request, workflow_dispatch] +on: + push: + merge_group: + workflow_dispatch: + pull_request: + branches: + - master jobs: - build: - name: Tests Ubuntu + ubuntu-cpp-python-tests: + name: C++/Python tests Ubuntu + runs-on: ubuntu-22.04 - # TODO: prepare image with more deps preinstalled - runs-on: ubuntu-20.04 - - env: - ENABLE_GCOV_COVERAGE: "TRUE" - CI_SONARCLOUD: "TRUE" + strategy: + matrix: + python-version: [ "3.9" ] steps: - - uses: actions/checkout@master + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + + - uses: actions/checkout@v3 - run: git fetch --prune --unshallow - run: echo "AMICI_DIR=$(pwd)" >> $GITHUB_ENV - - run: echo "BNGPATH=${GITHUB_WORKSPACE}/ThirdParty/BioNetGen-2.7.0" >> $GITHUB_ENV - # sonar cloud - - run: echo "SONAR_SCANNER_VERSION=4.6.1.2450" >> $GITHUB_ENV - - run: echo "SONAR_SCANNER_HOME=${HOME}/.sonar/sonar-scanner-$SONAR_SCANNER_VERSION-linux" >> $GITHUB_ENV - - run: echo "SONAR_SCANNER_OPTS=-server" >> $GITHUB_ENV - - run: echo "${SONAR_SCANNER_HOME}/bin" >> $GITHUB_PATH - - run: echo "${HOME}/.sonar/build-wrapper-linux-x86" >> $GITHUB_PATH + - name: Set up AMICI C++ libraries + uses: ./.github/actions/setup-amici-cpp - # TODO: add to ci image - - name: Install sonarcloud tools - run: | - sudo apt 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/ \ - && curl --create-dirs -sSLo $HOME/.sonar/build-wrapper-linux-x86.zip \ - https://sonarcloud.io/static/cpp/build-wrapper-linux-x86.zip \ - && unzip -o $HOME/.sonar/build-wrapper-linux-x86.zip -d $HOME/.sonar/ \ + - name: C++ tests + run: scripts/run-cpp-tests.sh - # install amici dependencies - - name: apt + - name: Install python package + 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())" + + - name: Python tests (part 1) run: | - sudo apt-get update \ - && sudo apt-get install -y \ - cmake \ - g++ \ - libatlas-base-dev \ - libboost-serialization-dev \ - libhdf5-serial-dev \ - python3-venv \ - swig \ - lcov \ - libboost-math-dev - - - name: Build AMICI dependencies + source build/venv/bin/activate \ + && pytest \ + --ignore-glob=*petab* \ + --ignore-glob=*test_splines.py \ + --ignore-glob=*test_splines_short.py \ + --ignore-glob=*test_pysb.py \ + --cov=amici \ + --cov-report=xml:"${AMICI_DIR}/build/coverage_py.xml" \ + --cov-append \ + --durations=10 \ + ${AMICI_DIR}/python/tests + + - name: Python tests splines + if: ${{ github.base_ref == 'master' || github.event.merge_group.base_ref == 'master'}} run: | - scripts/buildDependencies.sh + source build/venv/bin/activate \ + && pytest \ + --cov=amici \ + --cov-report=xml:"${AMICI_DIR}/build/coverage_py.xml" \ + --cov-append \ + --durations=10 \ + ${AMICI_DIR}/python/tests/test_splines.py - - name: Build AMICI + - name: Codecov Python + uses: codecov/codecov-action@v3.1.0 + with: + token: ${{ secrets.CODECOV_TOKEN }} + file: build/coverage_py.xml + flags: python + fail_ci_if_error: true + verbose: true + + - name: Capture coverage info (lcov) run: | - CI_SONARCLOUD=TRUE scripts/buildAmici.sh + lcov --compat-libtool --no-external \ + -d ${AMICI_DIR}/build/CMakeFiles/amici.dir/src \ + -b ${AMICI_DIR} -c -o coverage_cpp.info \ + && lcov --compat-libtool --no-external \ + -d ${AMICI_DIR}/python/sdist/build/temp_amici/CMakeFiles/amici.dir/src \ + -b ${AMICI_DIR}/python/sdist -c -o coverage_py.info \ + && lcov -a coverage_cpp.info -a coverage_py.info -o coverage.info - - name: Cache sonar files - id: cache-sonar - uses: actions/cache@v1 + - name: Codecov CPP + uses: codecov/codecov-action@v3.1.0 with: - path: sonar_cache - key: ${{ runner.os }}-sonar_cache + token: ${{ secrets.CODECOV_TOKEN }} + file: coverage.info + flags: cpp + fail_ci_if_error: true - - name: C++ tests + - name: Run sonar-scanner + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} run: | - scripts/run-cpp-tests.sh + sonar-scanner \ + -Dsonar.cfamily.build-wrapper-output=bw-output \ + -Dsonar.projectVersion="$(git describe --abbrev=4 --dirty=-dirty --always --tags | tr -d '\n')" + + ubuntu-python-tests: + name: Python tests Ubuntu + runs-on: ubuntu-22.04 + + strategy: + matrix: + python-version: [ "3.9" ] + + steps: + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + + - uses: actions/checkout@v3 + - run: git fetch --prune --unshallow + + - run: echo "AMICI_DIR=$(pwd)" >> $GITHUB_ENV + + - name: Set up AMICI C++ libraries + uses: ./.github/actions/setup-amici-cpp - name: Install python package - run: | - scripts/installAmiciSource.sh + run: scripts/installAmiciSource.sh - name: Python tests run: | source build/venv/bin/activate \ - && pip3 install coverage pytest pytest-cov \ && pytest \ - --ignore-glob=*petab* \ --cov=amici \ --cov-report=xml:"${AMICI_DIR}/build/coverage_py.xml" \ --cov-append \ - ${AMICI_DIR}/python/tests - - - name: example notebooks - run: | - scripts/runNotebook.sh python/examples/example_*/ - - - name: doc notebooks - run: | - scripts/runNotebook.sh documentation/GettingStarted.ipynb + --durations=10 \ + ${AMICI_DIR}/python/tests/test_pysb.py \ + ${AMICI_DIR}/python/tests/test_splines_short.py - name: Codecov Python - uses: codecov/codecov-action@v1 + uses: codecov/codecov-action@v3.1.0 with: token: ${{ secrets.CODECOV_TOKEN }} - file: ./build/coverage_py.xml + file: build/coverage_py.xml flags: python fail_ci_if_error: true + verbose: true - - name: lcov + - name: Capture coverage info (lcov) run: | lcov --compat-libtool --no-external \ -d ${AMICI_DIR}/build/CMakeFiles/amici.dir/src \ -b ${AMICI_DIR} -c -o coverage_cpp.info \ && lcov --compat-libtool --no-external \ - -d ${AMICI_DIR}/python/sdist/build/temp.linux-x86_64-$(python3 --version | sed -E 's/.*([0-9]+\.[0-9]+)\..*/\1/')/amici/src \ + -d ${AMICI_DIR}/python/sdist/build/temp_amici/CMakeFiles/amici.dir/src \ -b ${AMICI_DIR}/python/sdist -c -o coverage_py.info \ && lcov -a coverage_cpp.info -a coverage_py.info -o coverage.info - name: Codecov CPP - uses: codecov/codecov-action@v1 + uses: codecov/codecov-action@v3.1.0 with: token: ${{ secrets.CODECOV_TOKEN }} - file: ./coverage.info - flags: cpp + file: coverage.info + flags: cpp_python fail_ci_if_error: true - name: Run sonar-scanner env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} run: | sonar-scanner \ -Dsonar.cfamily.build-wrapper-output=bw-output \ -Dsonar.projectVersion="$(git describe --abbrev=4 --dirty=-dirty --always --tags | tr -d '\n')" + + ubuntu-notebook-tests: + name: Notebook tests Ubuntu + runs-on: ubuntu-22.04 + + strategy: + matrix: + python-version: [ "3.9" ] + + steps: + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + + - uses: actions/checkout@v3 + - run: git fetch --prune --unshallow + + - run: echo "AMICI_DIR=$(pwd)" >> $GITHUB_ENV + + - name: Set up AMICI C++ libraries + uses: ./.github/actions/setup-amici-cpp + + - name: Install python package + run: scripts/installAmiciSource.sh + + - name: Install notebook dependencies + run: | + source build/venv/bin/activate \ + && pip install jax[cpu] + + - name: example notebooks + run: scripts/runNotebook.sh python/examples/example_*/ + + - name: doc notebooks + run: scripts/runNotebook.sh documentation/GettingStarted.ipynb + + # TODO: Include notebooks in coverage report + osx: name: Tests OSX - runs-on: macos-latest steps: - - uses: actions/checkout@master + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: 3.9 + + - uses: actions/checkout@v3 - run: git fetch --prune --unshallow - run: echo "AMICI_DIR=$(pwd)" >> $GITHUB_ENV - run: echo "BNGPATH=${AMICI_DIR}/ThirdParty/BioNetGen-2.7.0" >> $GITHUB_ENV + # Ensure CMake is using the python version that we will use for the python tests later on + - run: echo "PYTHON_EXECUTABLE=${Python3_ROOT_DIR}/bin/python3" >> $GITHUB_ENV + - run: echo "OpenMP_ROOT=$(brew --prefix)/opt/libomp" >> $GITHUB_ENV + - run: echo "BOOST_ROOT=$(brew --prefix)/opt/boost" >> $GITHUB_ENV # install amici dependencies - name: homebrew - run: | - brew install hdf5 swig gcc cppcheck libomp boost + run: brew install hdf5 swig gcc cppcheck libomp boost - name: Build AMICI - run: | - scripts/buildAll.sh + run: scripts/buildAll.sh - name: Install python package - run: | - scripts/installAmiciSource.sh + 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())" - name: cppcheck - run: | - scripts/run-cppcheck.sh + run: scripts/run-cppcheck.sh - name: Python tests - run: | - scripts/run-python-tests.sh + run: scripts/run-python-tests.sh - name: C++ tests - run: | - scripts/run-cpp-tests.sh + run: scripts/run-cpp-tests.sh diff --git a/deps/AMICI/.github/workflows/test_python_ver_matrix.yml b/deps/AMICI/.github/workflows/test_python_ver_matrix.yml index 041248314..866a3fc0f 100644 --- a/deps/AMICI/.github/workflows/test_python_ver_matrix.yml +++ b/deps/AMICI/.github/workflows/test_python_ver_matrix.yml @@ -9,60 +9,50 @@ on: branches: - master workflow_dispatch: + schedule: + - cron: '48 4 * * *' jobs: build: name: Python Version Matrix - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 continue-on-error: ${{ matrix.experimental }} env: AMICI_SKIP_CMAKE_TESTS: "TRUE" + AMICI_PARALLEL_COMPILE: "" strategy: fail-fast: false matrix: - python-version: [3.7, 3.8, 3.9, '3.10'] + python-version: ['3.9', '3.10', '3.11'] experimental: [false] -# Temporarily disabled because the respective jobs somehow runs infinitely -# include: -# - python-version: 3.10.0-alpha.5 -# experimental: true steps: - run: echo "AMICI_DIR=$(pwd)" >> $GITHUB_ENV - run: echo "BNGPATH=${AMICI_DIR}/ThirdParty/BioNetGen-2.7.0" >> $GITHUB_ENV - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - - uses: actions/checkout@master + - uses: actions/checkout@v3 with: fetch-depth: 20 - # install dependencies - - name: apt - run: | - sudo apt-get update \ - && sudo apt-get install -y \ - swig \ - libatlas-base-dev \ - libhdf5-serial-dev \ - libboost-math-dev + - name: Install apt dependencies + uses: ./.github/actions/install-apt-dependencies # install AMICI - name: Build BNGL - run: | - scripts/buildBNGL.sh + run: scripts/buildBNGL.sh - name: Install python package - run: | - scripts/installAmiciSource.sh + run: scripts/installAmiciSource.sh - name: Python tests run: | source build/venv/bin/activate \ - && pip3 install pytest \ && pip3 install git+https://github.com/pysb/pysb \ - && python3 -m pytest --ignore-glob=*petab* ${AMICI_DIR}/python/tests + && python3 -m pytest --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 f08e4c5a7..0fde56b8f 100644 --- a/deps/AMICI/.github/workflows/test_sbml_semantic_test_suite.yml +++ b/deps/AMICI/.github/workflows/test_sbml_semantic_test_suite.yml @@ -8,11 +8,13 @@ on: pull_request: paths: - .github/workflows/test_sbml_semantic_test_suite.yml - - python/amici/ode_export.py - - python/amici/sbml_import.py - - python/amici/import_utils.py + - python/sdist/amici/de_export.py + - python/sdist/amici/de_model.py + - python/sdist/amici/sbml_import.py + - python/sdist/amici/import_utils.py - scripts/run-SBMLTestsuite.sh - tests/testSBMLSuite.py + - tests/conftest.py check_suite: types: [requested] workflow_dispatch: @@ -20,35 +22,41 @@ on: jobs: build: name: SBML Semantic Test Suite - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 strategy: fail-fast: false matrix: - cases: ["1 - 250", "251 - 500", "501 - 750", "751 - 1000", - "1000-1250", "1251-1780"] + cases: ["1-250", "251-500", "501-750", "751-1000", + "1000-1250", "1251-"] + python-version: [ "3.9" ] steps: - - uses: actions/checkout@master + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + + - uses: actions/checkout@v3 with: fetch-depth: 1 - - name: apt - run: | - sudo apt-get update \ - && sudo apt-get install -y swig4.0 libatlas-base-dev + + - 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 }} - name: "Upload artifact: SBML semantic test suite results" - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v3 with: name: amici-semantic-results path: tests/amici-semantic-results - name: Codecov SBMLSuite - uses: codecov/codecov-action@v1 + uses: codecov/codecov-action@v3.1.0 with: token: ${{ secrets.CODECOV_TOKEN }} - file: ./coverage_SBMLSuite.xml + file: coverage_SBMLSuite.xml flags: sbmlsuite fail_ci_if_error: true diff --git a/deps/AMICI/.github/workflows/test_valgrind.yml b/deps/AMICI/.github/workflows/test_valgrind.yml index 3a0f3a1ee..32eea2e0c 100644 --- a/deps/AMICI/.github/workflows/test_valgrind.yml +++ b/deps/AMICI/.github/workflows/test_valgrind.yml @@ -1,4 +1,4 @@ -name: C++ Tests +name: Valgrind tests on: push: branches: @@ -8,43 +8,86 @@ on: branches: - master workflow_dispatch: + schedule: + - cron: '48 4 * * *' jobs: - build: - name: Tests Valgrind + valgrind_cpp: + name: Valgrind C++ + runs-on: ubuntu-22.04 - # TODO: prepare image with more deps preinstalled - runs-on: ubuntu-20.04 + strategy: + matrix: + python-version: [ "3.9" ] + + env: + ENABLE_AMICI_DEBUGGING: "TRUE" steps: - - uses: actions/checkout@master + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + + - uses: actions/checkout@v3 - run: git fetch --prune --unshallow + - name: Install apt dependencies + uses: ./.github/actions/install-apt-dependencies + # install amici dependencies - name: apt run: | sudo apt-get update \ && sudo apt-get install -y \ cmake \ - g++ \ - libatlas-base-dev \ - libboost-serialization-dev \ - libhdf5-serial-dev \ - swig \ + python3-venv \ valgrind - name: Build AMICI - run: | - scripts/buildAll.sh + run: scripts/buildAll.sh - name: C++ tests / Valgrind + run: scripts/run-valgrind-cpp.sh + + valgrind_python: + name: Valgrind Python + + runs-on: ubuntu-22.04 + + strategy: + matrix: + python-version: [ "3.9" ] + + env: + ENABLE_AMICI_DEBUGGING: "TRUE" + + steps: + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + + - uses: actions/checkout@v3 + - run: git fetch --prune --unshallow + + - name: Install apt dependencies + uses: ./.github/actions/install-apt-dependencies + + # install amici dependencies + - name: apt run: | - scripts/run-valgrind-cpp.sh + sudo apt-get update \ + && sudo apt-get install -y \ + cmake \ + python3-venv \ + valgrind + + - name: Build AMICI + run: scripts/buildAll.sh - name: Install python package - run: | - scripts/installAmiciSource.sh + run: scripts/installAmiciSource.sh - name: Python tests / Valgrind - run: | - scripts/run-valgrind-py.sh + run: scripts/run-valgrind-py.sh diff --git a/deps/AMICI/.github/workflows/test_windows.yml b/deps/AMICI/.github/workflows/test_windows.yml index bf6c8d80d..53834c300 100644 --- a/deps/AMICI/.github/workflows/test_windows.yml +++ b/deps/AMICI/.github/workflows/test_windows.yml @@ -1,28 +1,50 @@ name: Windows Tests -on: [push, pull_request, workflow_dispatch] +on: + push: + merge_group: + workflow_dispatch: + schedule: + - cron: '48 4 * * *' + pull_request: + branches: + - master jobs: build: name: Tests Windows - runs-on: windows-2019 + runs-on: windows-latest env: AMICI_SKIP_CMAKE_TESTS: "TRUE" - openBLAS_version: "0.3.12" + openBLAS_version: "0.3.19" + AMICI_DLL_DIRS: "C:\\BLAS\\OpenBLAS\\bin" + LIB: "C:/BLAS/OpenBLAS/lib" + CFLAGS: "-nologo" + CXXFLAGS: "-nologo" + LDFLAGS: "-nologo" + + strategy: + matrix: + python-version: [ "3.9" ] steps: - - uses: actions/checkout@master + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + + - uses: actions/checkout@v3 - run: git fetch --prune --unshallow - shell: bash run: echo "AMICI_DIR=$(pwd)" >> $GITHUB_ENV - shell: bash - run: echo "C:\\BLAS\\bin" >> $GITHUB_PATH + run: echo "C:\\BLAS\\OpenBLAS\\bin" >> $GITHUB_PATH - shell: bash - run: echo "BLAS_LIBS=/LIBPATH:C:/BLAS/lib openblas.lib" >> $GITHUB_ENV + run: echo "BLAS_LIBS=-LIBPATH:C:/BLAS/OpenBLAS/lib openblas.lib" >> $GITHUB_ENV - shell: bash - run: echo "BLAS_CFLAGS=-IC:/BLAS/OpenBLAS-${openBLAS_version}/OpenBLAS-${openBLAS_version}" >> $GITHUB_ENV + run: echo "BLAS_CFLAGS=-IC:/BLAS/OpenBLAS/include/openblas/" >> $GITHUB_ENV # Developer Command Prompt for Microsoft Visual C++ - uses: ilammy/msvc-dev-cmd@v1 @@ -31,7 +53,6 @@ jobs: shell: bash run: | python -m pip install --upgrade pip \ - && pip install pytest petab \ && choco install -y ninja \ && choco install -y swig @@ -39,14 +60,31 @@ jobs: shell: powershell run: scripts/installOpenBLAS + - uses: actions/upload-artifact@v3 + with: + name: OpenBLAS + path: C:\BLAS\OpenBLAS + - name: Create sdist working-directory: python/sdist - run: python setup.py sdist + run: pip install build && python -m build --sdist - name: Install sdist working-directory: python/sdist shell: bash - run: pip install -v $(ls -t dist/amici-*.tar.gz | head -1) + run: pip install -v $(ls -t dist/amici-*.tar.gz | head -1)[petab,test] + + - run: python -m amici - name: Run Python tests - run: python -m pytest --ignore-glob=*petab* --ignore-glob=*special* python/tests + shell: bash + run: | + python -m pytest \ + --ignore-glob=*petab* \ + --ignore-glob=*special* \ + --ignore-glob=*test_splines.py \ + python/tests + + - name: Python tests splines + if: ${{ github.base_ref == 'master' || github.event.merge_group.base_ref == 'master'}} + run: python -m pytest python/tests/test_splines.py diff --git a/deps/AMICI/.gitignore b/deps/AMICI/.gitignore index cd7535983..60b9ff503 100644 --- a/deps/AMICI/.gitignore +++ b/deps/AMICI/.gitignore @@ -42,7 +42,7 @@ models/model_robertson/build/* !models/model_calvetti models/model_calvetti/build/* -amici_models/* +amici_models/ simulate_model_*_hdf.m simulate_model_*.m @@ -136,6 +136,7 @@ tests/test/* */tests/fricker_2010_apoptosis_amici/* */tests/explicit_amici/* */tests/fixed_initial_amici/* +*/tests/localfunc_amici/* tests/cpp/writeResults.h5 tests/cpp/writeResults.h5.bak tests/sbml-test-suite/* @@ -153,6 +154,7 @@ python/examples/example_presimulation/model_presimulation/* python/examples/example_presimulation/model_presimulation_re/* python/examples/example_constant_species/model_constant_species_reduced/* python/examples/example_constant_species/model_constant_species/* +python/examples/example_jax/amici_models/* python/tests/sbml_test_models/* python/tests/piecewise_test/* @@ -162,7 +164,9 @@ python/sdist/amici/amici.py python/sdist/amici/amici_wrap.cxx python/sdist/amici/amici_without_hdf5.py python/sdist/amici/amici_wrap_without_hdf5.cxx - +python/sdist/amici/include/ +python/sdist/amici/lib/ +python/sdist/amici/share/ python/sdist/build/* python/sdist/amici/git_version.txt @@ -176,6 +180,7 @@ ThirdParty/doxygen/* ThirdParty/mtocpp-master* ThirdParty/sundials/build/* ThirdParty/SuiteSparse/lib/* +ThirdParty/SuiteSparse/include/ ThirdParty/SuiteSparse/share/* ThirdParty/SuperLU_MT_3.1/ ThirdParty/superlu_mt_3.1.tar.gz @@ -191,6 +196,9 @@ tests/performance/CS_Signalling_ERBB_RAS_AKT/ tests/performance/CS_Signalling_ERBB_RAS_AKT_petab/* coverage_SBMLSuite.xml Benchmark-Models-PEtab/ - +/test_bmc/ CS_Signalling_ERBB_RAS_AKT/ +cache_fiddy/* +debug/* +tests/benchmark-models/cache_fiddy/* diff --git a/deps/AMICI/.gitrepo b/deps/AMICI/.gitrepo index 42eeb0939..16993f56c 100644 --- a/deps/AMICI/.gitrepo +++ b/deps/AMICI/.gitrepo @@ -5,8 +5,8 @@ ; [subrepo] remote = git@github.com:ICB-DCM/AMICI.git - branch = v0.11.23 - commit = 287364e9360413e8bdfa8d3d3c6bff38ced44077 - parent = 0f1aeda07794e0c6736ead9908cac2b46697e8e5 - cmdver = 0.4.3 + branch = v0.20.0 + commit = ffcbf6652f0d9e28125647a471bd09bead78c537 + parent = 540e2f4f8a0f99cb949d948793dc297a58fbfe03 + cmdver = 0.4.6 method = merge diff --git a/deps/AMICI/.pre-commit-config.yaml b/deps/AMICI/.pre-commit-config.yaml new file mode 100644 index 000000000..521ff54f8 --- /dev/null +++ b/deps/AMICI/.pre-commit-config.yaml @@ -0,0 +1,30 @@ +# 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 + hooks: + - id: check-added-large-files + - id: check-merge-conflict + - id: check-yaml + args: [--allow-multiple-documents] + - id: end-of-file-fixer + - id: trailing-whitespace +- repo: https://github.com/psf/black + rev: 23.7.0 + 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"] + +exclude: '^(ThirdParty|models)/' diff --git a/deps/AMICI/.readthedocs.yml b/deps/AMICI/.readthedocs.yml index 3e75f7dc8..659015764 100644 --- a/deps/AMICI/.readthedocs.yml +++ b/deps/AMICI/.readthedocs.yml @@ -17,12 +17,11 @@ formats: python: install: - requirements: documentation/rtd_requirements.txt - + - requirements: documentation/rtd_requirements2.txt build: - os: "ubuntu-20.04" + os: "ubuntu-22.04" apt_packages: - # for custom doxygen - - libclang-cpp9 - - libclang1-9 + - libatlas-base-dev + - swig tools: - python: "3.8" + python: "3.9" diff --git a/deps/AMICI/CHANGELOG.md b/deps/AMICI/CHANGELOG.md index 7cf659e6d..4b511d92d 100644 --- a/deps/AMICI/CHANGELOG.md +++ b/deps/AMICI/CHANGELOG.md @@ -1,39 +1,745 @@ -# Changelog +# Changelog ## v0.X Series +### v0.20.0 (2023-11-23) + +**Fixes** + +* 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 +* Removed setuptools<64 restriction + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2180 +* Fixed ExpData equality operator for Python + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2194 +* Enabled deepcopy for ExpData(View) + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2196 +* Allowed subsetting simulation conditions in simulate_petab + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2199 +* Set CMake CMP0144 to prevent warning + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2209 + +**Features** + +* Possibility to evaluate and plot symbolic expressions based on simulation results + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2152 +* Easier access to timepoints via ExpDataView + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2193 +* Nicer `__repr__` for ReturnDataView + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2192 + +**Documentation** + +* Added installation instructions for Arch Linux + by @stephanmg in https://github.com/AMICI-dev/AMICI/pull/2173 +* Updated reference list + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2172 +* Installation guide: optional requirements + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2207 + +**Full Changelog**: https://github.com/AMICI-dev/AMICI/compare/v0.19.0...v0.20.0 + + +### v0.19.0 (2023-08-26) + +**Features** +* SBML import now supports `rateOf` + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2120 +* Added `Model.{get,set}SteadyStateComputationMode` (analogous to `SteadyStateSensitivityMode`) + which allows to choose how steady state is computed. + by @plakrisenko in https://github.com/AMICI-dev/AMICI/pull/2074 + + **Note: The default `SteadyStateSensitivityMode` changed from `newtonOnly` to `integrateIfNewtonFails`.** + +* SBML import: Allow hardcoding of numerical values + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2134 +* Added `antimony2amici` for more convenient antimony import + (simplifies working with raw ODEs, see documentation) + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2142 +* Added `AMICI_TRY_ENABLE_HDF5` environment variable to control whether to search for HDF5 or not + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2148 + +**Fixes** + +* Fixed SBML import for events with trigger functions depending on parameters that are initial + assignment targets + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2145 +* Fixed SBML import for event-assigned parameters with non-float initial assignments + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2156 +* Fixed `unistd.h` dependency of `hdf5.cpp` that led to compilation + failures on Windows + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2154 +* Set CMake policies for cmake 3.27 by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2162 +* Fixed a `lib/` vs `lib64/` issue, leading to `SUNDIALSConfig.cmake`-not-found issues + on some systems + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2165 +* CMake: fixed scope of `-DHAS_BOOST_CHRONO` which may have lead to a mix of + `boost::chrono::thread_clock` and `std::clock` being used in programs using amici, + and potentially segmentation faults + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2163 + +Performance: +* Combined code for sparse model functions and their index files for slightly faster + compilation of small models + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2159 + +* Removed complex / complex long KLU functions for slightly faster amici package installation + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2160 + +**Full Changelog**: https://github.com/AMICI-dev/AMICI/compare/v0.18.1...v0.19.0 + + +### v0.18.1 (2023-06-26) + +Fixes: +* Fixed pysb pattern matching during PEtab import + by @FFroehlich in https://github.com/AMICI-dev/AMICI/pull/2118 +* Fixed `sp.Matrix` errors with `numpy==1.25` + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2124 +* Readme: added info containers + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2125 +* Fixed deprecation warnings + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2122 + https://github.com/AMICI-dev/AMICI/pull/2131 +* Fixed logging typo in SBML import + by @dilpath in https://github.com/AMICI-dev/AMICI/pull/2126 +* Added minimum version for `pandas` + by @dilpath in https://github.com/AMICI-dev/AMICI/pull/2129 + +**Full Changelog**: https://github.com/AMICI-dev/AMICI/compare/v0.18.0...v0.18.1 + +### v0.18.0 (2023-05-26) + +Features: +* More efficient handling of splines in SBML models + by @paulstapor, @lcontento, @dweindl + in https://github.com/AMICI-dev/AMICI/pull/1515 +* Partial support of current PEtab2.0 draft, including support for PySB models + by @dweindl, @FFroehlich in https://github.com/AMICI-dev/AMICI/pull/1800 + +Fixes: +* **Fixed incorrect forward sensitivities for models with events with** + **state-dependent trigger functions** + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2084 +* Model import: Don't create spl.h and sspl.h for models without splines + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2088 +* SBML import - faster processing of SpeciesReference IDs + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2094 +* Update swig ignores + by @FFroehlich in https://github.com/AMICI-dev/AMICI/pull/2098 +* CMake: Fixed choosing SWIG via `SWIG` env variable + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2100 +* CMake: Try FindBLAS if no other information was provided + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2104 +* Fixed cblas error for models without solver states in combination with + forward sensitivities + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2108 +* Fixed compilation error for models with events and xdot=0 + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2111 +* Fixed import error for models with events and 0 states + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2112 + +**Full Changelog**: https://github.com/AMICI-dev/AMICI/compare/v0.17.1...v0.18.0 + +### v0.17.1 (2023-05-10) + +This release fixes two bugs: + +* One bug introduced in v0.17.0, that causes an `ImportError` + on macOS (https://github.com/AMICI-dev/AMICI/issues/2075). +* An AttributeError in petab_import_pysb with petab>=0.2.0 + https://github.com/AMICI-dev/AMICI/pull/2079 + +### v0.17.0 (2023-05-09) + +AMICI v0.17.0 requires Python>=3.9 and a C++17 compatible compiler + +Features +* DAE support in SBML + by @FFroehlich in https://github.com/AMICI-dev/AMICI/pull/2017 +* SBML import: flatten SBML-comp models + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2063 +* Added sllh computation back to `petab_objective.simulate_petab` + by @dilpath in https://github.com/AMICI-dev/AMICI/pull/1548 +* CMake-based Python extension builds + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1992 + +Fixes +* Fixed CPU time tracking with multi-threading (partially) + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2023 +* Fixed HDF5 ambiguous overload + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2031 +* Fixed varying cmake libdir lib(64)/ + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2033 +* Fixed Equilibration cpu time computation + by @plakrisenko in https://github.com/AMICI-dev/AMICI/pull/2035 +* CMake: add header files to library sources for generated models + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2047 +* CMake: Handle header-dependency of swig files + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2046 +* Don't try to detect conservation laws for models with Species-AssignmentRules + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2056 +* Smith benchmark and SBML initialization fix + by @FFroehlich in https://github.com/AMICI-dev/AMICI/pull/2034 +* SBML import: Fixed check for required packages + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2064 +* Nan observables + by @FFroehlich in https://github.com/AMICI-dev/AMICI/pull/2065 +* Fixed check for discontinuities for conservation law computation + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2068 +* Specify visualization dependencies + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2070 +* Fixed sympy symbol name clashes during PEtab import + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2069 +* Fixed ReturnData::{preeq_wrms,posteq_wrms} with FSA and check_sensi_steadystate_conv_=True + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2071 + +Extended / updated documentation, for example: +* Jax example notebook + by @FFroehlich in https://github.com/AMICI-dev/AMICI/pull/1996 +* Updated Windows/MSVC installation instructions + by @Podde1 in https://github.com/AMICI-dev/AMICI/pull/2053 + +New Contributors +* @Podde1 made their first contribution in https://github.com/AMICI-dev/AMICI/pull/2053 + +**Full Changelog**: https://github.com/AMICI-dev/AMICI/compare/v0.16.1...v0.17.0 + + +### v0.16.1 (2023-02-24) + +Fixes: +* Additional package names for finding blas via pkg-config + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1959 +* Changed default interpolation type from hermite to polynomial + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1960 +* PySB import: Change default simplify to work with multiprocessing + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1961 +* Add --no-validate to amici_import_petab + @dweindl in https://github.com/AMICI-dev/AMICI/pull/1963 +* Fix get_model for direct import of swig interface + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1969 +* Fix PytestReturnNotNoneWarning in test_conserved_quantities_demartino.py + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1968 +* Fix MSVC builds / remove -W* flags + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1972 +* Add option to use IDs when plotting trajectories + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1974 +* Fix assignmentRules2observables - skip non-assignment-rule targets + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1973 +* Use std::clock for measuring solver time + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1982 + (*Note that this uses cpu-time consumed by all threads*) +* Fix narrowing-conversion-warning + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1983 +* PEtab import: allow specifying default values for output parameters + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1987 +* Print stacktraces only with debug logging level + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1985 +* Change default ReturnData::status to AMICI_NOT_RUN + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1984 +* Reduce time-tracking overhead + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1988 +* Fix equilibraton status discrepancy + by @plakrisenko in https://github.com/AMICI-dev/AMICI/pull/1991 +* Pass model_name to _create_model_output_dir_name + by @FFroehlich in https://github.com/AMICI-dev/AMICI/pull/1994 +* CMake: Build with OpenMP support if available + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2000 +* Fix SuiteSparse Makefiles for compiler-paths + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2003 +* CMake: Build with HDF5 support if available + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1999 +* CMake: Fix reading version file on Windows + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2001 +* CMake: raise minimum required version to 3.15 + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2002 +* Fix/extend runtime logging + by @FFroehlich in https://github.com/AMICI-dev/AMICI/pull/2005 +* Fix error logging in steadystate solver + by @FFroehlich in https://github.com/AMICI-dev/AMICI/pull/2008 +* Don't pass `-py3` to swig after 4.1.0 + by @FFroehlich in https://github.com/AMICI-dev/AMICI/pull/2010 +* SWIG __repr__s for different templated vector classes + by @FFroehlich in https://github.com/AMICI-dev/AMICI/pull/2009 +* Matlab: If mex fails, print mex arguments + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2013 +* Simplify OpenBLAS installation on Windows + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2016 +* Remove model name prefix in generated model files + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2015 +* ... + +Documentation: +* Restructure sphinx doc + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1978 +* Instructions for AMICI with singularity + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1964 +* Illustrate options for potentially speeding up model import/simulation + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1965 +* ... + +Dependencies: +* Updated SuiteSparse to v7.0.1 + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2018 + +**Full Changelog**: https://github.com/AMICI-dev/AMICI/compare/v0.16.0...v0.16.1 + + +### v0.16.0 (2023-01-25) + +Features +* Python 3.11 compatibility + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1876 +* AMICI now runs on binder (https://mybinder.org/v2/gh/AMICI-dev/AMICI/develop?labpath=binder%2Foverview.ipynb) + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1935, + https://github.com/AMICI-dev/AMICI/pull/1937, + https://github.com/AMICI-dev/AMICI/pull/1939 +* More informative `Solver.__repr__` and `ExpData.__repr__` + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1928 + and @FFroehlich in https://github.com/AMICI-dev/AMICI/pull/1948 +* `simulate_petab` returns the generated/used `ExpData`s + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1933 +* Model module is now accessible from model instance + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1932 +* Added `plot_jacobian` + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1930 +* Now logs all nested execution times as debug + by @FFroehlich in https://github.com/AMICI-dev/AMICI/pull/1947 +* Always check for finite initial states, not only with + `Model.setAlwaysCheckFinite(True)` + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1955 + +Fixes +* `ReturnDataView.status` now returns `int` instead of `float` + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1929 +* Updated simulation status codes + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1931 +* Skip irrelevant frames in stacktraces + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1934 +* Fixed compiler warning (matlab) + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1954 + +Documentation: +* Added a notebook demonstrating common simulation failures and show how to + analyze / fix them + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1946 +* various minor fixes / updates + +**Full Changelog**: https://github.com/AMICI-dev/AMICI/compare/v0.15.0...v0.16.0 + + +### v0.15.0 (2023-01-11) + +Features +* Improved logging by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1907 + + For Python: Don't print messages to stdout, but collect them in ReturnData + and forward them to python logging, making it easier to filter specific + messages or to disable output completely. Messages are also available via + `ReturnData.messages`. + + **breaking change for C++ interface**: + Messages aren't printed to stdout by default, but are collected in + `ReturnData`. The user has to decide what to do with them. + +* MultiArch docker build by @FFroehlich + in https://github.com/AMICI-dev/AMICI/pull/1903 +* Added cmake target for cmake-format + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1909 +* Updated clang-format style, fixed clang-format target + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1908 +* Subsetting `ReturnData` fields by ID via `ReturnDataView.by_id` + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1911 https://github.com/AMICI-dev/AMICI/pull/1916 + +Fixes +* PEtab import: fixed handling of fixed parameters for rule targets + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1915 +* Fixed compiler warnings for matlab interface + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1919 +* Fixed pandas DeprecationWarning for Series.iteritems() + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1921 +* Fixed circular import in amici.petab_import_pysb + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1922 +* Fixed 'operator ==' swig warning + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1923 +* Prevent swig4.0.1 segfault + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1924 + +**Full Changelog**: https://github.com/AMICI-dev/AMICI/compare/v0.14.0...v0.15.0 + + +### v0.14.0 (2022-11-23) + +#### Features: + +* Added optional functionality to apply C99 math optimization to generated C++ code + by @dweindl and @lcontento in https://github.com/AMICI-dev/AMICI/pull/1377, https://github.com/AMICI-dev/AMICI/pull/1878 + +* Added option to treat fixed parameters as constants in PEtab import + + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1877 + +* Added equality operator for ExpData + + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1881 + +* Updated base image for Dockerfile to Ubuntu 22.04/Python 3.10 + + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1896 + + +#### Fixes: + +* Fixed deprecation warnings + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1873, https://github.com/AMICI-dev/AMICI/pull/1893 + +* Fixes/updates to GitHub actions + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1885, https://github.com/AMICI-dev/AMICI/pull/1893, https://github.com/AMICI-dev/AMICI/pull/1889, https://github.com/AMICI-dev/AMICI/pull/1891 + +* Added hdf5 search directories for arm64 architecture (M1/M2 macs) + + by @Doresic in https://github.com/AMICI-dev/AMICI/pull/1894 + +* Fixed missing return in generated non-void functions + + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1892 + +* Fixed import failure for pre-compiled models + + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1897 + +#### Documentation: + +* Update reference list + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1874, https://github.com/AMICI-dev/AMICI/pull/1884 + +**Full Changelog**: +https://github.com/AMICI-dev/AMICI/compare/v0.13.0...v0.14.0 + +### v0.13.0 (2022-10-04) + +* Fixed extraction of common subexpressions + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1865 +* Added function to convert `ReturnData::status` flags to string + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1864 + +And further contributions by @dweindl, @FFroehlich + +**Full Changelog**: +https://github.com/AMICI-dev/AMICI/compare/v0.12.0...v0.13.0 + +### v0.12.0 (2022-08-26) + +Features: +* Support for event observables via the Python interface + by @FFroehlich in https://github.com/AMICI-dev/AMICI/pull/1845 +* Treat non-estimated parameters as constants during SBML-PEtab import + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1810 +* Updated SUNDIALS to v5.8.0 + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1836 +* Option to extract common subexpressions + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1852, + https://github.com/AMICI-dev/AMICI/pull/1856 + **not available in this release, use v0.13.0** +* Parallelize matrix simplification + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1778 +* Validate PEtab problems before attempting import + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1842 +* Improved type annotations for the swig interface + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1860 + +Fixes: +* Fixed an issue with potentially infinite loops during conservation law + processing by @FFroehlich in https://github.com/AMICI-dev/AMICI/pull/1833 +* Fixed potential deadlocks during parallel simplification + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1844 +* Fix resetting `ReturnData::numstepsB` when re-using Solver + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1841 + +And further contributions by @dilpath, @dweindl, @FFroehlich + +**Full Changelog**: +https://github.com/AMICI-dev/AMICI/compare/v0.11.32...v0.12.0 + +### v0.11.32 (2022-07-15) + +Fixes: +* Fixed `ImportError`s during package installation with recent setuptools + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1830 + +### v0.11.31 (2022-07-12) + +Fixes: +* Fixed `ParameterMapping.__getitem__` to either return a + `ParameterMappingForCondition` or a new `ParameterMapping`, but not a list + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1826 + +### v0.11.30 (2022-07-07) + +Features: +* Allow overriding model name during PySB import by @dweindl in + https://github.com/AMICI-dev/AMICI/pull/1801 +* Added __repr__ for parameter mapping classes by @dweindl in + https://github.com/AMICI-dev/AMICI/pull/1799 +* More informative warning messages for NaNs/Infs by @dweindl in + https://github.com/AMICI-dev/AMICI/pull/1798 +* Moved `sim_steps` increment by @plakrisenko in + https://github.com/AMICI-dev/AMICI/pull/1806 +* Re-arranged application of parameters from `ExpData` to avoid initial + sensitivities error by @dilpath in + https://github.com/AMICI-dev/AMICI/pull/1805 +* Checking for unused parameters in `simulate_petab` by @dweindl in + https://github.com/AMICI-dev/AMICI/pull/1816 +* Add `create_parameter_mapping` kwarg forwarding by @dweindl in + https://github.com/AMICI-dev/AMICI/pull/1820 + +Other +* Remove `constant_species_to_parameters` by @dweindl in + https://github.com/AMICI-dev/AMICI/pull/1809 + +Fixes +* Fixed handling of SBML models given as `pathlib.Path` in + `petab_import.import_model_sbml by @dweindl in + https://github.com/AMICI-dev/AMICI/pull/1808 +* Fixed missing CPU time reset by @dweindl in + https://github.com/AMICI-dev/AMICI/pull/1814 +* Raise in `simulate_petab` with `scaled_parameters=True` + `problem_parameters=None` by @dweindl in + https://github.com/AMICI-dev/AMICI/pull/1819 + +... + +**Full Changelog**: +https://github.com/AMICI-dev/AMICI/compare/v0.11.29...v0.11.30 + +### v0.11.29 (2022-05-06) + +## What's Changed + +Features: +* Performance: Limit newton step convergence check by @FFroehlich in + https://github.com/AMICI-dev/AMICI/pull/1780 +* More informative NaN/Inf warnings by @dweindl in + https://github.com/AMICI-dev/AMICI/pull/1640 +* SBML import can now handle initial events by @FFroehlich in + https://github.com/AMICI-dev/AMICI/pull/1789 + +Fixes: +* Avoid error if no measurements in PEtab problem; fixed type handling in + PEtab parameter mapping by @dilpath in + https://github.com/AMICI-dev/AMICI/pull/1783 +* Fixed substitution of expressions in root and stau by @dilpath in + https://github.com/AMICI-dev/AMICI/pull/1784 +* Workaround for PEtab problems with state-dependent noise models by @dweindl + in https://github.com/AMICI-dev/AMICI/pull/1791 + +**Full Changelog**: https://github.com/AMICI-dev/AMICI/compare/v0.11.28...v0.11.29 + + +### v0.11.28 (2022-04-08) + +New features: +* Added `Solver.setSteadyStateToleranceFactor` and + `Solver.setSteadyStateSensiToleranceFactor` to specify a steady state + tolerance factor by @dilpath in https://github.com/AMICI-dev/AMICI/pull/1758 + + **NOTE: This also relaxed the default steady state (sensitivity)** + **tolerances by a factor of 100.** +* Added support for `pathlib.Path` by @dweindl in + https://github.com/AMICI-dev/AMICI/pull/1769 +* Allow specifying initial timepoint with `ExpData` by @dilpath in + https://github.com/AMICI-dev/AMICI/pull/1776 + +Performance: +* Speedup for models with conservation laws by @FFroehlich in + https://github.com/AMICI-dev/AMICI/pull/1765 +* Improved efficiency of newton step convergence check by @FFroehlich in + https://github.com/AMICI-dev/AMICI/pull/1775 + +Fixes: +* Fixed deprecation warning for pandas.DataFrame.append in + `rdatas_to_measurement_df` by @dweindl in + https://github.com/AMICI-dev/AMICI/pull/1770 +* Fixed Rule-target handling in PEtab import by @dweindl in + https://github.com/AMICI-dev/AMICI/pull/1753 + +Removed functionality: +* Removed long deprecated `sbml2amici` arguments `modelName` + and `constantParameters` by @dweindl in + https://github.com/AMICI-dev/AMICI/pull/1774 + +**Full Changelog**: +https://github.com/AMICI-dev/AMICI/compare/v0.11.27...v0.11.28 + +### v0.11.27 (2022-04-04) + +New features: +* Checking condition number when computing sensitivities via Newton + by @FFroehlich in https://github.com/AMICI-dev/AMICI/pull/1730 +* Removed SPBCG solver by @FFroehlich in + https://github.com/AMICI-dev/AMICI/pull/1729 +* Added Newton step convergence checks to steadystate solver by @FFroehlich in + https://github.com/AMICI-dev/AMICI/pull/1737 +* Removed legacy options/members `amioption.newton_preeq` and `Solver::r… by + @dweindl in https://github.com/AMICI-dev/AMICI/pull/1744 +* Added `ReturnData::cpu_time_total` to track total time spent in + `runAmiciSimulation` by @dweindl in + https://github.com/AMICI-dev/AMICI/pull/1743 +* SBML import: Alternative algorithm for identifying conservation laws by + @dweindl in https://github.com/AMICI-dev/AMICI/pull/1748 +* Use `amici.AmiciVersionError` to indicate version mismatch by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1764 + +Performance: +* Optional parallel computation of derivatives during model import by @dweindl + in https://github.com/AMICI-dev/AMICI/pull/1740 +* Sparsify jacobian by @FFroehlich in https://github.com/AMICI-dev/AMICI/pull/1766 +* Speedup conservation law computation by @FFroehlich in + https://github.com/AMICI-dev/AMICI/pull/1754 +* Exploit stoichiometric matrix in pysb import by @FFroehlich in + https://github.com/AMICI-dev/AMICI/pull/1761 +* Speedup edata construction from petab problems by @FFroehlich in + https://github.com/AMICI-dev/AMICI/pull/1746 + +Fixes: +* Fixed `get_model_settings` that would to setting incorrect initial states and + initial state sensitivities for models with parameter-dependent initial + states by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1751 +* Use correct tolerances for convergence check in Newton solver by @FFroehlich + in https://github.com/AMICI-dev/AMICI/pull/1728 +* Harmonized convergence checks by @FFroehlich in + https://github.com/AMICI-dev/AMICI/pull/1731 +* Made sundials' KLU_INDEXTYPE match actual klu index type by @dweindl in + https://github.com/AMICI-dev/AMICI/pull/1733 +* Fixed `Model::setStateIsNonNegative` logic that would raise exceptions in + cases where it shouldn't by @dweindl in + https://github.com/AMICI-dev/AMICI/pull/1736 +* Fixed undefined reference to dladdr by @kristianmeyerr in + https://github.com/AMICI-dev/AMICI/pull/1738 +* Fixed HDF5 OSX intermediate group creation errors by @dweindl in + https://github.com/AMICI-dev/AMICI/pull/1741 +* Fixed recent cmake-based build issues due to changed sundials library + directory by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1756 +* Updated Windows installation instructions by @paulflang in + https://github.com/AMICI-dev/AMICI/pull/1763 + +... and other contributions by @FFroehlich, @dweindl + +**Full Changelog**: +https://github.com/AMICI-dev/AMICI/compare/v0.11.26...v0.11.27 + +### v0.11.26 (2022-03-14) + +New features: +* Import of BioNetGenLanguage (BNGL) models by @FFroehlich in + https://github.com/AMICI-dev/AMICI/pull/1709 +* Added support for observable-dependent sigmas by @dweindl, @FFroehlich in + https://github.com/AMICI-dev/AMICI/pull/1692 +* Added support for pysb local functions by @FFroehlich in + https://github.com/AMICI-dev/AMICI/pull/1666 +* Added experimental support for conservation laws for non-constant species to + SBML import: conservation laws for non-constant species + by @stephanmg, @dweindl in https://github.com/AMICI-dev/AMICI/pull/1669 + Enable this feature by setting environment variable + `AMICI_EXPERIMENTAL_SBML_NONCONST_CLS` to any value + * Allow using states eliminated by conservation laws to be used in root + functions by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1677 + * Added support for parameter-dependent conservation laws by @dweindl, + @FFroehlich in https://github.com/AMICI-dev/AMICI/pull/1678 +* Added optional caching for symbolic simplifications in ODE export by @dilpath + in https://github.com/AMICI-dev/AMICI/pull/1672 +* Added CLI option `--no-sensitivities` to `amici_import_petab` by @dweindl in + https://github.com/AMICI-dev/AMICI/pull/1688 + +Fixes: +* SBML import: Raise in case of nested observables by @dweindl in + https://github.com/AMICI-dev/AMICI/pull/1690 +* Sympy 1.10 compatibility by @dweindl in + https://github.com/AMICI-dev/AMICI/pull/1694 + +**Full Changelog**: https://github.com/AMICI-dev/AMICI/compare/v0.11.25...v0.11.26 + +### v0.11.25 (2022-02-09) + +* Fixed a bug + where `Model::setStateIsNonNegative(Model::getStateIsNonNegative())` would + raise an exception in case conservation laws were enabled - by @dweindl + in https://github.com/AMICI-dev/AMICI/pull/1648 +* Fixed a bug where `Model::setStateIsNonNegative` would be ignored in certain + model expressions - by @FFroehlich + in https://github.com/AMICI-dev/AMICI/pull/1650 +* Fixed a bug where special function parsing inside `min()` and `max()` would + not be parsed correctly - by @dweindl + in https://github.com/AMICI-dev/AMICI/pull/1655 +* Fixed a numpy dependency issues for Mac+ARM systems - by @dweindl + in https://github.com/AMICI-dev/AMICI/pull/1657 +* Fixed convergence check in Newton method - by @plakrisenko + in https://github.com/AMICI-dev/AMICI/pull/1663 +* Add `AMICI_CXX_OPTIONS` to pass libamici-specific compiler options during + CMake-based builds - by @dweindl + in https://github.com/AMICI-dev/AMICI/pull/1664 +* Fixed various warnings and updated documentation - by @dweindl + +**Full Changelog**: +https://github.com/AMICI-dev/AMICI/compare/v0.11.24...v0.11.25 + +### v0.11.24 (2022-02-01) + +Features: +* Introduced environment variable `AMICI_DLL_DIRS` to control DLL directories + on Windows (useful for setting BLAS library directory, as required by + Python>=3.8) by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1637 +* Dropped Python3.7 support by @dweindl in + https://github.com/AMICI-dev/AMICI/pull/1635 +* Include header files in CMake targets for better IDE integration by @dweindl + in https://github.com/AMICI-dev/AMICI/pull/1639 + +Fixes: +* Fixed an issue in PEtab import where all-integer parameters would previously + result in a TypeError by @stephanmg in + https://github.com/AMICI-dev/AMICI/pull/1634 +* Fixed tempdir deletion issues for test suite on Windows by @dweindl in + https://github.com/AMICI-dev/AMICI/pull/1636 +* Added functions to provide state IDs/names for x_solver by @dweindl in + https://github.com/AMICI-dev/AMICI/pull/1638 +* Fixed docs on RTD by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1643 + +**Full Changelog**: https://github.com/AMICI-dev/AMICI/compare/v0.11.23...v0.11.24 + ### v0.11.23 (2022-01-11) Features: * Added overload for Model::setParameterScale with vector by @dilpath in https://github.com/AMICI-dev/AMICI/pull/1614 -* Removed assert_fun argument from gradient checking, improve output +* Removed assert_fun argument from gradient checking, improve output by @dweindl, @FFroehlich in https://github.com/AMICI-dev/AMICI/pull/1609 * Added get_expressions_as_dataframe by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1621 -* Added `id` field to ExpData and ReturnData by @dweindl in +* Added `id` field to ExpData and ReturnData by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1622 -* Included condition id in dataframes by @dweindl in +* Included condition id in dataframes by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1623 Fixes: -* C++: Fixed SUNMatrixWrapper ctor for size 0 matrices by @dweindl in +* C++: Fixed SUNMatrixWrapper ctor for size 0 matrices by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1608 -* Python: Handle TemporaryDirectory cleanup failures on Windows by @dweindl in +* Python: Handle TemporaryDirectory cleanup failures on Windows by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1617 -* Python: pysb.Model.initial_conditions throws a DeprecationWarning by +* Python: pysb.Model.initial_conditions throws a DeprecationWarning by @PaulJonasJost in https://github.com/AMICI-dev/AMICI/pull/1620 * Fixed wrong array size in warnings by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1624 - -NOTE: AMICI 0.11.23 requires numpy<1.22.0 + +NOTE: AMICI 0.11.23 requires numpy<1.22.0 **Full Changelog**: https://github.com/AMICI-dev/AMICI/compare/v0.11.22...v0.11.23 ### v0.11.22 (2021-12-02) -* **Require sympy>=1.9,pysb>=1.13.1** by @FFroehlich, @dweindl +* **Require sympy>=1.9,pysb>=1.13.1* by @FFroehlich, @dweindl in https://github.com/AMICI-dev/AMICI/pull/1599 * Fixed sympy deprecation warning by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1600 @@ -61,7 +767,7 @@ New: ### v0.11.20 (2021-11-12) -New: +New: * Changed parameter mappings such that unassigned values have non-nan default values. This fixes erroneous evaluation of `llh` as `NaN` in some situations (#1574) * Added support for Python 3.10 (#1555) @@ -195,7 +901,7 @@ Misc: Breaking changes: * AMICI requires Python>=3.7 -* Updated package installation (PEP517/518): +* Updated package installation (PEP517/518): Creating source distributions requires https://github.com/pypa/build (#1384) (but now handles all package building dependencies properly) @@ -229,7 +935,7 @@ Other: ### v0.11.12 (2021-01-26) -Features: +Features: * Add expression IDs and names to generated models (#1374) Fixes: @@ -317,7 +1023,7 @@ Bugfix release that restores compatibility with sympy 1.7 * Overload python interface functions for amici.{Model,Solver,ExpData} and amici.{Model,Solver,ExpData}Ptr (#1271) #### C++ -* Fix and extend use of sparse matrix operations (#1230, #1240, #1244, #1247, #1271) +* Fix and extend use of sparse matrix operations (#1230, #1240, #1244, #1247, #1271) * **Fix application of maximal number of steps**, MaxNumStep parameter now limit total number of steps, not number of steps between output times. (#1267) #### Doc @@ -348,7 +1054,7 @@ Bugfix release that restores compatibility with sympy 1.7 * Create sdist on GHA using swig4.0.1 (#1204) (Fixing broken pypi package) * Fix links after repository move * Speed-up swig build: disable all languages except python (#1211) -* Fix doc generation on readthedocs (#1196) +* Fix doc generation on readthedocs (#1196) ### v0.11.5 (2020-08-07) @@ -412,7 +1118,7 @@ Bugfix release that restores compatibility with sympy 1.7 #### Python * Upgrade to sympy 1.6.0, which is now required minimum version (#1098, #1103) -* Speed up model import +* Speed up model import * Speed-up computation of sx0, reduce file size (#1109) * Replace terribly slow sympy.MutableDenseMatrix.is_zero_matrix by custom implementation (#1104) * speedup dataframe creation in `get*AsDataFrame` (#1088) @@ -538,11 +1244,11 @@ CI: ### v0.10.17 (2020-01-15) -- **added python 3.8 support, dropped python 3.6 support** (#898) +- **added python 3.8 support, dropped python 3.6 support** (#898) - Added logging functionality (#900) - Fixes PySB import (#879, #902) - Fixes symbolic processing (#899) -- Improved build scripts (#894, +- Improved build scripts (#894, - Improved petab support (#886, #888, #891) - CI related fixes (#865, #896) @@ -565,15 +1271,15 @@ No other changes. **NOTE: For Python-imported SBML-models this release may compute incorrect sensitivities w.r.t. sigma. Bug introduced in 0.10.14, fixed in 0.10.15.** -Python: +Python: * Don't require use of ModelPtr.get to call ExpData(Model) * Fix import in generated model Python package * Setup AMICI standalone scripts as setuptools entrypoints * Simplify symbolic sensitivity expressions during Python SBML import Fixes Infs in the Jacobian when using Hill-functions with states of 0.0. -* Extended Newton solver #848 - The changes that allow performing Newton tests from the paper: +* Extended Newton solver #848 + The changes that allow performing Newton tests from the paper: G. T. Lines, Ł. Paszkowski, L. Schmiester, D. Weindl, P. Stapor, and J. Hasenauer. Efficient computation of steady states in large-scale ODE models of biochemical reaction networks. accepted for Proceedings of the 8th IFAC Conference on Foundations of Systems Biology in Engineering (FOSBE), Valencia, Spain, October 2019. * Use SWIG>=4.0 on travis to include PyDoc in sdist / pypi package (#841) * **Fix choice of likelihood formula; failed if observable names were not equal to observable IDs** @@ -618,8 +1324,8 @@ Misc: ### v0.10.11 (2019-08-31) -* Fixed setting initial conditions for preequilibration (#784) -* Fixed species->parameter conversion during PEtab import (#782) +* Fixed setting initial conditions for preequilibration (#784) +* Fixed species->parameter conversion during PEtab import (#782) * Set correct Matlab include directories in CMake (#793) * Extended and updated documentation (#785, #787) * Fix various SBML import issues @@ -629,7 +1335,7 @@ Misc: * Simplify/fix AMICI installation * If available use environment modules to detect dependencies - + * Add SWIG installation script * Update list of publication @@ -651,7 +1357,7 @@ Detaills: * feature(python) Use MKL from environment modules to provide cblas * fix(python) Fix define_macros not being passed to setuptools for Extension * fix(python) Fix define_macros not being passed to setuptools for clibs - * Do not always add 'cblas' library since users may want to override that by a cblas-compatible library with a different name (closes #736) + * Do not always add 'cblas' library since users may want to override that by a cblas-compatible library with a different name (closes #736) * Update HDF5 path hints; use shared library if static is not available. * Check for HDF5_BASE from environment module * Fix system-dependent sundials library directory (Fixes #749) (#750) @@ -660,7 +1366,7 @@ Detaills: * Improve finding swig executable and allow user override via SWIG environment variable * Provide installation hints if no SWIG found (Closes #724) * Allow overriding cmake executable with environment variables in build scripts (Closes #738) - + ### v0.10.9 (2019-07-24) @@ -675,7 +1381,7 @@ All: - Fix reuse of `Solver` instances (#541) C++: -- Check for correct AMICI version for model in CMake +- Check for correct AMICI version for model in CMake - Add reporting of computation times (#699) Python: @@ -712,12 +1418,12 @@ Doc C++ - Fix missing source files in CMakeLists.txt (#658) - Set CMake policies to prevent warnings (Closes #676) (#677) -- Start using gsl::span instead of raw pointers (#393) (#678) +- Start using gsl::span instead of raw pointers (#393) (#678) Python - PySB parsing fix (#669) -- Fix failure to propagate BLAS_LIBS contents (#665) -- Require setuptools at setup (#673) +- Fix failure to propagate BLAS_LIBS contents (#665) +- Require setuptools at setup (#673) - Updated PEtab import to allow for different noise models @@ -790,7 +1496,7 @@ Bugfixes: Maintenance: -- use newer CI images +- use newer CI images ### v0.9.4 (2019-02-11) @@ -822,9 +1528,9 @@ Bugfixes: - fixes a critical bug in the newton solver - fixes multiple bugs in sbml import for degenerate models, empty stoichiometry assignments and conversion factors - improved error messages for sbml import -- #560 -- #557 -- #559 +- #560 +- #557 +- #559 ### v0.9.1 (2019-01-21) @@ -853,7 +1559,7 @@ Features / improvements: - Allow more detailed finiteness checks (#514) Bugfixes: - - #491 + - #491 Maintenance: - Several improvements to travis log sizes and folding @@ -910,7 +1616,7 @@ Maintenance: ### v0.7.11 (2018-10-15) - [python] Added numpy and python wrappers that provide a more user friendly python API -- [python] Enable import of SBML models with non-float assignment rules +- [python] Enable import of SBML models with non-float assignment rules - [python] Enable handling of exceptions in python - [python] Enable nativ python access to std::vector data-structures - [core] Provide an API for more fine-grained control over sensitivity tolerances and steady-state tolerances @@ -990,7 +1696,7 @@ Features: Major bugfixes: - Fix python sbml model import / compilation error (undefined function) -- Fix model preequilibration +- Fix model preequilibration Minor fixes: - Various fixes for mingw compilation of python source distribution @@ -1020,8 +1726,8 @@ WARNING: Implement experimental support for python via swig. Python interface is now usable, but API will still receive some updates in the future. -WARNING: -- There is a bug in sensitivity computation for Python-generated models +WARNING: +- There is a bug in sensitivity computation for Python-generated models - Matlab C++ compilation will fail due to undefined M_PI -> Please use v0.7.0 diff --git a/deps/AMICI/CITATION.cff b/deps/AMICI/CITATION.cff index c5e6d8d6f..d25165803 100644 --- a/deps/AMICI/CITATION.cff +++ b/deps/AMICI/CITATION.cff @@ -1,32 +1,32 @@ authors: - - + - family-names: "Fröhlich" given-names: "Fabian" orcid: "https://orcid.org/0000-0002-5360-4292" - - + - family-names: "Weindl" given-names: "Daniel" orcid: "https://orcid.org/0000-0001-9963-6057" - - + - family-names: "Schälte" given-names: "Yannik" orcid: "https://orcid.org/0000-0003-1293-820X" - - + - family-names: "Pathirana" given-names: "Dilan" orcid: "https://orcid.org/0000-0001-7000-2659" - - + - family-names: "Paszkowski" given-names: "Lukasz" - - + - family-names: "Lines" given-names: "Glenn Terje" orcid: "https://orcid.org/0000-0002-6294-1805" - - + - family-names: "Stapor" given-names: "Paul" orcid: "https://orcid.org/0000-0002-7567-3985" - - + - family-names: "Hasenauer" given-names: "Jan" orcid: "https://orcid.org/0000-0002-4935-3312" @@ -42,34 +42,34 @@ preferred-citation: start: 1 end: 1 authors: - - + - family-names: "Fröhlich" given-names: "Fabian" orcid: "https://orcid.org/0000-0002-5360-4292" - - + - family-names: "Weindl" given-names: "Daniel" orcid: "https://orcid.org/0000-0001-9963-6057" - - + - family-names: "Schälte" given-names: "Yannik" orcid: "https://orcid.org/0000-0003-1293-820X" - - + - family-names: "Pathirana" given-names: "Dilan" orcid: "https://orcid.org/0000-0001-7000-2659" - - + - family-names: "Paszkowski" given-names: "Lukasz" - - + - family-names: "Lines" given-names: "Glenn Terje" orcid: "https://orcid.org/0000-0002-6294-1805" - - + - family-names: "Stapor" given-names: "Paul" orcid: "https://orcid.org/0000-0002-7567-3985" - - + - family-names: "Hasenauer" given-names: "Jan" orcid: "https://orcid.org/0000-0002-4935-3312" diff --git a/deps/AMICI/CMakeLists.txt b/deps/AMICI/CMakeLists.txt index 09d464352..d6f08a509 100644 --- a/deps/AMICI/CMakeLists.txt +++ b/deps/AMICI/CMakeLists.txt @@ -1,276 +1,462 @@ # # Build AMICI library # -cmake_minimum_required(VERSION 3.3) - -if(POLICY CMP0065) - cmake_policy(SET CMP0065 NEW) -endif(POLICY CMP0065) -if(POLICY CMP0077) - cmake_policy(SET CMP0077 NEW) -endif(POLICY CMP0077) -if(POLICY CMP0074) - # Use package_ROOT environment variables - cmake_policy(SET CMP0074 NEW) -endif(POLICY CMP0074) +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(amici) -set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) -set(CMAKE_POSITION_INDEPENDENT_CODE ON) -set(CMAKE_CXX_STANDARD 14) +# misc options +if(DEFINED ENV{AMICI_TRY_ENABLE_HDF5}) + option(AMICI_TRY_ENABLE_HDF5 "Build with HDF5 support if available?" + $ENV{AMICI_TRY_ENABLE_HDF5}) +else() + option(AMICI_TRY_ENABLE_HDF5 "Build with HDF5 support if available?" ON) +endif() +option(AMICI_PYTHON_BUILD_EXT_ONLY "Build only the Python extension?" OFF) +option(ENABLE_HDF5 "Build with HDF5 support?" OFF) +option(SUNDIALS_SUPERLUMT_ENABLE "Enable sundials SuperLUMT?" OFF) +option(EXPORT_PACKAGE "Export AMICI library to CMake package registry?" ON) +option(ENABLE_SWIG "Build AMICI swig library?" ON) +option(ENABLE_PYTHON "Create Python module?" ON) +option(BUILD_TESTS "Build integration tests?" ON) + +message(STATUS "CMAKE_CURRENT_SOURCE_DIR: ${CMAKE_CURRENT_SOURCE_DIR}") +message(STATUS "CMAKE_BINARY_DIR: ${CMAKE_BINARY_DIR}") +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!") - endif() + # 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!") + endif() endif() + +set(CMAKE_POSITION_INDEPENDENT_CODE ON) +set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(ENABLE_SWIG ON) # Compiler flags include(CheckCXXCompilerFlag) set(MY_CXX_FLAGS -Wall) -foreach(FLAG ${MY_CXX_FLAGS}) - unset(CUR_FLAG_SUPPORTED CACHE) - CHECK_CXX_COMPILER_FLAG(${FLAG} CUR_FLAG_SUPPORTED) +foreach(flag ${MY_CXX_FLAGS}) + unset(CUR_FLAG_SUPPORTED CACHE) + check_cxx_compiler_flag(${flag} CUR_FLAG_SUPPORTED) + if(${CUR_FLAG_SUPPORTED}) + string(APPEND CMAKE_CXX_FLAGS " ${flag}") + endif() +endforeach() + +if(MSVC) + set(MY_CXX_FLAGS + # hide copyright message + -nologo + # -wd4820: hide padding + -wd4820 + # hide unreferenced inline function removed + -wd4514 + # hide not inlined + -wd4710 + # hide auto-inlined + -wd4711 + # hide copy-ctor implicitly deleted + -wd4625 + # hide assignment-ctor implicitly deleted + -wd4626 + # hide spectre mitigation + -wd5045 + # hide enum-switch no explicitly handled + -wd4061) + foreach(flag ${MY_CXX_FLAGS}) + check_cxx_compiler_flag(${flag} CUR_FLAG_SUPPORTED) if(${CUR_FLAG_SUPPORTED}) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FLAG}") + string(APPEND CMAKE_CXX_FLAGS " ${flag}") endif() -endforeach(FLAG) + endforeach() +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() + +# Legacy environment variables from Python-sdist times +if(DEFINED ENV{AMICI_CXXFLAGS}) + message(STATUS "Appending flags from AMICI_CXXFLAGS: $ENV{AMICI_CXXFLAGS}") + add_compile_options("$ENV{AMICI_CXXFLAGS}") +endif() +if(DEFINED ENV{AMICI_LDFLAGS}) + message(STATUS "Appending flags from AMICI_LDFLAGS: $ENV{AMICI_LDFLAGS}") + link_libraries("$ENV{AMICI_LDFLAGS}") +endif() + +if(DEFINED ENV{SWIG}) + message(STATUS "Setting SWIG_EXECUTABLE to $ENV{SWIG} ($SWIG)") + unset(SWIG_VERSION CACHE) + unset(SWIG_DIR CACHE) + set(SWIG_EXECUTABLE $ENV{SWIG}) +endif() # find dependencies include(GNUInstallDirs) -option(ENABLE_HDF5 "Build with HDF5 support?" ON) +find_package(OpenMP) +find_package(Boost COMPONENTS chrono) + if(ENABLE_HDF5) - find_package(HDF5 COMPONENTS C HL CXX REQUIRED) - set(HDF5_LIBRARIES ${HDF5_HL_LIBRARIES} ${HDF5_C_LIBRARIES} ${HDF5_CXX_LIBRARIES}) + find_package( + HDF5 + COMPONENTS C HL CXX + REQUIRED) +elseif(AMICI_TRY_ENABLE_HDF5) + find_package(HDF5 COMPONENTS C HL CXX) endif() -set(SUITESPARSE_DIR "${CMAKE_SOURCE_DIR}/ThirdParty/SuiteSparse/") -set(SUITESPARSE_INCLUDE_DIRS "${SUITESPARSE_DIR}/include" "${CMAKE_SOURCE_DIR}/ThirdParty/sundials/src") -set(SUITESPARSE_LIBRARIES - ${SUITESPARSE_DIR}/KLU/Lib/libklu${CMAKE_STATIC_LIBRARY_SUFFIX} - ${SUITESPARSE_DIR}/COLAMD/Lib/libcolamd${CMAKE_STATIC_LIBRARY_SUFFIX} - ${SUITESPARSE_DIR}/BTF/Lib/libbtf${CMAKE_STATIC_LIBRARY_SUFFIX} - ${SUITESPARSE_DIR}/AMD/Lib/libamd${CMAKE_STATIC_LIBRARY_SUFFIX} - ${SUITESPARSE_DIR}/SuiteSparse_config/libsuitesparseconfig${CMAKE_STATIC_LIBRARY_SUFFIX} - ) - -set(SUNDIALS_LIB_DIR "${CMAKE_SOURCE_DIR}/ThirdParty/sundials/build/${CMAKE_INSTALL_LIBDIR}") -set(SUNDIALS_LIBRARIES - ${SUNDIALS_LIB_DIR}/libsundials_nvecserial${CMAKE_STATIC_LIBRARY_SUFFIX} - ${SUNDIALS_LIB_DIR}/libsundials_sunlinsolband${CMAKE_STATIC_LIBRARY_SUFFIX} - ${SUNDIALS_LIB_DIR}/libsundials_sunlinsolklu${CMAKE_STATIC_LIBRARY_SUFFIX} - ${SUNDIALS_LIB_DIR}/libsundials_sunlinsolpcg${CMAKE_STATIC_LIBRARY_SUFFIX} - ${SUNDIALS_LIB_DIR}/libsundials_sunlinsolspbcgs${CMAKE_STATIC_LIBRARY_SUFFIX} - ${SUNDIALS_LIB_DIR}/libsundials_sunlinsolspfgmr${CMAKE_STATIC_LIBRARY_SUFFIX} - ${SUNDIALS_LIB_DIR}/libsundials_sunmatrixband${CMAKE_STATIC_LIBRARY_SUFFIX} - ${SUNDIALS_LIB_DIR}/libsundials_sunmatrixdense${CMAKE_STATIC_LIBRARY_SUFFIX} - ${SUNDIALS_LIB_DIR}/libsundials_sunmatrixsparse${CMAKE_STATIC_LIBRARY_SUFFIX} - ${SUNDIALS_LIB_DIR}/libsundials_sunnonlinsolfixedpoint${CMAKE_STATIC_LIBRARY_SUFFIX} - ${SUNDIALS_LIB_DIR}/libsundials_sunnonlinsolnewton${CMAKE_STATIC_LIBRARY_SUFFIX} - ${SUNDIALS_LIB_DIR}/libsundials_cvodes${CMAKE_STATIC_LIBRARY_SUFFIX} - ${SUNDIALS_LIB_DIR}/libsundials_idas${CMAKE_STATIC_LIBRARY_SUFFIX} - ) -set(SUNDIALS_INCLUDE_DIRS "${CMAKE_SOURCE_DIR}/ThirdParty/sundials/build/include") +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/") +message(STATUS "Found SUNDIALS: ${SUNDIALS_DIR}") -option(SUNDIALS_SUPERLUMT_ENABLE "Enable sundials SuperLUMT?" OFF) -if(SUNDIALS_SUPERLUMT_ENABLE) - set(SUNDIALS_LIBRARIES ${SUNDIALS_LIBRARIES} - ${SUNDIALS_LIB_DIR}/libsundials_sunlinsolsuperlumt${CMAKE_STATIC_LIBRARY_SUFFIX} - ${CMAKE_SOURCE_DIR}/ThirdParty/SuperLU_MT_3.1/lib/libsuperlu_mt_PTHREAD${CMAKE_STATIC_LIBRARY_SUFFIX} - -lblas - ) - set(SUNDIALS_INCLUDE_DIRS ${SUNDIALS_INCLUDE_DIRS} - "${CMAKE_SOURCE_DIR}/ThirdParty/SuperLU_MT_3.1/SRC/") -endif() - -set(GSL_LITE_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/ThirdParty/gsl") +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(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() -else() - set(BLAS_INCLUDE_DIRS "" CACHE STRING "") - set(BLAS_LIBRARIES -lcblas CACHE STRING "") + 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_definitions(-DAMICI_BLAS_${BLAS}) +add_compile_definitions(AMICI_BLAS_${BLAS}) # Add target to create version file add_custom_target( - version - ${CMAKE_COMMAND} -D SRC=${CMAKE_SOURCE_DIR}/include/amici/version.in.h - -D DST=${CMAKE_BINARY_DIR}/include/amici/version.h - -P ${CMAKE_SOURCE_DIR}/cmake/configureVersion.cmake - ) + version + ${CMAKE_COMMAND} + -D + SRC=${CMAKE_CURRENT_SOURCE_DIR}/include/amici/version.in.h + -D + DST=${CMAKE_CURRENT_BINARY_DIR}/include/amici/version.h + -P + ${CMAKE_CURRENT_SOURCE_DIR}/cmake/configureVersion.cmake + COMMENT "Writing amici/version.h") # Library source files set(AMICI_SRC_LIST - ${CMAKE_SOURCE_DIR}/src/symbolic_functions.cpp - ${CMAKE_SOURCE_DIR}/src/cblas.cpp - ${CMAKE_SOURCE_DIR}/src/amici.cpp - ${CMAKE_SOURCE_DIR}/src/misc.cpp - ${CMAKE_SOURCE_DIR}/src/rdata.cpp - ${CMAKE_SOURCE_DIR}/src/edata.cpp - ${CMAKE_SOURCE_DIR}/src/exception.cpp - ${CMAKE_SOURCE_DIR}/src/simulation_parameters.cpp - ${CMAKE_SOURCE_DIR}/src/spline.cpp - ${CMAKE_SOURCE_DIR}/src/solver.cpp - ${CMAKE_SOURCE_DIR}/src/solver_cvodes.cpp - ${CMAKE_SOURCE_DIR}/src/solver_idas.cpp - ${CMAKE_SOURCE_DIR}/src/model.cpp - ${CMAKE_SOURCE_DIR}/src/model_ode.cpp - ${CMAKE_SOURCE_DIR}/src/model_dae.cpp - ${CMAKE_SOURCE_DIR}/src/model_state.cpp - ${CMAKE_SOURCE_DIR}/src/newton_solver.cpp - ${CMAKE_SOURCE_DIR}/src/forwardproblem.cpp - ${CMAKE_SOURCE_DIR}/src/steadystateproblem.cpp - ${CMAKE_SOURCE_DIR}/src/backwardproblem.cpp - ${CMAKE_SOURCE_DIR}/src/sundials_matrix_wrapper.cpp - ${CMAKE_SOURCE_DIR}/src/sundials_linsol_wrapper.cpp - ${CMAKE_SOURCE_DIR}/src/abstract_model.cpp - ${CMAKE_SOURCE_DIR}/src/vector.cpp - ) -if(ENABLE_HDF5) - list(APPEND AMICI_SRC_LIST ${CMAKE_SOURCE_DIR}/src/hdf5.cpp) -endif() + src/symbolic_functions.cpp + src/splinefunctions.cpp + src/cblas.cpp + src/amici.cpp + src/misc.cpp + src/rdata.cpp + src/edata.cpp + src/exception.cpp + src/simulation_parameters.cpp + src/spline.cpp + src/solver.cpp + src/solver_cvodes.cpp + src/solver_idas.cpp + src/logging.cpp + src/model.cpp + src/model_ode.cpp + src/model_dae.cpp + src/model_state.cpp + src/newton_solver.cpp + src/forwardproblem.cpp + src/steadystateproblem.cpp + src/backwardproblem.cpp + src/sundials_matrix_wrapper.cpp + src/sundials_linsol_wrapper.cpp + src/abstract_model.cpp + src/vector.cpp + include/amici/abstract_model.h + include/amici/amici.h + include/amici/backwardproblem.h + include/amici/cblas.h + include/amici/defines.h + include/amici/edata.h + include/amici/exception.h + include/amici/forwardproblem.h + include/amici/hdf5.h + include/amici/logging.h + include/amici/misc.h + include/amici/model_dae.h + include/amici/model_dimensions.h + include/amici/model.h + include/amici/model_ode.h + include/amici/model_state.h + include/amici/newton_solver.h + include/amici/rdata.h + include/amici/serialization.h + include/amici/simulation_parameters.h + include/amici/solver_cvodes.h + include/amici/solver.h + include/amici/solver_idas.h + include/amici/spline.h + include/amici/splinefunctions.h + include/amici/steadystateproblem.h + include/amici/sundials_linsol_wrapper.h + include/amici/sundials_matrix_wrapper.h + include/amici/symbolic_functions.h + include/amici/vector.h + $<$:src/hdf5.cpp>) 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)") +target_compile_options(${PROJECT_NAME} PRIVATE "${AMICI_CXX_OPTIONS}") + add_dependencies(${PROJECT_NAME} version) + file(GLOB PUBLIC_HEADERS include/amici/*.h) -set_target_properties(${PROJECT_NAME} PROPERTIES PUBLIC_HEADER "${PUBLIC_HEADERS}") -target_include_directories(${PROJECT_NAME} - PUBLIC $ - $ - PUBLIC $ - PUBLIC swig - PUBLIC ${GSL_LITE_INCLUDE_DIR} - PUBLIC ${SUNDIALS_INCLUDE_DIRS} - PUBLIC ${SUITESPARSE_INCLUDE_DIRS} - PUBLIC ${HDF5_INCLUDE_DIRS} - ) +set_target_properties(${PROJECT_NAME} PROPERTIES PUBLIC_HEADER + "${PUBLIC_HEADERS}") +target_include_directories( + ${PROJECT_NAME} + PUBLIC $ + $ + $ + $ + $ + $ + PRIVATE ${SUNDIALS_PRIVATE_INCLUDE_DIRS}) if(NOT "${BLAS_INCLUDE_DIRS}" STREQUAL "") - target_include_directories(${PROJECT_NAME} PUBLIC ${BLAS_INCLUDE_DIRS}) + target_include_directories(${PROJECT_NAME} PUBLIC ${BLAS_INCLUDE_DIRS}) endif() -target_link_libraries(${PROJECT_NAME} - PUBLIC ${SUNDIALS_LIBRARIES} - PUBLIC ${SUITESPARSE_LIBRARIES} - PUBLIC ${HDF5_LIBRARIES} - PUBLIC ${BLAS_LIBRARIES} - ) +if("$ENV{ENABLE_AMICI_DEBUGGING}" + OR "$ENV{ENABLE_GCOV_COVERAGE}" + OR CMAKE_BUILD_TYPE MATCHES "Debug") + set(MY_CXX_FLAGS -Werror -Wno-error=deprecated-declarations) + foreach(flag ${MY_CXX_FLAGS}) + unset(CUR_FLAG_SUPPORTED CACHE) + check_cxx_compiler_flag(${flag} CUR_FLAG_SUPPORTED) + if(${CUR_FLAG_SUPPORTED}) + target_compile_options(${PROJECT_NAME} PRIVATE ${flag}) + endif() + endforeach() +endif() + +target_compile_definitions( + ${PROJECT_NAME} PUBLIC $<$:HAS_BOOST_CHRONO>) + +target_link_libraries( + ${PROJECT_NAME} + PUBLIC SUNDIALS::generic_static + SUNDIALS::nvecserial_static + SUNDIALS::sunmatrixband_static + SUNDIALS::sunmatrixdense_static + SUNDIALS::sunmatrixsparse_static + SUNDIALS::sunlinsolband_static + SUNDIALS::sunlinsoldense_static + SUNDIALS::sunlinsolpcg_static + SUNDIALS::sunlinsolspbcgs_static + SUNDIALS::sunlinsolspfgmr_static + SUNDIALS::sunlinsolspgmr_static + SUNDIALS::sunlinsolsptfqmr_static + SUNDIALS::sunlinsolklu_static + SUNDIALS::sunnonlinsolnewton_static + SUNDIALS::sunnonlinsolfixedpoint_static + SUNDIALS::cvodes_static + SUNDIALS::idas_static + ${BLAS_LIBRARIES} + $<$:Boost::chrono> + $<$:OpenMP::OpenMP_CXX> + ${CMAKE_DL_LIBS} + PRIVATE + $<$:SUNDIALS::sundials_sunlinsolsuperlumt> +) + +if(HDF5_FOUND) + message(STATUS "HDF5 library found. Building AMICI with HDF5 support.") + target_link_libraries(${PROJECT_NAME} PUBLIC hdf5::hdf5_hl_cpp hdf5::hdf5_hl + hdf5::hdf5_cpp hdf5::hdf5) +else() + message( + STATUS + "HDF5 support disabled or HDF5 library not found. Building AMICI WITHOUT HDF5 support." + ) +endif() + +if(AMICI_PYTHON_BUILD_EXT_ONLY) + add_compile_definitions("gsl_CONFIG_CONTRACT_VIOLATION_THROWS" + "gsl_CONFIG_NARROW_THROWS_ON_TRUNCATION=1") +endif() # Create targets to make the sources show up in IDEs for convenience # For matlab interface -add_custom_target(matlabInterface - SOURCES - src/interface_matlab.cpp - src/returndata_matlab.cpp - include/amici/interface_matlab.h) -set_target_properties(matlabInterface - PROPERTIES INCLUDE_DIRECTORIES "${CMAKE_SOURCE_DIR}/include/") -find_package(Matlab) -if (${Matlab_FOUND}) - # In case we can find Matlab, use the respective include directories - # for better IDE integration (set Matlab_ROOT_DIR cmake variable - # if CMake cannot find your Matlab installation) - set_property(TARGET matlabInterface APPEND - PROPERTY INCLUDE_DIRECTORIES "${Matlab_INCLUDE_DIRS}") +if(NOT AMICI_PYTHON_BUILD_EXT_ONLY) + + set(MATLAB_SOURCES + src/interface_matlab.cpp src/returndata_matlab.cpp + include/amici/interface_matlab.h include/amici/returndata_matlab.h) + find_package(Matlab) + # In case we can find Matlab, we create a respective library to compile the + # extension from cmake. Otherwise we just create a dummy target for the files + # to show up inside IDEs. (Set the Matlab_ROOT_DIR cmake variable if CMake + # cannot find your Matlab installation) + if(${Matlab_FOUND}) + add_library(matlabInterface ${MATLAB_SOURCES}) + set_target_properties(matlabInterface PROPERTIES INCLUDE_DIRECTORIES + "${Matlab_INCLUDE_DIRS}") + target_link_libraries(matlabInterface PUBLIC amici) + else() + add_custom_target( + matlabInterface + SOURCES ${MATLAB_SOURCES} + COMMENT "Dummy target for MATLAB interface files") + endif() + set_property( + TARGET matlabInterface + APPEND + PROPERTY INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/include/") endif() # For template files add_custom_target( - fileTemplates - SOURCES - src/CMakeLists.template.cmake - src/main.template.cpp - src/model_header.ODE_template.h - src/model.ODE_template.cpp - src/wrapfunctions.ODE_template.h - src/wrapfunctions.template.cpp - swig/CMakeLists_model.cmake - swig/modelname.template.i - ) -set_target_properties(fileTemplates - PROPERTIES INCLUDE_DIRECTORIES "${CMAKE_SOURCE_DIR}/include/") - + fileTemplates + SOURCES src/CMakeLists.template.cmake + src/main.template.cpp + src/model_header.template.h + src/model.template.cpp + src/wrapfunctions.template.h + src/wrapfunctions.template.cpp + swig/CMakeLists_model.cmake + swig/modelname.template.i + COMMENT "Dummy target for SWIG files") +set_target_properties( + fileTemplates PROPERTIES INCLUDE_DIRECTORIES + "${CMAKE_CURRENT_SOURCE_DIR}/include/") -if($ENV{ENABLE_GCOV_COVERAGE}) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 --coverage") +if(NOT AMICI_PYTHON_BUILD_EXT_ONLY) + include(clang-tools) + include(cmakelang-tools) endif() -include(clang-tools) - set(AUTHORS "Fabian Froehlich, Jan Hasenauer, Daniel Weindl and Paul Stapor") set(AUTHOR_EMAIL "Fabian_Froehlich@hms.harvard.edu") # -install(TARGETS ${PROJECT_NAME} EXPORT AmiciTargets - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} - PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/amici -) -export(EXPORT AmiciTargets FILE AmiciTargets.cmake - NAMESPACE Upstream:: - ) +install( + TARGETS ${PROJECT_NAME} + EXPORT AmiciTargets + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + INCLUDES + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/amici) +export( + EXPORT AmiciTargets + FILE AmiciTargets.cmake + NAMESPACE Upstream::) include(CMakePackageConfigHelpers) include(version) configure_package_config_file( - cmake/AmiciConfig.cmake - "${CMAKE_CURRENT_BINARY_DIR}/AmiciConfig.cmake" - INSTALL_DESTINATION "${LIB_INSTALL_DIR}/cmake/" - ) -write_basic_package_version_file(AmiciConfigVersion.cmake COMPATIBILITY ExactVersion) + cmake/AmiciConfig.cmake "${CMAKE_CURRENT_BINARY_DIR}/AmiciConfig.cmake" + INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Amici") +write_basic_package_version_file(AmiciConfigVersion.cmake + COMPATIBILITY ExactVersion) +install( + EXPORT AmiciTargets + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Amici" + NAMESPACE Upstream::) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/AmiciConfig.cmake - ${CMAKE_CURRENT_BINARY_DIR}/AmiciTargets.cmake - ${CMAKE_CURRENT_BINARY_DIR}/AmiciConfigVersion.cmake - DESTINATION share/Amici/cmake ) -# Register package + ${CMAKE_CURRENT_BINARY_DIR}/AmiciConfigVersion.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/Amici) -option(EXPORT_PACKAGE "Export AMICI library to CMake package registry?" ON) +install(DIRECTORY ThirdParty/gsl/gsl TYPE INCLUDE) +# When running from setup.py, this is a symlink we need to dereference +get_filename_component(_swig_realpath "swig" REALPATH) +install(DIRECTORY "${_swig_realpath}" + DESTINATION ${CMAKE_INSTALL_DATADIR}/amici) + +# Register package? if(EXPORT_PACKAGE) - export(PACKAGE Amici) + export(PACKAGE Amici) endif() # - # build interfaces for other languages -option(ENABLE_SWIG "Build AMICI swig library?" ON) if(ENABLE_SWIG) - add_subdirectory(swig) + add_subdirectory(swig) endif() -option(ENABLE_PYTHON "Create Python module?" ON) -if(ENABLE_PYTHON) - add_subdirectory(python) +if(ENABLE_PYTHON AND NOT AMICI_PYTHON_BUILD_EXT_ONLY) + add_subdirectory(python) endif() -option(BUILD_TESTS "Build integration tests?" ON) -if(BUILD_TESTS) - if(ENABLE_HDF5) - enable_testing() +if(BUILD_TESTS AND NOT AMICI_PYTHON_BUILD_EXT_ONLY) + if(HDF5_FOUND) + enable_testing() - add_subdirectory(tests/cpp) - else() - message(WARNING "Cannot build tests with ENABLE_HDF5=OFF.") - endif() + add_subdirectory(tests/cpp) + else() + message(WARNING "Cannot build tests without HDF5 support.") + endif() endif() diff --git a/deps/AMICI/LICENSE.md b/deps/AMICI/LICENSE.md index d504f8447..4dadbc28a 100644 --- a/deps/AMICI/LICENSE.md +++ b/deps/AMICI/LICENSE.md @@ -27,7 +27,7 @@ The AMICI logo is released under the Creative Commons CC0 1.0 Universal * Parts of the *SUNDIALS* solver suite are redistributed under the BSD 3-Clause License (BSD-3-Clause) with terms given in - `ThirdParty/SuiteSparse/LICENSE.txt` + `ThirdParty/sundials/LICENSE` * Parts of *SuiteSparse* are redistributed under the various licenses with the terms given in `ThirdParty/SuiteSparse/LICENSE.txt` * *gsl-lite* is redistributed under the MIT License (MIT) with the terms given diff --git a/deps/AMICI/README.md b/deps/AMICI/README.md index 52856ae21..7b45537dc 100644 --- a/deps/AMICI/README.md +++ b/deps/AMICI/README.md @@ -2,7 +2,7 @@ ## Advanced Multilanguage Interface for CVODES and IDAS -## About +## About AMICI provides a multi-language (Python, C++, Matlab) interface for the [SUNDIALS](https://computing.llnl.gov/projects/sundials/) solvers @@ -25,7 +25,7 @@ forward sensitivity analysis, steady state sensitivity analysis and adjoint sensitivity analysis for likelihood-based output functions. The interface was designed to provide routines for efficient gradient -computation in parameter estimation of biochemical reaction models but +computation in parameter estimation of biochemical reaction models, but it is also applicable to a wider range of differential equation constrained optimization problems. @@ -62,7 +62,6 @@ constrained optimization problems. * Pre-equilibration and pre-simulation conditions * Support for [discrete events and logical operations](https://academic.oup.com/bioinformatics/article/33/7/1049/2769435) - (Matlab-only) ## Interfaces & workflow @@ -83,9 +82,12 @@ To install AMICI, first read the installation instructions for [Python](https://amici.readthedocs.io/en/latest/python_installation.html), [C++](https://amici.readthedocs.io/en/develop/cpp_installation.html) or [Matlab](https://amici.readthedocs.io/en/develop/matlab_installation.html). +There are also instructions for using AMICI inside +[containers](https://github.com/AMICI-dev/AMICI/tree/master/container). To get you started with Python-AMICI, the best way might be checking out this -[Jupyter notebook](https://github.com/AMICI-dev/AMICI/blob/master/documentation/GettingStarted.ipynb). +[Jupyter notebook](https://github.com/AMICI-dev/AMICI/blob/master/documentation/GettingStarted.ipynb) +[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/AMICI-dev/AMICI/develop?labpath=documentation%2FGettingStarted.ipynb). To get started with Matlab-AMICI, various examples are available in [matlab/examples/](https://github.com/AMICI-dev/AMICI/tree/master/matlab/examples). @@ -124,7 +126,7 @@ with AMICI: There is a list of [publications using AMICI](https://amici.readthedocs.io/en/latest/references.html). If you used AMICI in your work, we are happy to include -your project, please let us know via a Github issue. +your project, please let us know via a GitHub issue. When using AMICI in your project, please cite * Fröhlich, F., Weindl, D., Schälte, Y., Pathirana, D., Paszkowski, Ł., Lines, G.T., Stapor, P. and Hasenauer, J., 2021. @@ -143,8 +145,8 @@ When using AMICI in your project, please cite eprint = {https://academic.oup.com/bioinformatics/advance-article-pdf/doi/10.1093/bioinformatics/btab227/36866220/btab227.pdf}, } ``` - -When presenting work that employs AMICI, feel free to use one of the icons in + +When presenting work that employs AMICI, feel free to use one of the icons in [documentation/gfx/](https://github.com/AMICI-dev/AMICI/tree/master/documentation/gfx), which are available under a [CC0](https://github.com/AMICI-dev/AMICI/tree/master/documentation/gfx/LICENSE.md) diff --git a/deps/AMICI/ThirdParty/SuiteSparse/.gitattributes b/deps/AMICI/ThirdParty/SuiteSparse/.gitattributes new file mode 100644 index 000000000..49e7a27f9 --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/.gitattributes @@ -0,0 +1,2 @@ +# line endings in repository match line endings on disc +* -text diff --git a/deps/AMICI/ThirdParty/SuiteSparse/.gitignore b/deps/AMICI/ThirdParty/SuiteSparse/.gitignore new file mode 100644 index 000000000..d0d4223fd --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/.gitignore @@ -0,0 +1,218 @@ +# Ignore these files: +*.o +*.so.* +*.so +*.dylib +*.a +*.obj +*.ln +*.bb +*.bbg +*.da +*.tcov +*.gcov +gmon.out +*.bak +*.d +*.gcda +*.gcno +*.aux +*.bbl +*.blg +*.log +*.toc +*.dvi +*.lof +*.lot +*.dll +*.dSYM +my_*.out +*.gcda +*.gcno +*.mex* +*.profile +*.swp +.DS_Store +.nfs* +.pyc + +# ignore these specific programs in the Package/Demo directories +AMD/Demo/amd_demo +AMD/Demo/amd_demo2 +AMD/Demo/amd_l_demo +AMD/Demo/amd_simple +CAMD/Demo/camd_demo +CAMD/Demo/camd_demo2 +CAMD/Demo/camd_l_demo +CAMD/Demo/camd_simple +CCOLAMD/Demo/ccolamd_example +CCOLAMD/Demo/ccolamd_l_example +CHOLMOD/Demo/cholmod_demo +CHOLMOD/Demo/cholmod_l_demo +CHOLMOD/Demo/cholmod_simple +CHOLMOD/Demo/timelog.m +COLAMD/Demo/colamd_example +COLAMD/Demo/colamd_l_example +CSparse/Demo/cs_demo1 +CSparse/Demo/cs_demo2 +CSparse/Demo/cs_demo3 +CXSparse/Demo/cs_ci_demo1 +CXSparse/Demo/cs_ci_demo2 +CXSparse/Demo/cs_ci_demo3 +CXSparse/Demo/cs_cl_demo1 +CXSparse/Demo/cs_cl_demo2 +CXSparse/Demo/cs_cl_demo3 +CXSparse/Demo/cs_demo1 +CXSparse/Demo/cs_demo2 +CXSparse/Demo/cs_demo3 +CXSparse/Demo/cs_di_demo1 +CXSparse/Demo/cs_di_demo2 +CXSparse/Demo/cs_di_demo3 +CXSparse/Demo/cs_dl_demo1 +CXSparse/Demo/cs_dl_demo2 +CXSparse/Demo/cs_dl_demo3 +CXSparse/Demo/cs_idemo +CXSparse/Demo/cs_ldemo +KLU/Demo/klu_simple +KLU/Demo/kludemo +KLU/Demo/kluldemo +LDL/Demo/ldlamd +LDL/Demo/ldllamd +LDL/Demo/ldllmain +LDL/Demo/ldllsimple +LDL/Demo/ldlmain +LDL/Demo/ldlsimple +RBio/Demo/RBdemo +RBio/Demo/temp.rb +SPQR/Demo/qrdemo +SPQR/Demo/qrsimple +SPQR/Demo/qrsimplec +SPQR/Demo/C.mtx +SPQR/Demo/E.txt +SPQR/Demo/R.mtx +SPQR/Demo/X.mtx +SPQR/Demo/gpu_results.txt +SPQR/Demo/qrdemo_gpu +SPQR/Demo/qrdemo_gpu2 +SPQR/Demo/qrdemo_gpu3 +SPQR/Demo/pfile +SPQR/Demo/tfile +UMFPACK/Demo/numeric.umf +UMFPACK/Demo/symbolic.umf +UMFPACK/Demo/umfpack_di_demo +UMFPACK/Demo/umfpack_dl_demo +UMFPACK/Demo/umfpack_simple +UMFPACK/Demo/umfpack_zi_demo +UMFPACK/Demo/umfpack_zl_demo + +# ignore these specific programs in the Package/Tcov directories +CHOLMOD/Tcov/cl +CHOLMOD/Tcov/clread +CHOLMOD/Tcov/cm +CHOLMOD/Tcov/cmread +CHOLMOD/Tcov/covs.out +CHOLMOD/Tcov/ldemo +CHOLMOD/Tcov/ldemo.c +CHOLMOD/Tcov/temp*.mtx +CHOLMOD/Tcov/timelog.m +CHOLMOD/Tcov/l_*.c +CHOLMOD/Tcov/z_*.c +CHOLMOD/Tcov/zz_*.c +CHOLMOD/Tcov/zl_*.c +CHOLMOD/Tcov/zdemo +CHOLMOD/Tcov/zdemo.c + +CSparse/Tcov/cov.out +CSparse/Tcov/cov.sort +CSparse/Tcov/cover.out +CSparse/Tcov/covs.out +CSparse/Tcov/cs_*.c +CSparse/Tcov/cstcov_test +CSparse/Tcov/*.out +CSparse/Tcov/cs_demo1 +CSparse/Tcov/cs_demo2 +CSparse/Tcov/cs_demo3 + +CXSparse/Tcov/cov.out +CXSparse/Tcov/cov.sort +CXSparse/Tcov/cover.out +CXSparse/Tcov/covs.out +CXSparse/Tcov/cs_*.c +CXSparse/Tcov/*.out +CXSparse/Tcov/cs_demo1_ci +CXSparse/Tcov/cs_demo1_cl +CXSparse/Tcov/cs_demo1_di +CXSparse/Tcov/cs_demo1_dl +CXSparse/Tcov/cs_demo2_ci +CXSparse/Tcov/cs_demo2_cl +CXSparse/Tcov/cs_demo2_di +CXSparse/Tcov/cs_demo2_dl +CXSparse/Tcov/cs_demo3_ci +CXSparse/Tcov/cs_demo3_cl +CXSparse/Tcov/cs_demo3_di +CXSparse/Tcov/cs_demo3_dl +CXSparse/Tcov/cs_idemo +CXSparse/Tcov/cs_ldemo +CXSparse/Tcov/cstcov_test_ci +CXSparse/Tcov/cstcov_test_cl +CXSparse/Tcov/cstcov_test_di +CXSparse/Tcov/cstcov_test_dl + +KLU/Tcov/cov_*.c +KLU/Tcov/klutest +KLU/Tcov/klultest +KLU/Tcov/*.out + +SPQR/Tcov/X.mtx +SPQR/Tcov/gpu_results.txt +SPQR/Tcov/gpuqrengine_demo +SPQR/Tcov/qrdemo_gpu +SPQR/Tcov/qrtest +SPQR/Tcov/qrtest_out.txt +SPQR/Tcov/troll.m +SPQR/Tcov/cov.out + +UMFPACK/Tcov/covall_err.out +UMFPACK/Tcov/cover.out + +# ignore these specific files in the Package/MATLAB directories +MATLAB_Tools/spqr_rank/save_samples_demo_spqr_rank.mat +CXSparse/MATLAB/CSparse/cs_cl_*.c +CXSparse/MATLAB/Test/cs_cl_*.c + +RBio/Tcov/RBdemo +RBio/Tcov/RBdemo.c +RBio/Tcov/RBio.c +RBio/Tcov/RBio.h +RBio/Tcov/RBtest +RBio/Tcov/SuiteSparse_config.c +RBio/Tcov/SuiteSparse_config.h +RBio/Tcov/*.out +RBio/Tcov/*.rb + +# GraphBLAS +GraphBLAS/Demo/bfs_demo.out +GraphBLAS/Demo/complex_demo.m +GraphBLAS/Demo/mis_demo.out +GraphBLAS/Demo/simple_demo.out +GraphBLAS/Demo/wildtype_demo.out +GraphBLAS/Demo/tri_demo.out +GraphBLAS/Test/errlog.txt +GraphBLAS/Test/log.txt +GraphBLAS/Doc/GraphBLAS_UserGuide.out +GraphBLAS/Tcov/errlog.txt +GraphBLAS/Tcov/log.txt +GraphBLAS/Tcov/gbstat.mat + +tri/tri_main +ktruss/allktruss_graphblas_main +ktruss/allktruss_main +ktruss/ktruss_graphblas_main +ktruss/ktruss_main + +ssget/files/ss_index_old.mat +ssget/files/ssstats_old.csv + +# Do not ignore this file +!.gitignore + diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/CMakeLists.txt b/deps/AMICI/ThirdParty/SuiteSparse/AMD/CMakeLists.txt new file mode 100644 index 000000000..4fdf61499 --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/AMD/CMakeLists.txt @@ -0,0 +1,195 @@ +#------------------------------------------------------------------------------- +# SuiteSparse/AMD/CMakeLists.txt: cmake for AMD +#------------------------------------------------------------------------------- + +# Copyright (c) 1996-2022, Timothy A. Davis, Patrick Amestoy, Iain Duff. +# All Rights Reserved. +# SPDX-License-Identifier: BSD-3-clause + +#------------------------------------------------------------------------------- +# get the version +#------------------------------------------------------------------------------- + +cmake_minimum_required ( VERSION 3.19 ) + +set ( AMD_DATE "Jan 17, 2023" ) +set ( AMD_VERSION_MAJOR 3 ) +set ( AMD_VERSION_MINOR 0 ) +set ( AMD_VERSION_SUB 3 ) + +message ( STATUS "Building AMD version: v" + ${AMD_VERSION_MAJOR}. + ${AMD_VERSION_MINOR}. + ${AMD_VERSION_SUB} " (" ${AMD_DATE} ")" ) + +#------------------------------------------------------------------------------- +# SuiteSparse policies +#------------------------------------------------------------------------------- + +set ( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} + ${CMAKE_SOURCE_DIR}/cmake_modules + ${CMAKE_SOURCE_DIR}/../SuiteSparse_config/cmake_modules ) + +include ( SuiteSparsePolicy ) + +#------------------------------------------------------------------------------- +# define the project +#------------------------------------------------------------------------------- + +if ( WIN32 ) + # disable Fortran in AMD when compiling on Windows + set ( NFORTRAN true ) +endif ( ) + +if ( NOT NFORTRAN ) + # 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 ) +endif ( ) + +#------------------------------------------------------------------------------- +# find library dependencies +#------------------------------------------------------------------------------- + +find_package ( SuiteSparse_config 7.0.0 REQUIRED ) + +#------------------------------------------------------------------------------- +# configure files +#------------------------------------------------------------------------------- + +configure_file ( "Config/amd.h.in" "${PROJECT_SOURCE_DIR}/Include/amd.h" + NEWLINE_STYLE LF ) +configure_file ( "Config/amd_version.tex.in" "${PROJECT_SOURCE_DIR}/Doc/amd_version.tex" + NEWLINE_STYLE LF ) + +#------------------------------------------------------------------------------- +# include directories +#------------------------------------------------------------------------------- + +include_directories ( Source Include ${SUITESPARSE_CONFIG_INCLUDE_DIR} ) + +#------------------------------------------------------------------------------- +# dynamic amd library properties +#------------------------------------------------------------------------------- + +if ( NOT NFORTRAN ) + 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 ) + +#------------------------------------------------------------------------------- +# 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 + OUTPUT_NAME amd + SOVERSION ${AMD_VERSION_MAJOR} ) + + if ( MSVC ) + set_target_properties ( amd_static PROPERTIES + OUTPUT_NAME amd_static ) + endif ( ) +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} ) +endif ( ) + +# libm: +if ( NOT WIN32 ) + target_link_libraries ( amd PUBLIC m ) + if ( NOT NSTATIC ) + target_link_libraries ( amd_static PUBLIC m ) + endif ( ) +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} ) +endif ( ) + +#------------------------------------------------------------------------------- +# Demo library and programs +#------------------------------------------------------------------------------- + +option ( DEMO "ON: Build the demo programs. OFF (default): do not build the demo programs." off ) +if ( DEMO ) + + #--------------------------------------------------------------------------- + # demo library + #--------------------------------------------------------------------------- + + message ( STATUS "Also compiling the demos in AMD/Demo" ) + + #--------------------------------------------------------------------------- + # Demo programs + #--------------------------------------------------------------------------- + + add_executable ( amd_demo "Demo/amd_demo.c" ) + 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 ) + 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 ) + endif ( ) + +else ( ) + + message ( STATUS "Skipping the demos in AMD/Demo" ) + +endif ( ) + +#------------------------------------------------------------------------------- +# report status +#------------------------------------------------------------------------------- + +include ( SuiteSparseReport ) + diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Config/amd.h.in b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Config/amd.h.in new file mode 100644 index 000000000..f2a691665 --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Config/amd.h.in @@ -0,0 +1,389 @@ +//------------------------------------------------------------------------------ +// AMD/Include/amd.h: approximate minimum degree ordering +//------------------------------------------------------------------------------ + +// AMD, Copyright (c) 1996-2022, Timothy A. Davis, Patrick R. Amestoy, and +// Iain S. Duff. All Rights Reserved. +// SPDX-License-Identifier: BSD-3-clause + +//------------------------------------------------------------------------------ + +/* AMD finds a symmetric ordering P of a matrix A so that the Cholesky + * factorization of P*A*P' has fewer nonzeros and takes less work than the + * Cholesky factorization of A. If A is not symmetric, then it performs its + * ordering on the matrix A+A'. Two sets of user-callable routines are + * provided, one for int32_t integers and the other for int64_t integers. + * + * The method is based on the approximate minimum degree algorithm, discussed + * in Amestoy, Davis, and Duff, "An approximate degree ordering algorithm", + * SIAM Journal of Matrix Analysis and Applications, vol. 17, no. 4, pp. + * 886-905, 1996. This package can perform both the AMD ordering (with + * aggressive absorption), and the AMDBAR ordering (without aggressive + * absorption) discussed in the above paper. This package differs from the + * Fortran codes discussed in the paper: + * + * (1) it can ignore "dense" rows and columns, leading to faster run times + * (2) it computes the ordering of A+A' if A is not symmetric + * (3) it is followed by a depth-first post-ordering of the assembly tree + * (or supernodal elimination tree) + * + * For historical reasons, the Fortran versions, amd.f and amdbar.f, have + * been left (nearly) unchanged. They compute the identical ordering as + * described in the above paper. + */ + +#ifndef AMD_H +#define AMD_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 */ +( + int32_t n, /* A is n-by-n. n must be >= 0. */ + const int32_t Ap [ ], /* column pointers for A, of size n+1 */ + const int32_t Ai [ ], /* row indices of A, of size nz = Ap [n] */ + int32_t P [ ], /* output permutation, of size n */ + double Control [ ], /* input Control settings, of size AMD_CONTROL */ + double Info [ ] /* output Info statistics, of size AMD_INFO */ +) ; + +int amd_l_order /* see above for description */ +( + int64_t n, + const int64_t Ap [ ], + const int64_t Ai [ ], + int64_t P [ ], + double Control [ ], + double Info [ ] +) ; + +/* Input arguments (not modified): + * + * n: the matrix A is n-by-n. + * Ap: an int32_t/int64_t array of size n+1, containing column + * pointers of A. + * Ai: an int32_t/int64_t array of size nz, containing the row + * indices of A, where nz = Ap [n]. + * Control: a double array of size AMD_CONTROL, containing control + * parameters. Defaults are used if Control is NULL. + * + * Output arguments (not defined on input): + * + * P: an int32_t/int64_t array of size n, containing the output + * permutation. If row i is the kth pivot row, then P [k] = i. In + * MATLAB notation, the reordered matrix is A (P,P). + * Info: a double array of size AMD_INFO, containing statistical + * information. Ignored if Info is NULL. + * + * On input, the matrix A is stored in column-oriented form. The row indices + * of nonzero entries in column j are stored in Ai [Ap [j] ... Ap [j+1]-1]. + * + * If the row indices appear in ascending order in each column, and there + * are no duplicate entries, then amd_order is slightly more efficient in + * terms of time and memory usage. If this condition does not hold, a copy + * of the matrix is created (where these conditions do hold), and the copy is + * ordered. + * + * Row indices must be in the range 0 to + * n-1. Ap [0] must be zero, and thus nz = Ap [n] is the number of nonzeros + * in A. The array Ap is of size n+1, and the array Ai is of size nz = Ap [n]. + * The matrix does not need to be symmetric, and the diagonal does not need to + * be present (if diagonal entries are present, they are ignored except for + * the output statistic Info [AMD_NZDIAG]). The arrays Ai and Ap are not + * modified. This form of the Ap and Ai arrays to represent the nonzero + * pattern of the matrix A is the same as that used internally by MATLAB. + * If you wish to use a more flexible input structure, please see the + * umfpack_*_triplet_to_col routines in the UMFPACK package, at + * http://www.suitesparse.com. + * + * Restrictions: n >= 0. Ap [0] = 0. Ap [j] <= Ap [j+1] for all j in the + * range 0 to n-1. nz = Ap [n] >= 0. Ai [0..nz-1] must be in the range 0 + * to n-1. Finally, Ai, Ap, and P must not be NULL. If any of these + * restrictions are not met, AMD returns AMD_INVALID. + * + * AMD returns: + * + * AMD_OK if the matrix is valid and sufficient memory can be allocated to + * perform the ordering. + * + * AMD_OUT_OF_MEMORY if not enough memory can be allocated. + * + * AMD_INVALID if the input arguments n, Ap, Ai are invalid, or if P is + * NULL. + * + * AMD_OK_BUT_JUMBLED if the matrix had unsorted columns, and/or duplicate + * entries, but was otherwise valid. + * + * The AMD routine first forms the pattern of the matrix A+A', and then + * computes a fill-reducing ordering, P. If P [k] = i, then row/column i of + * the original is the kth pivotal row. In MATLAB notation, the permuted + * matrix is A (P,P), except that 0-based indexing is used instead of the + * 1-based indexing in MATLAB. + * + * The Control array is used to set various parameters for AMD. If a NULL + * pointer is passed, default values are used. The Control array is not + * modified. + * + * Control [AMD_DENSE]: controls the threshold for "dense" rows/columns. + * A dense row/column in A+A' can cause AMD to spend a lot of time in + * ordering the matrix. If Control [AMD_DENSE] >= 0, rows/columns + * with more than Control [AMD_DENSE] * sqrt (n) entries are ignored + * during the ordering, and placed last in the output order. The + * default value of Control [AMD_DENSE] is 10. If negative, no + * rows/columns are treated as "dense". Rows/columns with 16 or + * fewer off-diagonal entries are never considered "dense". + * + * Control [AMD_AGGRESSIVE]: controls whether or not to use aggressive + * absorption, in which a prior element is absorbed into the current + * element if is a subset of the current element, even if it is not + * adjacent to the current pivot element (refer to Amestoy, Davis, + * & Duff, 1996, for more details). The default value is nonzero, + * which means to perform aggressive absorption. This nearly always + * leads to a better ordering (because the approximate degrees are + * more accurate) and a lower execution time. There are cases where + * it can lead to a slightly worse ordering, however. To turn it off, + * set Control [AMD_AGGRESSIVE] to 0. + * + * Control [2..4] are not used in the current version, but may be used in + * future versions. + * + * The Info array provides statistics about the ordering on output. If it is + * not present, the statistics are not returned. This is not an error + * condition. + * + * Info [AMD_STATUS]: the return value of AMD, either AMD_OK, + * AMD_OK_BUT_JUMBLED, AMD_OUT_OF_MEMORY, or AMD_INVALID. + * + * Info [AMD_N]: n, the size of the input matrix + * + * Info [AMD_NZ]: the number of nonzeros in A, nz = Ap [n] + * + * Info [AMD_SYMMETRY]: the symmetry of the matrix A. It is the number + * of "matched" off-diagonal entries divided by the total number of + * off-diagonal entries. An entry A(i,j) is matched if A(j,i) is also + * an entry, for any pair (i,j) for which i != j. In MATLAB notation, + * S = spones (A) ; + * B = tril (S, -1) + triu (S, 1) ; + * symmetry = nnz (B & B') / nnz (B) ; + * + * Info [AMD_NZDIAG]: the number of entries on the diagonal of A. + * + * Info [AMD_NZ_A_PLUS_AT]: the number of nonzeros in A+A', excluding the + * diagonal. If A is perfectly symmetric (Info [AMD_SYMMETRY] = 1) + * with a fully nonzero diagonal, then Info [AMD_NZ_A_PLUS_AT] = nz-n + * (the smallest possible value). If A is perfectly unsymmetric + * (Info [AMD_SYMMETRY] = 0, for an upper triangular matrix, for + * example) with no diagonal, then Info [AMD_NZ_A_PLUS_AT] = 2*nz + * (the largest possible value). + * + * Info [AMD_NDENSE]: the number of "dense" rows/columns of A+A' that were + * removed from A prior to ordering. These are placed last in the + * output order P. + * + * Info [AMD_MEMORY]: the amount of memory used by AMD, in bytes. In the + * current version, this is 1.2 * Info [AMD_NZ_A_PLUS_AT] + 9*n + * times the size of an integer. This is at most 2.4nz + 9n. This + * excludes the size of the input arguments Ai, Ap, and P, which have + * a total size of nz + 2*n + 1 integers. + * + * Info [AMD_NCMPA]: the number of garbage collections performed. + * + * Info [AMD_LNZ]: the number of nonzeros in L (excluding the diagonal). + * This is a slight upper bound because mass elimination is combined + * with the approximate degree update. It is a rough upper bound if + * there are many "dense" rows/columns. The rest of the statistics, + * below, are also slight or rough upper bounds, for the same reasons. + * The post-ordering of the assembly tree might also not exactly + * correspond to a true elimination tree postordering. + * + * Info [AMD_NDIV]: the number of divide operations for a subsequent LDL' + * or LU factorization of the permuted matrix A (P,P). + * + * Info [AMD_NMULTSUBS_LDL]: the number of multiply-subtract pairs for a + * subsequent LDL' factorization of A (P,P). + * + * Info [AMD_NMULTSUBS_LU]: the number of multiply-subtract pairs for a + * subsequent LU factorization of A (P,P), assuming that no numerical + * pivoting is required. + * + * Info [AMD_DMAX]: the maximum number of nonzeros in any column of L, + * including the diagonal. + * + * Info [14..19] are not used in the current version, but may be used in + * future versions. + */ + +/* ------------------------------------------------------------------------- */ +/* direct interface to AMD */ +/* ------------------------------------------------------------------------- */ + +/* amd_2 is the primary AMD ordering routine. It is not meant to be + * user-callable because of its restrictive inputs and because it destroys + * the user's input matrix. It does not check its inputs for errors, either. + * However, if you can work with these restrictions it can be faster than + * amd_order and use less memory (assuming that you can create your own copy + * of the matrix for AMD to destroy). Refer to AMD/Source/amd_2.c for a + * description of each parameter. */ + +void amd_2 +( + int32_t n, + int32_t Pe [ ], + int32_t Iw [ ], + int32_t Len [ ], + int32_t iwlen, + int32_t pfree, + int32_t Nv [ ], + int32_t Next [ ], + int32_t Last [ ], + int32_t Head [ ], + int32_t Elen [ ], + int32_t Degree [ ], + int32_t W [ ], + double Control [ ], + double Info [ ] +) ; + +void amd_l2 +( + int64_t n, + int64_t Pe [ ], + int64_t Iw [ ], + int64_t Len [ ], + int64_t iwlen, + int64_t pfree, + int64_t Nv [ ], + int64_t Next [ ], + int64_t Last [ ], + int64_t Head [ ], + int64_t Elen [ ], + int64_t Degree [ ], + int64_t W [ ], + double Control [ ], + double Info [ ] +) ; + +/* ------------------------------------------------------------------------- */ +/* amd_valid */ +/* ------------------------------------------------------------------------- */ + +/* Returns AMD_OK or AMD_OK_BUT_JUMBLED if the matrix is valid as input to + * amd_order; the latter is returned if the matrix has unsorted and/or + * duplicate row indices in one or more columns. Returns AMD_INVALID if the + * matrix cannot be passed to amd_order. For amd_order, the matrix must also + * be square. The first two arguments are the number of rows and the number + * of columns of the matrix. For its use in AMD, these must both equal n. + */ + +int amd_valid +( + int32_t n_row, /* # of rows */ + int32_t n_col, /* # of columns */ + const int32_t Ap [ ], /* column pointers, of size n_col+1 */ + const int32_t Ai [ ] /* row indices, of size Ap [n_col] */ +) ; + +int amd_l_valid +( + int64_t n_row, + int64_t n_col, + const int64_t Ap [ ], + const int64_t Ai [ ] +) ; + +/* ------------------------------------------------------------------------- */ +/* AMD Control and Info arrays */ +/* ------------------------------------------------------------------------- */ + +/* amd_defaults: sets the default control settings */ +void amd_defaults (double Control [ ]) ; +void amd_l_defaults (double Control [ ]) ; + +/* amd_control: prints the control settings */ +void amd_control (double Control [ ]) ; +void amd_l_control (double Control [ ]) ; + +/* amd_info: prints the statistics */ +void amd_info (double Info [ ]) ; +void amd_l_info (double Info [ ]) ; + +#define AMD_CONTROL 5 /* size of Control array */ +#define AMD_INFO 20 /* size of Info array */ + +/* contents of Control */ +#define AMD_DENSE 0 /* "dense" if degree > Control [0] * sqrt (n) */ +#define AMD_AGGRESSIVE 1 /* do aggressive absorption if Control [1] != 0 */ + +/* default Control settings */ +#define AMD_DEFAULT_DENSE 10.0 /* default "dense" degree 10*sqrt(n) */ +#define AMD_DEFAULT_AGGRESSIVE 1 /* do aggressive absorption by default */ + +/* contents of Info */ +#define AMD_STATUS 0 /* return value of amd_order and amd_l_order */ +#define AMD_N 1 /* A is n-by-n */ +#define AMD_NZ 2 /* number of nonzeros in A */ +#define AMD_SYMMETRY 3 /* symmetry of pattern (1 is sym., 0 is unsym.) */ +#define AMD_NZDIAG 4 /* # of entries on diagonal */ +#define AMD_NZ_A_PLUS_AT 5 /* nz in A+A' */ +#define AMD_NDENSE 6 /* number of "dense" rows/columns in A */ +#define AMD_MEMORY 7 /* amount of memory used by AMD */ +#define AMD_NCMPA 8 /* number of garbage collections in AMD */ +#define AMD_LNZ 9 /* approx. nz in L, excluding the diagonal */ +#define AMD_NDIV 10 /* number of fl. point divides for LU and LDL' */ +#define AMD_NMULTSUBS_LDL 11 /* number of fl. point (*,-) pairs for LDL' */ +#define AMD_NMULTSUBS_LU 12 /* number of fl. point (*,-) pairs for LU */ +#define AMD_DMAX 13 /* max nz. in any column of L, incl. diagonal */ + +/* ------------------------------------------------------------------------- */ +/* return values of AMD */ +/* ------------------------------------------------------------------------- */ + +#define AMD_OK 0 /* success */ +#define AMD_OUT_OF_MEMORY -1 /* malloc failed, or problem too large */ +#define AMD_INVALID -2 /* input arguments are not valid */ +#define AMD_OK_BUT_JUMBLED 1 /* input matrix is OK for amd_order, but + * columns were not sorted, and/or duplicate entries were present. AMD had + * to do extra work before ordering the matrix. This is a warning, not an + * error. */ + +/* ========================================================================== */ +/* === AMD version ========================================================== */ +/* ========================================================================== */ + +/* AMD Version 1.2 and later include the following definitions. + * As an example, to test if the version you are using is 1.2 or later: + * + * #ifdef AMD_VERSION + * if (AMD_VERSION >= AMD_VERSION_CODE (1,2)) ... + * #endif + * + * This also works during compile-time: + * + * #if defined(AMD_VERSION) && (AMD_VERSION >= AMD_VERSION_CODE (1,2)) + * printf ("This is version 1.2 or later\n") ; + * #else + * printf ("This is an early version\n") ; + * #endif + * + * Versions 1.1 and earlier of AMD do not include a #define'd version number. + */ + +#define AMD_DATE "@AMD_DATE@" +#define AMD_MAIN_VERSION @AMD_VERSION_MAJOR@ +#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) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Config/amd_version.tex.in b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Config/amd_version.tex.in new file mode 100644 index 000000000..1e47eb859 --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Config/amd_version.tex.in @@ -0,0 +1,2 @@ +% version of SuiteSparse/AMD +\date{VERSION @AMD_VERSION_MAJOR@.@AMD_VERSION_MINOR@.@AMD_VERSION_SUB@, @AMD_DATE@} diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Demo/Makefile b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Demo/Makefile deleted file mode 100644 index 452e0e34c..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Demo/Makefile +++ /dev/null @@ -1,97 +0,0 @@ -#----------------------------------------------------------------------------- -# compile the AMD demo -#----------------------------------------------------------------------------- - -default: amd_simple amd_demo amd_demo2 amd_l_demo - -gak: - echo $(SUITESPARSE) - -include ../../SuiteSparse_config/SuiteSparse_config.mk - -C = $(CC) $(CF) -I../../include - -LIB2 = $(LDFLAGS) -L../../lib -lamd -lsuitesparseconfig $(LDLIBS) - -library: - ( cd ../../SuiteSparse_config ; $(MAKE) ) - ( cd ../Lib ; $(MAKE) ) - -f77lib: - ( cd ../Lib ; $(MAKE) fortran ) - -#------------------------------------------------------------------------------ -# Create the demo program, run it, and compare the output -#------------------------------------------------------------------------------ - -dist: - -amd_demo: amd_demo.c library - $(C) -o amd_demo amd_demo.c $(LIB2) - ./amd_demo > my_amd_demo.out - - diff amd_demo.out my_amd_demo.out - -amd_l_demo: amd_l_demo.c library - $(C) -o amd_l_demo amd_l_demo.c $(LIB2) - ./amd_l_demo > my_amd_l_demo.out - - diff amd_l_demo.out my_amd_l_demo.out - -amd_demo2: amd_demo2.c library - $(C) -o amd_demo2 amd_demo2.c $(LIB2) - ./amd_demo2 > my_amd_demo2.out - - diff amd_demo2.out my_amd_demo2.out - -amd_simple: amd_simple.c library - echo $(LD_LIBRARY_PATH) - $(C) -o amd_simple amd_simple.c $(LIB2) - ./amd_simple > my_amd_simple.out - - diff amd_simple.out my_amd_simple.out - -#------------------------------------------------------------------------------ -# compile the Fortran demo -#------------------------------------------------------------------------------ - -fortran: amd_f77demo amd_f77simple - -cross: amd_f77cross - -amd_f77demo: amd_f77demo.f f77lib - $(F77) $(F77FLAGS) -o amd_f77demo amd_f77demo.f ../Lib/libamdf77.a \ - $(F77LIB) - ./amd_f77demo > my_amd_f77demo.out - - diff amd_f77demo.out my_amd_f77demo.out - -amd_f77simple: amd_f77simple.f f77lib - $(F77) $(F77FLAGS) -o amd_f77simple amd_f77simple.f \ - ../Lib/libamdf77.a $(F77LIB) - ./amd_f77simple > my_amd_f77simple.out - - diff amd_f77simple.out my_amd_f77simple.out - -amd_f77wrapper.o: amd_f77wrapper.c - $(C) -DDINT -c amd_f77wrapper.c - -amd_f77cross: amd_f77cross.f amd_f77wrapper.o ../Lib/libamd.a - $(F77) $(F77FLAGS) -o amd_f77cross amd_f77cross.f amd_f77wrapper.o \ - ../Lib/libamd.a $(F77LIB) - ./amd_f77cross > my_amd_f77cross.out - - diff amd_f77cross.out my_amd_f77cross.out - -#------------------------------------------------------------------------------ -# Remove all but the files in the original distribution -#------------------------------------------------------------------------------ - -clean: - - $(RM) -r $(CLEAN) - -purge: distclean - -distclean: clean - - $(RM) amd_demo my_amd_demo.out - - $(RM) amd_l_demo my_amd_l_demo.out - - $(RM) amd_demo2 my_amd_demo2.out - - $(RM) amd_simple my_amd_simple.out - - $(RM) amd_f77demo my_amd_f77demo.out - - $(RM) amd_f77simple my_amd_f77simple.out - - $(RM) amd_f77cross my_amd_f77cross.out - - $(RM) -r $(PURGE) - diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Demo/amd_demo.c b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Demo/amd_demo.c deleted file mode 100644 index 70d90070a..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Demo/amd_demo.c +++ /dev/null @@ -1,177 +0,0 @@ -/* ========================================================================= */ -/* === AMD demo main program =============================================== */ -/* ========================================================================= */ - -/* ------------------------------------------------------------------------- */ -/* AMD Copyright (c) by Timothy A. Davis, */ -/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ -/* DrTimothyAldenDavis@gmail.com, http://www.suitesparse.com */ -/* ------------------------------------------------------------------------- */ - -/* A simple C main program that illustrates the use of the ANSI C interface - * to AMD. - */ - -#include "amd.h" -#include -#include - -int main (void) -{ - /* The symmetric can_24 Harwell/Boeing matrix, including upper and lower - * triangular parts, and the diagonal entries. Note that this matrix is - * 0-based, with row and column indices in the range 0 to n-1. */ - int n = 24, nz, - Ap [ ] = { 0, 9, 15, 21, 27, 33, 39, 48, 57, 61, 70, 76, 82, 88, 94, 100, - 106, 110, 119, 128, 137, 143, 152, 156, 160 }, - Ai [ ] = { - /* column 0: */ 0, 5, 6, 12, 13, 17, 18, 19, 21, - /* column 1: */ 1, 8, 9, 13, 14, 17, - /* column 2: */ 2, 6, 11, 20, 21, 22, - /* column 3: */ 3, 7, 10, 15, 18, 19, - /* column 4: */ 4, 7, 9, 14, 15, 16, - /* column 5: */ 0, 5, 6, 12, 13, 17, - /* column 6: */ 0, 2, 5, 6, 11, 12, 19, 21, 23, - /* column 7: */ 3, 4, 7, 9, 14, 15, 16, 17, 18, - /* column 8: */ 1, 8, 9, 14, - /* column 9: */ 1, 4, 7, 8, 9, 13, 14, 17, 18, - /* column 10: */ 3, 10, 18, 19, 20, 21, - /* column 11: */ 2, 6, 11, 12, 21, 23, - /* column 12: */ 0, 5, 6, 11, 12, 23, - /* column 13: */ 0, 1, 5, 9, 13, 17, - /* column 14: */ 1, 4, 7, 8, 9, 14, - /* column 15: */ 3, 4, 7, 15, 16, 18, - /* column 16: */ 4, 7, 15, 16, - /* column 17: */ 0, 1, 5, 7, 9, 13, 17, 18, 19, - /* column 18: */ 0, 3, 7, 9, 10, 15, 17, 18, 19, - /* column 19: */ 0, 3, 6, 10, 17, 18, 19, 20, 21, - /* column 20: */ 2, 10, 19, 20, 21, 22, - /* column 21: */ 0, 2, 6, 10, 11, 19, 20, 21, 22, - /* column 22: */ 2, 20, 21, 22, - /* column 23: */ 6, 11, 12, 23 } ; - - int P [24], Pinv [24], i, j, k, jnew, p, inew, result ; - double Control [AMD_CONTROL], Info [AMD_INFO] ; - char A [24][24] ; - - /* here is an example of how to use AMD_VERSION. This code will work in - * any version of AMD. */ -#if defined(AMD_VERSION) && (AMD_VERSION >= AMD_VERSION_CODE(1,2)) - printf ("AMD version %d.%d.%d, date: %s\n", - AMD_MAIN_VERSION, AMD_SUB_VERSION, AMD_SUBSUB_VERSION, AMD_DATE) ; -#else - printf ("AMD version: 1.1 or earlier\n") ; -#endif - - printf ("AMD demo, with the 24-by-24 Harwell/Boeing matrix, can_24:\n") ; - - /* get the default parameters, and print them */ - amd_defaults (Control) ; - amd_control (Control) ; - - /* print the input matrix */ - nz = Ap [n] ; - printf ("\nInput matrix: %d-by-%d, with %d entries.\n" - " Note that for a symmetric matrix such as this one, only the\n" - " strictly lower or upper triangular parts would need to be\n" - " passed to AMD, since AMD computes the ordering of A+A'. The\n" - " diagonal entries are also not needed, since AMD ignores them.\n" - , n, n, nz) ; - for (j = 0 ; j < n ; j++) - { - printf ("\nColumn: %d, number of entries: %d, with row indices in" - " Ai [%d ... %d]:\n row indices:", - j, Ap [j+1] - Ap [j], Ap [j], Ap [j+1]-1) ; - for (p = Ap [j] ; p < Ap [j+1] ; p++) - { - i = Ai [p] ; - printf (" %d", i) ; - } - printf ("\n") ; - } - - /* print a character plot of the input matrix. This is only reasonable - * because the matrix is small. */ - printf ("\nPlot of input matrix pattern:\n") ; - for (j = 0 ; j < n ; j++) - { - for (i = 0 ; i < n ; i++) A [i][j] = '.' ; - for (p = Ap [j] ; p < Ap [j+1] ; p++) - { - i = Ai [p] ; - A [i][j] = 'X' ; - } - } - printf (" ") ; - for (j = 0 ; j < n ; j++) printf (" %1d", j % 10) ; - printf ("\n") ; - for (i = 0 ; i < n ; i++) - { - printf ("%2d: ", i) ; - for (j = 0 ; j < n ; j++) - { - printf (" %c", A [i][j]) ; - } - printf ("\n") ; - } - - /* order the matrix */ - result = amd_order (n, Ap, Ai, P, Control, Info) ; - printf ("return value from amd_order: %d (should be %d)\n", - result, AMD_OK) ; - - /* print the statistics */ - amd_info (Info) ; - - if (result != AMD_OK) - { - printf ("AMD failed\n") ; - exit (1) ; - } - - /* print the permutation vector, P, and compute the inverse permutation */ - printf ("Permutation vector:\n") ; - for (k = 0 ; k < n ; k++) - { - /* row/column j is the kth row/column in the permuted matrix */ - j = P [k] ; - Pinv [j] = k ; - printf (" %2d", j) ; - } - printf ("\n\n") ; - - printf ("Inverse permutation vector:\n") ; - for (j = 0 ; j < n ; j++) - { - k = Pinv [j] ; - printf (" %2d", k) ; - } - printf ("\n\n") ; - - /* print a character plot of the permuted matrix. */ - printf ("\nPlot of permuted matrix pattern:\n") ; - for (jnew = 0 ; jnew < n ; jnew++) - { - j = P [jnew] ; - for (inew = 0 ; inew < n ; inew++) A [inew][jnew] = '.' ; - for (p = Ap [j] ; p < Ap [j+1] ; p++) - { - inew = Pinv [Ai [p]] ; - A [inew][jnew] = 'X' ; - } - } - printf (" ") ; - for (j = 0 ; j < n ; j++) printf (" %1d", j % 10) ; - printf ("\n") ; - for (i = 0 ; i < n ; i++) - { - printf ("%2d: ", i) ; - for (j = 0 ; j < n ; j++) - { - printf (" %c", A [i][j]) ; - } - printf ("\n") ; - } - - return (0) ; -} diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Demo/amd_demo.out b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Demo/amd_demo.out deleted file mode 100644 index 9f44f24f5..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Demo/amd_demo.out +++ /dev/null @@ -1,179 +0,0 @@ -AMD version 2.4.6, date: May 4, 2016 -AMD demo, with the 24-by-24 Harwell/Boeing matrix, can_24: - -AMD version 2.4.6, May 4, 2016: approximate minimum degree ordering - dense row parameter: 10 - (rows with more than max (10 * sqrt (n), 16) entries are - considered "dense", and placed last in output permutation) - aggressive absorption: yes - size of AMD integer: 4 - - -Input matrix: 24-by-24, with 160 entries. - Note that for a symmetric matrix such as this one, only the - strictly lower or upper triangular parts would need to be - passed to AMD, since AMD computes the ordering of A+A'. The - diagonal entries are also not needed, since AMD ignores them. - -Column: 0, number of entries: 9, with row indices in Ai [0 ... 8]: - row indices: 0 5 6 12 13 17 18 19 21 - -Column: 1, number of entries: 6, with row indices in Ai [9 ... 14]: - row indices: 1 8 9 13 14 17 - -Column: 2, number of entries: 6, with row indices in Ai [15 ... 20]: - row indices: 2 6 11 20 21 22 - -Column: 3, number of entries: 6, with row indices in Ai [21 ... 26]: - row indices: 3 7 10 15 18 19 - -Column: 4, number of entries: 6, with row indices in Ai [27 ... 32]: - row indices: 4 7 9 14 15 16 - -Column: 5, number of entries: 6, with row indices in Ai [33 ... 38]: - row indices: 0 5 6 12 13 17 - -Column: 6, number of entries: 9, with row indices in Ai [39 ... 47]: - row indices: 0 2 5 6 11 12 19 21 23 - -Column: 7, number of entries: 9, with row indices in Ai [48 ... 56]: - row indices: 3 4 7 9 14 15 16 17 18 - -Column: 8, number of entries: 4, with row indices in Ai [57 ... 60]: - row indices: 1 8 9 14 - -Column: 9, number of entries: 9, with row indices in Ai [61 ... 69]: - row indices: 1 4 7 8 9 13 14 17 18 - -Column: 10, number of entries: 6, with row indices in Ai [70 ... 75]: - row indices: 3 10 18 19 20 21 - -Column: 11, number of entries: 6, with row indices in Ai [76 ... 81]: - row indices: 2 6 11 12 21 23 - -Column: 12, number of entries: 6, with row indices in Ai [82 ... 87]: - row indices: 0 5 6 11 12 23 - -Column: 13, number of entries: 6, with row indices in Ai [88 ... 93]: - row indices: 0 1 5 9 13 17 - -Column: 14, number of entries: 6, with row indices in Ai [94 ... 99]: - row indices: 1 4 7 8 9 14 - -Column: 15, number of entries: 6, with row indices in Ai [100 ... 105]: - row indices: 3 4 7 15 16 18 - -Column: 16, number of entries: 4, with row indices in Ai [106 ... 109]: - row indices: 4 7 15 16 - -Column: 17, number of entries: 9, with row indices in Ai [110 ... 118]: - row indices: 0 1 5 7 9 13 17 18 19 - -Column: 18, number of entries: 9, with row indices in Ai [119 ... 127]: - row indices: 0 3 7 9 10 15 17 18 19 - -Column: 19, number of entries: 9, with row indices in Ai [128 ... 136]: - row indices: 0 3 6 10 17 18 19 20 21 - -Column: 20, number of entries: 6, with row indices in Ai [137 ... 142]: - row indices: 2 10 19 20 21 22 - -Column: 21, number of entries: 9, with row indices in Ai [143 ... 151]: - row indices: 0 2 6 10 11 19 20 21 22 - -Column: 22, number of entries: 4, with row indices in Ai [152 ... 155]: - row indices: 2 20 21 22 - -Column: 23, number of entries: 4, with row indices in Ai [156 ... 159]: - row indices: 6 11 12 23 - -Plot of input matrix pattern: - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 - 0: X . . . . X X . . . . . X X . . . X X X . X . . - 1: . X . . . . . . X X . . . X X . . X . . . . . . - 2: . . X . . . X . . . . X . . . . . . . . X X X . - 3: . . . X . . . X . . X . . . . X . . X X . . . . - 4: . . . . X . . X . X . . . . X X X . . . . . . . - 5: X . . . . X X . . . . . X X . . . X . . . . . . - 6: X . X . . X X . . . . X X . . . . . . X . X . X - 7: . . . X X . . X . X . . . . X X X X X . . . . . - 8: . X . . . . . . X X . . . . X . . . . . . . . . - 9: . X . . X . . X X X . . . X X . . X X . . . . . -10: . . . X . . . . . . X . . . . . . . X X X X . . -11: . . X . . . X . . . . X X . . . . . . . . X . X -12: X . . . . X X . . . . X X . . . . . . . . . . X -13: X X . . . X . . . X . . . X . . . X . . . . . . -14: . X . . X . . X X X . . . . X . . . . . . . . . -15: . . . X X . . X . . . . . . . X X . X . . . . . -16: . . . . X . . X . . . . . . . X X . . . . . . . -17: X X . . . X . X . X . . . X . . . X X X . . . . -18: X . . X . . . X . X X . . . . X . X X X . . . . -19: X . . X . . X . . . X . . . . . . X X X X X . . -20: . . X . . . . . . . X . . . . . . . . X X X X . -21: X . X . . . X . . . X X . . . . . . . X X X X . -22: . . X . . . . . . . . . . . . . . . . . X X X . -23: . . . . . . X . . . . X X . . . . . . . . . . X -return value from amd_order: 0 (should be 0) - -AMD version 2.4.6, May 4, 2016, results: - status: OK - n, dimension of A: 24 - nz, number of nonzeros in A: 160 - symmetry of A: 1.0000 - number of nonzeros on diagonal: 24 - nonzeros in pattern of A+A' (excl. diagonal): 136 - # dense rows/columns of A+A': 0 - memory used, in bytes: 1516 - # of memory compactions: 0 - - The following approximate statistics are for a subsequent - factorization of A(P,P) + A(P,P)'. They are slight upper - bounds if there are no dense rows/columns in A+A', and become - looser if dense rows/columns exist. - - nonzeros in L (excluding diagonal): 97 - nonzeros in L (including diagonal): 121 - # divide operations for LDL' or LU: 97 - # multiply-subtract operations for LDL': 275 - # multiply-subtract operations for LU: 453 - max nz. in any column of L (incl. diagonal): 8 - - chol flop count for real A, sqrt counted as 1 flop: 671 - LDL' flop count for real A: 647 - LDL' flop count for complex A: 3073 - LU flop count for real A (with no pivoting): 1003 - LU flop count for complex A (with no pivoting): 4497 - -Permutation vector: - 22 20 10 23 12 5 16 8 14 4 15 7 1 9 13 17 0 2 3 6 11 18 21 19 - -Inverse permutation vector: - 16 12 17 18 9 5 19 11 7 13 2 20 4 14 8 10 6 15 21 23 1 22 0 3 - - -Plot of permuted matrix pattern: - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 - 0: X X . . . . . . . . . . . . . . . X . . . . X . - 1: X X X . . . . . . . . . . . . . . X . . . . X X - 2: . X X . . . . . . . . . . . . . . . X . . X X X - 3: . . . X X . . . . . . . . . . . . . . X X . . . - 4: . . . X X X . . . . . . . . . . X . . X X . . . - 5: . . . . X X . . . . . . . . X X X . . X . . . . - 6: . . . . . . X . . X X X . . . . . . . . . . . . - 7: . . . . . . . X X . . . X X . . . . . . . . . . - 8: . . . . . . . X X X . X X X . . . . . . . . . . - 9: . . . . . . X . X X X X . X . . . . . . . . . . -10: . . . . . . X . . X X X . . . . . . X . . X . . -11: . . . . . . X . X X X X . X . X . . X . . X . . -12: . . . . . . . X X . . . X X X X . . . . . . . . -13: . . . . . . . X X X . X X X X X . . . . . X . . -14: . . . . . X . . . . . . X X X X X . . . . . . . -15: . . . . . X . . . . . X X X X X X . . . . X . X -16: . . . . X X . . . . . . . . X X X . . X . X X X -17: X X . . . . . . . . . . . . . . . X . X X . X . -18: . . X . . . . . . . X X . . . . . . X . . X . X -19: . . . X X X . . . . . . . . . . X X . X X . X X -20: . . . X X . . . . . . . . . . . . X . X X . X . -21: . . X . . . . . . . X X . X . X X . X . . X . X -22: X X X . . . . . . . . . . . . . X X . X X . X X -23: . X X . . . . . . . . . . . . X X . X X . X X X diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Demo/amd_demo2.c b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Demo/amd_demo2.c deleted file mode 100644 index 68c1e9e8f..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Demo/amd_demo2.c +++ /dev/null @@ -1,208 +0,0 @@ -/* ========================================================================= */ -/* === AMD demo main program (jumbled matrix version) ====================== */ -/* ========================================================================= */ - -/* ------------------------------------------------------------------------- */ -/* AMD Copyright (c) by Timothy A. Davis, */ -/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ -/* DrTimothyAldenDavis@gmail.com, http://www.suitesparse.com */ -/* ------------------------------------------------------------------------- */ - -/* A simple C main program that illustrates the use of the ANSI C interface - * to AMD. - * - * Identical to amd_demo.c, except that it operates on an input matrix that has - * unsorted columns and duplicate entries. - */ - -#include "amd.h" -#include -#include - -int main (void) -{ - /* The symmetric can_24 Harwell/Boeing matrix (jumbled, and not symmetric). - * Since AMD operates on A+A', only A(i,j) or A(j,i) need to be specified, - * or both. The diagonal entries are optional (some are missing). - * There are many duplicate entries, which must be removed. */ - int n = 24, nz, - Ap [ ] = { 0, 9, 14, 20, 28, 33, 37, 44, 53, 58, 63, 63, 66, 69, 72, 75, - 78, 82, 86, 91, 97, 101, 112, 112, 116 }, - Ai [ ] = { - /* column 0: */ 0, 17, 18, 21, 5, 12, 5, 0, 13, - /* column 1: */ 14, 1, 8, 13, 17, - /* column 2: */ 2, 20, 11, 6, 11, 22, - /* column 3: */ 3, 3, 10, 7, 18, 18, 15, 19, - /* column 4: */ 7, 9, 15, 14, 16, - /* column 5: */ 5, 13, 6, 17, - /* column 6: */ 5, 0, 11, 6, 12, 6, 23, - /* column 7: */ 3, 4, 9, 7, 14, 16, 15, 17, 18, - /* column 8: */ 1, 9, 14, 14, 14, - /* column 9: */ 7, 13, 8, 1, 17, - /* column 10: */ - /* column 11: */ 2, 12, 23, - /* column 12: */ 5, 11, 12, - /* column 13: */ 0, 13, 17, - /* column 14: */ 1, 9, 14, - /* column 15: */ 3, 15, 16, - /* column 16: */ 16, 4, 4, 15, - /* column 17: */ 13, 17, 19, 17, - /* column 18: */ 15, 17, 19, 9, 10, - /* column 19: */ 17, 19, 20, 0, 6, 10, - /* column 20: */ 22, 10, 20, 21, - /* column 21: */ 6, 2, 10, 19, 20, 11, 21, 22, 22, 22, 22, - /* column 22: */ - /* column 23: */ 12, 11, 12, 23 } ; - - int P [24], Pinv [24], i, j, k, jnew, p, inew, result ; - double Control [AMD_CONTROL], Info [AMD_INFO] ; - char A [24][24] ; - - printf ("AMD demo, with a jumbled version of the 24-by-24\n") ; - printf ("Harwell/Boeing matrix, can_24:\n") ; - - /* get the default parameters, and print them */ - amd_defaults (Control) ; - amd_control (Control) ; - - /* print the input matrix */ - nz = Ap [n] ; - printf ("\nJumbled input matrix: %d-by-%d, with %d entries.\n" - " Note that for a symmetric matrix such as this one, only the\n" - " strictly lower or upper triangular parts would need to be\n" - " passed to AMD, since AMD computes the ordering of A+A'. The\n" - " diagonal entries are also not needed, since AMD ignores them.\n" - " This version of the matrix has jumbled columns and duplicate\n" - " row indices.\n", n, n, nz) ; - for (j = 0 ; j < n ; j++) - { - printf ("\nColumn: %d, number of entries: %d, with row indices in" - " Ai [%d ... %d]:\n row indices:", - j, Ap [j+1] - Ap [j], Ap [j], Ap [j+1]-1) ; - for (p = Ap [j] ; p < Ap [j+1] ; p++) - { - i = Ai [p] ; - printf (" %d", i) ; - } - printf ("\n") ; - } - - /* print a character plot of the input matrix. This is only reasonable - * because the matrix is small. */ - printf ("\nPlot of (jumbled) input matrix pattern:\n") ; - for (j = 0 ; j < n ; j++) - { - for (i = 0 ; i < n ; i++) A [i][j] = '.' ; - for (p = Ap [j] ; p < Ap [j+1] ; p++) - { - i = Ai [p] ; - A [i][j] = 'X' ; - } - } - printf (" ") ; - for (j = 0 ; j < n ; j++) printf (" %1d", j % 10) ; - printf ("\n") ; - for (i = 0 ; i < n ; i++) - { - printf ("%2d: ", i) ; - for (j = 0 ; j < n ; j++) - { - printf (" %c", A [i][j]) ; - } - printf ("\n") ; - } - - /* print a character plot of the matrix A+A'. */ - printf ("\nPlot of symmetric matrix to be ordered by amd_order:\n") ; - for (j = 0 ; j < n ; j++) - { - for (i = 0 ; i < n ; i++) A [i][j] = '.' ; - } - for (j = 0 ; j < n ; j++) - { - A [j][j] = 'X' ; - for (p = Ap [j] ; p < Ap [j+1] ; p++) - { - i = Ai [p] ; - A [i][j] = 'X' ; - A [j][i] = 'X' ; - } - } - printf (" ") ; - for (j = 0 ; j < n ; j++) printf (" %1d", j % 10) ; - printf ("\n") ; - for (i = 0 ; i < n ; i++) - { - printf ("%2d: ", i) ; - for (j = 0 ; j < n ; j++) - { - printf (" %c", A [i][j]) ; - } - printf ("\n") ; - } - - /* order the matrix */ - result = amd_order (n, Ap, Ai, P, Control, Info) ; - printf ("return value from amd_order: %d (should be %d)\n", - result, AMD_OK_BUT_JUMBLED) ; - - /* print the statistics */ - amd_info (Info) ; - - if (result != AMD_OK_BUT_JUMBLED) - { - printf ("AMD failed\n") ; - exit (1) ; - } - - /* print the permutation vector, P, and compute the inverse permutation */ - printf ("Permutation vector:\n") ; - for (k = 0 ; k < n ; k++) - { - /* row/column j is the kth row/column in the permuted matrix */ - j = P [k] ; - Pinv [j] = k ; - printf (" %2d", j) ; - } - printf ("\n\n") ; - - printf ("Inverse permutation vector:\n") ; - for (j = 0 ; j < n ; j++) - { - k = Pinv [j] ; - printf (" %2d", k) ; - } - printf ("\n\n") ; - - /* print a character plot of the permuted matrix. */ - printf ("\nPlot of (symmetrized) permuted matrix pattern:\n") ; - for (j = 0 ; j < n ; j++) - { - for (i = 0 ; i < n ; i++) A [i][j] = '.' ; - } - for (jnew = 0 ; jnew < n ; jnew++) - { - j = P [jnew] ; - A [jnew][jnew] = 'X' ; - for (p = Ap [j] ; p < Ap [j+1] ; p++) - { - inew = Pinv [Ai [p]] ; - A [inew][jnew] = 'X' ; - A [jnew][inew] = 'X' ; - } - } - printf (" ") ; - for (j = 0 ; j < n ; j++) printf (" %1d", j % 10) ; - printf ("\n") ; - for (i = 0 ; i < n ; i++) - { - printf ("%2d: ", i) ; - for (j = 0 ; j < n ; j++) - { - printf (" %c", A [i][j]) ; - } - printf ("\n") ; - } - - return (0) ; -} diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Demo/amd_demo2.out b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Demo/amd_demo2.out deleted file mode 100644 index bd65055fd..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Demo/amd_demo2.out +++ /dev/null @@ -1,208 +0,0 @@ -AMD demo, with a jumbled version of the 24-by-24 -Harwell/Boeing matrix, can_24: - -AMD version 2.4.6, May 4, 2016: approximate minimum degree ordering - dense row parameter: 10 - (rows with more than max (10 * sqrt (n), 16) entries are - considered "dense", and placed last in output permutation) - aggressive absorption: yes - size of AMD integer: 4 - - -Jumbled input matrix: 24-by-24, with 116 entries. - Note that for a symmetric matrix such as this one, only the - strictly lower or upper triangular parts would need to be - passed to AMD, since AMD computes the ordering of A+A'. The - diagonal entries are also not needed, since AMD ignores them. - This version of the matrix has jumbled columns and duplicate - row indices. - -Column: 0, number of entries: 9, with row indices in Ai [0 ... 8]: - row indices: 0 17 18 21 5 12 5 0 13 - -Column: 1, number of entries: 5, with row indices in Ai [9 ... 13]: - row indices: 14 1 8 13 17 - -Column: 2, number of entries: 6, with row indices in Ai [14 ... 19]: - row indices: 2 20 11 6 11 22 - -Column: 3, number of entries: 8, with row indices in Ai [20 ... 27]: - row indices: 3 3 10 7 18 18 15 19 - -Column: 4, number of entries: 5, with row indices in Ai [28 ... 32]: - row indices: 7 9 15 14 16 - -Column: 5, number of entries: 4, with row indices in Ai [33 ... 36]: - row indices: 5 13 6 17 - -Column: 6, number of entries: 7, with row indices in Ai [37 ... 43]: - row indices: 5 0 11 6 12 6 23 - -Column: 7, number of entries: 9, with row indices in Ai [44 ... 52]: - row indices: 3 4 9 7 14 16 15 17 18 - -Column: 8, number of entries: 5, with row indices in Ai [53 ... 57]: - row indices: 1 9 14 14 14 - -Column: 9, number of entries: 5, with row indices in Ai [58 ... 62]: - row indices: 7 13 8 1 17 - -Column: 10, number of entries: 0, with row indices in Ai [63 ... 62]: - row indices: - -Column: 11, number of entries: 3, with row indices in Ai [63 ... 65]: - row indices: 2 12 23 - -Column: 12, number of entries: 3, with row indices in Ai [66 ... 68]: - row indices: 5 11 12 - -Column: 13, number of entries: 3, with row indices in Ai [69 ... 71]: - row indices: 0 13 17 - -Column: 14, number of entries: 3, with row indices in Ai [72 ... 74]: - row indices: 1 9 14 - -Column: 15, number of entries: 3, with row indices in Ai [75 ... 77]: - row indices: 3 15 16 - -Column: 16, number of entries: 4, with row indices in Ai [78 ... 81]: - row indices: 16 4 4 15 - -Column: 17, number of entries: 4, with row indices in Ai [82 ... 85]: - row indices: 13 17 19 17 - -Column: 18, number of entries: 5, with row indices in Ai [86 ... 90]: - row indices: 15 17 19 9 10 - -Column: 19, number of entries: 6, with row indices in Ai [91 ... 96]: - row indices: 17 19 20 0 6 10 - -Column: 20, number of entries: 4, with row indices in Ai [97 ... 100]: - row indices: 22 10 20 21 - -Column: 21, number of entries: 11, with row indices in Ai [101 ... 111]: - row indices: 6 2 10 19 20 11 21 22 22 22 22 - -Column: 22, number of entries: 0, with row indices in Ai [112 ... 111]: - row indices: - -Column: 23, number of entries: 4, with row indices in Ai [112 ... 115]: - row indices: 12 11 12 23 - -Plot of (jumbled) input matrix pattern: - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 - 0: X . . . . . X . . . . . . X . . . . . X . . . . - 1: . X . . . . . . X X . . . . X . . . . . . . . . - 2: . . X . . . . . . . . X . . . . . . . . . X . . - 3: . . . X . . . X . . . . . . . X . . . . . . . . - 4: . . . . . . . X . . . . . . . . X . . . . . . . - 5: X . . . . X X . . . . . X . . . . . . . . . . . - 6: . . X . . X X . . . . . . . . . . . . X . X . . - 7: . . . X X . . X . X . . . . . . . . . . . . . . - 8: . X . . . . . . . X . . . . . . . . . . . . . . - 9: . . . . X . . X X . . . . . X . . . X . . . . . -10: . . . X . . . . . . . . . . . . . . X X X X . . -11: . . X . . . X . . . . . X . . . . . . . . X . X -12: X . . . . . X . . . . X X . . . . . . . . . . X -13: X X . . . X . . . X . . . X . . . X . . . . . . -14: . X . . X . . X X . . . . . X . . . . . . . . . -15: . . . X X . . X . . . . . . . X X . X . . . . . -16: . . . . X . . X . . . . . . . X X . . . . . . . -17: X X . . . X . X . X . . . X . . . X X X . . . . -18: X . . X . . . X . . . . . . . . . . . . . . . . -19: . . . X . . . . . . . . . . . . . X X X . X . . -20: . . X . . . . . . . . . . . . . . . . X X X . . -21: X . . . . . . . . . . . . . . . . . . . X X . . -22: . . X . . . . . . . . . . . . . . . . . X X . . -23: . . . . . . X . . . . X . . . . . . . . . . . X - -Plot of symmetric matrix to be ordered by amd_order: - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 - 0: X . . . . X X . . . . . X X . . . X X X . X . . - 1: . X . . . . . . X X . . . X X . . X . . . . . . - 2: . . X . . . X . . . . X . . . . . . . . X X X . - 3: . . . X . . . X . . X . . . . X . . X X . . . . - 4: . . . . X . . X . X . . . . X X X . . . . . . . - 5: X . . . . X X . . . . . X X . . . X . . . . . . - 6: X . X . . X X . . . . X X . . . . . . X . X . X - 7: . . . X X . . X . X . . . . X X X X X . . . . . - 8: . X . . . . . . X X . . . . X . . . . . . . . . - 9: . X . . X . . X X X . . . X X . . X X . . . . . -10: . . . X . . . . . . X . . . . . . . X X X X . . -11: . . X . . . X . . . . X X . . . . . . . . X . X -12: X . . . . X X . . . . X X . . . . . . . . . . X -13: X X . . . X . . . X . . . X . . . X . . . . . . -14: . X . . X . . X X X . . . . X . . . . . . . . . -15: . . . X X . . X . . . . . . . X X . X . . . . . -16: . . . . X . . X . . . . . . . X X . . . . . . . -17: X X . . . X . X . X . . . X . . . X X X . . . . -18: X . . X . . . X . X X . . . . X . X X X . . . . -19: X . . X . . X . . . X . . . . . . X X X X X . . -20: . . X . . . . . . . X . . . . . . . . X X X X . -21: X . X . . . X . . . X X . . . . . . . X X X X . -22: . . X . . . . . . . . . . . . . . . . . X X X . -23: . . . . . . X . . . . X X . . . . . . . . . . X -return value from amd_order: 1 (should be 1) - -AMD version 2.4.6, May 4, 2016, results: - status: OK, but jumbled - n, dimension of A: 24 - nz, number of nonzeros in A: 102 - symmetry of A: 0.4000 - number of nonzeros on diagonal: 17 - nonzeros in pattern of A+A' (excl. diagonal): 136 - # dense rows/columns of A+A': 0 - memory used, in bytes: 2080 - # of memory compactions: 0 - - The following approximate statistics are for a subsequent - factorization of A(P,P) + A(P,P)'. They are slight upper - bounds if there are no dense rows/columns in A+A', and become - looser if dense rows/columns exist. - - nonzeros in L (excluding diagonal): 97 - nonzeros in L (including diagonal): 121 - # divide operations for LDL' or LU: 97 - # multiply-subtract operations for LDL': 275 - # multiply-subtract operations for LU: 453 - max nz. in any column of L (incl. diagonal): 8 - - chol flop count for real A, sqrt counted as 1 flop: 671 - LDL' flop count for real A: 647 - LDL' flop count for complex A: 3073 - LU flop count for real A (with no pivoting): 1003 - LU flop count for complex A (with no pivoting): 4497 - -Permutation vector: - 22 20 10 23 12 5 16 8 14 4 15 7 1 9 13 17 0 2 3 6 11 18 21 19 - -Inverse permutation vector: - 16 12 17 18 9 5 19 11 7 13 2 20 4 14 8 10 6 15 21 23 1 22 0 3 - - -Plot of (symmetrized) permuted matrix pattern: - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 - 0: X X . . . . . . . . . . . . . . . X . . . . X . - 1: X X X . . . . . . . . . . . . . . X . . . . X X - 2: . X X . . . . . . . . . . . . . . . X . . X X X - 3: . . . X X . . . . . . . . . . . . . . X X . . . - 4: . . . X X X . . . . . . . . . . X . . X X . . . - 5: . . . . X X . . . . . . . . X X X . . X . . . . - 6: . . . . . . X . . X X X . . . . . . . . . . . . - 7: . . . . . . . X X . . . X X . . . . . . . . . . - 8: . . . . . . . X X X . X X X . . . . . . . . . . - 9: . . . . . . X . X X X X . X . . . . . . . . . . -10: . . . . . . X . . X X X . . . . . . X . . X . . -11: . . . . . . X . X X X X . X . X . . X . . X . . -12: . . . . . . . X X . . . X X X X . . . . . . . . -13: . . . . . . . X X X . X X X X X . . . . . X . . -14: . . . . . X . . . . . . X X X X X . . . . . . . -15: . . . . . X . . . . . X X X X X X . . . . X . X -16: . . . . X X . . . . . . . . X X X . . X . X X X -17: X X . . . . . . . . . . . . . . . X . X X . X . -18: . . X . . . . . . . X X . . . . . . X . . X . X -19: . . . X X X . . . . . . . . . . X X . X X . X X -20: . . . X X . . . . . . . . . . . . X . X X . X . -21: . . X . . . . . . . X X . X . X X . X . . X . X -22: X X X . . . . . . . . . . . . . X X . X X . X X -23: . X X . . . . . . . . . . . . X X . X X . X X X diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Demo/amd_f77cross.f b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Demo/amd_f77cross.f deleted file mode 100644 index b79920d80..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Demo/amd_f77cross.f +++ /dev/null @@ -1,50 +0,0 @@ -C ====================================================================== -C === AMD_cross ======================================================== -C ====================================================================== - -C ---------------------------------------------------------------------- -C AMD, Copyright (c) by Timothy A. Davis, Patrick R. -C Amestoy, and Iain S. Duff. See ../README.txt for License. -C email: DrTimothyAldenDavis@gmail.com -C ---------------------------------------------------------------------- - -C This program provides an example of how to call the C version of AMD -C from a Fortran program. It is HIGHLY non-portable. - -C The amd_order routine returns PERM (1) < 0 if an error occurs. -C (-1: out of memory, -2: invalid matrix) - -C Note that the input matrix is 0-based. From Fortran, column j of the -C matrix is in AI (AP (I)+1 ... AP (I+1)). The row indices in this -C set are in the range 0 to N-1. To demonstrate this translation, -C the input matrix is printed in 1-based form. This program uses -C the same 5-by-5 test matrix as amd_simple.c. - - INTEGER N, NZ, K, P - PARAMETER (N = 5, NZ = 14) - INTEGER AP (N+1), AI (NZ), PERM (N) - DATA AP / 0, 2, 6, 10, 12, 14 / - DATA AI / 0,1, 0,1,2,4, 1,2,3,4, 2,3, 1,4 / - DOUBLE PRECISION CONTROL (5), INFO (20) - -C print the input matrix - PRINT 10, N, N, NZ -10 FORMAT ('Input matrix:', I2, '-by-', I2, ' with',I3,' entries') - DO 40 J = 1, N - PRINT 20, J, AP (J+1) - AP (J), AP (J)+1, AP (J+1) -20 FORMAT ( /, 'Column: ', I2, ' number of entries: ', I2, - $ ' with row indices in AI (', I3, ' ... ', I3, ')') - PRINT 30, ((AI (P) + 1), P = AP (J) + 1, AP (J+1)) -30 FORMAT (' row indices: ', 24I3) -40 CONTINUE - - CALL AMDDEFAULTS (CONTROL) - CALL AMDORDER (N, AP, AI, PERM, CONTROL, INFO) - CALL AMDINFO (INFO) - - DO 60 K = 1, N - PRINT 50, K, PERM (K) + 1 -50 FORMAT ('PERM (',I2,') = ', I2) -60 CONTINUE - END - diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Demo/amd_f77cross.out b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Demo/amd_f77cross.out deleted file mode 100644 index 6d8a670da..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Demo/amd_f77cross.out +++ /dev/null @@ -1,51 +0,0 @@ -Input matrix: 5-by- 5 with 14 entries - -Column: 1 number of entries: 2 with row indices in AI ( 1 ... 2) - row indices: 1 2 - -Column: 2 number of entries: 4 with row indices in AI ( 3 ... 6) - row indices: 1 2 3 5 - -Column: 3 number of entries: 4 with row indices in AI ( 7 ... 10) - row indices: 2 3 4 5 - -Column: 4 number of entries: 2 with row indices in AI ( 11 ... 12) - row indices: 3 4 - -Column: 5 number of entries: 2 with row indices in AI ( 13 ... 14) - row indices: 2 5 - -amd: approximate minimum degree ordering, results: - status: OK - n, dimension of A: 5 - nz, number of nonzeros in A: 14 - symmetry of A: 0.8889 - number of nonzeros on diagonal: 5 - nonzeros in pattern of A+A' (excl. diagonal): 10 - # dense rows/columns of A+A': 0 - memory used, in bytes: 228 - # of memory compactions: 0 - - The following approximate statistics are for a subsequent - factorization of A(P,P) + A(P,P)'. They are slight upper - bounds if there are no dense rows/columns in A+A', and become - looser if dense rows/columns exist. - - nonzeros in L (excluding diagonal): 5 - nonzeros in L (including diagonal): 10 - # divide operations for LDL' or LU: 5 - # multiply-subtract operations for LDL': 6 - # multiply-subtract operations for LU: 7 - max nz. in any column of L (incl. diagonal): 3 - - chol flop count for real A, sqrt counted as 1 flop: 22 - LDL' flop count for real A: 17 - LDL' flop count for complex A: 93 - LU flop count for real A (with no pivoting): 19 - LU flop count for complex A (with no pivoting): 101 - -PERM ( 1) = 1 -PERM ( 2) = 4 -PERM ( 3) = 3 -PERM ( 4) = 5 -PERM ( 5) = 2 diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Demo/amd_f77demo.f b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Demo/amd_f77demo.f deleted file mode 100644 index 6aa6296a2..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Demo/amd_f77demo.f +++ /dev/null @@ -1,160 +0,0 @@ -C ====================================================================== -C === Fortran AMD demo main program ==================================== -C ====================================================================== - -C ---------------------------------------------------------------------- -C AMD, Copyright (c) by Timothy A. Davis, Patrick R. -C Amestoy, and Iain S. Duff. See ../README.txt for License. -C email: DrTimothyAldenDavis@gmail.com -C ---------------------------------------------------------------------- - -C A simple Fortran 77 main program that illustrates the use of the -C Fortran version of AMD (both the AMD and AMDBAR routines). Note -C that aggressive absorption has no effect on this particular matrix. - -C AP and AI contain the symmetric can_24 Harwell/Boeing matrix, -C including upper and lower triangular parts, but excluding the -C diagonal entries. Note that this matrix is 1-based, with row -C and column indices in the range 1 to N. - - INTEGER N, NZ, IWLEN, PFREE, I, J, K, JNEW, P, INEW, - $ METHOD, NCMPA - PARAMETER (N = 24, NZ = 136, IWLEN = 200) - INTEGER PE (N), DEGREE (N), NV (N), NEXT (N), PERM (N), W (N), - $ HEAD (N), PINV (N), LEN (N), AP (N+1), AI (NZ), IW (IWLEN) - CHARACTER A (24,24) - - DATA AP - $ / 1, 9, 14, 19, 24, 29, 34, 42, 50, 53, 61, 66, 71, - $ 76, 81, 86, 91, 94, 102, 110, 118, 123, 131, 134, 137 / - DATA AI / - $ 6, 7, 13, 14, 18, 19, 20, 22, - $ 9, 10, 14, 15, 18, - $ 7, 12, 21, 22, 23, - $ 8, 11, 16, 19, 20, - $ 8, 10, 15, 16, 17, - $ 1, 7, 13, 14, 18, - $ 1, 3, 6, 12, 13, 20, 22, 24, - $ 4, 5, 10, 15, 16, 17, 18, 19, - $ 2, 10, 15, - $ 2, 5, 8, 9, 14, 15, 18, 19, - $ 4, 19, 20, 21, 22, - $ 3, 7, 13, 22, 24, - $ 1, 6, 7, 12, 24, - $ 1, 2, 6, 10, 18, - $ 2, 5, 8, 9, 10, - $ 4, 5, 8, 17, 19, - $ 5, 8, 16, - $ 1, 2, 6, 8, 10, 14, 19, 20, - $ 1, 4, 8, 10, 11, 16, 18, 20, - $ 1, 4, 7, 11, 18, 19, 21, 22, - $ 3, 11, 20, 22, 23, - $ 1, 3, 7, 11, 12, 20, 21, 23, - $ 3, 21, 22, - $ 7, 12, 13 / - -C print the input matrix - PRINT 11, N, N, NZ -11 FORMAT ('AMD Fortran 77 demo, with the 24-by-24', - $ ' Harwell/Boeing matrix, can_24:' - $ /, 'Input matrix: ', I2, '-by-', I2,' with ',I3,' entries', - $ /, 'Note that the Fortran version of AMD requires that' - $ /, 'no diagonal entries be present.') - DO 20 J = 1, N - PRINT 21, J, AP (J+1) - AP (J), AP (J), AP (J+1)-1 -21 FORMAT ( /, 'Column: ', I2, ' number of entries: ', I2, - $ ' with row indices in AI (', I3, ' ... ', I3, ')') - PRINT 10, ((AI (P)), P = AP (J), AP (J+1) - 1) -10 FORMAT (' row indices: ', 24I3) -20 CONTINUE - -C print a character plot of the input matrix. This is only -C reasonable because the matrix is small. - PRINT 31 -31 FORMAT ('Plot of input matrix pattern:') - DO 50 J = 1,N - DO 30 I = 1,N - A (I, J) = '.' -30 CONTINUE -C add the diagonal entry to the plot - A (J, J) = 'X' - DO 40 P = AP (J), AP (J+1) - 1 - I = AI (P) - A (I, J) = 'X' -40 CONTINUE -50 CONTINUE - PRINT 60, ((MOD (J, 10)), J = 1,N) -60 FORMAT (' ', 24I2) - DO 80 I = 1,N - PRINT 70, I, (A (I, J), J = 1,N) -70 FORMAT (' ', I2, ': ', 24A2) -80 CONTINUE - - DO 190 METHOD = 1,2 - -C load the matrix into AMD's workspace - DO 90 J = 1,N - PE (J) = AP (J) - LEN (J) = AP (J+1) - AP (J) -90 CONTINUE - DO 100 P = 1,NZ - IW (P) = AI (P) -100 CONTINUE - PFREE = NZ + 1 - -C order the matrix using AMD or AMDBAR - IF (METHOD .EQ. 1) THEN - PRINT 101 -101 FORMAT (/, '------------------------------------------', - $ /, 'ordering the matrix with AMD', - $ /, '------------------------------------------') - CALL AMD (N, PE, IW, LEN, IWLEN, PFREE, NV, NEXT, - $ PERM, HEAD, PINV, DEGREE, NCMPA, W) - ELSE - PRINT 102 -102 FORMAT (/, '------------------------------------------', - $ /, 'ordering the matrix with AMDBAR', - $ /, '------------------------------------------') - CALL AMDBAR (N, PE, IW, LEN, IWLEN, PFREE, NV, NEXT, - $ PERM, HEAD, PINV, DEGREE, NCMPA, W) - ENDIF - -C print the permutation vector, PERM, and its inverse, PINV. -C row/column J = PERM (K) is the Kth row/column in the -C permuted matrix. - PRINT 110, (PERM (K), K = 1,N) -110 FORMAT (/, 'Permutation vector: ', /, 24I3) - PRINT 120, (PINV (J), J = 1,N) -120 FORMAT (/, 'Inverse permutation vector: ', /, 24I3) - -C print a character plot of the permuted matrix. - PRINT 121 -121 FORMAT ('Plot of permuted matrix pattern:') - DO 150 JNEW = 1,N - J = PERM (JNEW) - DO 130 INEW = 1,N - A (INEW, JNEW) = '.' -130 CONTINUE -C add the diagonal entry to the plot - A (JNEW, JNEW) = 'X' - DO 140 P = AP (J), AP (J+1) - 1 - INEW = PINV (AI (P)) - A (INEW, JNEW) = 'X' -140 CONTINUE -150 CONTINUE - PRINT 60, ((MOD (J, 10)), J = 1,N) - DO 160 I = 1,N - PRINT 70, I, (A (I, J), J = 1,N) -160 CONTINUE - -C print the permuted matrix, PERM*A*PERM' - DO 180 JNEW = 1,N - J = PERM (JNEW) - PRINT 171, JNEW, J, AP (J+1) - AP (J) -171 FORMAT (/, 'New column: ', I2, ' old column: ', I2, - $ ' number of entries: ', I2) - PRINT 170, (PINV (AI (P)), P = AP (J), AP (J+1) - 1) -170 FORMAT (' new row indices: ', 24I3) -180 CONTINUE -190 CONTINUE - END diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Demo/amd_f77demo.out b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Demo/amd_f77demo.out deleted file mode 100644 index 10758dd51..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Demo/amd_f77demo.out +++ /dev/null @@ -1,318 +0,0 @@ -AMD Fortran 77 demo, with the 24-by-24 Harwell/Boeing matrix, can_24: -Input matrix: 24-by-24 with 136 entries -Note that the Fortran version of AMD requires that -no diagonal entries be present. - -Column: 1 number of entries: 8 with row indices in AI ( 1 ... 8) - row indices: 6 7 13 14 18 19 20 22 - -Column: 2 number of entries: 5 with row indices in AI ( 9 ... 13) - row indices: 9 10 14 15 18 - -Column: 3 number of entries: 5 with row indices in AI ( 14 ... 18) - row indices: 7 12 21 22 23 - -Column: 4 number of entries: 5 with row indices in AI ( 19 ... 23) - row indices: 8 11 16 19 20 - -Column: 5 number of entries: 5 with row indices in AI ( 24 ... 28) - row indices: 8 10 15 16 17 - -Column: 6 number of entries: 5 with row indices in AI ( 29 ... 33) - row indices: 1 7 13 14 18 - -Column: 7 number of entries: 8 with row indices in AI ( 34 ... 41) - row indices: 1 3 6 12 13 20 22 24 - -Column: 8 number of entries: 8 with row indices in AI ( 42 ... 49) - row indices: 4 5 10 15 16 17 18 19 - -Column: 9 number of entries: 3 with row indices in AI ( 50 ... 52) - row indices: 2 10 15 - -Column: 10 number of entries: 8 with row indices in AI ( 53 ... 60) - row indices: 2 5 8 9 14 15 18 19 - -Column: 11 number of entries: 5 with row indices in AI ( 61 ... 65) - row indices: 4 19 20 21 22 - -Column: 12 number of entries: 5 with row indices in AI ( 66 ... 70) - row indices: 3 7 13 22 24 - -Column: 13 number of entries: 5 with row indices in AI ( 71 ... 75) - row indices: 1 6 7 12 24 - -Column: 14 number of entries: 5 with row indices in AI ( 76 ... 80) - row indices: 1 2 6 10 18 - -Column: 15 number of entries: 5 with row indices in AI ( 81 ... 85) - row indices: 2 5 8 9 10 - -Column: 16 number of entries: 5 with row indices in AI ( 86 ... 90) - row indices: 4 5 8 17 19 - -Column: 17 number of entries: 3 with row indices in AI ( 91 ... 93) - row indices: 5 8 16 - -Column: 18 number of entries: 8 with row indices in AI ( 94 ... 101) - row indices: 1 2 6 8 10 14 19 20 - -Column: 19 number of entries: 8 with row indices in AI (102 ... 109) - row indices: 1 4 8 10 11 16 18 20 - -Column: 20 number of entries: 8 with row indices in AI (110 ... 117) - row indices: 1 4 7 11 18 19 21 22 - -Column: 21 number of entries: 5 with row indices in AI (118 ... 122) - row indices: 3 11 20 22 23 - -Column: 22 number of entries: 8 with row indices in AI (123 ... 130) - row indices: 1 3 7 11 12 20 21 23 - -Column: 23 number of entries: 3 with row indices in AI (131 ... 133) - row indices: 3 21 22 - -Column: 24 number of entries: 3 with row indices in AI (134 ... 136) - row indices: 7 12 13 -Plot of input matrix pattern: - 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 - 1: X . . . . X X . . . . . X X . . . X X X . X . . - 2: . X . . . . . . X X . . . X X . . X . . . . . . - 3: . . X . . . X . . . . X . . . . . . . . X X X . - 4: . . . X . . . X . . X . . . . X . . X X . . . . - 5: . . . . X . . X . X . . . . X X X . . . . . . . - 6: X . . . . X X . . . . . X X . . . X . . . . . . - 7: X . X . . X X . . . . X X . . . . . . X . X . X - 8: . . . X X . . X . X . . . . X X X X X . . . . . - 9: . X . . . . . . X X . . . . X . . . . . . . . . - 10: . X . . X . . X X X . . . X X . . X X . . . . . - 11: . . . X . . . . . . X . . . . . . . X X X X . . - 12: . . X . . . X . . . . X X . . . . . . . . X . X - 13: X . . . . X X . . . . X X . . . . . . . . . . X - 14: X X . . . X . . . X . . . X . . . X . . . . . . - 15: . X . . X . . X X X . . . . X . . . . . . . . . - 16: . . . X X . . X . . . . . . . X X . X . . . . . - 17: . . . . X . . X . . . . . . . X X . . . . . . . - 18: X X . . . X . X . X . . . X . . . X X X . . . . - 19: X . . X . . . X . X X . . . . X . X X X . . . . - 20: X . . X . . X . . . X . . . . . . X X X X X . . - 21: . . X . . . . . . . X . . . . . . . . X X X X . - 22: X . X . . . X . . . X X . . . . . . . X X X X . - 23: . . X . . . . . . . . . . . . . . . . . X X X . - 24: . . . . . . X . . . . X X . . . . . . . . . . X - ------------------------------------------- -ordering the matrix with AMD ------------------------------------------- - -Permutation vector: - 24 23 17 9 15 5 21 13 6 11 16 8 2 10 14 18 1 3 4 19 7 12 22 20 - -Inverse permutation vector: - 17 13 18 19 6 9 21 12 4 14 10 22 8 15 5 11 3 16 20 24 7 23 2 1 -Plot of permuted matrix pattern: - 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 - 1: X . . . . . . X . . . . . . . . . . . . X X . . - 2: . X . . . . X . . . . . . . . . . X . . . . X . - 3: . . X . . X . . . . X X . . . . . . . . . . . . - 4: . . . X X . . . . . . . X X . . . . . . . . . . - 5: . . . X X X . . . . . X X X . . . . . . . . . . - 6: . . X . X X . . . . X X . X . . . . . . . . . . - 7: . X . . . . X . . X . . . . . . . X . . . . X X - 8: X . . . . . . X X . . . . . . . X . . . X X . . - 9: . . . . . . . X X . . . . . X X X . . . X . . . - 10: . . . . . . X . . X . . . . . . . . X X . . X X - 11: . . X . . X . . . . X X . . . . . . X X . . . . - 12: . . X . X X . . . . X X . X . X . . X X . . . . - 13: . . . X X . . . . . . . X X X X . . . . . . . . - 14: . . . X X X . . . . . X X X X X . . . X . . . . - 15: . . . . . . . . X . . . X X X X X . . . . . . . - 16: . . . . . . . . X . . X X X X X X . . X . . . X - 17: . . . . . . . X X . . . . . X X X . . X X . X X - 18: . X . . . . X . . . . . . . . . . X . . X X X . - 19: . . . . . . . . . X X X . . . . . . X X . . . X - 20: . . . . . . . . . X X X . X . X X . X X . . . X - 21: X . . . . . . X X . . . . . . . X X . . X X X X - 22: X . . . . . . X . . . . . . . . . X . . X X X . - 23: . X . . . . X . . X . . . . . . X X . . X X X X - 24: . . . . . . X . . X . . . . . X X . X X X . X X - -New column: 1 old column: 24 number of entries: 3 - new row indices: 21 22 8 - -New column: 2 old column: 23 number of entries: 3 - new row indices: 18 7 23 - -New column: 3 old column: 17 number of entries: 3 - new row indices: 6 12 11 - -New column: 4 old column: 9 number of entries: 3 - new row indices: 13 14 5 - -New column: 5 old column: 15 number of entries: 5 - new row indices: 13 6 12 4 14 - -New column: 6 old column: 5 number of entries: 5 - new row indices: 12 14 5 11 3 - -New column: 7 old column: 21 number of entries: 5 - new row indices: 18 10 24 23 2 - -New column: 8 old column: 13 number of entries: 5 - new row indices: 17 9 21 22 1 - -New column: 9 old column: 6 number of entries: 5 - new row indices: 17 21 8 15 16 - -New column: 10 old column: 11 number of entries: 5 - new row indices: 19 20 24 7 23 - -New column: 11 old column: 16 number of entries: 5 - new row indices: 19 6 12 3 20 - -New column: 12 old column: 8 number of entries: 8 - new row indices: 19 6 14 5 11 3 16 20 - -New column: 13 old column: 2 number of entries: 5 - new row indices: 4 14 15 5 16 - -New column: 14 old column: 10 number of entries: 8 - new row indices: 13 6 12 4 15 5 16 20 - -New column: 15 old column: 14 number of entries: 5 - new row indices: 17 13 9 14 16 - -New column: 16 old column: 18 number of entries: 8 - new row indices: 17 13 9 12 14 15 20 24 - -New column: 17 old column: 1 number of entries: 8 - new row indices: 9 21 8 15 16 20 24 23 - -New column: 18 old column: 3 number of entries: 5 - new row indices: 21 22 7 23 2 - -New column: 19 old column: 4 number of entries: 5 - new row indices: 12 10 11 20 24 - -New column: 20 old column: 19 number of entries: 8 - new row indices: 17 19 12 14 10 11 16 24 - -New column: 21 old column: 7 number of entries: 8 - new row indices: 17 18 9 22 8 24 23 1 - -New column: 22 old column: 12 number of entries: 5 - new row indices: 18 21 8 23 1 - -New column: 23 old column: 22 number of entries: 8 - new row indices: 17 18 21 10 22 24 7 2 - -New column: 24 old column: 20 number of entries: 8 - new row indices: 17 19 21 10 16 20 7 23 - ------------------------------------------- -ordering the matrix with AMDBAR ------------------------------------------- - -Permutation vector: - 24 23 17 9 15 5 21 13 6 11 16 8 2 10 14 18 1 3 4 19 7 12 22 20 - -Inverse permutation vector: - 17 13 18 19 6 9 21 12 4 14 10 22 8 15 5 11 3 16 20 24 7 23 2 1 -Plot of permuted matrix pattern: - 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 - 1: X . . . . . . X . . . . . . . . . . . . X X . . - 2: . X . . . . X . . . . . . . . . . X . . . . X . - 3: . . X . . X . . . . X X . . . . . . . . . . . . - 4: . . . X X . . . . . . . X X . . . . . . . . . . - 5: . . . X X X . . . . . X X X . . . . . . . . . . - 6: . . X . X X . . . . X X . X . . . . . . . . . . - 7: . X . . . . X . . X . . . . . . . X . . . . X X - 8: X . . . . . . X X . . . . . . . X . . . X X . . - 9: . . . . . . . X X . . . . . X X X . . . X . . . - 10: . . . . . . X . . X . . . . . . . . X X . . X X - 11: . . X . . X . . . . X X . . . . . . X X . . . . - 12: . . X . X X . . . . X X . X . X . . X X . . . . - 13: . . . X X . . . . . . . X X X X . . . . . . . . - 14: . . . X X X . . . . . X X X X X . . . X . . . . - 15: . . . . . . . . X . . . X X X X X . . . . . . . - 16: . . . . . . . . X . . X X X X X X . . X . . . X - 17: . . . . . . . X X . . . . . X X X . . X X . X X - 18: . X . . . . X . . . . . . . . . . X . . X X X . - 19: . . . . . . . . . X X X . . . . . . X X . . . X - 20: . . . . . . . . . X X X . X . X X . X X . . . X - 21: X . . . . . . X X . . . . . . . X X . . X X X X - 22: X . . . . . . X . . . . . . . . . X . . X X X . - 23: . X . . . . X . . X . . . . . . X X . . X X X X - 24: . . . . . . X . . X . . . . . X X . X X X . X X - -New column: 1 old column: 24 number of entries: 3 - new row indices: 21 22 8 - -New column: 2 old column: 23 number of entries: 3 - new row indices: 18 7 23 - -New column: 3 old column: 17 number of entries: 3 - new row indices: 6 12 11 - -New column: 4 old column: 9 number of entries: 3 - new row indices: 13 14 5 - -New column: 5 old column: 15 number of entries: 5 - new row indices: 13 6 12 4 14 - -New column: 6 old column: 5 number of entries: 5 - new row indices: 12 14 5 11 3 - -New column: 7 old column: 21 number of entries: 5 - new row indices: 18 10 24 23 2 - -New column: 8 old column: 13 number of entries: 5 - new row indices: 17 9 21 22 1 - -New column: 9 old column: 6 number of entries: 5 - new row indices: 17 21 8 15 16 - -New column: 10 old column: 11 number of entries: 5 - new row indices: 19 20 24 7 23 - -New column: 11 old column: 16 number of entries: 5 - new row indices: 19 6 12 3 20 - -New column: 12 old column: 8 number of entries: 8 - new row indices: 19 6 14 5 11 3 16 20 - -New column: 13 old column: 2 number of entries: 5 - new row indices: 4 14 15 5 16 - -New column: 14 old column: 10 number of entries: 8 - new row indices: 13 6 12 4 15 5 16 20 - -New column: 15 old column: 14 number of entries: 5 - new row indices: 17 13 9 14 16 - -New column: 16 old column: 18 number of entries: 8 - new row indices: 17 13 9 12 14 15 20 24 - -New column: 17 old column: 1 number of entries: 8 - new row indices: 9 21 8 15 16 20 24 23 - -New column: 18 old column: 3 number of entries: 5 - new row indices: 21 22 7 23 2 - -New column: 19 old column: 4 number of entries: 5 - new row indices: 12 10 11 20 24 - -New column: 20 old column: 19 number of entries: 8 - new row indices: 17 19 12 14 10 11 16 24 - -New column: 21 old column: 7 number of entries: 8 - new row indices: 17 18 9 22 8 24 23 1 - -New column: 22 old column: 12 number of entries: 5 - new row indices: 18 21 8 23 1 - -New column: 23 old column: 22 number of entries: 8 - new row indices: 17 18 21 10 22 24 7 2 - -New column: 24 old column: 20 number of entries: 8 - new row indices: 17 19 21 10 16 20 7 23 diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Demo/amd_f77simple.f b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Demo/amd_f77simple.f deleted file mode 100644 index 8f39606e6..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Demo/amd_f77simple.f +++ /dev/null @@ -1,37 +0,0 @@ -C ---------------------------------------------------------------------- -C AMD, Copyright (c) by Timothy A. Davis, Patrick R. -C Amestoy, and Iain S. Duff. See ../README.txt for License. -C email: DrTimothyAldenDavis@gmail.com -C ---------------------------------------------------------------------- - -C This program provides an example of how to call the Fortran version -C of AMD. It uses the same matrix as the amd_simple.c demo (in C). -C Note that the diagonal entries are not present, and the matrix is -C symmetric. - - INTEGER N, NZ, J, K, P, IWLEN, PFREE, NCMPA - PARAMETER (N = 5, NZ = 10, IWLEN = 17) - INTEGER AP (N+1), AI (NZ), LAST (N), PE (N), LEN (N), ELEN (N), - $ IW (IWLEN), DEGREE (N), NV (N), NEXT (N), HEAD (N), W (N) - DATA AP / 1, 2, 5, 8, 9, 11/ - DATA AI / 2, 1,3,5, 2,4,5, 3, 2,3 / - -C load the matrix into the AMD workspace - DO 10 J = 1,N - PE (J) = AP (J) - LEN (J) = AP (J+1) - AP (J) -10 CONTINUE - DO 20 P = 1,NZ - IW (P) = AI (P) -20 CONTINUE - PFREE = NZ + 1 - -C order the matrix (destroys the copy of A in IW, PE, and LEN) - CALL AMD (N, PE, IW, LEN, IWLEN, PFREE, NV, NEXT, LAST, HEAD, - $ ELEN, DEGREE, NCMPA, W) - - DO 60 K = 1, N - PRINT 50, K, LAST (K) -50 FORMAT ('P (',I2,') = ', I2) -60 CONTINUE - END diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Demo/amd_f77simple.out b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Demo/amd_f77simple.out deleted file mode 100644 index c7f902e0a..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Demo/amd_f77simple.out +++ /dev/null @@ -1,5 +0,0 @@ -P ( 1) = 4 -P ( 2) = 1 -P ( 3) = 3 -P ( 4) = 5 -P ( 5) = 2 diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Demo/amd_f77wrapper.c b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Demo/amd_f77wrapper.c deleted file mode 100644 index d918845aa..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Demo/amd_f77wrapper.c +++ /dev/null @@ -1,87 +0,0 @@ -/* ========================================================================= */ -/* === amd_f77wrapper ====================================================== */ -/* ========================================================================= */ - -/* ------------------------------------------------------------------------- */ -/* AMD Copyright (c) by Timothy A. Davis, */ -/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ -/* email: DrTimothyAldenDavis@gmail.com */ -/* ------------------------------------------------------------------------- */ - -/* Fortran interface for the C-callable AMD library (int version only). This - * is HIGHLY non-portable. You will need to modify this depending on how your - * Fortran and C compilers behave. Two examples are provided. - * - * To avoid using I/O, and to avoid the extra porting step of a Fortran - * function, the status code is returned as the first entry in P (P [0] in C - * and P (1) in Fortran) if an error occurs. The error codes are negative - * (-1: out of memory, -2: invalid matrix). - * - * For some C and Fortran compilers, the Fortran compiler appends a single "_" - * after each routine name. C doesn't do this, so the translation is made - * here. Some Fortran compilers don't append an underscore (xlf on IBM AIX, - * for * example). - */ - -#include "amd.h" -#include - -/* ------------------------------------------------------------------------- */ -/* Linux, Solaris, SGI */ -/* ------------------------------------------------------------------------- */ - -void amdorder_ (int *n, const int *Ap, const int *Ai, int *P, - double *Control, double *Info) -{ - int result = amd_order (*n, Ap, Ai, P, Control, Info) ; - if (result != AMD_OK && P) P [0] = result ; -} - -void amddefaults_ (double *Control) -{ - amd_defaults (Control) ; -} - -void amdcontrol_ (double *Control) -{ - fflush (stdout) ; - amd_control (Control) ; - fflush (stdout) ; -} - -void amdinfo_ (double *Info) -{ - fflush (stdout) ; - amd_info (Info) ; - fflush (stdout) ; -} - -/* ------------------------------------------------------------------------- */ -/* IBM AIX. Probably Windows, Compaq Alpha, and HP Unix as well. */ -/* ------------------------------------------------------------------------- */ - -void amdorder (int *n, const int *Ap, const int *Ai, int *P, - double *Control, double *Info) -{ - int result = amd_order (*n, Ap, Ai, P, Control, Info) ; - if (result != AMD_OK && P) P [0] = result ; -} - -void amddefaults (double *Control) -{ - amd_defaults (Control) ; -} - -void amdcontrol (double *Control) -{ - fflush (stdout) ; - amd_control (Control) ; - fflush (stdout) ; -} - -void amdinfo (double *Info) -{ - fflush (stdout) ; - amd_info (Info) ; - fflush (stdout) ; -} diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Demo/amd_l_demo.c b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Demo/amd_l_demo.c deleted file mode 100644 index 62a05913e..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Demo/amd_l_demo.c +++ /dev/null @@ -1,178 +0,0 @@ -/* ========================================================================= */ -/* === AMD demo main program (long integer version) ======================== */ -/* ========================================================================= */ - -/* ------------------------------------------------------------------------- */ -/* AMD Copyright (c) by Timothy A. Davis, */ -/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ -/* email: DrTimothyAldenDavis@gmail.com */ -/* ------------------------------------------------------------------------- */ - -/* A simple C main program that illustrates the use of the ANSI C interface - * to AMD. - */ - -#include "amd.h" -#include -#include -#define Long SuiteSparse_long - -int main (void) -{ - /* The symmetric can_24 Harwell/Boeing matrix, including upper and lower - * triangular parts, and the diagonal entries. Note that this matrix is - * 0-based, with row and column indices in the range 0 to n-1. */ - Long n = 24, nz, - Ap [ ] = { 0, 9, 15, 21, 27, 33, 39, 48, 57, 61, 70, 76, 82, 88, 94, 100, - 106, 110, 119, 128, 137, 143, 152, 156, 160 }, - Ai [ ] = { - /* column 0: */ 0, 5, 6, 12, 13, 17, 18, 19, 21, - /* column 1: */ 1, 8, 9, 13, 14, 17, - /* column 2: */ 2, 6, 11, 20, 21, 22, - /* column 3: */ 3, 7, 10, 15, 18, 19, - /* column 4: */ 4, 7, 9, 14, 15, 16, - /* column 5: */ 0, 5, 6, 12, 13, 17, - /* column 6: */ 0, 2, 5, 6, 11, 12, 19, 21, 23, - /* column 7: */ 3, 4, 7, 9, 14, 15, 16, 17, 18, - /* column 8: */ 1, 8, 9, 14, - /* column 9: */ 1, 4, 7, 8, 9, 13, 14, 17, 18, - /* column 10: */ 3, 10, 18, 19, 20, 21, - /* column 11: */ 2, 6, 11, 12, 21, 23, - /* column 12: */ 0, 5, 6, 11, 12, 23, - /* column 13: */ 0, 1, 5, 9, 13, 17, - /* column 14: */ 1, 4, 7, 8, 9, 14, - /* column 15: */ 3, 4, 7, 15, 16, 18, - /* column 16: */ 4, 7, 15, 16, - /* column 17: */ 0, 1, 5, 7, 9, 13, 17, 18, 19, - /* column 18: */ 0, 3, 7, 9, 10, 15, 17, 18, 19, - /* column 19: */ 0, 3, 6, 10, 17, 18, 19, 20, 21, - /* column 20: */ 2, 10, 19, 20, 21, 22, - /* column 21: */ 0, 2, 6, 10, 11, 19, 20, 21, 22, - /* column 22: */ 2, 20, 21, 22, - /* column 23: */ 6, 11, 12, 23 } ; - - Long P [24], Pinv [24], i, j, k, jnew, p, inew, result ; - double Control [AMD_CONTROL], Info [AMD_INFO] ; - char A [24][24] ; - - /* here is an example of how to use AMD_VERSION. This code will work in - * any version of AMD. */ -#if defined(AMD_VERSION) && (AMD_VERSION >= AMD_VERSION_CODE(1,2)) - printf ("AMD version %d.%d.%d, date: %s\n", - AMD_MAIN_VERSION, AMD_SUB_VERSION, AMD_SUBSUB_VERSION, AMD_DATE) ; -#else - printf ("AMD version: 1.1 or earlier\n") ; -#endif - - printf ("AMD demo, with the 24-by-24 Harwell/Boeing matrix, can_24:\n") ; - - /* get the default parameters, and print them */ - amd_l_defaults (Control) ; - amd_l_control (Control) ; - - /* print the input matrix */ - nz = Ap [n] ; - printf ("\nInput matrix: %ld-by-%ld, with %ld entries.\n" - " Note that for a symmetric matrix such as this one, only the\n" - " strictly lower or upper triangular parts would need to be\n" - " passed to AMD, since AMD computes the ordering of A+A'. The\n" - " diagonal entries are also not needed, since AMD ignores them.\n" - , n, n, nz) ; - for (j = 0 ; j < n ; j++) - { - printf ("\nColumn: %ld, number of entries: %ld, with row indices in" - " Ai [%ld ... %ld]:\n row indices:", - j, Ap [j+1] - Ap [j], Ap [j], Ap [j+1]-1) ; - for (p = Ap [j] ; p < Ap [j+1] ; p++) - { - i = Ai [p] ; - printf (" %ld", i) ; - } - printf ("\n") ; - } - - /* print a character plot of the input matrix. This is only reasonable - * because the matrix is small. */ - printf ("\nPlot of input matrix pattern:\n") ; - for (j = 0 ; j < n ; j++) - { - for (i = 0 ; i < n ; i++) A [i][j] = '.' ; - for (p = Ap [j] ; p < Ap [j+1] ; p++) - { - i = Ai [p] ; - A [i][j] = 'X' ; - } - } - printf (" ") ; - for (j = 0 ; j < n ; j++) printf (" %1ld", j % 10) ; - printf ("\n") ; - for (i = 0 ; i < n ; i++) - { - printf ("%2ld: ", i) ; - for (j = 0 ; j < n ; j++) - { - printf (" %c", A [i][j]) ; - } - printf ("\n") ; - } - - /* order the matrix */ - result = amd_l_order (n, Ap, Ai, P, Control, Info) ; - printf ("return value from amd_l_order: %ld (should be %d)\n", - result, AMD_OK) ; - - /* print the statistics */ - amd_l_info (Info) ; - - if (result != AMD_OK) - { - printf ("AMD failed\n") ; - exit (1) ; - } - - /* print the permutation vector, P, and compute the inverse permutation */ - printf ("Permutation vector:\n") ; - for (k = 0 ; k < n ; k++) - { - /* row/column j is the kth row/column in the permuted matrix */ - j = P [k] ; - Pinv [j] = k ; - printf (" %2ld", j) ; - } - printf ("\n\n") ; - - printf ("Inverse permutation vector:\n") ; - for (j = 0 ; j < n ; j++) - { - k = Pinv [j] ; - printf (" %2ld", k) ; - } - printf ("\n\n") ; - - /* print a character plot of the permuted matrix. */ - printf ("\nPlot of permuted matrix pattern:\n") ; - for (jnew = 0 ; jnew < n ; jnew++) - { - j = P [jnew] ; - for (inew = 0 ; inew < n ; inew++) A [inew][jnew] = '.' ; - for (p = Ap [j] ; p < Ap [j+1] ; p++) - { - inew = Pinv [Ai [p]] ; - A [inew][jnew] = 'X' ; - } - } - printf (" ") ; - for (j = 0 ; j < n ; j++) printf (" %1ld", j % 10) ; - printf ("\n") ; - for (i = 0 ; i < n ; i++) - { - printf ("%2ld: ", i) ; - for (j = 0 ; j < n ; j++) - { - printf (" %c", A [i][j]) ; - } - printf ("\n") ; - } - - return (0) ; -} diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Demo/amd_l_demo.out b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Demo/amd_l_demo.out deleted file mode 100644 index 83f4ae955..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Demo/amd_l_demo.out +++ /dev/null @@ -1,179 +0,0 @@ -AMD version 2.4.6, date: May 4, 2016 -AMD demo, with the 24-by-24 Harwell/Boeing matrix, can_24: - -AMD version 2.4.6, May 4, 2016: approximate minimum degree ordering - dense row parameter: 10 - (rows with more than max (10 * sqrt (n), 16) entries are - considered "dense", and placed last in output permutation) - aggressive absorption: yes - size of AMD integer: 8 - - -Input matrix: 24-by-24, with 160 entries. - Note that for a symmetric matrix such as this one, only the - strictly lower or upper triangular parts would need to be - passed to AMD, since AMD computes the ordering of A+A'. The - diagonal entries are also not needed, since AMD ignores them. - -Column: 0, number of entries: 9, with row indices in Ai [0 ... 8]: - row indices: 0 5 6 12 13 17 18 19 21 - -Column: 1, number of entries: 6, with row indices in Ai [9 ... 14]: - row indices: 1 8 9 13 14 17 - -Column: 2, number of entries: 6, with row indices in Ai [15 ... 20]: - row indices: 2 6 11 20 21 22 - -Column: 3, number of entries: 6, with row indices in Ai [21 ... 26]: - row indices: 3 7 10 15 18 19 - -Column: 4, number of entries: 6, with row indices in Ai [27 ... 32]: - row indices: 4 7 9 14 15 16 - -Column: 5, number of entries: 6, with row indices in Ai [33 ... 38]: - row indices: 0 5 6 12 13 17 - -Column: 6, number of entries: 9, with row indices in Ai [39 ... 47]: - row indices: 0 2 5 6 11 12 19 21 23 - -Column: 7, number of entries: 9, with row indices in Ai [48 ... 56]: - row indices: 3 4 7 9 14 15 16 17 18 - -Column: 8, number of entries: 4, with row indices in Ai [57 ... 60]: - row indices: 1 8 9 14 - -Column: 9, number of entries: 9, with row indices in Ai [61 ... 69]: - row indices: 1 4 7 8 9 13 14 17 18 - -Column: 10, number of entries: 6, with row indices in Ai [70 ... 75]: - row indices: 3 10 18 19 20 21 - -Column: 11, number of entries: 6, with row indices in Ai [76 ... 81]: - row indices: 2 6 11 12 21 23 - -Column: 12, number of entries: 6, with row indices in Ai [82 ... 87]: - row indices: 0 5 6 11 12 23 - -Column: 13, number of entries: 6, with row indices in Ai [88 ... 93]: - row indices: 0 1 5 9 13 17 - -Column: 14, number of entries: 6, with row indices in Ai [94 ... 99]: - row indices: 1 4 7 8 9 14 - -Column: 15, number of entries: 6, with row indices in Ai [100 ... 105]: - row indices: 3 4 7 15 16 18 - -Column: 16, number of entries: 4, with row indices in Ai [106 ... 109]: - row indices: 4 7 15 16 - -Column: 17, number of entries: 9, with row indices in Ai [110 ... 118]: - row indices: 0 1 5 7 9 13 17 18 19 - -Column: 18, number of entries: 9, with row indices in Ai [119 ... 127]: - row indices: 0 3 7 9 10 15 17 18 19 - -Column: 19, number of entries: 9, with row indices in Ai [128 ... 136]: - row indices: 0 3 6 10 17 18 19 20 21 - -Column: 20, number of entries: 6, with row indices in Ai [137 ... 142]: - row indices: 2 10 19 20 21 22 - -Column: 21, number of entries: 9, with row indices in Ai [143 ... 151]: - row indices: 0 2 6 10 11 19 20 21 22 - -Column: 22, number of entries: 4, with row indices in Ai [152 ... 155]: - row indices: 2 20 21 22 - -Column: 23, number of entries: 4, with row indices in Ai [156 ... 159]: - row indices: 6 11 12 23 - -Plot of input matrix pattern: - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 - 0: X . . . . X X . . . . . X X . . . X X X . X . . - 1: . X . . . . . . X X . . . X X . . X . . . . . . - 2: . . X . . . X . . . . X . . . . . . . . X X X . - 3: . . . X . . . X . . X . . . . X . . X X . . . . - 4: . . . . X . . X . X . . . . X X X . . . . . . . - 5: X . . . . X X . . . . . X X . . . X . . . . . . - 6: X . X . . X X . . . . X X . . . . . . X . X . X - 7: . . . X X . . X . X . . . . X X X X X . . . . . - 8: . X . . . . . . X X . . . . X . . . . . . . . . - 9: . X . . X . . X X X . . . X X . . X X . . . . . -10: . . . X . . . . . . X . . . . . . . X X X X . . -11: . . X . . . X . . . . X X . . . . . . . . X . X -12: X . . . . X X . . . . X X . . . . . . . . . . X -13: X X . . . X . . . X . . . X . . . X . . . . . . -14: . X . . X . . X X X . . . . X . . . . . . . . . -15: . . . X X . . X . . . . . . . X X . X . . . . . -16: . . . . X . . X . . . . . . . X X . . . . . . . -17: X X . . . X . X . X . . . X . . . X X X . . . . -18: X . . X . . . X . X X . . . . X . X X X . . . . -19: X . . X . . X . . . X . . . . . . X X X X X . . -20: . . X . . . . . . . X . . . . . . . . X X X X . -21: X . X . . . X . . . X X . . . . . . . X X X X . -22: . . X . . . . . . . . . . . . . . . . . X X X . -23: . . . . . . X . . . . X X . . . . . . . . . . X -return value from amd_l_order: 0 (should be 0) - -AMD version 2.4.6, May 4, 2016, results: - status: OK - n, dimension of A: 24 - nz, number of nonzeros in A: 160 - symmetry of A: 1.0000 - number of nonzeros on diagonal: 24 - nonzeros in pattern of A+A' (excl. diagonal): 136 - # dense rows/columns of A+A': 0 - memory used, in bytes: 3032 - # of memory compactions: 0 - - The following approximate statistics are for a subsequent - factorization of A(P,P) + A(P,P)'. They are slight upper - bounds if there are no dense rows/columns in A+A', and become - looser if dense rows/columns exist. - - nonzeros in L (excluding diagonal): 97 - nonzeros in L (including diagonal): 121 - # divide operations for LDL' or LU: 97 - # multiply-subtract operations for LDL': 275 - # multiply-subtract operations for LU: 453 - max nz. in any column of L (incl. diagonal): 8 - - chol flop count for real A, sqrt counted as 1 flop: 671 - LDL' flop count for real A: 647 - LDL' flop count for complex A: 3073 - LU flop count for real A (with no pivoting): 1003 - LU flop count for complex A (with no pivoting): 4497 - -Permutation vector: - 22 20 10 23 12 5 16 8 14 4 15 7 1 9 13 17 0 2 3 6 11 18 21 19 - -Inverse permutation vector: - 16 12 17 18 9 5 19 11 7 13 2 20 4 14 8 10 6 15 21 23 1 22 0 3 - - -Plot of permuted matrix pattern: - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 - 0: X X . . . . . . . . . . . . . . . X . . . . X . - 1: X X X . . . . . . . . . . . . . . X . . . . X X - 2: . X X . . . . . . . . . . . . . . . X . . X X X - 3: . . . X X . . . . . . . . . . . . . . X X . . . - 4: . . . X X X . . . . . . . . . . X . . X X . . . - 5: . . . . X X . . . . . . . . X X X . . X . . . . - 6: . . . . . . X . . X X X . . . . . . . . . . . . - 7: . . . . . . . X X . . . X X . . . . . . . . . . - 8: . . . . . . . X X X . X X X . . . . . . . . . . - 9: . . . . . . X . X X X X . X . . . . . . . . . . -10: . . . . . . X . . X X X . . . . . . X . . X . . -11: . . . . . . X . X X X X . X . X . . X . . X . . -12: . . . . . . . X X . . . X X X X . . . . . . . . -13: . . . . . . . X X X . X X X X X . . . . . X . . -14: . . . . . X . . . . . . X X X X X . . . . . . . -15: . . . . . X . . . . . X X X X X X . . . . X . X -16: . . . . X X . . . . . . . . X X X . . X . X X X -17: X X . . . . . . . . . . . . . . . X . X X . X . -18: . . X . . . . . . . X X . . . . . . X . . X . X -19: . . . X X X . . . . . . . . . . X X . X X . X X -20: . . . X X . . . . . . . . . . . . X . X X . X . -21: . . X . . . . . . . X X . X . X X . X . . X . X -22: X X X . . . . . . . . . . . . . X X . X X . X X -23: . X X . . . . . . . . . . . . X X . X X . X X X diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Demo/amd_simple.c b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Demo/amd_simple.c deleted file mode 100644 index 5241fb150..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Demo/amd_simple.c +++ /dev/null @@ -1,22 +0,0 @@ -/* ------------------------------------------------------------------------- */ -/* AMD Copyright (c) by Timothy A. Davis, */ -/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ -/* email: DrTimothyAldenDavis@gmail.com */ -/* ------------------------------------------------------------------------- */ - -#include -#include "amd.h" - -int n = 5 ; -int Ap [ ] = { 0, 2, 6, 10, 12, 14} ; -int Ai [ ] = { 0,1, 0,1,2,4, 1,2,3,4, 2,3, 1,4 } ; -int P [5] ; - -int main (void) -{ - int k ; - (void) amd_order (n, Ap, Ai, P, (double *) NULL, (double *) NULL) ; - for (k = 0 ; k < n ; k++) printf ("P [%d] = %d\n", k, P [k]) ; - return (0) ; -} - diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Demo/amd_simple.out b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Demo/amd_simple.out deleted file mode 100644 index 08a04e0b9..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Demo/amd_simple.out +++ /dev/null @@ -1,5 +0,0 @@ -P [0] = 0 -P [1] = 3 -P [2] = 2 -P [3] = 4 -P [4] = 1 diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Doc/AMD_UserGuide.pdf b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Doc/AMD_UserGuide.pdf deleted file mode 100644 index a0e7a6c69..000000000 Binary files a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Doc/AMD_UserGuide.pdf and /dev/null differ diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Doc/AMD_UserGuide.tex b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Doc/AMD_UserGuide.tex index 862ad69c8..2e5e1edef 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Doc/AMD_UserGuide.tex +++ b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Doc/AMD_UserGuide.tex @@ -1,4 +1,5 @@ \documentclass[11pt]{article} +\batchmode \newcommand{\m}[1]{{\bf{#1}}} % for matrices and vectors \newcommand{\tr}{^{\sf T}} % transpose @@ -32,7 +33,7 @@ This work was supported by the EPSRC under grant GR/R46441. }} -\date{VERSION 2.4.6, May 4, 2016} +\input{amd_version.tex} \maketitle %------------------------------------------------------------------------------ @@ -45,7 +46,7 @@ \end{abstract} %------------------------------------------------------------------------------ -AMD Copyright\copyright 2013 by Timothy A. +AMD Copyright\copyright 1996-2022 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. @@ -53,7 +54,7 @@ for your particular copy of AMD. {\bf Availability:} - http://www.suitesparse.com + http://www.suitesparse.com. {\bf Acknowledgments:} @@ -203,25 +204,20 @@ \section{Using AMD in a C program} 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 -{\tt int} and {\tt long} integers. +\verb'int32_t' and \verb'int64_t' integers. The routines with prefix -{\tt amd\_l\_} use {\tt long} integer arguments; the others use -{\tt int} integer arguments. If you compile AMD in the standard -ILP32 mode (32-bit {\tt int}'s, {\tt long}'s, and pointers) then the versions -are essentially identical. You will be able to solve problems using up to 2GB -of memory. If you compile AMD in the standard LP64 mode, the size of an -{\tt int} remains 32-bits, but the size of a {\tt long} and a pointer both get -promoted to 64-bits. +{\tt amd\_l\_} use \verb'int64_t' integer arguments; the others use +\verb'int32_t' integer arguments. The following routines are fully described in Section~\ref{Primary}: \begin{itemize} \item {\tt amd\_order} -({\tt long} version: {\tt amd\_l\_order}) +(\verb'int64_t' version: {\tt amd\_l\_order}) {\footnotesize \begin{verbatim} #include "amd.h" - int n, Ap [n+1], Ai [nz], P [n] ; + int32_t n, Ap [n+1], Ai [nz], P [n] ; double Control [AMD_CONTROL], Info [AMD_INFO] ; int result = amd_order (n, Ap, Ai, P, Control, Info) ; \end{verbatim} @@ -244,7 +240,7 @@ \section{Using AMD in a C program} {\tt AMD\_OUT\_OF\_MEMORY} if out of memory. \item {\tt amd\_defaults} -({\tt long} version: {\tt amd\_l\_defaults}) +(\verb'int64_t' version: {\tt amd\_l\_defaults}) {\footnotesize \begin{verbatim} #include "amd.h" @@ -257,7 +253,7 @@ \section{Using AMD in a C program} routines. \item {\tt amd\_control} -({\tt long} version: {\tt amd\_l\_control}) +(\verb'int64_t' version: {\tt amd\_l\_control}) {\footnotesize \begin{verbatim} #include "amd.h" @@ -268,7 +264,7 @@ \section{Using AMD in a C program} Prints a description of the control parameters, and their values. \item {\tt amd\_info} -({\tt long} version: {\tt amd\_l\_info}) +(\verb'int64_t' version: {\tt amd\_l\_info}) {\footnotesize \begin{verbatim} #include "amd.h" @@ -279,11 +275,11 @@ \section{Using AMD in a C program} Prints a description of the statistics computed by AMD, and their values. \item {\tt amd\_valid} -({\tt long} version: {\tt amd\_valid}) +(\verb'int64_t' version: {\tt amd\_l\_valid}) {\footnotesize \begin{verbatim} #include "amd.h" - int n, Ap [n+1], Ai [nz] ; + int32_t n, Ap [n+1], Ai [nz] ; int result = amd_valid (n, n, Ap, Ai) ; \end{verbatim} } @@ -299,7 +295,7 @@ \section{Using AMD in a C program} equal {\tt n}. \item {\tt amd\_2} -({\tt long} version: {\tt amd\_l2}) +(\verb'int64_t' version: {\tt amd\_l2}) AMD ordering kernel. It is faster than {\tt amd\_order}, and can be called by the user, but it is difficult to use. It does not check its inputs for errors. @@ -390,17 +386,16 @@ \subsection{Sample C program} {\footnotesize \begin{verbatim} -#include #include "amd.h" -int n = 5 ; -int Ap [ ] = { 0, 2, 6, 10, 12, 14} ; -int Ai [ ] = { 0,1, 0,1,2,4, 1,2,3,4, 2,3, 1,4 } ; -int P [5] ; +int32_t n = 5 ; +int32_t Ap [ ] = { 0, 2, 6, 10, 12, 14} ; +int32_t Ai [ ] = { 0,1, 0,1,2,4, 1,2,3,4, 2,3, 1,4 } ; +int32_t P [5] ; int main (void) { - int k ; + int32_t k ; (void) amd_order (n, Ap, Ai, P, (double *) NULL, (double *) NULL) ; for (k = 0 ; k < n ; k++) printf ("P [%d] = %d\n", k, P [k]) ; return (0) ; @@ -459,7 +454,7 @@ \section{Synopsis of C-callable routines} {\footnotesize \begin{verbatim} #include "amd.h" -int n, status, Ap [n+1], Ai [nz], P [n] ; +int32_t n, status, Ap [n+1], Ai [nz], P [n] ; double Control [AMD_CONTROL], Info [AMD_INFO] ; amd_defaults (Control) ; status = amd_order (n, Ap, Ai, P, Control, Info) ; @@ -469,13 +464,13 @@ \section{Synopsis of C-callable routines} \end{verbatim} } -The {\tt amd\_l\_*} routines are identical, except that all {\tt int} -arguments become {\tt long}: +The {\tt amd\_l\_*} routines are identical, except that all \verb'int32_t' +arguments become \verb'int64_t': {\footnotesize \begin{verbatim} #include "amd.h" -long n, status, Ap [n+1], Ai [nz], P [n] ; +int64_t n, status, Ap [n+1], Ai [nz], P [n] ; double Control [AMD_CONTROL], Info [AMD_INFO] ; amd_l_defaults (Control) ; status = amd_l_order (n, Ap, Ai, P, Control, Info) ; @@ -656,92 +651,45 @@ \section{Installation} \label{Install} %------------------------------------------------------------------------------ -The following discussion assumes you have the {\tt make} program, either in -Unix, or in Windows with Cygwin. - -System-dependent configurations are in the -{\tt ../SuiteSparse\_config/SuiteSparse\_config.mk} -file. You can edit that file to customize the compilation. The default -settings will work on most systems. -Sample configuration files are provided -for Mac, Linux, Sun Solaris, and IBM AIX. -The system you are on is detected automatically. - -To compile and install the C-callable AMD library, -go to the {\tt AMD} directory and type {\tt make}. -A dynamic library is placed in -in {\tt AMD/Lib/libamd.so.*}, ({\tt *.dylib} for the Mac). -Three demo programs of the AMD ordering routine will be compiled and tested in -the {\tt AMD/Demo} directory. -The outputs of these demo programs will then be compared with output -files in the distribution. - -To compile and install the Fortran-callable AMD library, -go to the {\tt AMD} directory and type {\tt make fortran}. -The library will be placed in {\tt AMD/Lib/libamdf77.a}. -A demo program will be compiled and tested in the {\tt AMD/Demo} directory. -The output will be compared with an output file in the distribution. - -Typing {\tt make clean} will remove all but the final compiled libraries -and demo programs. Typing {\tt make purge} or {\tt make distclean} -removes all files not in the original distribution. -If you compile AMD and then later change the -{\tt ../SuiteSparse\_config/SuiteSparse\_config.mk} file -then you should type {\tt make purge} and then {\tt make} to recompile. - -When you compile your program that uses the C-callable AMD library, -you need to add the {\tt AMD/Lib/libamd.*} library -and you need to tell your compiler to look in the -{\tt AMD/Include} directory for include -files. To compile a Fortran program that calls the Fortran AMD library, -you need to add the {\tt AMD/Lib/libamdf77.a} library. -See {\tt AMD/Demo/Makefile} for an example. - -By doing {\tt make}, all compiled dynamic libraries are also copied into {\tt -SuiteSparse/lib} the include files are coped into {\tt SuiteSparse/include}, -and documentation is copied into {\tt SuiteSparse/doc}. - -Alternatively, you can install the shared library -and include files in /usr/local/lib and /usr/local/include, and -the documenation in /usr/local/doc. -Just do {\tt make} then {\tt make install}. -Now you can simply use {\tt -lamd} when you compile your own program. - -If all you want to use is the AMD2 mexFunction in MATLAB, you can skip -the use of the {\tt make} command entirely. Simply type -{\tt amd\_make} in MATLAB while in the {\tt AMD/MATLAB} directory. -This works on any system with MATLAB, including Windows. -Alternately, type {\tt make} in the {\tt AMD/MATLAB} directory, -or just use the built-in {\tt amd} in MATLAB 7.3 or later. - -If you are including AMD as a subset of a larger library and do not want -to link the C standard I/O library, or if you simply do not need to use -them, you can safely remove the {\tt amd\_control.c} and {\tt amd\_info.c} -files. Similarly, if you use default parameters (or define your -own {\tt Control} array), then you can exclude the {\tt amd\_defaults.c} -file. -Each of these files contains the user-callable routines of the same -name. None of these auxiliary routines are directly called by -{\tt amd\_order}. -The {\tt amd\_dump.c} file contains debugging routines -that are neither used nor compiled unless debugging is enabled. -The {\tt amd\_internal.h} file must be edited to enable debugging; -refer to the instructions in that file. -The bare minimum files required to use just {\tt amd\_order} are -{\tt amd.h} and {\tt amd\_internal.h} -in the {\tt Include} directory, -and -{\tt amd\_1.c}, -{\tt amd\_2.c}, -{\tt amd\_aat.c}, -{\tt amd\_global.c}, -{\tt and\_order.c}, -{\tt amd\_postorder.c}, -{\tt amd\_post\_tree.c}, -{\tt amd\_preprocess.c}, -and -{\tt amd\_valid.c} -in the {\tt Source} directory. +AMD now relies primarily on CMake to build the library. It also includes +a simple \verb'AMD/Makefile' which uses cmake to do the actual build. +The use of this \verb'AMD/Makefile' is optional; for Windows, just import +the CMakeLists.txt into MS Visual Studio. + +To compile and install the library for both system-wide usage and local +usage: + + \begin{verbatim} + make + sudo make install + \end{verbatim} + +To compile/install for just local usage (SuiteSparse/lib and +SuiteSparse/include): + + \begin{verbatim} + make local + make install + \end{verbatim} + +To run the demos + + \begin{verbatim} + make demos + \end{verbatim} + +To remove all files in \verb'AMD/' not in the original distribution (leaves +SuiteSparse/lib and SuiteSparse/include unchanged): + + \begin{verbatim} + make clean + \end{verbatim} + +To use the AMD2 mexFunction in MATLAB, simply type {\tt amd\_make} in MATLAB +while in the {\tt AMD/MATLAB} directory. This works on any system with MATLAB, +including Windows. +Alternatively, just use the built-in {\tt amd} in MATLAB 7.3 or later, +which is the same as this package. %------------------------------------------------------------------------------ \newpage diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Doc/ChangeLog b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Doc/ChangeLog index 27868a7b6..97dbc0d87 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Doc/ChangeLog +++ b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Doc/ChangeLog @@ -1,3 +1,21 @@ +Jan 17, 2023: version 3.0.3 + + * NFORTRAN: option added to disable Fortran entirely + * SuiteSparse_config: now v7.0.0 + +Dec 9, 2022: version 3.0.2 + + * minor changes to build system + * Fortran: allow AMD to be compiled if no Fortran compiler is available. + The Fortran source codes (Source/amd.f, Source/amdbar.f) and Demo/*.f + are skipped. + +Nov 12, 2022: version 3.0.0 + + * using CMake build system + * integers: int (32-bit) and SuiteSparse_long (nominally 64-bit) replaced + with int32_t and int64_t. + May 4, 2016: version 2.4.6 * minor changes to Makefile diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Doc/License.txt b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Doc/License.txt index 7a2bce91e..561b86557 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Doc/License.txt +++ b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Doc/License.txt @@ -1,4 +1,4 @@ -AMD, Copyright (c), 1996-2015, Timothy A. Davis, +AMD, Copyright (c), 1996-2022, Timothy A. Davis, Patrick R. Amestoy, and Iain S. Duff. All Rights Reserved. Availability: diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Doc/Makefile b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Doc/Makefile index 4769bf1af..73140a65e 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Doc/Makefile +++ b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Doc/Makefile @@ -1,36 +1,29 @@ #------------------------------------------------------------------------------ -# AMD Makefile for compiling on Unix systems +# AMD/Doc/Makefile: Create the User Guide and Quick Start Guide #------------------------------------------------------------------------------ -default: dist +# Copyright (c) 1996-2022, Timothy A. Davis, Patrick Amestoy, Iain Duff. +# All Rights Reserved. +# SPDX-License-Identifier: BSD-3-clause -include ../../SuiteSparse_config/SuiteSparse_config.mk - -#------------------------------------------------------------------------------ -# Remove all but the files in the original distribution -#------------------------------------------------------------------------------ - -clean: - - $(RM) -r $(CLEAN) - -purge: distclean - -distclean: clean - - $(RM) -r $(PURGE) - -#------------------------------------------------------------------------------ -# Create the User Guide and Quick Start Guide -#------------------------------------------------------------------------------ +default: AMD_UserGuide.pdf + - $(RM) *.aux *.bbl *.blg *.log *.toc amd_h.tex -AMD_UserGuide.pdf: AMD_UserGuide.tex AMD_UserGuide.bib ../Include/amd.h - echo '\\begin{verbatim}' > amd_h.tex +AMD_UserGuide.pdf: AMD_UserGuide.tex AMD_UserGuide.bib ../Include/amd.h \ + amd_version.tex + printf '\\begin{verbatim}\n' > amd_h.tex expand -8 ../Include/amd.h >> amd_h.tex - echo '\\end{verbatim}' >> amd_h.tex + printf '\\end{verbatim}\n' >> amd_h.tex pdflatex AMD_UserGuide bibtex AMD_UserGuide pdflatex AMD_UserGuide pdflatex AMD_UserGuide -dist: AMD_UserGuide.pdf - - $(RM) *.aux *.bbl *.blg *.log *.toc amd_h.tex +# Remove all but the files in the original distribution +clean: + - $(RM) -r *.out *.aux *.log *.bbl *.blg *.toc amd_h.tex + +purge: distclean + +distclean: clean diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Doc/amd_version.tex b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Doc/amd_version.tex new file mode 100644 index 000000000..e24c8cda6 --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Doc/amd_version.tex @@ -0,0 +1,2 @@ +% version of SuiteSparse/AMD +\date{VERSION 3.0.3, Jan 17, 2023} diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Doc/lesser.txt b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Doc/lesser.txt deleted file mode 100644 index 8add30ad5..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Doc/lesser.txt +++ /dev/null @@ -1,504 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - - Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - - When we speak of free software, we are referring to freedom of use, -not price. Our General Public Licenses are designed to make sure that -you have the freedom to distribute copies of free software (and charge -for this service if you wish); that you receive source code or can get -it if you want it; that you can change the software and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Libraries - - If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms of the -ordinary General Public License). - - To apply these terms, attach the following notices to the library. It is -safest to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random Hacker. - - , 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! - - diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Include/amd.h b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Include/amd.h index a72851fcf..94aa96f11 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Include/amd.h +++ b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Include/amd.h @@ -1,18 +1,18 @@ -/* ========================================================================= */ -/* === AMD: approximate minimum degree ordering =========================== */ -/* ========================================================================= */ +//------------------------------------------------------------------------------ +// AMD/Include/amd.h: approximate minimum degree ordering +//------------------------------------------------------------------------------ -/* ------------------------------------------------------------------------- */ -/* AMD Version 2.4, Copyright (c) 1996-2013 by Timothy A. Davis, */ -/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ -/* email: DrTimothyAldenDavis@gmail.com */ -/* ------------------------------------------------------------------------- */ +// AMD, Copyright (c) 1996-2022, Timothy A. Davis, Patrick R. Amestoy, and +// Iain S. Duff. All Rights Reserved. +// SPDX-License-Identifier: BSD-3-clause + +//------------------------------------------------------------------------------ /* AMD finds a symmetric ordering P of a matrix A so that the Cholesky * factorization of P*A*P' has fewer nonzeros and takes less work than the * Cholesky factorization of A. If A is not symmetric, then it performs its * ordering on the matrix A+A'. Two sets of user-callable routines are - * provided, one for int integers and the other for SuiteSparse_long integers. + * provided, one for int32_t integers and the other for int64_t integers. * * The method is based on the approximate minimum degree algorithm, discussed * in Amestoy, Davis, and Duff, "An approximate degree ordering algorithm", @@ -40,49 +40,46 @@ extern "C" { #endif -/* get the definition of size_t: */ -#include - #include "SuiteSparse_config.h" -int amd_order /* returns AMD_OK, AMD_OK_BUT_JUMBLED, - * AMD_INVALID, or AMD_OUT_OF_MEMORY */ +int amd_order /* returns AMD_OK, AMD_OK_BUT_JUMBLED, + * AMD_INVALID, or AMD_OUT_OF_MEMORY */ ( - int n, /* A is n-by-n. n must be >= 0. */ - const int Ap [ ], /* column pointers for A, of size n+1 */ - const int Ai [ ], /* row indices of A, of size nz = Ap [n] */ - int P [ ], /* output permutation, of size n */ - double Control [ ], /* input Control settings, of size AMD_CONTROL */ - double Info [ ] /* output Info statistics, of size AMD_INFO */ + int32_t n, /* A is n-by-n. n must be >= 0. */ + const int32_t Ap [ ], /* column pointers for A, of size n+1 */ + const int32_t Ai [ ], /* row indices of A, of size nz = Ap [n] */ + int32_t P [ ], /* output permutation, of size n */ + double Control [ ], /* input Control settings, of size AMD_CONTROL */ + double Info [ ] /* output Info statistics, of size AMD_INFO */ ) ; -SuiteSparse_long amd_l_order /* see above for description of arguments */ +int amd_l_order /* see above for description */ ( - SuiteSparse_long n, - const SuiteSparse_long Ap [ ], - const SuiteSparse_long Ai [ ], - SuiteSparse_long P [ ], + int64_t n, + const int64_t Ap [ ], + const int64_t Ai [ ], + int64_t P [ ], double Control [ ], double Info [ ] ) ; /* Input arguments (not modified): * - * n: the matrix A is n-by-n. - * Ap: an int/SuiteSparse_long array of size n+1, containing column - * pointers of A. - * Ai: an int/SuiteSparse_long array of size nz, containing the row - * indices of A, where nz = Ap [n]. - * Control: a double array of size AMD_CONTROL, containing control - * parameters. Defaults are used if Control is NULL. + * n: the matrix A is n-by-n. + * Ap: an int32_t/int64_t array of size n+1, containing column + * pointers of A. + * Ai: an int32_t/int64_t array of size nz, containing the row + * indices of A, where nz = Ap [n]. + * Control: a double array of size AMD_CONTROL, containing control + * parameters. Defaults are used if Control is NULL. * * Output arguments (not defined on input): * - * P: an int/SuiteSparse_long array of size n, containing the output - * permutation. If row i is the kth pivot row, then P [k] = i. In - * MATLAB notation, the reordered matrix is A (P,P). - * Info: a double array of size AMD_INFO, containing statistical - * information. Ignored if Info is NULL. + * P: an int32_t/int64_t array of size n, containing the output + * permutation. If row i is the kth pivot row, then P [k] = i. In + * MATLAB notation, the reordered matrix is A (P,P). + * Info: a double array of size AMD_INFO, containing statistical + * information. Ignored if Info is NULL. * * On input, the matrix A is stored in column-oriented form. The row indices * of nonzero entries in column j are stored in Ai [Ap [j] ... Ap [j+1]-1]. @@ -91,8 +88,7 @@ SuiteSparse_long amd_l_order /* see above for description of arguments */ * are no duplicate entries, then amd_order is slightly more efficient in * terms of time and memory usage. If this condition does not hold, a copy * of the matrix is created (where these conditions do hold), and the copy is - * ordered. This feature is new to v2.0 (v1.2 and earlier required this - * condition to hold for the input matrix). + * ordered. * * Row indices must be in the range 0 to * n-1. Ap [0] must be zero, and thus nz = Ap [n] is the number of nonzeros @@ -107,22 +103,22 @@ SuiteSparse_long amd_l_order /* see above for description of arguments */ * http://www.suitesparse.com. * * Restrictions: n >= 0. Ap [0] = 0. Ap [j] <= Ap [j+1] for all j in the - * range 0 to n-1. nz = Ap [n] >= 0. Ai [0..nz-1] must be in the range 0 - * to n-1. Finally, Ai, Ap, and P must not be NULL. If any of these - * restrictions are not met, AMD returns AMD_INVALID. + * range 0 to n-1. nz = Ap [n] >= 0. Ai [0..nz-1] must be in the range 0 + * to n-1. Finally, Ai, Ap, and P must not be NULL. If any of these + * restrictions are not met, AMD returns AMD_INVALID. * * AMD returns: * - * AMD_OK if the matrix is valid and sufficient memory can be allocated to - * perform the ordering. + * AMD_OK if the matrix is valid and sufficient memory can be allocated to + * perform the ordering. * - * AMD_OUT_OF_MEMORY if not enough memory can be allocated. + * AMD_OUT_OF_MEMORY if not enough memory can be allocated. * - * AMD_INVALID if the input arguments n, Ap, Ai are invalid, or if P is - * NULL. + * AMD_INVALID if the input arguments n, Ap, Ai are invalid, or if P is + * NULL. * - * AMD_OK_BUT_JUMBLED if the matrix had unsorted columns, and/or duplicate - * entries, but was otherwise valid. + * AMD_OK_BUT_JUMBLED if the matrix had unsorted columns, and/or duplicate + * entries, but was otherwise valid. * * The AMD routine first forms the pattern of the matrix A+A', and then * computes a fill-reducing ordering, P. If P [k] = i, then row/column i of @@ -134,93 +130,93 @@ SuiteSparse_long amd_l_order /* see above for description of arguments */ * pointer is passed, default values are used. The Control array is not * modified. * - * Control [AMD_DENSE]: controls the threshold for "dense" rows/columns. - * A dense row/column in A+A' can cause AMD to spend a lot of time in - * ordering the matrix. If Control [AMD_DENSE] >= 0, rows/columns - * with more than Control [AMD_DENSE] * sqrt (n) entries are ignored - * during the ordering, and placed last in the output order. The - * default value of Control [AMD_DENSE] is 10. If negative, no - * rows/columns are treated as "dense". Rows/columns with 16 or - * fewer off-diagonal entries are never considered "dense". - * - * Control [AMD_AGGRESSIVE]: controls whether or not to use aggressive - * absorption, in which a prior element is absorbed into the current - * element if is a subset of the current element, even if it is not - * adjacent to the current pivot element (refer to Amestoy, Davis, - * & Duff, 1996, for more details). The default value is nonzero, - * which means to perform aggressive absorption. This nearly always - * leads to a better ordering (because the approximate degrees are - * more accurate) and a lower execution time. There are cases where - * it can lead to a slightly worse ordering, however. To turn it off, - * set Control [AMD_AGGRESSIVE] to 0. - * - * Control [2..4] are not used in the current version, but may be used in - * future versions. + * Control [AMD_DENSE]: controls the threshold for "dense" rows/columns. + * A dense row/column in A+A' can cause AMD to spend a lot of time in + * ordering the matrix. If Control [AMD_DENSE] >= 0, rows/columns + * with more than Control [AMD_DENSE] * sqrt (n) entries are ignored + * during the ordering, and placed last in the output order. The + * default value of Control [AMD_DENSE] is 10. If negative, no + * rows/columns are treated as "dense". Rows/columns with 16 or + * fewer off-diagonal entries are never considered "dense". + * + * Control [AMD_AGGRESSIVE]: controls whether or not to use aggressive + * absorption, in which a prior element is absorbed into the current + * element if is a subset of the current element, even if it is not + * adjacent to the current pivot element (refer to Amestoy, Davis, + * & Duff, 1996, for more details). The default value is nonzero, + * which means to perform aggressive absorption. This nearly always + * leads to a better ordering (because the approximate degrees are + * more accurate) and a lower execution time. There are cases where + * it can lead to a slightly worse ordering, however. To turn it off, + * set Control [AMD_AGGRESSIVE] to 0. + * + * Control [2..4] are not used in the current version, but may be used in + * future versions. * * The Info array provides statistics about the ordering on output. If it is * not present, the statistics are not returned. This is not an error * condition. * - * Info [AMD_STATUS]: the return value of AMD, either AMD_OK, - * AMD_OK_BUT_JUMBLED, AMD_OUT_OF_MEMORY, or AMD_INVALID. - * - * Info [AMD_N]: n, the size of the input matrix - * - * Info [AMD_NZ]: the number of nonzeros in A, nz = Ap [n] - * - * Info [AMD_SYMMETRY]: the symmetry of the matrix A. It is the number - * of "matched" off-diagonal entries divided by the total number of - * off-diagonal entries. An entry A(i,j) is matched if A(j,i) is also - * an entry, for any pair (i,j) for which i != j. In MATLAB notation, - * S = spones (A) ; - * B = tril (S, -1) + triu (S, 1) ; - * symmetry = nnz (B & B') / nnz (B) ; - * - * Info [AMD_NZDIAG]: the number of entries on the diagonal of A. - * - * Info [AMD_NZ_A_PLUS_AT]: the number of nonzeros in A+A', excluding the - * diagonal. If A is perfectly symmetric (Info [AMD_SYMMETRY] = 1) - * with a fully nonzero diagonal, then Info [AMD_NZ_A_PLUS_AT] = nz-n - * (the smallest possible value). If A is perfectly unsymmetric - * (Info [AMD_SYMMETRY] = 0, for an upper triangular matrix, for - * example) with no diagonal, then Info [AMD_NZ_A_PLUS_AT] = 2*nz - * (the largest possible value). - * - * Info [AMD_NDENSE]: the number of "dense" rows/columns of A+A' that were - * removed from A prior to ordering. These are placed last in the - * output order P. - * - * Info [AMD_MEMORY]: the amount of memory used by AMD, in bytes. In the - * current version, this is 1.2 * Info [AMD_NZ_A_PLUS_AT] + 9*n - * times the size of an integer. This is at most 2.4nz + 9n. This - * excludes the size of the input arguments Ai, Ap, and P, which have - * a total size of nz + 2*n + 1 integers. - * - * Info [AMD_NCMPA]: the number of garbage collections performed. - * - * Info [AMD_LNZ]: the number of nonzeros in L (excluding the diagonal). - * This is a slight upper bound because mass elimination is combined - * with the approximate degree update. It is a rough upper bound if - * there are many "dense" rows/columns. The rest of the statistics, - * below, are also slight or rough upper bounds, for the same reasons. - * The post-ordering of the assembly tree might also not exactly - * correspond to a true elimination tree postordering. - * - * Info [AMD_NDIV]: the number of divide operations for a subsequent LDL' - * or LU factorization of the permuted matrix A (P,P). - * - * Info [AMD_NMULTSUBS_LDL]: the number of multiply-subtract pairs for a - * subsequent LDL' factorization of A (P,P). - * - * Info [AMD_NMULTSUBS_LU]: the number of multiply-subtract pairs for a - * subsequent LU factorization of A (P,P), assuming that no numerical - * pivoting is required. - * - * Info [AMD_DMAX]: the maximum number of nonzeros in any column of L, - * including the diagonal. - * - * Info [14..19] are not used in the current version, but may be used in - * future versions. + * Info [AMD_STATUS]: the return value of AMD, either AMD_OK, + * AMD_OK_BUT_JUMBLED, AMD_OUT_OF_MEMORY, or AMD_INVALID. + * + * Info [AMD_N]: n, the size of the input matrix + * + * Info [AMD_NZ]: the number of nonzeros in A, nz = Ap [n] + * + * Info [AMD_SYMMETRY]: the symmetry of the matrix A. It is the number + * of "matched" off-diagonal entries divided by the total number of + * off-diagonal entries. An entry A(i,j) is matched if A(j,i) is also + * an entry, for any pair (i,j) for which i != j. In MATLAB notation, + * S = spones (A) ; + * B = tril (S, -1) + triu (S, 1) ; + * symmetry = nnz (B & B') / nnz (B) ; + * + * Info [AMD_NZDIAG]: the number of entries on the diagonal of A. + * + * Info [AMD_NZ_A_PLUS_AT]: the number of nonzeros in A+A', excluding the + * diagonal. If A is perfectly symmetric (Info [AMD_SYMMETRY] = 1) + * with a fully nonzero diagonal, then Info [AMD_NZ_A_PLUS_AT] = nz-n + * (the smallest possible value). If A is perfectly unsymmetric + * (Info [AMD_SYMMETRY] = 0, for an upper triangular matrix, for + * example) with no diagonal, then Info [AMD_NZ_A_PLUS_AT] = 2*nz + * (the largest possible value). + * + * Info [AMD_NDENSE]: the number of "dense" rows/columns of A+A' that were + * removed from A prior to ordering. These are placed last in the + * output order P. + * + * Info [AMD_MEMORY]: the amount of memory used by AMD, in bytes. In the + * current version, this is 1.2 * Info [AMD_NZ_A_PLUS_AT] + 9*n + * times the size of an integer. This is at most 2.4nz + 9n. This + * excludes the size of the input arguments Ai, Ap, and P, which have + * a total size of nz + 2*n + 1 integers. + * + * Info [AMD_NCMPA]: the number of garbage collections performed. + * + * Info [AMD_LNZ]: the number of nonzeros in L (excluding the diagonal). + * This is a slight upper bound because mass elimination is combined + * with the approximate degree update. It is a rough upper bound if + * there are many "dense" rows/columns. The rest of the statistics, + * below, are also slight or rough upper bounds, for the same reasons. + * The post-ordering of the assembly tree might also not exactly + * correspond to a true elimination tree postordering. + * + * Info [AMD_NDIV]: the number of divide operations for a subsequent LDL' + * or LU factorization of the permuted matrix A (P,P). + * + * Info [AMD_NMULTSUBS_LDL]: the number of multiply-subtract pairs for a + * subsequent LDL' factorization of A (P,P). + * + * Info [AMD_NMULTSUBS_LU]: the number of multiply-subtract pairs for a + * subsequent LU factorization of A (P,P), assuming that no numerical + * pivoting is required. + * + * Info [AMD_DMAX]: the maximum number of nonzeros in any column of L, + * including the diagonal. + * + * Info [14..19] are not used in the current version, but may be used in + * future versions. */ /* ------------------------------------------------------------------------- */ @@ -237,38 +233,38 @@ SuiteSparse_long amd_l_order /* see above for description of arguments */ void amd_2 ( - int n, - int Pe [ ], - int Iw [ ], - int Len [ ], - int iwlen, - int pfree, - int Nv [ ], - int Next [ ], - int Last [ ], - int Head [ ], - int Elen [ ], - int Degree [ ], - int W [ ], + int32_t n, + int32_t Pe [ ], + int32_t Iw [ ], + int32_t Len [ ], + int32_t iwlen, + int32_t pfree, + int32_t Nv [ ], + int32_t Next [ ], + int32_t Last [ ], + int32_t Head [ ], + int32_t Elen [ ], + int32_t Degree [ ], + int32_t W [ ], double Control [ ], double Info [ ] ) ; void amd_l2 ( - SuiteSparse_long n, - SuiteSparse_long Pe [ ], - SuiteSparse_long Iw [ ], - SuiteSparse_long Len [ ], - SuiteSparse_long iwlen, - SuiteSparse_long pfree, - SuiteSparse_long Nv [ ], - SuiteSparse_long Next [ ], - SuiteSparse_long Last [ ], - SuiteSparse_long Head [ ], - SuiteSparse_long Elen [ ], - SuiteSparse_long Degree [ ], - SuiteSparse_long W [ ], + int64_t n, + int64_t Pe [ ], + int64_t Iw [ ], + int64_t Len [ ], + int64_t iwlen, + int64_t pfree, + int64_t Nv [ ], + int64_t Next [ ], + int64_t Last [ ], + int64_t Head [ ], + int64_t Elen [ ], + int64_t Degree [ ], + int64_t W [ ], double Control [ ], double Info [ ] ) ; @@ -283,32 +279,24 @@ void amd_l2 * matrix cannot be passed to amd_order. For amd_order, the matrix must also * be square. The first two arguments are the number of rows and the number * of columns of the matrix. For its use in AMD, these must both equal n. - * - * NOTE: this routine returned TRUE/FALSE in v1.2 and earlier. */ int amd_valid ( - int n_row, /* # of rows */ - int n_col, /* # of columns */ - const int Ap [ ], /* column pointers, of size n_col+1 */ - const int Ai [ ] /* row indices, of size Ap [n_col] */ + int32_t n_row, /* # of rows */ + int32_t n_col, /* # of columns */ + const int32_t Ap [ ], /* column pointers, of size n_col+1 */ + const int32_t Ai [ ] /* row indices, of size Ap [n_col] */ ) ; -SuiteSparse_long amd_l_valid +int amd_l_valid ( - SuiteSparse_long n_row, - SuiteSparse_long n_col, - const SuiteSparse_long Ap [ ], - const SuiteSparse_long Ai [ ] + int64_t n_row, + int64_t n_col, + const int64_t Ap [ ], + const int64_t Ai [ ] ) ; -/* ------------------------------------------------------------------------- */ -/* AMD memory manager and printf routines */ -/* ------------------------------------------------------------------------- */ - - /* moved to SuiteSparse_config.c */ - /* ------------------------------------------------------------------------- */ /* AMD Control and Info arrays */ /* ------------------------------------------------------------------------- */ @@ -386,11 +374,12 @@ void amd_l_info (double Info [ ]) ; * Versions 1.1 and earlier of AMD do not include a #define'd version number. */ -#define AMD_DATE "May 4, 2016" +#define AMD_DATE "Jan 17, 2023" +#define AMD_MAIN_VERSION 3 +#define AMD_SUB_VERSION 0 +#define AMD_SUBSUB_VERSION 3 + #define AMD_VERSION_CODE(main,sub) ((main) * 1000 + (sub)) -#define AMD_MAIN_VERSION 2 -#define AMD_SUB_VERSION 4 -#define AMD_SUBSUB_VERSION 6 #define AMD_VERSION AMD_VERSION_CODE(AMD_MAIN_VERSION,AMD_SUB_VERSION) #ifdef __cplusplus diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Include/amd_internal.h b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Include/amd_internal.h index 67305273e..85d43930b 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Include/amd_internal.h +++ b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Include/amd_internal.h @@ -1,12 +1,12 @@ -/* ========================================================================= */ -/* === amd_internal.h ====================================================== */ -/* ========================================================================= */ +//------------------------------------------------------------------------------ +// AMD/Include/amd_internal.h: internal definitions for AMD +//------------------------------------------------------------------------------ -/* ------------------------------------------------------------------------- */ -/* AMD, Copyright (c) Timothy A. Davis, */ -/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ -/* email: DrTimothyAldenDavis@gmail.com */ -/* ------------------------------------------------------------------------- */ +// AMD, Copyright (c) 1996-2022, Timothy A. Davis, Patrick R. Amestoy, and +// Iain S. Duff. All Rights Reserved. +// SPDX-License-Identifier: BSD-3-clause + +//------------------------------------------------------------------------------ /* This file is for internal use in AMD itself, and does not normally need to * be included in user code (it is included in UMFPACK, however). All others @@ -42,33 +42,8 @@ #undef NDEBUG */ -/* ------------------------------------------------------------------------- */ -/* ANSI include files */ -/* ------------------------------------------------------------------------- */ - -/* from stdlib.h: size_t, malloc, free, realloc, and calloc */ -#include - -#if !defined(NPRINT) || !defined(NDEBUG) -/* from stdio.h: printf. Not included if NPRINT is defined at compile time. - * fopen and fscanf are used when debugging. */ -#include -#endif - -/* from limits.h: INT_MAX and LONG_MAX */ -#include - -/* from math.h: sqrt */ -#include - -/* ------------------------------------------------------------------------- */ -/* MATLAB include files (only if being used in or via MATLAB) */ -/* ------------------------------------------------------------------------- */ - -#ifdef MATLAB_MEX_FILE -#include "matrix.h" -#include "mex.h" -#endif +#define SUITESPARSE_LIBRARY +#include "amd.h" /* ------------------------------------------------------------------------- */ /* basic definitions */ @@ -90,13 +65,7 @@ #undef EMPTY #endif -#ifdef GLOBAL -#undef GLOBAL -#endif - -#ifdef PRIVATE -#undef PRIVATE -#endif +#define PRIVATE static /* FLIP is a "negation about -1", and is used to mark an integer i that is * normally non-negative. FLIP (EMPTY) is EMPTY. FLIP of a number > EMPTY @@ -124,19 +93,8 @@ #define TRUE (1) #define FALSE (0) -#define PRIVATE static -#define GLOBAL #define EMPTY (-1) -/* Note that Linux's gcc 2.96 defines NULL as ((void *) 0), but other */ -/* compilers (even gcc 2.95.2 on Solaris) define NULL as 0 or (0). We */ -/* need to use the ANSI standard value of 0. */ -#ifdef NULL -#undef NULL -#endif - -#define NULL 0 - /* largest value of size_t */ #ifndef SIZE_T_MAX #ifdef SIZE_MAX @@ -148,16 +106,15 @@ #endif /* ------------------------------------------------------------------------- */ -/* integer type for AMD: int or SuiteSparse_long */ +/* integer type for AMD: int32_t or int64_t */ /* ------------------------------------------------------------------------- */ -#include "amd.h" - #if defined (DLONG) || defined (ZLONG) -#define Int SuiteSparse_long -#define ID SuiteSparse_long_id -#define Int_MAX SuiteSparse_long_max +#define Int int64_t +#define UInt uint64_t +#define ID "%" PRId64 +#define Int_MAX INT64_MAX #define AMD_order amd_l_order #define AMD_defaults amd_l_defaults @@ -176,9 +133,10 @@ #else -#define Int int +#define Int int32_t +#define UInt uint32_t #define ID "%d" -#define Int_MAX INT_MAX +#define Int_MAX INT32_MAX #define AMD_order amd_order #define AMD_defaults amd_defaults @@ -201,7 +159,7 @@ /* AMD routine definitions (not user-callable) */ /* ------------------------------------------------------------------------- */ -GLOBAL size_t AMD_aat +size_t AMD_aat ( Int n, const Int Ap [ ], @@ -211,7 +169,7 @@ GLOBAL size_t AMD_aat double Info [ ] ) ; -GLOBAL void AMD_1 +void AMD_1 ( Int n, const Int Ap [ ], @@ -225,7 +183,7 @@ GLOBAL void AMD_1 double Info [ ] ) ; -GLOBAL void AMD_postorder +void AMD_postorder ( Int nn, Int Parent [ ], @@ -237,7 +195,7 @@ GLOBAL void AMD_postorder Int Stack [ ] ) ; -GLOBAL Int AMD_post_tree +Int AMD_post_tree ( Int root, Int k, @@ -250,7 +208,7 @@ GLOBAL Int AMD_post_tree #endif ) ; -GLOBAL void AMD_preprocess +void AMD_preprocess ( Int n, const Int Ap [ ], @@ -270,15 +228,11 @@ GLOBAL void AMD_preprocess /* from assert.h: assert macro */ #include -#ifndef EXTERN -#define EXTERN extern -#endif - -EXTERN Int AMD_debug ; +extern Int AMD_debug ; -GLOBAL void AMD_debug_init ( char *s ) ; +void AMD_debug_init ( char *s ) ; -GLOBAL void AMD_dump +void AMD_dump ( Int n, Int Pe [ ], diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Lib/Makefile b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Lib/Makefile deleted file mode 100644 index 3b92f6a07..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Lib/Makefile +++ /dev/null @@ -1,121 +0,0 @@ -#------------------------------------------------------------------------------- -# AMD Lib/Makefile -#------------------------------------------------------------------------------- - -LIBRARY = libamd -VERSION = 2.4.6 -SO_VERSION = 2 - -default: library - -include ../../SuiteSparse_config/SuiteSparse_config.mk - -# AMD depends on SuiteSparse_config -LDLIBS += -lsuitesparseconfig - -# compile and install in SuiteSparse/lib -library: - $(MAKE) install INSTALL=$(SUITESPARSE) - -C = $(CC) $(CF) -I../Include -I../../SuiteSparse_config - -#------------------------------------------------------------------------------- -# source files -#------------------------------------------------------------------------------- - -AMD = amd_aat amd_1 amd_2 amd_dump amd_postorder amd_defaults \ - amd_post_tree \ - amd_order amd_control amd_info amd_valid amd_preprocess - -INC = ../Include/amd.h ../Include/amd_internal.h \ - ../../SuiteSparse_config/SuiteSparse_config.h - -#------------------------------------------------------------------------------- -# object files for each version -#------------------------------------------------------------------------------- - -AMDI = $(addsuffix .o, $(subst amd_,amd_i_,$(AMD))) -AMDL = $(addsuffix .o, $(subst amd_,amd_l_,$(AMD))) -OBJ = $(AMDI) $(AMDL) - -#------------------------------------------------------------------------------- -# compile each int and long routine (with no real/complex version) -#------------------------------------------------------------------------------- - -amd_i_%.o: ../Source/amd_%.c $(INC) - $(C) -DDINT -c $< -o $@ - -amd_l_%.o: ../Source/amd_%.c $(INC) - $(C) -DDLONG -c $< -o $@ - -#------------------------------------------------------------------------------- -# Create the static library (C versions only) -#------------------------------------------------------------------------------- - -static: $(AR_TARGET) - -$(AR_TARGET): $(OBJ) - $(ARCHIVE) $@ $^ - - $(RANLIB) $@ - -#------------------------------------------------------------------------------- -# compile the Fortran versions and the libamdf77.a library (static only) -#------------------------------------------------------------------------------- - -fortran: libamdf77.a - -AMDF77 = amd.o amdbar.o - -amd.o: ../Source/amd.f - $(F77) $(F77FLAGS) -c ../Source/amd.f -o amd.o - -amdbar.o: ../Source/amdbar.f - $(F77) $(F77FLAGS) -c ../Source/amdbar.f -o amdbar.o - -libamdf77.a: $(AMDF77) - $(ARCHIVE) libamdf77.a $^ - - $(RANLIB) libamdf77.a - -#------------------------------------------------------------------------------- -# install (shared C version only) -#------------------------------------------------------------------------------- - -# install AMD -install: $(AR_TARGET) $(INSTALL_LIB)/$(SO_TARGET) - -$(INSTALL_LIB)/$(SO_TARGET): $(OBJ) - @mkdir -p $(INSTALL_LIB) - @mkdir -p $(INSTALL_INCLUDE) - @mkdir -p $(INSTALL_DOC) - $(CC) $(SO_OPTS) $^ -o $@ $(LDLIBS) - ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_PLAIN) ) - ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_MAIN) ) - $(CP) ../Include/amd.h $(INSTALL_INCLUDE) - $(CP) ../Doc/AMD_UserGuide.pdf $(INSTALL_DOC) - $(CP) ../README.txt $(INSTALL_DOC)/AMD_README.txt - chmod 755 $(INSTALL_LIB)/$(SO_TARGET) - chmod 644 $(INSTALL_INCLUDE)/amd.h - chmod 644 $(INSTALL_DOC)/AMD_UserGuide.pdf - chmod 644 $(INSTALL_DOC)/AMD_README.txt - -# uninstall AMD -uninstall: - $(RM) $(INSTALL_LIB)/$(SO_TARGET) - $(RM) $(INSTALL_LIB)/$(SO_PLAIN) - $(RM) $(INSTALL_LIB)/$(SO_MAIN) - $(RM) $(INSTALL_INCLUDE)/amd.h - $(RM) $(INSTALL_DOC)/AMD_UserGuide.pdf - $(RM) $(INSTALL_DOC)/AMD_README.txt - -#------------------------------------------------------------------------------- -# Remove all but the files in the original distribution -#------------------------------------------------------------------------------- - -clean: - - $(RM) -r $(CLEAN) - -purge: distclean - -distclean: clean - - $(RM) -r $(PURGE) - diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/MATLAB/Contents.m b/deps/AMICI/ThirdParty/SuiteSparse/AMD/MATLAB/Contents.m deleted file mode 100644 index c9c5eec4e..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/MATLAB/Contents.m +++ /dev/null @@ -1,18 +0,0 @@ -%Contents of the AMD sparse matrix ordering package: -% -% amd2 - p = amd2 (A), the approximate minimum degree ordering of A -% amd_demo - a demo of amd2, using the can_24 matrix -% amd_make - to compile amd2 for use in MATLAB -% amd_install - compile and install amd2 for use in MATLAB -% -% See also: amd, amd2, colamd, symamd, colmmd, symmmd, umfpack -% -% Note that amd2 and the built-in amd function in MATLAB 7.3 and later are one -% and the same. -% -% Example: -% p = amd2 (A) ; - -% Copyright 1994-2007, Tim Davis, Patrick R. Amestoy, and Iain S. Duff. - -help Contents diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/MATLAB/amd2.m b/deps/AMICI/ThirdParty/SuiteSparse/AMD/MATLAB/amd2.m deleted file mode 100644 index 60e09d5e5..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/MATLAB/amd2.m +++ /dev/null @@ -1,72 +0,0 @@ -function [p, Info] = amd2 (A, Control) %#ok -%AMD2 p = amd2 (A), the approximate minimum degree ordering of A -% P = AMD2 (S) returns the approximate minimum degree permutation vector for -% the sparse matrix C = S+S'. The Cholesky factorization of C (P,P), or -% S (P,P), tends to be sparser than that of C or S. AMD tends to be faster -% than SYMMMD and SYMAMD, and tends to return better orderings than SYMMMD. -% S must be square. If S is full, amd(S) is equivalent to amd(sparse(S)). -% -% Note that the built-in AMD routine in MATLAB is identical to AMD2, -% except that AMD in MATLAB allows for a struct input to set the parameters. -% -% Usage: P = amd2 (S) ; % finds the ordering -% [P, Info] = amd2 (S, Control) ; % optional parameters & statistics -% Control = amd2 ; % returns default parameters -% amd2 ; % prints default parameters. -% -% Control (1); If S is n-by-n, then rows/columns with more than -% max (16, (Control (1))* sqrt(n)) entries in S+S' are considered -% "dense", and ignored during ordering. They are placed last in the -% output permutation. The default is 10.0 if Control is not present. -% Control (2): If nonzero, then aggressive absorption is performed. -% This is the default if Control is not present. -% Control (3): If nonzero, print statistics about the ordering. -% -% Info (1): status (0: ok, -1: out of memory, -2: matrix invalid) -% Info (2): n = size (A,1) -% Info (3): nnz (A) -% Info (4): the symmetry of the matrix S (0.0 means purely unsymmetric, -% 1.0 means purely symmetric). Computed as: -% B = tril (S, -1) + triu (S, 1) ; symmetry = nnz (B & B') / nnz (B); -% Info (5): nnz (diag (S)) -% Info (6): nnz in S+S', excluding the diagonal (= nnz (B+B')) -% Info (7): number "dense" rows/columns in S+S' -% Info (8): the amount of memory used by AMD, in bytes -% Info (9): the number of memory compactions performed by AMD -% -% The following statistics are slight upper bounds because of the -% approximate degree in AMD. The bounds are looser if "dense" rows/columns -% are ignored during ordering (Info (7) > 0). The statistics are for a -% subsequent factorization of the matrix C (P,P). The LU factorization -% statistics assume no pivoting. -% -% Info (10): the number of nonzeros in L, excluding the diagonal -% Info (11): the number of divide operations for LL', LDL', or LU -% Info (12): the number of multiply-subtract pairs for LL' or LDL' -% Info (13): the number of multiply-subtract pairs for LU -% Info (14): the max # of nonzeros in any column of L (incl. diagonal) -% Info (15:20): unused, reserved for future use -% -% An assembly tree post-ordering is performed, which is typically the same -% as an elimination tree post-ordering. It is not always identical because -% of the approximate degree update used, and because "dense" rows/columns -% do not take part in the post-order. It well-suited for a subsequent -% "chol", however. If you require a precise elimination tree post-ordering, -% then see the example below: -% -% Example: -% -% P = amd2 (S) ; -% C = spones (S) + spones (S') ; % skip this if S already symmetric -% [ignore, Q] = etree (C (P,P)) ; -% P = P (Q) ; -% -% See also AMD, COLMMD, COLAMD, COLPERM, SYMAMD, SYMMMD, SYMRCM. - -% Copyright 1994-2012, Timothy A. Davis, http://www.suitesparse.com, -% Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. -% -% Acknowledgements: This work was supported by the National Science -% Foundation, under grants ASC-9111263, DMS-9223088, and CCR-0203270. - -error ('amd2 mexFunction not found') ; diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/MATLAB/amd_demo.m b/deps/AMICI/ThirdParty/SuiteSparse/AMD/MATLAB/amd_demo.m deleted file mode 100644 index 06deba221..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/MATLAB/amd_demo.m +++ /dev/null @@ -1,80 +0,0 @@ -function amd_demo -%AMD_DEMO a demo of amd2, using the can_24 matrix -% -% A demo of AMD for MATLAB. -% -% Example: -% amd_demo -% -% See also: amd, amd2, amd_make - -% Copyright 1994-2007, Tim Davis, Patrick R. Amestoy, and Iain S. Duff. - -% This orders the same matrix as the ANSI C demo, amd_demo.c. It includes an -% additional analysis of the matrix via MATLAB's symbfact routine. - -% First, print the help information for AMD -help amd2 - -% Get the Harwell/Boeing can_24 matrix. - -load can_24 -A = spconvert (can_24) ; - -n = size (A,1) ; - -clf -subplot (2,2,1) ; -spy (A) -title ('HB/can24 matrix') ; - -% order the matrix. Note that the Info argument is optional. -fprintf ('\nIf the next step fails, then you have\n') ; -fprintf ('not yet compiled the AMD mexFunction.\n') ; -[p, Info] = amd2 (A) ; %#ok - -% order again, but this time print some statistics -[p, Info] = amd2 (A, [10 1 1]) ; - -fprintf ('Permutation vector:\n') ; -fprintf (' %2d', p) ; -fprintf ('\n\n') ; - -subplot (2,2,2) ; -spy (A (p,p)) ; -title ('Permuted matrix') ; - -% The amd_demo.c program stops here. - -fprintf ('Analyze A(p,p) with MATLAB''s symbfact routine:\n') ; -[cn, height, parent, post, R] = symbfact (A (p,p)) ; - -subplot (2,2,3) ; -spy (R') ; -title ('Cholesky factor, L') ; - -subplot (2,2,4) ; -treeplot (parent) ; -title ('elimination tree') ; - -% results from symbfact -lnz = sum (cn) ; % number of nonzeros in L, incl. diagonal -cn = cn - 1 ; % get the count of off-diagonal entries -fl = n + sum (cn.^2 + 2*cn) ; % flop count for chol (A (p,p) -fprintf ('number of nonzeros in L (including diagonal): %d\n', lnz) ; -fprintf ('floating point operation count for chol (A (p,p)): %d\n', fl) ; - -% approximations from amd: -lnz2 = n + Info (10) ; -fl2 = n + Info (11) + 2 * Info (12) ; -fprintf ('\nResults from AMD''s approximate analysis:\n') ; -fprintf ('number of nonzeros in L (including diagonal): %d\n', lnz2) ; -fprintf ('floating point operation count for chol (A (p,p)): %d\n\n', fl2) ; - -if (lnz2 ~= lnz | fl ~= fl2) %#ok - fprintf ('Note that the nonzero and flop counts from AMD are slight\n') ; - fprintf ('upper bounds. This is due to the approximate minimum degree\n'); - fprintf ('method used, in conjunction with "mass elimination".\n') ; - fprintf ('See the discussion about mass elimination in amd.h and\n') ; - fprintf ('amd_2.c for more details.\n') ; -end diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/MATLAB/amd_demo.m.out b/deps/AMICI/ThirdParty/SuiteSparse/AMD/MATLAB/amd_demo.m.out deleted file mode 100644 index 4e644826d..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/MATLAB/amd_demo.m.out +++ /dev/null @@ -1,125 +0,0 @@ -amd_demo - AMD2 p = amd2 (A), the approximate minimum degree ordering of A - P = AMD2 (S) returns the approximate minimum degree permutation vector for - the sparse matrix C = S+S'. The Cholesky factorization of C (P,P), or - S (P,P), tends to be sparser than that of C or S. AMD tends to be faster - than SYMMMD and SYMAMD, and tends to return better orderings than SYMMMD. - S must be square. If S is full, amd(S) is equivalent to amd(sparse(S)). - - Note that the built-in AMD routine in MATLAB is identical to AMD2, - except that AMD in MATLAB allows for a struct input to set the parameters. - - Usage: P = amd2 (S) ; % finds the ordering - [P, Info] = amd2 (S, Control) ; % optional parameters & statistics - Control = amd2 ; % returns default parameters - amd2 ; % prints default parameters. - - Control (1); If S is n-by-n, then rows/columns with more than - max (16, (Control (1))* sqrt(n)) entries in S+S' are considered - "dense", and ignored during ordering. They are placed last in the - output permutation. The default is 10.0 if Control is not present. - Control (2): If nonzero, then aggressive absorption is performed. - This is the default if Control is not present. - Control (3): If nonzero, print statistics about the ordering. - - Info (1): status (0: ok, -1: out of memory, -2: matrix invalid) - Info (2): n = size (A,1) - Info (3): nnz (A) - Info (4): the symmetry of the matrix S (0.0 means purely unsymmetric, - 1.0 means purely symmetric). Computed as: - B = tril (S, -1) + triu (S, 1) ; symmetry = nnz (B & B') / nnz (B); - Info (5): nnz (diag (S)) - Info (6): nnz in S+S', excluding the diagonal (= nnz (B+B')) - Info (7): number "dense" rows/columns in S+S' - Info (8): the amount of memory used by AMD, in bytes - Info (9): the number of memory compactions performed by AMD - - The following statistics are slight upper bounds because of the - approximate degree in AMD. The bounds are looser if "dense" rows/columns - are ignored during ordering (Info (7) > 0). The statistics are for a - subsequent factorization of the matrix C (P,P). The LU factorization - statistics assume no pivoting. - - Info (10): the number of nonzeros in L, excluding the diagonal - Info (11): the number of divide operations for LL', LDL', or LU - Info (12): the number of multiply-subtract pairs for LL' or LDL' - Info (13): the number of multiply-subtract pairs for LU - Info (14): the max # of nonzeros in any column of L (incl. diagonal) - Info (15:20): unused, reserved for future use - - An assembly tree post-ordering is performed, which is typically the same - as an elimination tree post-ordering. It is not always identical because - of the approximate degree update used, and because "dense" rows/columns - do not take part in the post-order. It well-suited for a subsequent - "chol", however. If you require a precise elimination tree post-ordering, - then see the example below: - - Example: - - P = amd2 (S) ; - C = spones (S) + spones (S') ; % skip this if S already symmetric - [ignore, Q] = etree (C (P,P)) ; - P = P (Q) ; - - See also AMD, COLMMD, COLAMD, COLPERM, SYMAMD, SYMMMD, SYMRCM. - - -If the next step fails, then you have -not yet compiled the AMD mexFunction. - -AMD version 2.2.0, May 31, 2007: approximate minimum degree ordering - dense row parameter: 10 - (rows with more than max (10 * sqrt (n), 16) entries are - considered "dense", and placed last in output permutation) - aggressive absorption: yes - size of AMD integer: 4 - - input matrix A is 24-by-24 - input matrix A has 160 nonzero entries - -AMD version 2.2.0, May 31, 2007, results: - status: OK - n, dimension of A: 24 - nz, number of nonzeros in A: 160 - symmetry of A: 1.0000 - number of nonzeros on diagonal: 24 - nonzeros in pattern of A+A' (excl. diagonal): 136 - # dense rows/columns of A+A': 0 - memory used, in bytes: 1516 - # of memory compactions: 0 - - The following approximate statistics are for a subsequent - factorization of A(P,P) + A(P,P)'. They are slight upper - bounds if there are no dense rows/columns in A+A', and become - looser if dense rows/columns exist. - - nonzeros in L (excluding diagonal): 97 - nonzeros in L (including diagonal): 121 - # divide operations for LDL' or LU: 97 - # multiply-subtract operations for LDL': 275 - # multiply-subtract operations for LU: 453 - max nz. in any column of L (incl. diagonal): 8 - - chol flop count for real A, sqrt counted as 1 flop: 671 - LDL' flop count for real A: 647 - LDL' flop count for complex A: 3073 - LU flop count for real A (with no pivoting): 1003 - LU flop count for complex A (with no pivoting): 4497 - -Permutation vector: - 23 21 11 24 13 6 17 9 15 5 16 8 2 10 14 18 1 3 4 7 12 19 22 20 - -Analyze A(p,p) with MATLAB's symbfact routine: -number of nonzeros in L (including diagonal): 120 -floating point operation count for chol (A (p,p)): 656 - -Results from AMD's approximate analysis: -number of nonzeros in L (including diagonal): 121 -floating point operation count for chol (A (p,p)): 671 - -Note that the nonzero and flop counts from AMD are slight -upper bounds. This is due to the approximate minimum degree -method used, in conjunction with "mass elimination". -See the discussion about mass elimination in amd.h and -amd_2.c for more details. -diary off diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/MATLAB/amd_install.m b/deps/AMICI/ThirdParty/SuiteSparse/AMD/MATLAB/amd_install.m deleted file mode 100644 index 6b9f3628d..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/MATLAB/amd_install.m +++ /dev/null @@ -1,19 +0,0 @@ -function amd_install -%AMD_INSTALL compile and install amd2 for use in MATLAB -% Your current directory must be AMD/MATLAB for this function to work. -% -% Example: -% amd_install -% -% See also amd, amd2. - -% Copyright 1994-2007, Tim Davis, Patrick R. Amestoy, and Iain S. Duff. - -% This orders the same matrix as the ANSI C demo, amd_demo.c. It includes an - -amd_make -addpath (pwd) -fprintf ('\nThe following path has been added. You may wish to add it\n') ; -fprintf ('permanently, using the MATLAB pathtool command.\n') ; -fprintf ('%s\n\n', pwd) ; -amd_demo diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/MATLAB/amd_make.m b/deps/AMICI/ThirdParty/SuiteSparse/AMD/MATLAB/amd_make.m deleted file mode 100644 index e43ec87b3..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/MATLAB/amd_make.m +++ /dev/null @@ -1,43 +0,0 @@ -function amd_make -%AMD_MAKE to compile amd2 for use in MATLAB -% -% Example: -% amd_make -% -% See also amd, amd2. - -% Copyright 1994-2007, Tim Davis, Patrick R. Amestoy, and Iain S. Duff. - -details = 0 ; % 1 if details of each command are to be printed - -d = '' ; -if (~isempty (strfind (computer, '64'))) - d = '-largeArrayDims' ; -end - -% MATLAB 8.3.0 now has a -silent option to keep 'mex' from burbling too much -if (~verLessThan ('matlab', '8.3.0')) - d = ['-silent ' d] ; -end - -i = sprintf ('-I../Include -I../../SuiteSparse_config') ; -cmd = sprintf ('mex -O %s -DDLONG -output amd2 %s amd_mex.c %s', d, i, ... - '../../SuiteSparse_config/SuiteSparse_config.c') ; -files = {'amd_order', 'amd_dump', 'amd_postorder', 'amd_post_tree', ... - 'amd_aat', 'amd_2', 'amd_1', 'amd_defaults', 'amd_control', ... - 'amd_info', 'amd_valid', 'amd_preprocess' } ; -for i = 1 : length (files) - cmd = sprintf ('%s ../Source/%s.c', cmd, files {i}) ; -end -if (details) - fprintf ('%s\n', cmd) ; -end - -if (~(ispc || ismac)) - % for POSIX timing routine - cmd = [cmd ' -lrt'] ; -end - -eval (cmd) ; - -fprintf ('AMD successfully compiled.\n') ; diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/MATLAB/amd_mex.c b/deps/AMICI/ThirdParty/SuiteSparse/AMD/MATLAB/amd_mex.c deleted file mode 100644 index 89fdc9b7c..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/MATLAB/amd_mex.c +++ /dev/null @@ -1,192 +0,0 @@ -/* ========================================================================= */ -/* === AMD mexFunction ===================================================== */ -/* ========================================================================= */ - -/* ------------------------------------------------------------------------- */ -/* AMD, Copyright (c) Timothy A. Davis, */ -/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ -/* email: DrTimothyAldenDavis@gmail.com */ -/* ------------------------------------------------------------------------- */ - -/* - * Usage: - * p = amd (A) - * p = amd (A, Control) - * [p, Info] = amd (A) - * [p, Info] = amd (A, Control) - * Control = amd ; % return the default Control settings for AMD - * amd ; % print the default Control settings for AMD - * - * Given a square matrix A, compute a permutation P suitable for a Cholesky - * factorization of the matrix B (P,P), where B = spones (A) + spones (A'). - * The method used is the approximate minimum degree ordering method. See - * amd.m and amd.h for more information. - * - * The input matrix need not have sorted columns, and can have duplicate - * entries. - */ - -#include "amd.h" -#include "mex.h" -#include "matrix.h" -#define Long SuiteSparse_long - -void mexFunction -( - int nargout, - mxArray *pargout [ ], - int nargin, - const mxArray *pargin [ ] -) -{ - Long i, m, n, *Ap, *Ai, *P, nc, result, spumoni, full ; - double *Pout, *InfoOut, Control [AMD_CONTROL], Info [AMD_INFO], *ControlIn ; - mxArray *A ; - - /* --------------------------------------------------------------------- */ - /* get control parameters */ - /* --------------------------------------------------------------------- */ - - spumoni = 0 ; - if (nargin == 0) - { - /* get the default control parameters, and return */ - pargout [0] = mxCreateDoubleMatrix (AMD_CONTROL, 1, mxREAL) ; - amd_l_defaults (mxGetPr (pargout [0])) ; - if (nargout == 0) - { - amd_l_control (mxGetPr (pargout [0])) ; - } - return ; - } - - amd_l_defaults (Control) ; - if (nargin > 1) - { - ControlIn = mxGetPr (pargin [1]) ; - nc = mxGetM (pargin [1]) * mxGetN (pargin [1]) ; - Control [AMD_DENSE] - = (nc > 0) ? ControlIn [AMD_DENSE] : AMD_DEFAULT_DENSE ; - Control [AMD_AGGRESSIVE] - = (nc > 1) ? ControlIn [AMD_AGGRESSIVE] : AMD_DEFAULT_AGGRESSIVE ; - spumoni = (nc > 2) ? (ControlIn [2] != 0) : 0 ; - } - - if (spumoni > 0) - { - amd_l_control (Control) ; - } - - /* --------------------------------------------------------------------- */ - /* get inputs */ - /* --------------------------------------------------------------------- */ - - if (nargout > 2 || nargin > 2) - { - mexErrMsgTxt ("Usage: p = amd (A)\nor [p, Info] = amd (A, Control)") ; - } - - A = (mxArray *) pargin [0] ; - n = mxGetN (A) ; - m = mxGetM (A) ; - if (spumoni > 0) - { - mexPrintf (" input matrix A is %d-by-%d\n", m, n) ; - } - if (mxGetNumberOfDimensions (A) != 2) - { - mexErrMsgTxt ("amd: A must be 2-dimensional") ; - } - if (m != n) - { - mexErrMsgTxt ("amd: A must be square") ; - } - - /* --------------------------------------------------------------------- */ - /* allocate workspace for output permutation */ - /* --------------------------------------------------------------------- */ - - P = mxMalloc ((n+1) * sizeof (Long)) ; - - /* --------------------------------------------------------------------- */ - /* if A is full, convert to a sparse matrix */ - /* --------------------------------------------------------------------- */ - - full = !mxIsSparse (A) ; - if (full) - { - if (spumoni > 0) - { - mexPrintf ( - " input matrix A is full (sparse copy of A will be created)\n"); - } - mexCallMATLAB (1, &A, 1, (mxArray **) pargin, "sparse") ; - } - Ap = (Long *) mxGetJc (A) ; - Ai = (Long *) mxGetIr (A) ; - if (spumoni > 0) - { - mexPrintf (" input matrix A has %d nonzero entries\n", Ap [n]) ; - } - - /* --------------------------------------------------------------------- */ - /* order the matrix */ - /* --------------------------------------------------------------------- */ - - result = amd_l_order (n, Ap, Ai, P, Control, Info) ; - - /* --------------------------------------------------------------------- */ - /* if A is full, free the sparse copy of A */ - /* --------------------------------------------------------------------- */ - - if (full) - { - mxDestroyArray (A) ; - } - - /* --------------------------------------------------------------------- */ - /* print results (including return value) */ - /* --------------------------------------------------------------------- */ - - if (spumoni > 0) - { - amd_l_info (Info) ; - } - - /* --------------------------------------------------------------------- */ - /* check error conditions */ - /* --------------------------------------------------------------------- */ - - if (result == AMD_OUT_OF_MEMORY) - { - mexErrMsgTxt ("amd: out of memory") ; - } - else if (result == AMD_INVALID) - { - mexErrMsgTxt ("amd: input matrix A is corrupted") ; - } - - /* --------------------------------------------------------------------- */ - /* copy the outputs to MATLAB */ - /* --------------------------------------------------------------------- */ - - /* output permutation, P */ - pargout [0] = mxCreateDoubleMatrix (1, n, mxREAL) ; - Pout = mxGetPr (pargout [0]) ; - for (i = 0 ; i < n ; i++) - { - Pout [i] = P [i] + 1 ; /* change to 1-based indexing for MATLAB */ - } - mxFree (P) ; - - /* Info */ - if (nargout > 1) - { - pargout [1] = mxCreateDoubleMatrix (AMD_INFO, 1, mxREAL) ; - InfoOut = mxGetPr (pargout [1]) ; - for (i = 0 ; i < AMD_INFO ; i++) - { - InfoOut [i] = Info [i] ; - } - } -} diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/MATLAB/can_24 b/deps/AMICI/ThirdParty/SuiteSparse/AMD/MATLAB/can_24 deleted file mode 100644 index 38158a159..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/MATLAB/can_24 +++ /dev/null @@ -1,160 +0,0 @@ - 1 1 1 - 6 1 1 - 7 1 1 -13 1 1 -14 1 1 -18 1 1 -19 1 1 -20 1 1 -22 1 1 - 2 2 1 - 9 2 1 -10 2 1 -14 2 1 -15 2 1 -18 2 1 - 3 3 1 - 7 3 1 -12 3 1 -21 3 1 -22 3 1 -23 3 1 - 4 4 1 - 8 4 1 -11 4 1 -16 4 1 -19 4 1 -20 4 1 - 5 5 1 - 8 5 1 -10 5 1 -15 5 1 -16 5 1 -17 5 1 - 1 6 1 - 6 6 1 - 7 6 1 -13 6 1 -14 6 1 -18 6 1 - 1 7 1 - 3 7 1 - 6 7 1 - 7 7 1 -12 7 1 -13 7 1 -20 7 1 -22 7 1 -24 7 1 - 4 8 1 - 5 8 1 - 8 8 1 -10 8 1 -15 8 1 -16 8 1 -17 8 1 -18 8 1 -19 8 1 - 2 9 1 - 9 9 1 -10 9 1 -15 9 1 - 2 10 1 - 5 10 1 - 8 10 1 - 9 10 1 -10 10 1 -14 10 1 -15 10 1 -18 10 1 -19 10 1 - 4 11 1 -11 11 1 -19 11 1 -20 11 1 -21 11 1 -22 11 1 - 3 12 1 - 7 12 1 -12 12 1 -13 12 1 -22 12 1 -24 12 1 - 1 13 1 - 6 13 1 - 7 13 1 -12 13 1 -13 13 1 -24 13 1 - 1 14 1 - 2 14 1 - 6 14 1 -10 14 1 -14 14 1 -18 14 1 - 2 15 1 - 5 15 1 - 8 15 1 - 9 15 1 -10 15 1 -15 15 1 - 4 16 1 - 5 16 1 - 8 16 1 -16 16 1 -17 16 1 -19 16 1 - 5 17 1 - 8 17 1 -16 17 1 -17 17 1 - 1 18 1 - 2 18 1 - 6 18 1 - 8 18 1 -10 18 1 -14 18 1 -18 18 1 -19 18 1 -20 18 1 - 1 19 1 - 4 19 1 - 8 19 1 -10 19 1 -11 19 1 -16 19 1 -18 19 1 -19 19 1 -20 19 1 - 1 20 1 - 4 20 1 - 7 20 1 -11 20 1 -18 20 1 -19 20 1 -20 20 1 -21 20 1 -22 20 1 - 3 21 1 -11 21 1 -20 21 1 -21 21 1 -22 21 1 -23 21 1 - 1 22 1 - 3 22 1 - 7 22 1 -11 22 1 -12 22 1 -20 22 1 -21 22 1 -22 22 1 -23 22 1 - 3 23 1 -21 23 1 -22 23 1 -23 23 1 - 7 24 1 -12 24 1 -13 24 1 -24 24 1 diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Makefile b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Makefile index b9bfbb588..ad08cc1d1 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Makefile +++ b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Makefile @@ -1,73 +1,94 @@ -#------------------------------------------------------------------------------ -# AMD Makefile -#------------------------------------------------------------------------------ - -SUITESPARSE ?= $(realpath $(CURDIR)/..) -export SUITESPARSE - -default: all - -include ../SuiteSparse_config/SuiteSparse_config.mk - -demos: all - -# Compile all C code. Do not compile the FORTRAN versions. -all: - ( cd Lib ; $(MAKE) ) - ( cd Demo ; $(MAKE) ) - -# compile just the C-callable libraries (not Demos) +#------------------------------------------------------------------------------- +# SuiteSparse/AMD/Makefile +#------------------------------------------------------------------------------- + +# AMD: Copyright (c) 2009-2022, Timothy A. Davis, Patrick Amestoy, Iain Duff. +# All Rights Reserved. +# SPDX-License-Identifier: BSD-3-clause + +#------------------------------------------------------------------------------- + +# A simple Makefile for AMD, which relies on cmake to do the +# actual build. All the work is done in cmake so this Makefile is just for +# convenience. + +# To compile with an alternate compiler: +# +# make CC=gcc CXX=g++ +# +# To compile/install for system-wide usage: +# +# make +# sudo make install +# +# To compile/install for local usage (SuiteSparse/lib and SuiteSparse/include): +# +# make local +# make install +# +# To clean up the files: +# +# make clean + +JOBS ?= 8 + +default: library + +# default is to install only in /usr/local library: - ( cd Lib ; $(MAKE) ) - -# compile the static libraries only -static: - ( cd Lib ; $(MAKE) static ) + ( cd build && cmake $(CMAKE_OPTIONS) .. && cmake --build . -j${JOBS} ) + +# install only in SuiteSparse/lib and SuiteSparse/include +local: + ( cd build && cmake $(CMAKE_OPTIONS) -DLOCAL_INSTALL=1 .. && cmake --build . -j${JOBS} ) + +# install only in /usr/local (default) +global: + ( cd build && cmake $(CMAKE_OPTIONS) -DLOCAL_INSTALL=0 .. && cmake --build . -j${JOBS} ) + +debug: + ( cd build && cmake $(CMAKE_OPTIONS) -DCMAKE_BUILD_TYPE=Debug .. && cmake --build . -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 + # 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 + +# just compile after running cmake; do not run cmake again +remake: + ( cd build && cmake --build . -j${JOBS} ) + +# just run cmake to set things up +setup: + ( cd build && cmake $(CMAKE_OPTIONS) .. ) -# compile the FORTRAN libraries and demo programs (not compiled by "make all") -fortran: - ( cd Lib ; $(MAKE) fortran ) - ( cd Demo ; $(MAKE) fortran ) +install: + ( cd build && cmake --install . ) -# compile a FORTRAN demo program that calls the C version of AMD -# (not compiled by "make all") -cross: - ( cd Demo ; $(MAKE) cross ) +# remove any installed libraries and #include files +uninstall: + - xargs rm < build/install_manifest.txt -# remove object files, but keep the compiled programs and library archives +# remove all files not in the distribution clean: - ( cd Lib ; $(MAKE) clean ) - ( cd Demo ; $(MAKE) clean ) - ( cd MATLAB ; $(RM) $(CLEAN) ) - ( cd Doc ; $(MAKE) clean ) + - $(RM) -rf build/* Config/*.tmp MATLAB/*.o MATLAB/*.mex* -# clean, and then remove compiled programs and library archives -purge: - ( cd Lib ; $(MAKE) purge ) - ( cd Demo ; $(MAKE) purge ) - ( cd MATLAB ; $(RM) $(CLEAN) ; $(RM) *.mex* ) - ( cd Doc ; $(MAKE) purge ) +purge: clean -distclean: purge +distclean: clean -# create PDF documents for the original distribution docs: - ( cd Doc ; $(MAKE) ) - -# get ready for distribution -dist: purge - ( cd Demo ; $(MAKE) dist ) - ( cd Doc ; $(MAKE) ) - -ccode: library - -lib: library - -# install AMD -install: - ( cd Lib ; $(MAKE) install ) - -# uninstall AMD -uninstall: - ( cd Lib ; $(MAKE) uninstall ) - + ( cd Doc && $(MAKE) ) diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/README.txt b/deps/AMICI/ThirdParty/SuiteSparse/AMD/README.txt index 5aa64c473..e4696d593 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/README.txt +++ b/deps/AMICI/ThirdParty/SuiteSparse/AMD/README.txt @@ -1,4 +1,4 @@ -AMD, Copyright (c) 2009-2013 by Timothy A. Davis (http://www.suitesparse.com), +AMD, Copyright (c) 1996-2022 by Timothy A. Davis (http://www.suitesparse.com), Patrick R. Amestoy, and Iain S. Duff. All Rights Reserved. AMD is available under alternate licences; contact T. Davis for details. @@ -9,17 +9,21 @@ AMD: a set of routines for permuting sparse matrices prior to Requires SuiteSparse_config, in the ../SuiteSparse_config directory relative to this directory. -Quick start (Unix, or Windows with Cygwin): +Quick start (Linux or MacOSX): - To compile, test, and install AMD, you may wish to first configure the - installation by editting the ../SuiteSparse_config/SuiteSparse_config.mk - file. Next, cd to this directory (AMD) and type "make" (or "make lib" if - you do not have MATLAB). To compile and run a demo program for the Fortran - version, type "make fortran". When done, type "make clean" to remove - unused *.o files (keeps the compiled libraries and demo programs). See the - User Guide (Doc/AMD_UserGuide.pdf), or - ../SuiteSparse_config/SuiteSparse_config.mk for more details. - To install do "make install" + To compile and install the library for system-wide usage: + + make + sudo make install + + To compile/install for local usage (SuiteSparse/lib and SuiteSparse/include) + + make local + make install + + To run the demos + + make demos Quick start (for MATLAB users); @@ -76,16 +80,15 @@ Files and directories in the AMD distribution: Include include file for use in your code that calls AMD Demo demo programs. also serves as test of the AMD installation. MATLAB AMD mexFunction for MATLAB, and supporting m-files - Lib where the compiled C-callable and Fortran-callable - AMD libraries placed. + build where the compiled libraries and demos are placed + Config source file to construct amd.h --------------------------------------------------------------------------- Files in the AMD directory: --------------------------------------------------------------------------- - Makefile top-level Makefile - Windows users would require Cygwin to use "make" - + Makefile a very simple Makefile (optional); just for simplifying cmake + CMakeLists.txt cmake script for building AMD README.txt this file --------------------------------------------------------------------------- @@ -119,6 +122,8 @@ Files and directories in the AMD distribution: amd_valid.c non-user-callable, verifies a matrix amd_preprocess.c non-user-callable, computes A', removes duplic + amd_l* same as above, but with int64_t integers + amd.f user-callable Fortran 77 version amdbar.f user-callable Fortran 77 version @@ -127,20 +132,20 @@ Files and directories in the AMD distribution: --------------------------------------------------------------------------- amd.h include file for C programs that use AMD + constructed by cmake from Config/amd.h.in amd_internal.h non-user-callable, include file for AMD --------------------------------------------------------------------------- Demo directory: --------------------------------------------------------------------------- - Makefile to compile the demos amd_demo.c C demo program for AMD amd_demo.out output of amd_demo.c amd_demo2.c C demo program for AMD, jumbled matrix amd_demo2.out output of amd_demo2.c - amd_l_demo.c C demo program for AMD (long integer version) + amd_l_demo.c C demo program for AMD (int64_t version) amd_l_demo.out output of amd_l_demo.c amd_simple.c simple C demo program for AMD @@ -173,8 +178,8 @@ Files and directories in the AMD distribution: can_24.mat input file for AMD demo --------------------------------------------------------------------------- - Lib directory: libamd.a and libamd.so library placed here + build directory: libamd.a and libamd.so library placed here --------------------------------------------------------------------------- - Makefile Makefile for both shared and static libraries + .gitignore only file in the original distribution diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd.f b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd.f index ccfe3e8c9..037807364 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd.f +++ b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd.f @@ -1,5 +1,11 @@ C----------------------------------------------------------------------- -C AMD: approximate minimum degree, with aggressive absorption +C AMD/Source/amd.f: Fortran version of AMD +C----------------------------------------------------------------------- + +C AMD, Copyright (c) 1996-2022, Timothy A. Davis, Patrick R. Amestoy, +C and C Iain S. Duff. All Rights Reserved. +C SPDX-License-Identifier: BSD-3-clause + C----------------------------------------------------------------------- SUBROUTINE AMD diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_1.c b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_1.c index 2be486e01..81c7d5d02 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_1.c +++ b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_1.c @@ -1,12 +1,12 @@ -/* ========================================================================= */ -/* === AMD_1 =============================================================== */ -/* ========================================================================= */ +//------------------------------------------------------------------------------ +// AMD/Source/amd_1: construct input matrix and then order with amd_2 +//------------------------------------------------------------------------------ -/* ------------------------------------------------------------------------- */ -/* AMD, Copyright (c) Timothy A. Davis, */ -/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ -/* email: DrTimothyAldenDavis@gmail.com */ -/* ------------------------------------------------------------------------- */ +// AMD, Copyright (c) 1996-2022, Timothy A. Davis, Patrick R. Amestoy, and +// Iain S. Duff. All Rights Reserved. +// SPDX-License-Identifier: BSD-3-clause + +//------------------------------------------------------------------------------ /* AMD_1: Construct A+A' for a sparse matrix A and perform the AMD ordering. * @@ -26,7 +26,7 @@ #include "amd_internal.h" -GLOBAL void AMD_1 +void AMD_1 ( Int n, /* n > 0 */ const Int Ap [ ], /* input of size n+1, not modified */ diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_2.c b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_2.c index f144722cd..d0294fd43 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_2.c +++ b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_2.c @@ -1,12 +1,12 @@ -/* ========================================================================= */ -/* === AMD_2 =============================================================== */ -/* ========================================================================= */ +//------------------------------------------------------------------------------ +// AMD/Source/amd_2: AMD ordering +//------------------------------------------------------------------------------ + +// AMD, Copyright (c) 1996-2022, Timothy A. Davis, Patrick R. Amestoy, and +// Iain S. Duff. All Rights Reserved. +// SPDX-License-Identifier: BSD-3-clause -/* ------------------------------------------------------------------------- */ -/* AMD, Copyright (c) Timothy A. Davis, */ -/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ -/* email: DrTimothyAldenDavis@gmail.com */ -/* ------------------------------------------------------------------------- */ +//------------------------------------------------------------------------------ /* AMD_2: performs the AMD ordering on a symmetric sparse matrix A, followed * by a postordering (via depth-first search) of the assembly tree using the @@ -39,7 +39,7 @@ static Int clear_flag (Int wflg, Int wbig, Int W [ ], Int n) /* === AMD_2 =============================================================== */ /* ========================================================================= */ -GLOBAL void AMD_2 +void AMD_2 ( Int n, /* A is n-by-n, where n > 0 */ Int Pe [ ], /* Pe [0..n-1]: index in Iw of row i on input */ @@ -119,8 +119,8 @@ GLOBAL void AMD_2 * ouput. Many of these functions are also provided by the Fortran * Harwell Subroutine Library routine MC47A. * - * (6) both int and SuiteSparse_long versions are provided. In the - * descriptions below and integer is and int or SuiteSparse_long depending + * (6) both int32_t and int64_t versions are provided. In the + * descriptions below an integer is int32_t or int64_t depending * on which version is being used. ********************************************************************** @@ -462,7 +462,7 @@ GLOBAL void AMD_2 nvi, nvj, nvpiv, slenme, wbig, we, wflg, wnvi, ok, ndense, ncmpa, dense, aggressive ; - unsigned Int hash ; /* unsigned, so that hash % n is well defined.*/ + UInt hash ; /* unsigned, so that hash % n is well defined.*/ /* * deg: the degree of a variable or element @@ -494,8 +494,8 @@ GLOBAL void AMD_2 * nvj: the number of variables in a supervariable j (= Nv [j]) * nvpiv: number of pivots in current element * slenme: number of variables in variable list of pivotal variable - * wbig: = (INT_MAX - n) for the int version, (SuiteSparse_long_max - n) - * for the SuiteSparse_long version. wflg is not allowed to + * wbig: = (INT32_MAX - n) for the int32_t version, (INT64_MAX - n) + * for the int64_t version. wflg is not allowed to * be >= wbig. * we: W [e] * wflg: used for flagging the W array. See description of Iw. diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_aat.c b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_aat.c index 67c03f7a4..2f7b54771 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_aat.c +++ b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_aat.c @@ -1,12 +1,12 @@ -/* ========================================================================= */ -/* === AMD_aat ============================================================= */ -/* ========================================================================= */ +//------------------------------------------------------------------------------ +// AMD/Source/amd_aat: compute symmetry of A and nnz in each column of A+A' +//------------------------------------------------------------------------------ -/* ------------------------------------------------------------------------- */ -/* AMD, Copyright (c) Timothy A. Davis, */ -/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ -/* email: DrTimothyAldenDavis@gmail.com */ -/* ------------------------------------------------------------------------- */ +// AMD, Copyright (c) 1996-2022, Timothy A. Davis, Patrick R. Amestoy, and +// Iain S. Duff. All Rights Reserved. +// SPDX-License-Identifier: BSD-3-clause + +//------------------------------------------------------------------------------ /* AMD_aat: compute the symmetry of the pattern of A, and count the number of * nonzeros each column of A+A' (excluding the diagonal). Assumes the input @@ -17,7 +17,7 @@ #include "amd_internal.h" -GLOBAL size_t AMD_aat /* returns nz in A+A' */ +size_t AMD_aat /* returns nz in A+A' */ ( Int n, const Int Ap [ ], diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_control.c b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_control.c index f6a5e9a54..cf47294a0 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_control.c +++ b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_control.c @@ -1,12 +1,12 @@ -/* ========================================================================= */ -/* === AMD_control ========================================================= */ -/* ========================================================================= */ +//------------------------------------------------------------------------------ +// AMD/Source/amd_control: print control parameters for AMD +//------------------------------------------------------------------------------ -/* ------------------------------------------------------------------------- */ -/* AMD, Copyright (c) Timothy A. Davis, */ -/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ -/* email: DrTimothyAldenDavis@gmail.com */ -/* ------------------------------------------------------------------------- */ +// AMD, Copyright (c) 1996-2022, Timothy A. Davis, Patrick R. Amestoy, and +// Iain S. Duff. All Rights Reserved. +// SPDX-License-Identifier: BSD-3-clause + +//------------------------------------------------------------------------------ /* User-callable. Prints the control parameters for AMD. See amd.h * for details. If the Control array is not present, the defaults are @@ -15,7 +15,7 @@ #include "amd_internal.h" -GLOBAL void AMD_control +void AMD_control ( double Control [ ] ) diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_defaults.c b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_defaults.c index b9a9079a0..3fb470a11 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_defaults.c +++ b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_defaults.c @@ -1,12 +1,12 @@ -/* ========================================================================= */ -/* === AMD_defaults ======================================================== */ -/* ========================================================================= */ +//------------------------------------------------------------------------------ +// AMD/Source/amd_defaults: set defaults for AMD +//------------------------------------------------------------------------------ + +// AMD, Copyright (c) 1996-2022, Timothy A. Davis, Patrick R. Amestoy, and +// Iain S. Duff. All Rights Reserved. +// SPDX-License-Identifier: BSD-3-clause -/* ------------------------------------------------------------------------- */ -/* AMD, Copyright (c) Timothy A. Davis, */ -/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ -/* email: DrTimothyAldenDavis@gmail.com */ -/* ------------------------------------------------------------------------- */ +//------------------------------------------------------------------------------ /* User-callable. Sets default control parameters for AMD. See amd.h * for details. @@ -18,7 +18,7 @@ /* === AMD defaults ======================================================== */ /* ========================================================================= */ -GLOBAL void AMD_defaults +void AMD_defaults ( double Control [ ] ) diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_dump.c b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_dump.c index e58aaf555..03e11651d 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_dump.c +++ b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_dump.c @@ -1,12 +1,12 @@ -/* ========================================================================= */ -/* === AMD_dump ============================================================ */ -/* ========================================================================= */ +//------------------------------------------------------------------------------ +// AMD/Source/amd_dump: debug routines for AMD +//------------------------------------------------------------------------------ + +// AMD, Copyright (c) 1996-2022, Timothy A. Davis, Patrick R. Amestoy, and +// Iain S. Duff. All Rights Reserved. +// SPDX-License-Identifier: BSD-3-clause -/* ------------------------------------------------------------------------- */ -/* AMD, Copyright (c) Timothy A. Davis, */ -/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ -/* email: DrTimothyAldenDavis@gmail.com */ -/* ------------------------------------------------------------------------- */ +//------------------------------------------------------------------------------ /* Debugging routines for AMD. Not used if NDEBUG is not defined at compile- * time (the default). See comments in amd_internal.h on how to enable @@ -18,7 +18,7 @@ #ifndef NDEBUG /* This global variable is present only when debugging */ -GLOBAL Int AMD_debug = -999 ; /* default is no debug printing */ +Int AMD_debug = -999 ; /* default is no debug printing */ /* ========================================================================= */ /* === AMD_debug_init ====================================================== */ @@ -26,7 +26,7 @@ GLOBAL Int AMD_debug = -999 ; /* default is no debug printing */ /* Sets the debug print level, by reading the file debug.amd (if it exists) */ -GLOBAL void AMD_debug_init ( char *s ) +void AMD_debug_init ( char *s ) { FILE *f ; f = fopen ("debug.amd", "r") ; @@ -53,7 +53,7 @@ GLOBAL void AMD_debug_init ( char *s ) * cannot be called when the hash buckets are non-empty. */ -GLOBAL void AMD_dump ( +void AMD_dump ( Int n, /* A is n-by-n */ Int Pe [ ], /* pe [0..n-1]: index in iw of start of row i */ Int Iw [ ], /* workspace of size iwlen, iwlen [0..pfree-1] diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_global.c b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_global.c deleted file mode 100644 index 453e970e4..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_global.c +++ /dev/null @@ -1,14 +0,0 @@ -/* ========================================================================= */ -/* === amd_global ========================================================== */ -/* ========================================================================= */ - -/* ------------------------------------------------------------------------- */ -/* AMD, Copyright (c) Timothy A. Davis, */ -/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ -/* email: DrTimothyAldenDavis@gmail.com */ -/* ------------------------------------------------------------------------- */ - -/* In prior versions of AMD, this file declared the amd_malloc, amd_free, - amd_realloc, amd_calloc, and amd_printf functions. They are now replaced - by functions defined in SuiteSparse_config/SuiteSparse_config.c. - */ diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_info.c b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_info.c index 062651f49..2443f7805 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_info.c +++ b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_info.c @@ -1,12 +1,12 @@ -/* ========================================================================= */ -/* === AMD_info ============================================================ */ -/* ========================================================================= */ +//------------------------------------------------------------------------------ +// AMD/Source/amd_info: print output statistics for AMD +//------------------------------------------------------------------------------ -/* ------------------------------------------------------------------------- */ -/* AMD, Copyright (c) Timothy A. Davis, */ -/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ -/* email: DrTimothyAldenDavis@gmail.com */ -/* ------------------------------------------------------------------------- */ +// AMD, Copyright (c) 1996-2022, Timothy A. Davis, Patrick R. Amestoy, and +// Iain S. Duff. All Rights Reserved. +// SPDX-License-Identifier: BSD-3-clause + +//------------------------------------------------------------------------------ /* User-callable. Prints the output statistics for AMD. See amd.h * for details. If the Info array is not present, nothing is printed. @@ -16,7 +16,7 @@ #define PRI(format,x) { if (x >= 0) { SUITESPARSE_PRINTF ((format, x)) ; }} -GLOBAL void AMD_info +void AMD_info ( double Info [ ] ) diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_l1.c b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_l1.c new file mode 100644 index 000000000..feb9ef7db --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_l1.c @@ -0,0 +1,13 @@ +//------------------------------------------------------------------------------ +// AMD/Source/amd_l1: int64_t version of amd_1 +//------------------------------------------------------------------------------ + +// AMD, Copyright (c) 1996-2022, Timothy A. Davis, Patrick R. Amestoy, and +// Iain S. Duff. All Rights Reserved. +// SPDX-License-Identifier: BSD-3-clause + +//------------------------------------------------------------------------------ + +#define DLONG +#include "amd_1.c" + diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_l2.c b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_l2.c new file mode 100644 index 000000000..ebdbe2062 --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_l2.c @@ -0,0 +1,13 @@ +//------------------------------------------------------------------------------ +// AMD/Source/amd_l2.c: int64_t version of amd_2 +//------------------------------------------------------------------------------ + +// AMD, Copyright (c) 1996-2022, Timothy A. Davis, Patrick R. Amestoy, and +// Iain S. Duff. All Rights Reserved. +// SPDX-License-Identifier: BSD-3-clause + +//------------------------------------------------------------------------------ + +#define DLONG +#include "amd_2.c" + diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_l_aat.c b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_l_aat.c new file mode 100644 index 000000000..d4d3e074c --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_l_aat.c @@ -0,0 +1,14 @@ +//------------------------------------------------------------------------------ +// AMD/Source/amd_l_aat.c: int64_t version of amd_aat +//------------------------------------------------------------------------------ + +// AMD, Copyright (c) 1996-2022, Timothy A. Davis, Patrick R. Amestoy, and +// Iain S. Duff. All Rights Reserved. +// SPDX-License-Identifier: BSD-3-clause + +//------------------------------------------------------------------------------ + + +#define DLONG +#include "amd_aat.c" + diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_l_control.c b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_l_control.c new file mode 100644 index 000000000..b14835186 --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_l_control.c @@ -0,0 +1,14 @@ +//------------------------------------------------------------------------------ +// AMD/Source/amd_l_control.c: int64_t version of amd_control +//------------------------------------------------------------------------------ + +// AMD, Copyright (c) 1996-2022, Timothy A. Davis, Patrick R. Amestoy, and +// Iain S. Duff. All Rights Reserved. +// SPDX-License-Identifier: BSD-3-clause + +//------------------------------------------------------------------------------ + + +#define DLONG +#include "amd_control.c" + diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_l_defaults.c b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_l_defaults.c new file mode 100644 index 000000000..4a7f472f0 --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_l_defaults.c @@ -0,0 +1,14 @@ +//------------------------------------------------------------------------------ +// AMD/Source/amd_l_defaults.c: int64_t version of amd_defaults +//------------------------------------------------------------------------------ + +// AMD, Copyright (c) 1996-2022, Timothy A. Davis, Patrick R. Amestoy, and +// Iain S. Duff. All Rights Reserved. +// SPDX-License-Identifier: BSD-3-clause + +//------------------------------------------------------------------------------ + + +#define DLONG +#include "amd_defaults.c" + diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_l_dump.c b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_l_dump.c new file mode 100644 index 000000000..e14a8a59f --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_l_dump.c @@ -0,0 +1,14 @@ +//------------------------------------------------------------------------------ +// AMD/Source/amd_l_dump.c: int64_t version of amd_dump +//------------------------------------------------------------------------------ + +// AMD, Copyright (c) 1996-2022, Timothy A. Davis, Patrick R. Amestoy, and +// Iain S. Duff. All Rights Reserved. +// SPDX-License-Identifier: BSD-3-clause + +//------------------------------------------------------------------------------ + + +#define DLONG +#include "amd_dump.c" + diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_l_info.c b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_l_info.c new file mode 100644 index 000000000..955d4360a --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_l_info.c @@ -0,0 +1,14 @@ +//------------------------------------------------------------------------------ +// AMD/Source/amd_l_info.c: int64_t version of amd_info +//------------------------------------------------------------------------------ + +// AMD, Copyright (c) 1996-2022, Timothy A. Davis, Patrick R. Amestoy, and +// Iain S. Duff. All Rights Reserved. +// SPDX-License-Identifier: BSD-3-clause + +//------------------------------------------------------------------------------ + + +#define DLONG +#include "amd_info.c" + diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_l_order.c b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_l_order.c new file mode 100644 index 000000000..da8f826ab --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_l_order.c @@ -0,0 +1,14 @@ +//------------------------------------------------------------------------------ +// AMD/Source/amd_l_order.c: int64_t version of amd_order +//------------------------------------------------------------------------------ + +// AMD, Copyright (c) 1996-2022, Timothy A. Davis, Patrick R. Amestoy, and +// Iain S. Duff. All Rights Reserved. +// SPDX-License-Identifier: BSD-3-clause + +//------------------------------------------------------------------------------ + + +#define DLONG +#include "amd_order.c" + diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_l_post_tree.c b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_l_post_tree.c new file mode 100644 index 000000000..0169c3444 --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_l_post_tree.c @@ -0,0 +1,13 @@ +//------------------------------------------------------------------------------ +// AMD/Source/amd_l_post_tree.c: int64_t version of amd_post_tree +//------------------------------------------------------------------------------ + +// AMD, Copyright (c) 1996-2022, Timothy A. Davis, Patrick R. Amestoy, and +// Iain S. Duff. All Rights Reserved. +// SPDX-License-Identifier: BSD-3-clause + +//------------------------------------------------------------------------------ + +#define DLONG +#include "amd_post_tree.c" + diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_l_postorder.c b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_l_postorder.c new file mode 100644 index 000000000..2053c606d --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_l_postorder.c @@ -0,0 +1,13 @@ +//------------------------------------------------------------------------------ +// AMD/Source/amd_l_postorder.c: int64_t version of amd_postorder +//------------------------------------------------------------------------------ + +// AMD, Copyright (c) 1996-2022, Timothy A. Davis, Patrick R. Amestoy, and +// Iain S. Duff. All Rights Reserved. +// SPDX-License-Identifier: BSD-3-clause + +//------------------------------------------------------------------------------ + +#define DLONG +#include "amd_postorder.c" + diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_l_preprocess.c b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_l_preprocess.c new file mode 100644 index 000000000..96df6ab49 --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_l_preprocess.c @@ -0,0 +1,13 @@ +//------------------------------------------------------------------------------ +// AMD/Source/amd_l_preprocess.c: int64_t version of amd_preprocess +//------------------------------------------------------------------------------ + +// AMD, Copyright (c) 1996-2022, Timothy A. Davis, Patrick R. Amestoy, and +// Iain S. Duff. All Rights Reserved. +// SPDX-License-Identifier: BSD-3-clause + +//------------------------------------------------------------------------------ + +#define DLONG +#include "amd_preprocess.c" + diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_l_valid.c b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_l_valid.c new file mode 100644 index 000000000..345168cbe --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_l_valid.c @@ -0,0 +1,13 @@ +//------------------------------------------------------------------------------ +// AMD/Source/amd_l_valid.c: int64_t version of amd_valid +//------------------------------------------------------------------------------ + +// AMD, Copyright (c) 1996-2022, Timothy A. Davis, Patrick R. Amestoy, and +// Iain S. Duff. All Rights Reserved. +// SPDX-License-Identifier: BSD-3-clause + +//------------------------------------------------------------------------------ + +#define DLONG +#include "amd_valid.c" + diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_order.c b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_order.c index 7f199aea6..1dcc15a00 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_order.c +++ b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_order.c @@ -1,12 +1,12 @@ -/* ========================================================================= */ -/* === AMD_order =========================================================== */ -/* ========================================================================= */ +//------------------------------------------------------------------------------ +// AMD/Source/amd_order: user-callable AMD ordering method +//------------------------------------------------------------------------------ + +// AMD, Copyright (c) 1996-2022, Timothy A. Davis, Patrick R. Amestoy, and +// Iain S. Duff. All Rights Reserved. +// SPDX-License-Identifier: BSD-3-clause -/* ------------------------------------------------------------------------- */ -/* AMD, Copyright (c) Timothy A. Davis, */ -/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ -/* email: DrTimothyAldenDavis@gmail.com */ -/* ------------------------------------------------------------------------- */ +//------------------------------------------------------------------------------ /* User-callable AMD minimum degree ordering routine. See amd.h for * documentation. @@ -18,7 +18,7 @@ /* === AMD_order =========================================================== */ /* ========================================================================= */ -GLOBAL Int AMD_order +int AMD_order ( Int n, const Int Ap [ ], @@ -157,7 +157,6 @@ GLOBAL Int AMD_order } mem += slen ; ok = ok && (slen < SIZE_T_MAX / sizeof (Int)) ; /* check for overflow */ - ok = ok && (slen < Int_MAX) ; /* S[i] for Int i must be OK */ if (ok) { S = SuiteSparse_malloc (slen, sizeof (Int)) ; diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_post_tree.c b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_post_tree.c index 516c95c9a..d486732d4 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_post_tree.c +++ b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_post_tree.c @@ -1,18 +1,18 @@ -/* ========================================================================= */ -/* === AMD_post_tree ======================================================= */ -/* ========================================================================= */ +//------------------------------------------------------------------------------ +// AMD/Source/amd_post_tree: post-ordering of a single etree +//------------------------------------------------------------------------------ -/* ------------------------------------------------------------------------- */ -/* AMD, Copyright (c) Timothy A. Davis, */ -/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ -/* email: DrTimothyAldenDavis@gmail.com */ -/* ------------------------------------------------------------------------- */ +// AMD, Copyright (c) 1996-2022, Timothy A. Davis, Patrick R. Amestoy, and +// Iain S. Duff. All Rights Reserved. +// SPDX-License-Identifier: BSD-3-clause + +//------------------------------------------------------------------------------ /* Post-ordering of a supernodal elimination tree. */ #include "amd_internal.h" -GLOBAL Int AMD_post_tree +Int AMD_post_tree ( Int root, /* root of the tree */ Int k, /* start numbering at k */ @@ -41,7 +41,7 @@ GLOBAL Int AMD_post_tree /* recursive version (Stack [ ] is not used): */ /* --------------------------------------------------------------------- */ - /* this is simple, but can caouse stack overflow if nn is large */ + /* this is simple, but can cause stack overflow if nn is large */ i = root ; for (f = Child [i] ; f != EMPTY ; f = Sibling [f]) { diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_postorder.c b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_postorder.c index e5aea7b7b..7abe2ff08 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_postorder.c +++ b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_postorder.c @@ -1,18 +1,18 @@ -/* ========================================================================= */ -/* === AMD_postorder ======================================================= */ -/* ========================================================================= */ +//------------------------------------------------------------------------------ +// AMD/Source/amd_postorder: post-order the assembly tree from AMD +//------------------------------------------------------------------------------ -/* ------------------------------------------------------------------------- */ -/* AMD, Copyright (c) Timothy A. Davis, */ -/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ -/* email: DrTimothyAldenDavis@gmail.com */ -/* ------------------------------------------------------------------------- */ +// AMD, Copyright (c) 1996-2022, Timothy A. Davis, Patrick R. Amestoy, and +// Iain S. Duff. All Rights Reserved. +// SPDX-License-Identifier: BSD-3-clause + +//------------------------------------------------------------------------------ /* Perform a postordering (via depth-first search) of an assembly tree. */ #include "amd_internal.h" -GLOBAL void AMD_postorder +void AMD_postorder ( /* inputs, not modified on output: */ Int nn, /* nodes are in the range 0..nn-1 */ diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_preprocess.c b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_preprocess.c index a8139c300..048076507 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_preprocess.c +++ b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_preprocess.c @@ -1,12 +1,12 @@ -/* ========================================================================= */ -/* === AMD_preprocess ====================================================== */ -/* ========================================================================= */ +//------------------------------------------------------------------------------ +// AMD/Source/amd_preprocess: sort, remove duplicates, transpose a matrix +//------------------------------------------------------------------------------ -/* ------------------------------------------------------------------------- */ -/* AMD, Copyright (c) Timothy A. Davis, */ -/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ -/* email: DrTimothyAldenDavis@gmail.com */ -/* ------------------------------------------------------------------------- */ +// AMD, Copyright (c) 1996-2022, Timothy A. Davis, Patrick R. Amestoy, and +// Iain S. Duff. All Rights Reserved. +// SPDX-License-Identifier: BSD-3-clause + +//------------------------------------------------------------------------------ /* Sorts, removes duplicate entries, and transposes from the nonzero pattern of * a column-form matrix A, to obtain the matrix R. The input matrix can have @@ -18,15 +18,11 @@ #include "amd_internal.h" -/* ========================================================================= */ -/* === AMD_preprocess ====================================================== */ -/* ========================================================================= */ - /* AMD_preprocess does not check its input for errors or allocate workspace. * On input, the condition (AMD_valid (n,n,Ap,Ai) != AMD_INVALID) must hold. */ -GLOBAL void AMD_preprocess +void AMD_preprocess ( Int n, /* input matrix: A is n-by-n */ const Int Ap [ ], /* size n+1 */ diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_valid.c b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_valid.c index 609abcaa4..03c5c320b 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_valid.c +++ b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_valid.c @@ -1,12 +1,12 @@ -/* ========================================================================= */ -/* === AMD_valid =========================================================== */ -/* ========================================================================= */ +//------------------------------------------------------------------------------ +// AMD/Source/amd_valid: check if a matrix is valid for AMD +//------------------------------------------------------------------------------ -/* ------------------------------------------------------------------------- */ -/* AMD, Copyright (c) Timothy A. Davis, */ -/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ -/* email: DrTimothyAldenDavis@gmail.com */ -/* ------------------------------------------------------------------------- */ +// AMD, Copyright (c) 1996-2022, Timothy A. Davis, Patrick R. Amestoy, and +// Iain S. Duff. All Rights Reserved. +// SPDX-License-Identifier: BSD-3-clause + +//------------------------------------------------------------------------------ /* Check if a column-form matrix is valid or not. The matrix A is * n_row-by-n_col. The row indices of entries in column j are in @@ -35,7 +35,7 @@ #include "amd_internal.h" -GLOBAL Int AMD_valid +int AMD_valid ( /* inputs, not modified on output: */ Int n_row, /* A is n_row-by-n_col */ @@ -44,7 +44,8 @@ GLOBAL Int AMD_valid const Int Ai [ ] /* row indices of A, of size nz = Ap [n_col] */ ) { - Int nz, j, p1, p2, ilast, i, p, result = AMD_OK ; + Int nz, j, p1, p2, ilast, i, p ; + int result = AMD_OK ; if (n_row < 0 || n_col < 0 || Ap == NULL || Ai == NULL) { diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amdbar.f b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amdbar.f index 13843928b..45b829743 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amdbar.f +++ b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amdbar.f @@ -1,5 +1,11 @@ C----------------------------------------------------------------------- -C AMDBAR: approximate minimum degree, without aggressive absorption +C AMD/Source/amdbar.f: Fortran version of AMD, no aggress absorption +C----------------------------------------------------------------------- + +C AMD, Copyright (c) 1996-2022, Timothy A. Davis, Patrick R. Amestoy, +C and C Iain S. Duff. All Rights Reserved. +C SPDX-License-Identifier: BSD-3-clause + C----------------------------------------------------------------------- SUBROUTINE AMDBAR diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/build/.gitignore b/deps/AMICI/ThirdParty/SuiteSparse/AMD/build/.gitignore new file mode 100644 index 000000000..52e15321b --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/AMD/build/.gitignore @@ -0,0 +1,4 @@ +# Ignore all files except this file. +* +*/ +!.gitignore diff --git a/deps/AMICI/ThirdParty/SuiteSparse/AMD/cmake_modules/FindAMD.cmake b/deps/AMICI/ThirdParty/SuiteSparse/AMD/cmake_modules/FindAMD.cmake new file mode 100644 index 000000000..1b135e05a --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/AMD/cmake_modules/FindAMD.cmake @@ -0,0 +1,129 @@ +#------------------------------------------------------------------------------- +# 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 new file mode 100644 index 000000000..ce4ecf45a --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/BTF/CMakeLists.txt @@ -0,0 +1,136 @@ +#------------------------------------------------------------------------------- +# SuiteSparse/BTF/CMakeLists.txt: cmake for BTF +#------------------------------------------------------------------------------- + +# BTF, Copyright (c) 2004-2022, University of Florida. All Rights Reserved. +# Author: Timothy A. Davis. +# SPDX-License-Identifier: LGPL-2.1+ + +#------------------------------------------------------------------------------- +# get the version +#------------------------------------------------------------------------------- + +cmake_minimum_required ( VERSION 3.19 ) + +set ( BTF_DATE "Jan 17, 2023" ) +set ( BTF_VERSION_MAJOR 2 ) +set ( BTF_VERSION_MINOR 0 ) +set ( BTF_VERSION_SUB 3 ) + +message ( STATUS "Building BTF version: v" + ${BTF_VERSION_MAJOR}. + ${BTF_VERSION_MINOR}. + ${BTF_VERSION_SUB} " (" ${BTF_DATE} ")" ) + +#------------------------------------------------------------------------------- +# SuiteSparse policies +#------------------------------------------------------------------------------- + +set ( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} + ${CMAKE_SOURCE_DIR}/cmake_modules + ${CMAKE_SOURCE_DIR}/../SuiteSparse_config/cmake_modules ) + +include ( SuiteSparsePolicy ) + +#------------------------------------------------------------------------------- +# define the project +#------------------------------------------------------------------------------- + +project ( btf + VERSION "${BTF_VERSION_MAJOR}.${BTF_VERSION_MINOR}.${BTF_VERSION_SUB}" + LANGUAGES C ) + +#------------------------------------------------------------------------------- +# find library dependencies +#------------------------------------------------------------------------------- + +find_package ( SuiteSparse_config 7.0.0 REQUIRED ) + +#------------------------------------------------------------------------------- +# configure files +#------------------------------------------------------------------------------- + +configure_file ( "Config/btf.h.in" "${PROJECT_SOURCE_DIR}/Include/btf.h" + NEWLINE_STYLE LF ) + +#------------------------------------------------------------------------------- +# include directories +#------------------------------------------------------------------------------- + +include_directories ( Source Include ${SUITESPARSE_CONFIG_INCLUDE_DIR} ) + +#------------------------------------------------------------------------------- +# dynamic btf library properties +#------------------------------------------------------------------------------- + +file ( GLOB BTF_SOURCES "Source/*.c" ) + +add_library ( btf SHARED ${BTF_SOURCES} ) + +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) + +#------------------------------------------------------------------------------- +# static btf library properties +#------------------------------------------------------------------------------- + +if ( NOT NSTATIC ) + 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 + OUTPUT_NAME btf + SOVERSION ${BTF_VERSION_MAJOR} ) + + if ( MSVC ) + set_target_properties ( btf_static PROPERTIES + OUTPUT_NAME btf_static ) + endif ( ) +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} ) +endif ( ) + +# libm: +if ( NOT WIN32 ) + target_link_libraries ( btf PUBLIC m ) + if ( NOT NSTATIC ) + target_link_libraries ( btf_static PUBLIC m ) + endif ( ) +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} ) +endif ( ) + +#------------------------------------------------------------------------------- +# report status +#------------------------------------------------------------------------------- + +include ( SuiteSparseReport ) + diff --git a/deps/AMICI/ThirdParty/SuiteSparse/include/btf.h b/deps/AMICI/ThirdParty/SuiteSparse/BTF/Config/btf.h.in similarity index 80% rename from deps/AMICI/ThirdParty/SuiteSparse/include/btf.h rename to deps/AMICI/ThirdParty/SuiteSparse/BTF/Config/btf.h.in index c36de946a..01fda8fd8 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/include/btf.h +++ b/deps/AMICI/ThirdParty/SuiteSparse/BTF/Config/btf.h.in @@ -1,17 +1,19 @@ -/* ========================================================================== */ -/* === BTF package ========================================================== */ -/* ========================================================================== */ +//------------------------------------------------------------------------------ +// BTF/Include/btf.h: include file for BTF +//------------------------------------------------------------------------------ + +// BTF, Copyright (c) 2004-2022, University of Florida. All Rights Reserved. +// Author: Timothy A. Davis. +// SPDX-License-Identifier: LGPL-2.1+ + +//------------------------------------------------------------------------------ /* BTF_MAXTRANS: find a column permutation Q to give A*Q a zero-free diagonal * BTF_STRONGCOMP: find a symmetric permutation P to put P*A*P' into block * upper triangular form. * BTF_ORDER: do both of the above (btf_maxtrans then btf_strongcomp). - * - * By Tim Davis. Copyright (c) 2004-2007, University of Florida. - * with support from Sandia National Laboratories. All Rights Reserved. */ - /* ========================================================================== */ /* === BTF_MAXTRANS ========================================================= */ /* ========================================================================== */ @@ -95,13 +97,13 @@ extern "C" { #include "SuiteSparse_config.h" -int btf_maxtrans /* returns # of columns matched */ +int32_t btf_maxtrans /* returns # of columns matched */ ( /* --- input, not modified: --- */ - int nrow, /* A is nrow-by-ncol in compressed column form */ - int ncol, - int Ap [ ], /* size ncol+1 */ - int Ai [ ], /* size nz = Ap [ncol] */ + int32_t nrow, /* A is nrow-by-ncol in compressed column form */ + int32_t ncol, + int32_t Ap [ ], /* size ncol+1 */ + int32_t Ai [ ], /* size nz = Ap [ncol] */ double maxwork, /* maximum amount of work to do is maxwork*nnz(A); no limit * if <= 0 */ @@ -110,17 +112,17 @@ int btf_maxtrans /* returns # of columns matched */ * reached the maximum of maxwork*nnz(A). * Otherwise, work = the total work performed. */ - int Match [ ], /* size nrow. Match [i] = j if column j matched to row i + int32_t Match [ ], /* size nrow. Match [i] = j if column j matched to row i * (see above for the singular-matrix case) */ /* --- workspace, not defined on input or output --- */ - int Work [ ] /* size 5*ncol */ + int32_t Work [ ] /* size 5*ncol */ ) ; -/* long integer version (all "int" parameters become "SuiteSparse_long") */ -SuiteSparse_long btf_l_maxtrans (SuiteSparse_long, SuiteSparse_long, - SuiteSparse_long *, SuiteSparse_long *, double, double *, - SuiteSparse_long *, SuiteSparse_long *) ; +/* int64_t integer version */ +int64_t btf_l_maxtrans (int64_t, int64_t, + int64_t *, int64_t *, double, double *, + int64_t *, int64_t *) ; /* ========================================================================== */ @@ -145,29 +147,29 @@ SuiteSparse_long btf_l_maxtrans (SuiteSparse_long, SuiteSparse_long, * number of strongly connected components found. */ -int btf_strongcomp /* return # of strongly connected components */ +int32_t btf_strongcomp /* return # of strongly connected components */ ( /* input, not modified: */ - int n, /* A is n-by-n in compressed column form */ - int Ap [ ], /* size n+1 */ - int Ai [ ], /* size nz = Ap [n] */ + int32_t n, /* A is n-by-n in compressed column form */ + int32_t Ap [ ], /* size n+1 */ + int32_t Ai [ ], /* size nz = Ap [n] */ /* optional input, modified (if present) on output: */ - int Q [ ], /* size n, input column permutation */ + int32_t Q [ ], /* size n, input column permutation */ /* output, not defined on input */ - int P [ ], /* size n. P [k] = j if row and column j are kth row/col + int32_t P [ ], /* size n. P [k] = j if row and column j are kth row/col * in permuted matrix. */ - int R [ ], /* size n+1. block b is in rows/cols R[b] ... R[b+1]-1 */ + int32_t R [ ], /* size n+1. block b is in rows/cols R[b] ... R[b+1]-1 */ /* workspace, not defined on input or output */ - int Work [ ] /* size 4n */ + int32_t Work [ ] /* size 4n */ ) ; -SuiteSparse_long btf_l_strongcomp (SuiteSparse_long, SuiteSparse_long *, - SuiteSparse_long *, SuiteSparse_long *, SuiteSparse_long *, - SuiteSparse_long *, SuiteSparse_long *) ; +int64_t btf_l_strongcomp (int64_t, int64_t *, + int64_t *, int64_t *, int64_t *, + int64_t *, int64_t *) ; /* ========================================================================== */ @@ -193,30 +195,28 @@ SuiteSparse_long btf_l_strongcomp (SuiteSparse_long, SuiteSparse_long *, * number of strongly connected components found. */ -int btf_order /* returns number of blocks found */ +int32_t btf_order /* returns number of blocks found */ ( /* --- input, not modified: --- */ - int n, /* A is n-by-n in compressed column form */ - int Ap [ ], /* size n+1 */ - int Ai [ ], /* size nz = Ap [n] */ + int32_t n, /* A is n-by-n in compressed column form */ + int32_t Ap [ ], /* size n+1 */ + int32_t Ai [ ], /* size nz = Ap [n] */ double maxwork, /* do at most maxwork*nnz(A) work in the maximum * transversal; no limit if <= 0 */ /* --- output, not defined on input --- */ double *work, /* return value from btf_maxtrans */ - int P [ ], /* size n, row permutation */ - int Q [ ], /* size n, column permutation */ - int R [ ], /* size n+1. block b is in rows/cols R[b] ... R[b+1]-1 */ - int *nmatch, /* # nonzeros on diagonal of P*A*Q */ + int32_t P [ ], /* size n, row permutation */ + int32_t Q [ ], /* size n, column permutation */ + int32_t R [ ], /* size n+1. block b is in rows/cols R[b] ... R[b+1]-1 */ + int32_t *nmatch, /* # nonzeros on diagonal of P*A*Q */ /* --- workspace, not defined on input or output --- */ - int Work [ ] /* size 5n */ + int32_t Work [ ] /* size 5n */ ) ; -SuiteSparse_long btf_l_order (SuiteSparse_long, SuiteSparse_long *, - SuiteSparse_long *, double , double *, SuiteSparse_long *, - SuiteSparse_long *, SuiteSparse_long *, SuiteSparse_long *, - SuiteSparse_long *) ; +int64_t btf_l_order (int64_t, int64_t *, int64_t *, double , double *, + int64_t *, int64_t *, int64_t *, int64_t *, int64_t *) ; /* ========================================================================== */ @@ -254,11 +254,12 @@ SuiteSparse_long btf_l_order (SuiteSparse_long, SuiteSparse_long *, * #endif */ -#define BTF_DATE "May 4, 2016" +#define BTF_DATE "@BTF_DATE@" +#define BTF_MAIN_VERSION @BTF_VERSION_MAJOR@ +#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_MAIN_VERSION 1 -#define BTF_SUB_VERSION 2 -#define BTF_SUBSUB_VERSION 6 #define BTF_VERSION BTF_VERSION_CODE(BTF_MAIN_VERSION,BTF_SUB_VERSION) #ifdef __cplusplus diff --git a/deps/AMICI/ThirdParty/SuiteSparse/BTF/Doc/ChangeLog b/deps/AMICI/ThirdParty/SuiteSparse/BTF/Doc/ChangeLog index f454b151e..d984d8a69 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/BTF/Doc/ChangeLog +++ b/deps/AMICI/ThirdParty/SuiteSparse/BTF/Doc/ChangeLog @@ -1,3 +1,17 @@ +Jan 17, 2023: version 2.0.3 + + * SuiteSparse_config: now v7.0.0 + +Dec 9, 2022: version 2.0.2 + + * minor changes to build system + +Nov 12, 2022: version 2.0.0 + + * using CMake build system + * integers: int (32-bit) and SuiteSparse_long (nominally 64-bit) replaced + with int32_t and int64_t. + May 4, 2016: version 1.2.6 * minor changes to Makefile diff --git a/deps/AMICI/ThirdParty/SuiteSparse/BTF/Include/btf.h b/deps/AMICI/ThirdParty/SuiteSparse/BTF/Include/btf.h index c36de946a..58cb94d7e 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/BTF/Include/btf.h +++ b/deps/AMICI/ThirdParty/SuiteSparse/BTF/Include/btf.h @@ -1,17 +1,19 @@ -/* ========================================================================== */ -/* === BTF package ========================================================== */ -/* ========================================================================== */ +//------------------------------------------------------------------------------ +// BTF/Include/btf.h: include file for BTF +//------------------------------------------------------------------------------ + +// BTF, Copyright (c) 2004-2022, University of Florida. All Rights Reserved. +// Author: Timothy A. Davis. +// SPDX-License-Identifier: LGPL-2.1+ + +//------------------------------------------------------------------------------ /* BTF_MAXTRANS: find a column permutation Q to give A*Q a zero-free diagonal * BTF_STRONGCOMP: find a symmetric permutation P to put P*A*P' into block * upper triangular form. * BTF_ORDER: do both of the above (btf_maxtrans then btf_strongcomp). - * - * By Tim Davis. Copyright (c) 2004-2007, University of Florida. - * with support from Sandia National Laboratories. All Rights Reserved. */ - /* ========================================================================== */ /* === BTF_MAXTRANS ========================================================= */ /* ========================================================================== */ @@ -95,13 +97,13 @@ extern "C" { #include "SuiteSparse_config.h" -int btf_maxtrans /* returns # of columns matched */ +int32_t btf_maxtrans /* returns # of columns matched */ ( /* --- input, not modified: --- */ - int nrow, /* A is nrow-by-ncol in compressed column form */ - int ncol, - int Ap [ ], /* size ncol+1 */ - int Ai [ ], /* size nz = Ap [ncol] */ + int32_t nrow, /* A is nrow-by-ncol in compressed column form */ + int32_t ncol, + int32_t Ap [ ], /* size ncol+1 */ + int32_t Ai [ ], /* size nz = Ap [ncol] */ double maxwork, /* maximum amount of work to do is maxwork*nnz(A); no limit * if <= 0 */ @@ -110,17 +112,17 @@ int btf_maxtrans /* returns # of columns matched */ * reached the maximum of maxwork*nnz(A). * Otherwise, work = the total work performed. */ - int Match [ ], /* size nrow. Match [i] = j if column j matched to row i + int32_t Match [ ], /* size nrow. Match [i] = j if column j matched to row i * (see above for the singular-matrix case) */ /* --- workspace, not defined on input or output --- */ - int Work [ ] /* size 5*ncol */ + int32_t Work [ ] /* size 5*ncol */ ) ; -/* long integer version (all "int" parameters become "SuiteSparse_long") */ -SuiteSparse_long btf_l_maxtrans (SuiteSparse_long, SuiteSparse_long, - SuiteSparse_long *, SuiteSparse_long *, double, double *, - SuiteSparse_long *, SuiteSparse_long *) ; +/* int64_t integer version */ +int64_t btf_l_maxtrans (int64_t, int64_t, + int64_t *, int64_t *, double, double *, + int64_t *, int64_t *) ; /* ========================================================================== */ @@ -145,29 +147,29 @@ SuiteSparse_long btf_l_maxtrans (SuiteSparse_long, SuiteSparse_long, * number of strongly connected components found. */ -int btf_strongcomp /* return # of strongly connected components */ +int32_t btf_strongcomp /* return # of strongly connected components */ ( /* input, not modified: */ - int n, /* A is n-by-n in compressed column form */ - int Ap [ ], /* size n+1 */ - int Ai [ ], /* size nz = Ap [n] */ + int32_t n, /* A is n-by-n in compressed column form */ + int32_t Ap [ ], /* size n+1 */ + int32_t Ai [ ], /* size nz = Ap [n] */ /* optional input, modified (if present) on output: */ - int Q [ ], /* size n, input column permutation */ + int32_t Q [ ], /* size n, input column permutation */ /* output, not defined on input */ - int P [ ], /* size n. P [k] = j if row and column j are kth row/col + int32_t P [ ], /* size n. P [k] = j if row and column j are kth row/col * in permuted matrix. */ - int R [ ], /* size n+1. block b is in rows/cols R[b] ... R[b+1]-1 */ + int32_t R [ ], /* size n+1. block b is in rows/cols R[b] ... R[b+1]-1 */ /* workspace, not defined on input or output */ - int Work [ ] /* size 4n */ + int32_t Work [ ] /* size 4n */ ) ; -SuiteSparse_long btf_l_strongcomp (SuiteSparse_long, SuiteSparse_long *, - SuiteSparse_long *, SuiteSparse_long *, SuiteSparse_long *, - SuiteSparse_long *, SuiteSparse_long *) ; +int64_t btf_l_strongcomp (int64_t, int64_t *, + int64_t *, int64_t *, int64_t *, + int64_t *, int64_t *) ; /* ========================================================================== */ @@ -193,30 +195,28 @@ SuiteSparse_long btf_l_strongcomp (SuiteSparse_long, SuiteSparse_long *, * number of strongly connected components found. */ -int btf_order /* returns number of blocks found */ +int32_t btf_order /* returns number of blocks found */ ( /* --- input, not modified: --- */ - int n, /* A is n-by-n in compressed column form */ - int Ap [ ], /* size n+1 */ - int Ai [ ], /* size nz = Ap [n] */ + int32_t n, /* A is n-by-n in compressed column form */ + int32_t Ap [ ], /* size n+1 */ + int32_t Ai [ ], /* size nz = Ap [n] */ double maxwork, /* do at most maxwork*nnz(A) work in the maximum * transversal; no limit if <= 0 */ /* --- output, not defined on input --- */ double *work, /* return value from btf_maxtrans */ - int P [ ], /* size n, row permutation */ - int Q [ ], /* size n, column permutation */ - int R [ ], /* size n+1. block b is in rows/cols R[b] ... R[b+1]-1 */ - int *nmatch, /* # nonzeros on diagonal of P*A*Q */ + int32_t P [ ], /* size n, row permutation */ + int32_t Q [ ], /* size n, column permutation */ + int32_t R [ ], /* size n+1. block b is in rows/cols R[b] ... R[b+1]-1 */ + int32_t *nmatch, /* # nonzeros on diagonal of P*A*Q */ /* --- workspace, not defined on input or output --- */ - int Work [ ] /* size 5n */ + int32_t Work [ ] /* size 5n */ ) ; -SuiteSparse_long btf_l_order (SuiteSparse_long, SuiteSparse_long *, - SuiteSparse_long *, double , double *, SuiteSparse_long *, - SuiteSparse_long *, SuiteSparse_long *, SuiteSparse_long *, - SuiteSparse_long *) ; +int64_t btf_l_order (int64_t, int64_t *, int64_t *, double , double *, + int64_t *, int64_t *, int64_t *, int64_t *, int64_t *) ; /* ========================================================================== */ @@ -254,11 +254,12 @@ SuiteSparse_long btf_l_order (SuiteSparse_long, SuiteSparse_long *, * #endif */ -#define BTF_DATE "May 4, 2016" +#define BTF_DATE "Jan 17, 2023" +#define BTF_MAIN_VERSION 2 +#define BTF_SUB_VERSION 0 +#define BTF_SUBSUB_VERSION 3 + #define BTF_VERSION_CODE(main,sub) ((main) * 1000 + (sub)) -#define BTF_MAIN_VERSION 1 -#define BTF_SUB_VERSION 2 -#define BTF_SUBSUB_VERSION 6 #define BTF_VERSION BTF_VERSION_CODE(BTF_MAIN_VERSION,BTF_SUB_VERSION) #ifdef __cplusplus diff --git a/deps/AMICI/ThirdParty/SuiteSparse/BTF/Include/btf_internal.h b/deps/AMICI/ThirdParty/SuiteSparse/BTF/Include/btf_internal.h index fc0426dd6..15b1b0445 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/BTF/Include/btf_internal.h +++ b/deps/AMICI/ThirdParty/SuiteSparse/BTF/Include/btf_internal.h @@ -1,23 +1,24 @@ -/* ========================================================================== */ -/* === btf_internal include file ============================================ */ -/* ========================================================================== */ +//------------------------------------------------------------------------------ +// BTF/Include/btf_internsl.h: internal include file for BTF +//------------------------------------------------------------------------------ + +// BTF, Copyright (c) 2004-2022, University of Florida. All Rights Reserved. +// Author: Timothy A. Davis. +// SPDX-License-Identifier: LGPL-2.1+ + +//------------------------------------------------------------------------------ #ifndef _BTF_INTERNAL_H #define _BTF_INTERNAL_H -/* - * By Tim Davis. Copyright (c) 2004-2007, University of Florida. - * with support from Sandia National Laboratories. All Rights Reserved. - */ - /* Not to be included in any user program. */ #ifdef DLONG -#define Int SuiteSparse_long -#define Int_id SuiteSparse_long_id +#define Int int64_t +#define Int_id "%" PRId64 #define BTF(name) btf_l_ ## name #else -#define Int int +#define Int int32_t #define Int_id "%d" #define BTF(name) btf_ ## name #endif diff --git a/deps/AMICI/ThirdParty/SuiteSparse/BTF/Lib/Makefile b/deps/AMICI/ThirdParty/SuiteSparse/BTF/Lib/Makefile deleted file mode 100644 index 85b7a2646..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/BTF/Lib/Makefile +++ /dev/null @@ -1,99 +0,0 @@ -#------------------------------------------------------------------------------- -# BTF Lib/Makefile -#------------------------------------------------------------------------------- - -LIBRARY = libbtf -VERSION = 1.2.6 -SO_VERSION = 1 - -default: library - -include ../../SuiteSparse_config/SuiteSparse_config.mk - -# BTF depends on SuiteSparse_config -LDLIBS += -lsuitesparseconfig - -ccode: all - -# compile and install in SuiteSparse/lib -library: - $(MAKE) install INSTALL=$(SUITESPARSE) - -# for testing only: -# TEST = -DTESTING - -C = $(CC) $(CF) - -INC = ../Include/btf.h ../Include/btf_internal.h - -I = -I../Include -I../../SuiteSparse_config - -all: library - -OBJ = btf_order.o btf_maxtrans.o btf_strongcomp.o \ - btf_l_order.o btf_l_maxtrans.o btf_l_strongcomp.o - -static: $(AR_TARGET) - -$(AR_TARGET): $(OBJ) - $(ARCHIVE) $@ $^ - - $(RANLIB) $@ - -$(OBJ): $(INC) - -#------------------------------------------------------------------------------- - -btf_order.o: ../Source/btf_order.c - $(C) -c $(I) $< -o $@ - -btf_maxtrans.o: ../Source/btf_maxtrans.c - $(C) -c $(I) $< -o $@ - -btf_strongcomp.o: ../Source/btf_strongcomp.c - $(C) -c $(I) $< -o $@ - -#------------------------------------------------------------------------------- - -btf_l_order.o: ../Source/btf_order.c - $(C) -c $(I) -DDLONG $< -o $@ - -btf_l_maxtrans.o: ../Source/btf_maxtrans.c - $(C) -c $(I) -DDLONG $< -o $@ - -btf_l_strongcomp.o: ../Source/btf_strongcomp.c - $(C) -c $(I) -DDLONG $< -o $@ - -#------------------------------------------------------------------------------- - -# install BTF -install: $(AR_TARGET) $(INSTALL_LIB)/$(SO_TARGET) - -$(INSTALL_LIB)/$(SO_TARGET): $(OBJ) - @mkdir -p $(INSTALL_LIB) - @mkdir -p $(INSTALL_INCLUDE) - @mkdir -p $(INSTALL_DOC) - $(CC) $(SO_OPTS) $^ -o $@ $(LDLIBS) - ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_PLAIN) ) - ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_MAIN) ) - $(CP) ../Include/btf.h $(INSTALL_INCLUDE) - $(CP) ../README.txt $(INSTALL_DOC)/BTF_README.txt - chmod 755 $(INSTALL_LIB)/$(SO_TARGET) - chmod 644 $(INSTALL_INCLUDE)/btf.h - chmod 644 $(INSTALL_DOC)/BTF_README.txt - -uninstall: - $(RM) $(INSTALL_LIB)/$(SO_TARGET) - $(RM) $(INSTALL_LIB)/$(SO_PLAIN) - $(RM) $(INSTALL_LIB)/$(SO_MAIN) - $(RM) $(INSTALL_INCLUDE)/btf.h - $(RM) $(INSTALL_DOC)/BTF_README.txt - -#------------------------------------------------------------------------------- - -purge: distclean - -distclean: clean - - $(RM) -r $(PURGE) - -clean: - - $(RM) -r $(CLEAN) diff --git a/deps/AMICI/ThirdParty/SuiteSparse/BTF/MATLAB/Contents.m b/deps/AMICI/ThirdParty/SuiteSparse/BTF/MATLAB/Contents.m deleted file mode 100644 index bc535b049..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/BTF/MATLAB/Contents.m +++ /dev/null @@ -1,20 +0,0 @@ -% BTF ordering toolbox: -% -% Primary functions: -% -% btf - permute a square sparse matrix into upper block triangular form -% maxtrans - permute the columns of a sparse matrix so it has a zero-free diagonal -% strongcomp - symmetric permutation to upper block triangular form -% -% Other: -% btf_install - compile and install BTF for use in MATLAB. -% btf_demo - demo for BTF -% drawbtf - plot the BTF form of a matrix -% btf_make - compile BTF for use in MATLAB -% -% Example: -% q = maxtrans (A) -% [p,q,r] = btf (A) -% [p,r] = strongcomp (A) - -% Copyright 2004-2007, University of Florida diff --git a/deps/AMICI/ThirdParty/SuiteSparse/BTF/MATLAB/Test/btf_test.m b/deps/AMICI/ThirdParty/SuiteSparse/BTF/MATLAB/Test/btf_test.m deleted file mode 100644 index c0ede02b7..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/BTF/MATLAB/Test/btf_test.m +++ /dev/null @@ -1,19 +0,0 @@ -function btf_test (nmat) -%BTF_TEST test for BTF -% Requires CSparse (or CXSparse) and UFget -% Example: -% btf_test -% See also btf, maxtrans, strongcomp, dmperm, UFget, -% test1, test2, test3, test4, test5, test6. - -if (nargin < 1) - nmat = 200 ; -end - -test1 (nmat) ; -test2 (nmat) ; -test3 (nmat) ; -test4 (nmat) ; -test5 (nmat) ; -test6 ; - diff --git a/deps/AMICI/ThirdParty/SuiteSparse/BTF/MATLAB/Test/checkbtf.m b/deps/AMICI/ThirdParty/SuiteSparse/BTF/MATLAB/Test/checkbtf.m deleted file mode 100644 index 1ba99dfd7..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/BTF/MATLAB/Test/checkbtf.m +++ /dev/null @@ -1,46 +0,0 @@ -function checkbtf (A, p, q, r) -%CHECKBTF ensure A(p,q) is in BTF form -% -% A(p,q) is in BTF form, r the block boundaries -% -% Example: -% [p,q,r] = dmperm (A) -% checkbtf (A, p, q, r) -% -% See also drawbtf, maxtrans, strongcomp. - -% Copyright 2007, Timothy A. Davis, http://www.suitesparse.com - -[m n] = size (A) ; -if (m ~= n) - error ('A must be square') ; -end - -if (any (sort (p) ~= 1:n)) - error ('p not a permutation') ; -end - -if (any (sort (q) ~= 1:n)) - error ('q not a permutation') ; -end - -nblocks = length (r) - 1 ; - -if (r (1) ~= 1) - error ('r(1) not one') ; -end - -if (r (end) ~= n+1) - error ('r(end) not n+1') ; -end - -if (nblocks < 1 | nblocks > n) %#ok - error ('nblocks wrong') ; -end - -nblocks = length (r) - 1 ; -rdiff = r (2:(nblocks+1)) - r (1:nblocks) ; -if (any (rdiff < 1) | any (rdiff > n)) %#ok - error ('r bad') -end - diff --git a/deps/AMICI/ThirdParty/SuiteSparse/BTF/MATLAB/Test/test1.m b/deps/AMICI/ThirdParty/SuiteSparse/BTF/MATLAB/Test/test1.m deleted file mode 100644 index e97d17859..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/BTF/MATLAB/Test/test1.m +++ /dev/null @@ -1,141 +0,0 @@ -function test1 (nmat) -%TEST1 test for BTF -% Requires CSparse and UFget -% Example: -% test1 -% See also btf, maxtrans, strongcomp, dmperm, UFget, -% test1, test2, test3, test4, test5. - -% Copyright 2007, Timothy A. Davis, http://www.suitesparse.com - -index = UFget ; -% f = find (index.sprank < min (index.nrows, index.ncols)) ; -f = 1:length (index.nrows) ; - -% too much time: -skip = [1514 1297 1876 1301] ; - -f = setdiff (f, skip) ; - -[ignore i] = sort (index.nnz (f)) ; -f = f (i) ; - -if (nargin < 1) - nmat = 1000 ; -end -nmat = min (nmat, length (f)) ; -f = f (1:nmat) ; - -T0 = zeros (nmat,1) ; -T1 = zeros (nmat,1) ; -Anz = zeros (nmat,1) ; -MN = zeros (nmat, 2) ; -Nzdiag = zeros (nmat,1) ; -clf - -% warmup -p = maxtrans (sparse (1)) ; %#ok -p = cs_dmperm (sparse (1)) ; %#ok -a = cs_transpose (sparse (1)) ; %#ok - -h = waitbar (0, 'BTF test 1 of 6') ; - -try - - for k = 1:nmat - - Prob = UFget (f (k), index) ; - A = Prob.A ; - clear Prob - t = 0 ; - - waitbar (k/nmat, h) ; - - r = full (sum (spones (A), 2)) ; - c = full (sum (spones (A))) ; - m2 = length (find (r > 0)) ; - n2 = length (find (c > 0)) ; - - if (m2 < n2) - tic - A = cs_transpose (A) ; - t = toc ; - end - - Nzdiag (k) = nnz (diag (A)) ; - - [m n] = size (A) ; - Anz (k) = nnz (A) ; - MN (k,:) = [m n] ; - - tic - q = maxtrans (A) ; - t0 = toc ; - s0 = sum (q > 0) ; - T0 (k) = max (1e-9, t0) ; - - tic - p = cs_dmperm (A) ; - t1 = toc ; - s1 = sum (p > 0) ; - T1 (k) = max (1e-9, t1) ; - - fprintf (... - '%4d maxtrans %10.6f %10.6f cs_dmperm %10.6f m/n %8.2f', ... - f(k), t, t0, t1, m/n) ; - if (t1 ~= 0) - fprintf (' rel: %8.4f', t0 / t1) ; - end - fprintf ('\n') ; - if (s0 ~= s1) - error ('!') ; - end - - if (s0 == n & m == n) %#ok - B = A (:, q) ; - subplot (2,2,1) ; - cspy (B) ; - if (nnz (diag (B)) ~= n) - error ('?') - end - clear B - else - cspy (0) ; - end - - maxnz = nnz (A) ; - - zfree = find (MN (1:k,1) == MN (1:k,2) & Nzdiag (1:k) == MN(1:k,1)) ; - square = find (MN (1:k,1) == MN (1:k,2) & Nzdiag (1:k) ~= MN(1:k,1)) ; - tall = find (MN (1:k,1) > MN (1:k,2)) ; - squat = find (MN (1:k,1) < MN (1:k,2)) ; - - subplot (2,2,2) ; - loglog (Anz (square), T0 (square) ./ T1 (square), ... - 'o', [1 maxnz], [1 1], 'r-') ; - title ('square') ; - subplot (2,2,3) ; - loglog (Anz (tall), T0 (tall) ./ T1 (tall), ... - 'o', [1 maxnz], [1 1], 'r-') ; - title ('tall') ; - subplot (2,2,4) ; - title ('square, intially zero-free') ; - loglog (Anz (zfree), T0 (zfree) ./ T1 (zfree), ... - 'o', [1 maxnz], [1 1], 'r-') ; - title ('square, zero-free diag') ; - - drawnow - - end - -catch - % out-of-memory is OK, other errors are not - disp (lasterr) ; - if (isempty (strfind (lasterr, 'Out of memory'))) - error (lasterr) ; %#ok - else - fprintf ('test terminated early, but otherwise OK\n') ; - end -end - -close (h) ; diff --git a/deps/AMICI/ThirdParty/SuiteSparse/BTF/MATLAB/Test/test2.m b/deps/AMICI/ThirdParty/SuiteSparse/BTF/MATLAB/Test/test2.m deleted file mode 100644 index 20e1ad68c..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/BTF/MATLAB/Test/test2.m +++ /dev/null @@ -1,108 +0,0 @@ -function test2 (nmat) -%TEST2 test for BTF -% Requires CSparse and UFget -% Example: -% test2 -% See also btf, maxtrans, strongcomp, dmperm, UFget, -% test1, test2, test3, test4, test5. - -% Copyright 2007, Timothy A. Davis, http://www.suitesparse.com - -index = UFget ; -f = find (index.nrows == index.ncols) ; - -% too much time: -skip = [1514 1297 1876 1301] ; -f = setdiff (f, skip) ; - -[ignore i] = sort (index.nnz (f)) ; -f = f (i) ; - -if (nargin < 1) - nmat = 1000 ; -end -nmat = min (nmat, length (f)) ; -f = f (1:nmat) ; - -T0 = zeros (nmat,1) ; -T1 = zeros (nmat,1) ; -Anz = zeros (nmat,1) ; -MN = zeros (nmat, 2) ; -Nzdiag = zeros (nmat,1) ; -clf - -% warmup -p = maxtrans (sparse (1)) ; %#ok -p = btf (sparse (1)) ; %#ok -p = cs_dmperm (sparse (1)) ; %#ok -a = cs_transpose (sparse (1)) ; %#ok - -h = waitbar (0, 'BTF test 2 of 6') ; - -try - for k = 1:nmat - - Prob = UFget (f (k), index) ; - A = Prob.A ; - - waitbar (k/nmat, h) ; - - Nzdiag (k) = nnz (diag (A)) ; - - [m n] = size (A) ; - Anz (k) = nnz (A) ; - MN (k,:) = [m n] ; - - tic - [p,q,r] = btf (A) ; - t0 = toc ; - s0 = sum (q > 0) ; - T0 (k) = max (1e-9, t0) ; - - tic - [p2,q2,r2] = cs_dmperm (A) ; - t1 = toc ; - s1 = sum (dmperm (A) > 0) ; - T1 (k) = max (1e-9, t1) ; - - fprintf ('%4d btf %10.6f cs_dmperm %10.6f', f(k), t0, t1) ; - if (t1 ~= 0) - fprintf (' rel: %8.4f', t0 / t1) ; - end - fprintf ('\n') ; - - if (s0 ~= s1) - error ('!') ; - end - - C = A (p, abs (q)) ; - subplot (1,2,1) ; - cspy (C) ; - z = find (q < 0) ; - zd = nnz (diag (C (z,z))) ; - if (zd > 0) - error ('?') ; - end - - minnz = Anz (1) ; - maxnz = nnz (A) ; - - subplot (1,2,2) ; - loglog (Anz (1:k), T0 (1:k) ./ T1 (1:k), ... - 'o', [minnz maxnz], [1 1], 'r-') ; - drawnow - - clear C A Prob - end - -catch - % out-of-memory is OK, other errors are not - disp (lasterr) ; - if (isempty (strfind (lasterr, 'Out of memory'))) - error (lasterr) ; %#ok - else - fprintf ('test terminated early, but otherwise OK\n') ; - end -end - -close (h) ; diff --git a/deps/AMICI/ThirdParty/SuiteSparse/BTF/MATLAB/Test/test3.m b/deps/AMICI/ThirdParty/SuiteSparse/BTF/MATLAB/Test/test3.m deleted file mode 100644 index 408033aab..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/BTF/MATLAB/Test/test3.m +++ /dev/null @@ -1,527 +0,0 @@ -function test3 (nmat) -%TEST3 test for BTF -% Requires UFget -% Example: -% test3 -% See also btf, maxtrans, strongcomp, dmperm, UFget, -% test1, test2, test3, test4, test5. - -% Copyright 2007, Timothy A. Davis, http://www.suitesparse.com - -doplot = 1 ; -dopause = 0 ; -dostrong = 1 ; - -index = UFget ; -f = find (index.nrows == index.ncols) ; -[ignore i] = sort (index.nnz (f)) ; -f = f (i) ; -clear i - -% short test set: seg faults, lots of blocks, lots of work, and so on: -nasty = [ - % --- various test matrices (no seg fault, quick run time) - -(1:8)' % generated matrices - 904 % vanHeukelum/cage3 (5-by-5) - 819 % Simon/raefsky6 (permuted triangular matrix) - % - % --- older seg faults: - 264 % HB/west0156, causes older strongcomp_recursive to fail - 824 % TOKAMAK/utm300 (300-by-300), causes older code to fail - 868 % Pothen/bodyy4 - % - % --- seg faults in old MATLAB dmperm - 290 % Averous/epb3 - 983 % Sanghavi/ecl32 - 885 % Pothen/tandem_dual - 879 % Pothen/onera_dual - 955 % Schenk_IBMSDS/2D_54019_highK - 957 % Schenk_IBMSDS/3D_51448_3D - 958 % Schenk_IBMSDS/ibm_matrix_2 - 912 % vanHeukelum/cage11 - 924 % Andrews/Andrews - 960 % Schenk_IBMSDS/matrix-new_3 - 862 % Kim/kim1 - 544 % Hamm/scircuit - 897 % Norris/torso2 - 801 % Ronis/xenon1 - 53 % HB/bcsstk31 - 958 % Schenk_IBMSDS/matrix_9 - 844 % Cunningham/qa8fk - 845 % Cunningham/qa8fk - 821 % Simon/venkat25 - 822 % Simon/venkat50 - 820 % Simon/venkat01 - 812 % Simon/bbmat - 804 % Rothberg/cfd1 - 54 % HB/bcsstk32 - 913 % vanHeukelum/cage12 - 846 % Boeing/bcsstk39 - 972 % Schenk_IBMSDS/para-10 - 974 % Schenk_IBMSDS/para-5 - 975 % Schenk_IBMSDS/para-6 - 976 % Schenk_IBMSDS/para-7 - 977 % Schenk_IBMSDS/para-8 - 978 % Schenk_IBMSDS/para-9 - 961 % Schenk_ISEI/barrier2-10 - 962 % Schenk_ISEI/barrier2-11 - 963 % Schenk_ISEI/barrier2-12 - 964 % Schenk_ISEI/barrier2-1 - 965 % Schenk_ISEI/barrier2-2 - 966 % Schenk_ISEI/barrier2-3 - 967 % Schenk_ISEI/barrier2-4 - 968 % Schenk_ISEI/barrier2-9 - 851 % Chen/pkustk05 - 979 % Kamvar/Stanford - 374 % Bova/rma10 - % - % --- lots of time: - 395 % DRIVCAV/cavity16 - 396 % DRIVCAV/cavity17 - 397 % DRIVCAV/cavity18 - 398 % DRIVCAV/cavity19 - 399 % DRIVCAV/cavity20 - 400 % DRIVCAV/cavity21 - 401 % DRIVCAV/cavity22 - 402 % DRIVCAV/cavity23 - 403 % DRIVCAV/cavity24 - 404 % DRIVCAV/cavity25 - 405 % DRIVCAV/cavity26 - 1109 % Sandia/mult_dcop_01 - 1110 % Sandia/mult_dcop_02 - 1111 % Sandia/mult_dcop_03 - 376 % Brethour/coater2 - 284 % ATandT/onetone2 - 588 % Hollinger/mark3jac100 - 589 % Hollinger/mark3jac100sc - 452 % Grund/bayer01 - 920 % Hohn/sinc12 - 590 % Hollinger/mark3jac120 - 591 % Hollinger/mark3jac120sc - 809 % Shyy/shyy161 - 448 % Graham/graham1 - 283 % ATandT/onetone1 - 445 % Garon/garon2 - 541 % Hamm/bcircuit - 592 % Hollinger/mark3jac140 - 593 % Hollinger/mark3jac140sc - 435 % FIDAP/ex40 - 912 % Hohn/sinc15 - 894 % Norris/lung2 - 542 % Hamm/hcircuit - 752 % Mulvey/finan512 - 753 % Mulvey/pfinan512 - 564 % Hollinger/g7jac180 - 565 % Hollinger/g7jac180sc - 566 % Hollinger/g7jac200 - 567 % Hollinger/g7jac200sc - 748 % Mallya/lhr34 - 749 % Mallya/lhr34c - 922 % Hohn/sinc18 - 447 % Goodwin/rim - 807 % Rothberg/struct3 - 286 % ATandT/twotone - 982 % Tromble/language - 953 % Schenk_IBMNA/c-73 - 890 % Norris/heart1 - 750 % Mallya/lhr71 - 751 % Mallya/lhr71c - 925 % FEMLAB/ns3Da - 827 % Vavasis/av41092 - 931 % FEMLAB/sme3Db - 1297 % GHS_index/boyd2 - 1301 % GHS_indef/cont-300 - % - % --- lots of time, and seg faults: - 285 % ATandT/pre2 - % --- huge matrix, turn off plotting - 940 % Shenk/af_shell1, memory leak in plot, after call to btf, once. - % ---- -]' ; - -% maxtrans_recursive causes a seg fault on these matrices, because of -% stack overflow (this is expected) -skip_list_maxtrans_recursive = 285 ; - -% p = dmperm (A) in MATLAB 7.4 causes a seg fault on these matrices: -skip_list_dmperm = [285 1301 1231 1251 1232 1241] ; - -% [p,q,r] = dmperm (A) in MATLAB 7.4 causes a seg fault on these matrices: -skip_list_dmperm_btf = ... -[ 285 879 885 290 955 957 958 924 960 897 959 844 845 ... - 821 822 820 804 913 846 972 974:978 961:968 979 940 ... - 1422 1513 1412 1510 1301 1231 1251 1434 1213 1232 1241 1357 1579 1431 1281] ; -% length(skip_list_dmperm_btf) - -% time intensive -skip_costly = [1514 1297 1876 1301] ; - -% strongcomp (recursive) causes a seg fault on these matrices because of -% stack overflow (this is expected). -skip_list_strongcomp_recursive = ... -[983 285 879 885 290 955 957 958 912 924 960 862 544 897 801 53 959 844 845 ... - 821 822 820 812 804 54 913 846 972 974:978 961:968 851 374 940] ; -skip_list_strongcomp_recursive = ... -[ skip_list_strongcomp_recursive 592 593 752 753 807 286 982 855 566 567 ] ; - -% matrices with the largest # of nonzeros in the set (untested) -toobig = [ -928 853 852 356 761 368 973 895 805 849 932 ... -803 854 936 802 850 537 856 898 857 859 971 937 ... -914 858 980 896 806 538 863 369 938 860 941 942 ... -943 944 945 946 947 948 915 939 916 ] ; - -f = [ -(1:8) f ] ; -% f = nasty ; - -h = waitbar (0, 'BTF test 3 of 6') ; - -if (nargin < 1) - nmat = 1000 ; -end -nmat = min (nmat, length (f)) ; -f = f (1:nmat) ; - -try - - for matnum = 1:nmat - - waitbar (matnum/nmat, h) ; - - j = f (matnum) ; - - if (any (j == toobig) | any (j == skip_costly)) %#ok - fprintf ('\n%4d: %3d %s/%s too big\n', ... - matnum, j, index.Group{j}, index.Name{j}) ; - continue ; - end - - rand ('state', 0) ; - - % clear all unused variables. - % nothing here is left that is proportional to the matrix size - clear A p1 p2 p3 q3 r3 match1 match2 match4 pa ra sa qa B C pb rb pc rc - clear jumble B11 B12 B13 B21 B22 B23 B31 B32 B33 pjumble qjumble ans - clear c kbad kgood - % whos - % pause - - if (j > 0) - Problem = UFget (j, index) ; - name = Problem.name ; - A = Problem.A ; - clear Problem - else - % construct the jth test matrix - j = -j ; - if (j == 1 | j == 2) %#ok - B11 = UFget ('Grund/b1_ss') ; % 7-by-7 diagonal block - B11 = B11.A ; - B12 = sparse (zeros (7,2)) ; - B12 (3,2) = 1 ; - B13 = sparse (ones (7,5)) ; - B21 = sparse (zeros (2,7)) ; - B22 = sparse (ones (2,2)) ; % 2-by-2 diagonal block - B23 = sparse (ones (2,5)) ; - B31 = sparse (zeros (5,7)) ; - B32 = sparse (zeros (5,2)) ; - B33 = UFget ('vanHeukelum/cage3') ; % 5-by-5 diagonal block - B33 = B33.A ; - A = [ B11 B12 B13 ; B21 B22 B23 ; B31 B32 B33 ] ; - name = '(j=1 test matrix)' ; - end - if (j == 2) - pjumble = [ 10 7 11 1 13 12 8 2 5 14 9 6 4 3 ] ; - qjumble = [ 3 14 2 11 1 8 5 7 10 12 4 13 9 6 ] ; - A = A (pjumble, qjumble) ; - name = '(j=2 test matrix)' ; - elseif (j == 3) - A = sparse (1) ; - elseif (j == 4) - A = sparse (0) ; - elseif (j == 5) - A = sparse (ones (2)) ; - elseif (j == 6) - A = sparse (2,2) ; - elseif (j == 7) - A = speye (2) ; - elseif (j == 8) - A = sparse (2,2) ; - A (2,1) = 1 ; - end - if (j > 2) - full (A) - end - end - - [m n] = size (A) ; - if (m ~= n) - continue ; - end - fprintf ('\n%4d: ', matnum) ; - fprintf (' =========================== Matrix: %3d %s\n', j, name) ; - fprintf ('n: %d nz: %d\n', n, nnz (A)) ; - - if (nnz (A) > 6e6) - doplot = 0 ; - end - - %----------------------------------------------------------------------- - % now try maxtrans - tic - match1 = maxtrans (A) ; - t = toc ; - s1 = sum (match1 > 0) ; - fprintf ('n-sprank: %d\n', n-s1) ; - fprintf ('maxtrans: %8.2f seconds\n', t) ; - singular = s1 < n ; - - if (doplot) - clf - subplot (2,4,1) - spy (A) - title (name) ; - end - - p1 = match1 ; - if (any (p1 <= 0)) - % complete the permutation - badrow = find (p1 <= 0) ; - - badcol = ones (1,n) ; - badcol (p1 (p1 > 0)) = 0 ; - badcol = find (badcol) ; - - p1 (badrow) = badcol ; - - % construct the older form of match1 - match1 (badrow) = -p1 (badrow) ; - end - if (any (sort (p1) ~= 1:n)) - error ('!!') ; - end - - B = A (:,p1) ; - - if (doplot) - subplot (2,4,2) - hold off - spy (B) - hold on - badcol = find (match1 < 0) ; - Junk = sparse (badcol, badcol, ones (length (badcol), 1), n, n) ; - % if (~isempty (A)) - % spy (Junk, 'ro') ; - % end - title ('maxtrans') ; - end - - d = nnz (diag (B)) ; - if (d ~= s1) - error ('bad sprank') ; - end - clear B - - %----------------------------------------------------------------------- - % try p = dmperm(A) - skip_dmperm = any (j == skip_list_dmperm) ; - - if (~skip_dmperm) - tic - match4 = dmperm (A) ; - t = toc ; - fprintf ('p=dmperm(A): %8.2f seconds\n', t) ; - s4 = sum (match4 > 0) ; - singular4 = (s4 < n) ; - - if (doplot) - if (~singular4) - subplot (2,4,3) - spy (A (match4,:)) - title ('dmperm') ; - end - end - if (singular ~= singular4) - error ('s4?') ; - end - if (s1 ~= s4) - error ('bad sprank') ; - end - else - fprintf ('p=dmperm(A): skip\n') ; - end - - %----------------------------------------------------------------------- - nblocks = -1 ; - skip_dmperm_btf = any (j == skip_list_dmperm_btf) ; - if (~skip_dmperm_btf) - % get btf form - tic - [pa,qa,ra,sa] = dmperm (A) ; - t = toc ; - fprintf ('[p,q,r,s]=dmperm(A): %8.2f seconds\n', t) ; - nblocks = length (ra) - 1 ; - fprintf ('nblocks: %d\n', nblocks) ; - if (~singular4) - checkbtf (A, pa, qa, ra) ; - if (doplot) - subplot (2,4,4) - drawbtf (A, pa, qa, ra) - title ('dmperm blocks') - end - end - else - fprintf ('[p,q,r,s]=dmperm(A): skip\n') ; - end - - jumble = randperm (n) ; - - %----------------------------------------------------------------------- - % try strongcomp, non-recursive version - - %------------------------------------------------------------------- - % try strongcomp on original matrix - B = A (:,p1) ; - tic ; - [pb,rb] = strongcomp (B) ; - t = toc ; - fprintf ('strongcomp %8.2f seconds\n', t) ; - if (~singular & ~skip_dmperm_btf & (length (rb) ~= nblocks+1)) %#ok - error ('BTF:invalid (rb)') ; - end - checkbtf (B, pb, pb, rb) ; - if (doplot) - subplot (2,4,5) - drawbtf (B, pb, pb, rb) ; - title ('strongcomp') ; - end - - %------------------------------------------------------------------- - % try btf on original matrix - tic ; - [pw,qw,rw] = btf (A) ; - t = toc ; - fprintf ('btf %8.2f seconds nblocks %d\n', ... - t, length (rw)-1) ; - - if (any (pw ~= pb)) - error ('pw') ; - end - if (any (rw ~= rb)) - error ('rw') ; - end - if (any (abs (qw) ~= p1 (pw))) - error ('qw') ; - end - c = diag (A (pw,abs (qw))) ; - if (~singular & ~skip_dmperm_btf & (length (rw) ~= nblocks+1)) %#ok - error ('BTF:invalid (rw)') ; - end - checkbtf (A, pw, abs (qw), rw) ; - - kbad = find (qw < 0) ; - kgood = find (qw > 0) ; - if (any (c (kbad) ~= 0)) - error ('kbad') ; - end - if (any (c (kgood) == 0)) %#ok - error ('kgood') ; - end - - if (doplot) - subplot (2,4,6) - drawbtf (A, pw, abs (qw), rw) ; - if (n < 500) - for k = kbad - plot ([k (k+1) (k+1) k k]-.5, ... - [k k (k+1) (k+1) k]-.5, 'r') ; - end - end - title ('btf') ; - end - - %------------------------------------------------------------------- - % try [p,q,r] = strongcomp (A, qin) form - tic - [pz,qz,rz] = strongcomp (A, match1) ; - t = toc ; - fprintf ('[p,q,r]=strongcomp(A,qin)%8.2f seconds\n', t) ; - if (any (pz ~= pb)) - error ('pz') ; - end - if (any (rz ~= rb)) - error ('rz') ; - end - if (any (abs (qz) ~= p1 (pz))) - error ('qz') ; - end - c = diag (A (pz,abs (qz))) ; - if (~singular & ~skip_dmperm_btf & (length (rz) ~= nblocks+1)) %#ok - error ('BTF:invalid (rz)') ; - end - checkbtf (A, pz, abs (qz), rz) ; - - kbad = find (qz < 0) ; - kgood = find (qz > 0) ; - if (any (c (kbad) ~= 0)) - error ('kbad') ; - end - if (any (c (kgood) == 0)) %#ok - error ('kgood') ; - end - - if (doplot) - subplot (2,4,7) - drawbtf (A, pz, abs (qz), rz) ; - if (n < 500) - for k = kbad - plot ([k (k+1) (k+1) k k]-.5, ... - [k k (k+1) (k+1) k]-.5, 'r') ; - end - end - title ('strongcomp(A,qin)') ; - end - - %------------------------------------------------------------------- - % try strongcomp again, on a randomly jumbled matrix - C = sparse (B (jumble, jumble)) ; - tic ; - [pc,rc] = strongcomp (C) ; - t = toc ; - fprintf ('strongcomp (rand) %8.2f seconds\n', t) ; - if (~singular & ~skip_dmperm_btf & (length (rc) ~= nblocks+1)) %#ok - error ('BTF:invalid (rc)') ; - end - checkbtf (C, pc, pc, rc) ; - if (doplot) - subplot (2,4,8) - drawbtf (C, pc, pc, rc) ; - title ('strongcomp(rand)') ; - end - - if (length (rc) ~= length (rb)) - error ('strongcomp random mismatch') ; - end - - %----------------------------------------------------------------------- - if (doplot) - drawnow - end - - if (matnum ~= nmat & dopause) %#ok - input ('Hit enter: ') ; - end - - end - -catch - % out-of-memory is OK, other errors are not - disp (lasterr) ; - if (isempty (strfind (lasterr, 'Out of memory'))) - error (lasterr) ; %#ok - else - fprintf ('test terminated early, but otherwise OK\n') ; - end -end - -close (h) ; diff --git a/deps/AMICI/ThirdParty/SuiteSparse/BTF/MATLAB/Test/test4.m b/deps/AMICI/ThirdParty/SuiteSparse/BTF/MATLAB/Test/test4.m deleted file mode 100644 index c67273ad5..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/BTF/MATLAB/Test/test4.m +++ /dev/null @@ -1,86 +0,0 @@ -function test4 (nmat) -%TEST4 test for BTF -% Requires UFget -% Example: -% test4 -% See also btf, maxtrans, strongcomp, dmperm, UFget, -% test1, test2, test3, test4, test5. - -% Copyright 2007, Timothy A. Davis, http://www.suitesparse.com - -index = UFget ; -f = find (index.nrows == index.ncols) ; -[ignore i] = sort (index.nnz (f)) ; -f = f (i) ; - -% time intensive -skip_costly = [1514 1297 1876 1301] ; -f = setdiff (f, skip_costly) ; - -if (nargin < 1) - nmat = 1000 ; -end -nmat = min (nmat, length (f)) ; -f = f (1:nmat) ; - -h = waitbar (0, 'BTF test 4 of 6') ; - -try - for k = 1:nmat - - Prob = UFget (f (k), index) ; - A = Prob.A ; - - waitbar (k/nmat, h) ; - - for tr = [1 -1] - - if (tr == -1) - AT = A' ; - [m n] = size (A) ; - if (m == n) - if (nnz (spones (AT) - spones (A)) == 0) - fprintf ('skip transpose\n') ; - continue ; - end - end - A = AT ; - end - - tic - [p1,q1,r1,work1] = btf (A) ; - t1 = toc ; - n1 = length (r1) - 1 ; - - tic - [p2,q2,r2,work2] = btf (A, 10) ; - t2 = toc ; - n2 = length (r2) - 1 ; - - fprintf (... - '%4d %4d : %10.4f %8d %8g : %10.4f %8d %8g :', ... - k, f(k), t1, n1, work1, t2, n2, work2) ; - if (t2 ~= 0) - fprintf (' rel %8.4f %8.4f' , t1 / t2, n2 / (max (1, n1))) ; - end - fprintf ('\n') ; - - if (n1 ~= n2 | work1 ~= work2) %#ok - disp (Prob) ; - fprintf ('^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n') ; - end - - end - end - -catch - % out-of-memory is OK, other errors are not - disp (lasterr) ; - if (isempty (strfind (lasterr, 'Out of memory'))) - error (lasterr) ; %#ok - else - fprintf ('test terminated early, but otherwise OK\n') ; - end -end - -close (h) ; diff --git a/deps/AMICI/ThirdParty/SuiteSparse/BTF/MATLAB/Test/test5.m b/deps/AMICI/ThirdParty/SuiteSparse/BTF/MATLAB/Test/test5.m deleted file mode 100644 index 0118ed423..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/BTF/MATLAB/Test/test5.m +++ /dev/null @@ -1,86 +0,0 @@ -function test5 (nmat) -%TEST5 test for BTF -% Requires UFget -% Example: -% test5 -% See also btf, maxtrans, strongcomp, dmperm, UFget, -% test1, test2, test3, test4, test5. - -% Copyright 2007, Timothy A. Davis, http://www.suitesparse.com - -index = UFget ; - -[ignore f] = sort (index.nnz) ; - -% time intensive -skip_costly = [1514 1297 1876 1301] ; -f = setdiff (f, skip_costly) ; - -if (nargin < 1) - nmat = 1000 ; -end -nmat = min (nmat, length (f)) ; -f = f (1:nmat) ; - -h = waitbar (0, 'BTF test 5 of 6') ; - -try - for k = 1:nmat - - i = f(k) ; - Prob = UFget (i, index) ; - A = Prob.A ; - - waitbar (k/nmat, h) ; - - for tr = [1 -1] - - if (tr == -1) - AT = A' ; - [m n] = size (A) ; - if (m == n) - if (nnz (spones (AT) - spones (A)) == 0) - fprintf ('skip test with transpose\n') ; - continue ; - end - end - A = AT ; - end - - tic - q1 = maxtrans (A) ; - t1 = toc ; - r1 = sum (q1 > 0) ; - - tic - q2 = maxtrans (A, 10) ; - t2 = toc ; - r2 = sum (q2 > 0) ; - - fprintf (... - '%4d %4d : %10.4f %8d : %10.4f %8d', k, f(k), t1, r1, t2, r2) ; - fprintf (' rel sprank %8.4f', r2 / (max (1, r1))) ; - if (t2 ~= 0) - fprintf (': rel time %8.4f', t1 / t2) ; - end - fprintf ('\n') ; - - if (r1 ~= r2) - disp (Prob) ; - fprintf ('^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n') ; - end - - end - end - -catch - % out-of-memory is OK, other errors are not - disp (lasterr) ; - if (isempty (strfind (lasterr, 'Out of memory'))) - error (lasterr) ; %#ok - else - fprintf ('test terminated early, but otherwise OK\n') ; - end -end - -close (h) ; diff --git a/deps/AMICI/ThirdParty/SuiteSparse/BTF/MATLAB/Test/test6.m b/deps/AMICI/ThirdParty/SuiteSparse/BTF/MATLAB/Test/test6.m deleted file mode 100644 index 0a643ec77..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/BTF/MATLAB/Test/test6.m +++ /dev/null @@ -1,138 +0,0 @@ -function test6 -%TEST6 test for BTF -% Requires UFget -% Example: -% test6 -% See also btf, maxtrans, strongcomp, dmperm, UFget, -% test1, test2, test3, test4, test5. - -% Copyright 2007, Timothy A. Davis, http://www.suitesparse.com - -quick2 = [ ... - 1522 -272 1463 1521 460 1507 -838 1533 -1533 -1456 -1512 734 211 ... - -385 -735 394 -397 1109 -744 ... - -734 -375 -1200 -1536 -837 519 -519 520 -520 189 -189 454 385 ... - 387 -387 384 -384 386 -386 388 -388 525 -525 526 -526 735 ... - 1508 209 210 1243 -1243 1534 -840 1234 -1234 390 -390 392 -392 ... - -394 1472 1242 -1242 389 -389 391 -391 393 -393 1215 -1215 1216 ... --1216 736 -736 737 -737 455 -455 -224 -839 1426 -1426 -1473 396 ... - -396 398 -398 400 -400 402 -402 404 -404 -1531 395 -395 397 ... - 399 -399 401 -401 403 -403 405 -405 -738 -739 1459 -1459 1111 ... - 1110 376 -376 284 -284 -740 -742 -741 -743 1293 -1293 452 920 ... - -745 -446 1462 -1461 448 -448 283 -283 1502 -1502 1292 -1292 1503 ... --1503 1291 -1291 445 -445 -746 -747 1300 -1300 435 -435 -1343 -1345 ... --1344 1305 -1305 921 -1513 1307 -1307 1369 -1369 1374 -1374 1377 ... --1377 748 -748 -749 1510 922 -922 ] ; - -index = UFget ; -nmat = length (quick2) ; -dopause = 0 ; - -h = waitbar (0, 'BTF test 6 of 6') ; - -try - - for k = 1:nmat - - waitbar (k/nmat, h) ; - - i = quick2 (k) ; - Prob = UFget (abs (i), index) ; - disp (Prob) ; - if (i < 0) - fprintf ('transposed\n') ; - A = Prob.A' ; - [m n] = size (A) ; - if (m == n) - if (nnz (spones (A) - spones (Prob.A)) == 0) - fprintf ('skip...\n') ; - continue ; - end - end - else - A = Prob.A ; - end - - tic - [p1,q1,r1,work1] = btf (A) ; - t1 = toc ; - n1 = length (r1) - 1 ; - m1 = nnz (diag (A (p1, abs (q1)))) ; - - limit = work1/nnz(A) ; - - fprintf ('full search: %g * nnz(A)\n', limit) ; - - works = linspace(0,limit,9) ; - works (1) = eps ; - nw = length (works) ; - - T2 = zeros (nw, 1) ; - N2 = zeros (nw, 1) ; - M2 = zeros (nw, 1) ; - - T2 (end) = t1 ; - N2 (end) = n1 ; - M2 (end) = m1 ; - - fprintf ('full time %10.4f blocks %8d nnz(diag) %8d\n\n', t1, n1, m1) ; - - subplot (3,4,4) ; - drawbtf (A, p1, abs (q1), r1) ; - title (Prob.name, 'Interpreter', 'none') ; - - for j = 1:nw-1 - - maxwork = works (j) ; - - tic - [p2,q2,r2,work2] = btf (A, maxwork) ; - t2 = toc ; - n2 = length (r2) - 1 ; - m2 = nnz (diag (A (p2, abs (q2)))) ; - T2 (j) = t2 ; - N2 (j) = n2 ; - M2 (j) = m2 ; - - fprintf ('%9.1f %10.4f blocks %8d nnz(diag) %8d\n', ... - maxwork, t2, n2, m2) ; - - subplot (3,4,4+j) ; - drawbtf (A, p2, abs (q2), r2) ; - title (sprintf ('%g', maxwork)) ; - - ss = [1:j nw] ; - - subplot (3,4,1) ; - plot (works(ss), T2(ss), 'o-') ; title ('time vs work') ; - axis ([0 limit 0 max(0.1,max(T2))]) ; - - subplot (3,4,2) ; - plot (works(ss), N2(ss), 'o-') ; title ('blocks vs work') ; - axis ([0 limit 0 n1]) ; - - subplot (3,4,3) ; - plot (works(ss), M2(ss), 'o-') ; title ('nnz(diag) vs work') ; - axis ([0 limit 0 m1]) ; - drawnow - - end - fprintf ('full time %10.4f blocks %8d nnz(diag) %8d\n', t1, n1, m1) ; - - if (dopause) - input ('hit enter: ') ; - end - - end - -catch - % out-of-memory is OK, other errors are not - disp (lasterr) ; - if (isempty (strfind (lasterr, 'Out of memory'))) - error (lasterr) ; %#ok - else - fprintf ('test terminated early, but otherwise OK\n') ; - end -end - -close (h) ; diff --git a/deps/AMICI/ThirdParty/SuiteSparse/BTF/MATLAB/btf.c b/deps/AMICI/ThirdParty/SuiteSparse/BTF/MATLAB/btf.c deleted file mode 100644 index 45dcf75d8..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/BTF/MATLAB/btf.c +++ /dev/null @@ -1,145 +0,0 @@ -/* ========================================================================== */ -/* === btf mexFunction ====================================================== */ -/* ========================================================================== */ - -/* BTF: Permute a square matrix to upper block triangular form with a zero-free - * diagonal, or with a maximum number of nonzeros along the diagonal if a - * zero-free permutation does not exist. - * - * Usage: - * - * [p,q,r] = btf (A) ; - * [p,q,r] = btf (A, maxwork) ; - * - * If the matrix has structural full rank, this is essentially identical to - * - * [p,q,r] = dmperm (A) - * - * except that p, q, and r will differ in trivial ways. Both return an upper - * block triangular form with a zero-free diagonal, if the matrix is - * structurally non-singular. The number and sizes of the blocks will be - * identical, but the order of the blocks, and the ordering within the blocks, - * can be different. - * - * If the matrix is structurally singular, q will contain negative entries. - * The permuted matrix is C = A(p,abs(q)), and find(q<0) gives a list of - * indices of the diagonal of C that are equal to zero. This differs from - * dmperm, which does not place the maximum matching along the main diagonal - * of C=A(p,q), but places it above the diagonal instead. - * - * See maxtrans, or btf.m, for a description of maxwork. - * - * An optional fourth output [p,q,r,work] = btf (...) returns the amount of - * work performed, or -1 if the maximum work limit is reached (in which case - * the maximum matching might not have been found). - * - * By Tim Davis. Copyright (c) 2004-2007, University of Florida. - * with support from Sandia National Laboratories. All Rights Reserved. - * - * See also maxtrans, strongcomp, dmperm - */ - -/* ========================================================================== */ - -#include "mex.h" -#include "btf.h" -#define Long SuiteSparse_long - -void mexFunction -( - int nargout, - mxArray *pargout [ ], - int nargin, - const mxArray *pargin [ ] -) -{ - double work, maxwork ; - Long b, n, k, *Ap, *Ai, *P, *R, nblocks, *Work, *Q, nmatch ; - double *Px, *Rx, *Qx, *w ; - - /* ---------------------------------------------------------------------- */ - /* get inputs and allocate workspace */ - /* ---------------------------------------------------------------------- */ - - if (nargin < 1 || nargin > 2 || nargout > 4) - { - mexErrMsgTxt ("Usage: [p,q,r] = btf (A)") ; - } - n = mxGetM (pargin [0]) ; - if (!mxIsSparse (pargin [0]) || n != mxGetN (pargin [0])) - { - mexErrMsgTxt ("btf: A must be sparse, square, and non-empty") ; - } - - /* get sparse matrix A */ - Ap = (Long *) mxGetJc (pargin [0]) ; - Ai = (Long *) mxGetIr (pargin [0]) ; - - /* get output arrays */ - Q = mxMalloc (n * sizeof (Long)) ; - P = mxMalloc (n * sizeof (Long)) ; - R = mxMalloc ((n+1) * sizeof (Long)) ; - - /* get workspace */ - Work = mxMalloc (5*n * sizeof (Long)) ; - - maxwork = 0 ; - if (nargin > 1) - { - maxwork = mxGetScalar (pargin [1]) ; - } - work = 0 ; - - /* ---------------------------------------------------------------------- */ - /* find the permutation to BTF */ - /* ---------------------------------------------------------------------- */ - - nblocks = btf_l_order (n, Ap, Ai, maxwork, &work, P, Q, R, &nmatch, Work) ; - - /* ---------------------------------------------------------------------- */ - /* create outputs and free workspace */ - /* ---------------------------------------------------------------------- */ - - /* create P */ - pargout [0] = mxCreateDoubleMatrix (1, n, mxREAL) ; - Px = mxGetPr (pargout [0]) ; - for (k = 0 ; k < n ; k++) - { - Px [k] = P [k] + 1 ; /* convert to 1-based */ - } - - /* create Q */ - if (nargout > 1) - { - pargout [1] = mxCreateDoubleMatrix (1, n, mxREAL) ; - Qx = mxGetPr (pargout [1]) ; - for (k = 0 ; k < n ; k++) - { - Qx [k] = Q [k] + 1 ; /* convert to 1-based */ - } - } - - /* create R */ - if (nargout > 2) - { - pargout [2] = mxCreateDoubleMatrix (1, nblocks+1, mxREAL) ; - Rx = mxGetPr (pargout [2]) ; - for (b = 0 ; b <= nblocks ; b++) - { - Rx [b] = R [b] + 1 ; /* convert to 1-based */ - } - } - - /* create work output */ - if (nargout > 3) - { - pargout [3] = mxCreateDoubleMatrix (1, 1, mxREAL) ; - w = mxGetPr (pargout [3]) ; - w [0] = work ; - } - - mxFree (P) ; - mxFree (R) ; - mxFree (Work) ; - mxFree (Q) ; -} diff --git a/deps/AMICI/ThirdParty/SuiteSparse/BTF/MATLAB/btf.m b/deps/AMICI/ThirdParty/SuiteSparse/BTF/MATLAB/btf.m deleted file mode 100644 index a4d1c1138..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/BTF/MATLAB/btf.m +++ /dev/null @@ -1,38 +0,0 @@ -function [p,q,r] = btf (A) %#ok -%BTF permute a square sparse matrix into upper block triangular form -% with a zero-free diagonal, or with a maximum number of nonzeros along the -% diagonal if a zero-free permutation does not exist. -% -% Example: -% [p,q,r] = btf (A) ; -% [p,q,r] = btf (A,maxwork) ; -% -% If the matrix has structural full rank, this is essentially identical to -% -% [p,q,r] = dmperm (A) -% -% except that p, q, and r will differ in trivial ways. Both return an upper -% block triangular form with a zero-free diagonal, if the matrix is -% structurally non-singular. The number and sizes of the blocks will be -% identical, but the order of the blocks, and the ordering within the blocks, -% can be different. -% -% If the matrix is structurally singular, the q from btf will contain negative -% entries. The permuted matrix is C = A(p,abs(q)), and find(q<0) gives a list -% of indices of the diagonal of C that are equal to zero. This differs from -% dmperm, which does not place the maximum matching along the main diagonal -% of C=A(p,q), but places it above the diagonal instead. -% -% The second input limits the maximum amount of work the function does to -% be maxwork*nnz(A), or no limit at all if maxwork <= 0. If the function -% terminates early as a result, a maximum matching may not be found, and the -% diagonal of A(p,abs(q)) might not have the maximum number of nonzeros -% possible. Also, the number of blocks (length(r)-1) may be larger than -% what btf(A) or dmperm(A) would compute. -% -% See also maxtrans, strongcomp, dmperm, sprank - -% Copyright 2004-2007, University of Florida -% with support from Sandia National Laboratories. All Rights Reserved. - -error ('btf mexFunction not found') ; diff --git a/deps/AMICI/ThirdParty/SuiteSparse/BTF/MATLAB/btf_demo.m b/deps/AMICI/ThirdParty/SuiteSparse/BTF/MATLAB/btf_demo.m deleted file mode 100644 index 6f0901dcf..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/BTF/MATLAB/btf_demo.m +++ /dev/null @@ -1,47 +0,0 @@ -%BTF_DEMO demo for BTF -% -% Example: -% btf_demo -% -% See also btf, dmperm, strongcomp, maxtrans - -% Copyright 2004-2007, University of Florida - -load west0479 ; -A = west0479 ; - -clf - -subplot (2,3,1) ; -spy (A) -title ('west0479') ; - -subplot (2,3,2) ; -[p, q, r] = btf (A) ; -% spy (A (p, abs(q))) ; -drawbtf (A, p, q, r) ; -title ('btf') ; - -fprintf ('\nbtf_demo: n %d nnz(A) %d # of blocks %d\n', ... - size (A,1), nnz (A), length (r) - 1) ; - -subplot (2,3,3) ; -[p, q, r, s] = dmperm (A) ; -drawbtf (A, p, q, r) ; -title ('dmperm btf') - -subplot (2,3,4) ; -[p, r] = strongcomp (A) ; -% spy (A (p, abs(q))) ; -drawbtf (A, p, p, r) ; -title ('strongly conn. comp.') ; - -subplot (2,3,5) ; -q = maxtrans (A) ; -spy (A (:,q)) -title ('max transversal') ; - -subplot (2,3,6) ; -p = dmperm (A) ; -spy (A (p,:)) -title ('dmperm maxtrans') ; diff --git a/deps/AMICI/ThirdParty/SuiteSparse/BTF/MATLAB/btf_install.m b/deps/AMICI/ThirdParty/SuiteSparse/BTF/MATLAB/btf_install.m deleted file mode 100644 index d0b8b0fff..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/BTF/MATLAB/btf_install.m +++ /dev/null @@ -1,17 +0,0 @@ -function btf_install -%BTF_INSTALL compile and install BTF for use in MATLAB. -% Your current working directory must be BTF/MATLAB for this function to work. -% -% Example: -% btf_install -% -% See also btf, maxtrans, stroncomp, dmperm. - -% Copyright 2004-2007, University of Florida - -btf_make -addpath (pwd) ; -fprintf ('BTF has been compiled and installed. The path:\n') ; -disp (pwd) ; -fprintf ('has been added to your path. Use pathtool to add it permanently.\n'); -btf_demo diff --git a/deps/AMICI/ThirdParty/SuiteSparse/BTF/MATLAB/btf_make.m b/deps/AMICI/ThirdParty/SuiteSparse/BTF/MATLAB/btf_make.m deleted file mode 100644 index a222bbb3f..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/BTF/MATLAB/btf_make.m +++ /dev/null @@ -1,42 +0,0 @@ -function btf_make -%BTF_MAKE compile BTF for use in MATLAB -% Your current working directory must be BTF/MATLAB for this function to work. -% -% Example: -% btf_make -% -% See also btf, maxtrans, stroncomp, dmperm. - -% Copyright 2004-2007, University of Florida - -details = 0 ; % if 1, print details of each command - -mexcmd = 'mex -O -DDLONG -I../Include -I../../SuiteSparse_config ' ; -if (~isempty (strfind (computer, '64'))) - mexcmd = [mexcmd '-largeArrayDims '] ; -end - -% MATLAB 8.3.0 now has a -silent option to keep 'mex' from burbling too much -if (~verLessThan ('matlab', '8.3.0')) - mexcmd = [mexcmd ' -silent '] ; -end - -s = [mexcmd 'maxtrans.c ../Source/btf_maxtrans.c'] ; -if (details) - fprintf ('%s\n', s) ; -end -eval (s) ; - -s = [mexcmd 'strongcomp.c ../Source/btf_strongcomp.c'] ; -if (details) - fprintf ('%s\n', s) ; -end -eval (s) ; - -s = [mexcmd 'btf.c ../Source/btf_maxtrans.c ../Source/btf_strongcomp.c ../Source/btf_order.c'] ; -if (details) - fprintf ('%s\n', s) ; -end -eval (s) ; - -fprintf ('BTF successfully compiled.\n') ; diff --git a/deps/AMICI/ThirdParty/SuiteSparse/BTF/MATLAB/drawbtf.m b/deps/AMICI/ThirdParty/SuiteSparse/BTF/MATLAB/drawbtf.m deleted file mode 100644 index 96a7bc524..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/BTF/MATLAB/drawbtf.m +++ /dev/null @@ -1,27 +0,0 @@ -function drawbtf (A, p, q, r) -%DRAWBTF plot the BTF form of a matrix -% -% A(p,q) is in BTF form, r the block boundaries -% -% Example: -% [p,q,r] = dmperm (A) -% drawbtf (A, p, q, r) -% -% See also btf, maxtrans, strongcomp, dmperm. - -% Copyright 2004-2007, University of Florida - -nblocks = length (r) - 1 ; - -hold off -spy (A (p,abs(q))) -hold on - -for k = 1:nblocks - k1 = r (k) ; - k2 = r (k+1) ; - nk = k2 - k1 ; - if (nk > 1) - plot ([k1 k2 k2 k1 k1]-.5, [k1 k1 k2 k2 k1]-.5, 'r') ; - end -end diff --git a/deps/AMICI/ThirdParty/SuiteSparse/BTF/MATLAB/maxtrans.c b/deps/AMICI/ThirdParty/SuiteSparse/BTF/MATLAB/maxtrans.c deleted file mode 100644 index 8219981d5..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/BTF/MATLAB/maxtrans.c +++ /dev/null @@ -1,102 +0,0 @@ -/* ========================================================================== */ -/* === maxtrans mexFunction ================================================= */ -/* ========================================================================== */ - -#define MIN(a,b) (((a) < (b)) ? (a) : (b)) - -/* MAXTRANS: Find a column permutation for a zero-free diagonal. - * - * Usage: - * - * q = maxtrans (A) ; - * q = maxtrans (A,maxwork) ; - * - * A (:,q) has a zero-free diagonal if sprank(A) == n. - * If the matrix is structurally singular, q will contain zeros. Similar - * to p = dmperm (A), except that dmperm returns a row permutation. - * - * An optional second output [q,work] = maxtrans (...) returns the amount of - * work performed, or -1 if the maximum work limit is reached (in which case - * the maximum matching might not have been found). - * - * By Tim Davis. Copyright (c) 2004-2007, University of Florida. - * with support from Sandia National Laboratories. All Rights Reserved. - */ - -/* ========================================================================== */ - -#include "mex.h" -#include "btf.h" -#define Long SuiteSparse_long - -void mexFunction -( - int nargout, - mxArray *pargout [ ], - int nargin, - const mxArray *pargin [ ] -) -{ - double maxwork, work ; - Long nrow, ncol, i, *Ap, *Ai, *Match, nmatch, *Work ; - double *Matchx, *w ; - - /* ---------------------------------------------------------------------- */ - /* get inputs and allocate workspace */ - /* ---------------------------------------------------------------------- */ - - if (nargin < 1 || nargin > 2 || nargout > 2) - { - mexErrMsgTxt ("Usage: q = maxtrans (A)") ; - } - nrow = mxGetM (pargin [0]) ; - ncol = mxGetN (pargin [0]) ; - if (!mxIsSparse (pargin [0])) - { - mexErrMsgTxt ("maxtrans: A must be sparse, and non-empty") ; - } - - /* get sparse matrix A */ - Ap = (Long *) mxGetJc (pargin [0]) ; - Ai = (Long *) mxGetIr (pargin [0]) ; - - /* get output array */ - Match = mxMalloc (nrow * sizeof (Long)) ; - - /* get workspace of size 5n (recursive version needs only 2n) */ - Work = mxMalloc (5*ncol * sizeof (Long)) ; - - maxwork = 0 ; - if (nargin > 1) - { - maxwork = mxGetScalar (pargin [1]) ; - } - work = 0 ; - - /* ---------------------------------------------------------------------- */ - /* perform the maximum transversal */ - /* ---------------------------------------------------------------------- */ - - nmatch = btf_l_maxtrans (nrow, ncol, Ap, Ai, maxwork, &work, Match, Work) ; - - /* ---------------------------------------------------------------------- */ - /* create outputs and free workspace */ - /* ---------------------------------------------------------------------- */ - - pargout [0] = mxCreateDoubleMatrix (1, nrow, mxREAL) ; - Matchx = mxGetPr (pargout [0]) ; - for (i = 0 ; i < nrow ; i++) - { - Matchx [i] = Match [i] + 1 ; /* convert to 1-based */ - } - - if (nargout > 1) - { - pargout [1] = mxCreateDoubleMatrix (1, 1, mxREAL) ; - w = mxGetPr (pargout [1]) ; - w [0] = work ; - } - - mxFree (Work) ; - mxFree (Match) ; -} diff --git a/deps/AMICI/ThirdParty/SuiteSparse/BTF/MATLAB/maxtrans.m b/deps/AMICI/ThirdParty/SuiteSparse/BTF/MATLAB/maxtrans.m deleted file mode 100644 index aba591a41..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/BTF/MATLAB/maxtrans.m +++ /dev/null @@ -1,30 +0,0 @@ -function q = maxtrans (A) %#ok -%MAXTRANS permute the columns of a sparse matrix so it has a zero-free diagonal -% (if it exists). If no zero-free diagonal exists, then a maximum matching is -% found. Note that this differs from p=dmperm(A), which returns a row -% permutation. -% -% Example: -% q = maxtrans (A) -% q = maxtrans (A,maxwork) -% -% If row i and column j are matched, then q(i) = j. Otherwise, if row is -% unmatched, then q(i) = 0. This is similar to dmperm, except that -% p = dmperm(A) returns p(j)=i if row i and column j are matched, or p(j)=0 if -% column j is unmatched. -% -% If A is structurally nonsingular, then A(:,maxtrans(A)) has a zero-free -% diagonal, as does A (dmperm(A),:). -% -% The second input limits the maximum amount of work the function does -% (excluding the O(nnz(A)) cheap match phase) to be maxwork*nnz(A), or no limit -% at all if maxwork <= 0. If the function terminates early as a result, a -% maximum matching may not be found. An optional second output -% [q,work] = maxtrans (...) returns the amount of work performed, or -1 if the -% maximum work limit is reached. -% -% See also: btf, strongcomp, dmperm, sprank - -% Copyright 2004-2007, University of Florida - -error ('maxtrans mexfunction not found') ; diff --git a/deps/AMICI/ThirdParty/SuiteSparse/BTF/MATLAB/strongcomp.c b/deps/AMICI/ThirdParty/SuiteSparse/BTF/MATLAB/strongcomp.c deleted file mode 100644 index 0e1573580..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/BTF/MATLAB/strongcomp.c +++ /dev/null @@ -1,180 +0,0 @@ -/* ========================================================================== */ -/* === stongcomp mexFunction ================================================ */ -/* ========================================================================== */ - -/* STRONGCOMP: Find a symmetric permutation to upper block triangular form of - * a sparse square matrix. - * - * Usage: - * - * [p,r] = strongcomp (A) ; - * - * [p,q,r] = strongcomp (A,qin) ; - * - * In the first usage, the permuted matrix is C = A (p,p). In the second usage, - * the matrix A (:,qin) is symmetrically permuted to upper block triangular - * form, where qin is an input column permutation, and the final permuted - * matrix is C = A (p,q). This second usage is equivalent to - * - * [p,r] = strongcomp (A (:,qin)) ; - * q = qin (p) ; - * - * That is, if qin is not present it is assumed to be qin = 1:n. - * - * C is the permuted matrix, with a number of blocks equal to length(r)-1. - * The kth block is from row/col r(k) to row/col r(k+1)-1 of C. - * r(1) is one and the last entry in r is equal to n+1. - * The diagonal of A (or A (:,qin)) is ignored. - * - * strongcomp is normally proceeded by a maximum transversal: - * - * [p,q,r] = strongcomp (A, maxtrans (A)) - * - * if the matrix has full structural rank. This is identical to - * - * [p,q,r] = btf (A) - * - * (except that btf handles the case when A is structurally rank-deficient). - * It essentially the same as - * - * [p,q,r] = dmperm (A) - * - * except that p, q, and r will differ between btf and dmperm. Both return an - * upper block triangular form with a zero-free diagonal. The number and sizes - * of the blocks will be identical, but the order of the blocks, and the - * ordering within the blocks, can be different. For structurally rank - * deficient matrices, dmpmerm returns the maximum matching as a zero-free - * diagonal that is above the main diagonal; btf always returns the matching as - * the main diagonal (which will thus contain zeros). - * - * By Tim Davis. Copyright (c) 2004-2007, University of Florida. - * with support from Sandia National Laboratories. All Rights Reserved. - * - * See also maxtrans, btf, dmperm - */ - -/* ========================================================================== */ - -#include "mex.h" -#include "btf.h" -#define Long SuiteSparse_long - -void mexFunction -( - int nargout, - mxArray *pargout[], - int nargin, - const mxArray *pargin[] -) -{ - Long b, n, i, k, j, *Ap, *Ai, *P, *R, nblocks, *Work, *Q, jj ; - double *Px, *Rx, *Qx ; - - /* ---------------------------------------------------------------------- */ - /* get inputs and allocate workspace */ - /* ---------------------------------------------------------------------- */ - - if (!((nargin == 1 && nargout <= 2) || (nargin == 2 && nargout <= 3))) - { - mexErrMsgTxt ("Usage: [p,r] = strongcomp (A)" - " or [p,q,r] = strongcomp (A,qin)") ; - } - n = mxGetM (pargin [0]) ; - if (!mxIsSparse (pargin [0]) || n != mxGetN (pargin [0])) - { - mexErrMsgTxt ("strongcomp: A must be sparse, square, and non-empty") ; - } - - /* get sparse matrix A */ - Ap = (Long *) mxGetJc (pargin [0]) ; - Ai = (Long *) mxGetIr (pargin [0]) ; - - /* get output arrays */ - P = mxMalloc (n * sizeof (Long)) ; - R = mxMalloc ((n+1) * sizeof (Long)) ; - - /* get workspace of size 4n (recursive code only needs 2n) */ - Work = mxMalloc (4*n * sizeof (Long)) ; - - /* get the input column permutation Q */ - if (nargin == 2) - { - if (mxGetNumberOfElements (pargin [1]) != n) - { - mexErrMsgTxt - ("strongcomp: qin must be a permutation vector of size n") ; - } - Qx = mxGetPr (pargin [1]) ; - Q = mxMalloc (n * sizeof (Long)) ; - /* connvert Qin to 0-based and check validity */ - for (i = 0 ; i < n ; i++) - { - Work [i] = 0 ; - } - for (k = 0 ; k < n ; k++) - { - j = Qx [k] - 1 ; /* convert to 0-based */ - jj = BTF_UNFLIP (j) ; - if (jj < 0 || jj >= n || Work [jj] == 1) - { - mexErrMsgTxt - ("strongcomp: qin must be a permutation vector of size n") ; - } - Work [jj] = 1 ; - Q [k] = j ; - } - } - else - { - /* no input column permutation */ - Q = (Long *) NULL ; - } - - /* ---------------------------------------------------------------------- */ - /* find the strongly-connected components of A */ - /* ---------------------------------------------------------------------- */ - - nblocks = btf_l_strongcomp (n, Ap, Ai, Q, P, R, Work) ; - - /* ---------------------------------------------------------------------- */ - /* create outputs and free workspace */ - /* ---------------------------------------------------------------------- */ - - /* create P */ - pargout [0] = mxCreateDoubleMatrix (1, n, mxREAL) ; - Px = mxGetPr (pargout [0]) ; - for (k = 0 ; k < n ; k++) - { - Px [k] = P [k] + 1 ; /* convert to 1-based */ - } - - /* create Q */ - if (nargin == 2 && nargout > 1) - { - pargout [1] = mxCreateDoubleMatrix (1, n, mxREAL) ; - Qx = mxGetPr (pargout [1]) ; - for (k = 0 ; k < n ; k++) - { - Qx [k] = Q [k] + 1 ; /* convert to 1-based */ - } - } - - /* create R */ - if (nargout == nargin + 1) - { - pargout [nargin] = mxCreateDoubleMatrix (1, nblocks+1, mxREAL) ; - Rx = mxGetPr (pargout [nargin]) ; - for (b = 0 ; b <= nblocks ; b++) - { - Rx [b] = R [b] + 1 ; /* convert to 1-based */ - } - } - - mxFree (P) ; - mxFree (R) ; - mxFree (Work) ; - if (nargin == 2) - { - mxFree (Q) ; - } -} diff --git a/deps/AMICI/ThirdParty/SuiteSparse/BTF/MATLAB/strongcomp.m b/deps/AMICI/ThirdParty/SuiteSparse/BTF/MATLAB/strongcomp.m deleted file mode 100644 index b20f1b87e..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/BTF/MATLAB/strongcomp.m +++ /dev/null @@ -1,45 +0,0 @@ -function [p,q,r] = strongcomp (A, qin) %#ok -%STRONGCOMP symmetric permutation to upper block triangular form -% The matrix must be sparse and square. -% -% Example: -% [p,r] = strongcomp (A) ; -% [p,q,r] = strongcomp (A,qin) ; -% -% In the first usage, the permuted matrix is C = A (p,p). In the second usage, -% the matrix A (:,qin) is symmetrically permuted to upper block triangular -% form, where qin is an input column permutation, and the final permuted -% matrix is C = A (p,q). This second usage is equivalent to -% -% [p,r] = strongcomp (A (:,qin)) ; -% q = qin (p) ; -% -% That is, if qin is not present it is assumed to be qin = 1:n. -% -% C is the permuted matrix, with a number of blocks equal to length(r)-1. -% The kth block is from row/col r(k) to row/col r(k+1)-1 of C. -% r(1) is one and the last entry in r is equal to n+1. -% The diagonal of A (or A (:,qin)) is ignored. -% -% strongcomp is normally proceeded by a maximum transversal. -% Assuming A is square and structurally nonsingular, -% -% [p,q,r] = strongcomp (A, maxtrans (A)) -% -% is essentially identical to -% -% [p,q,r] = dmperm (A) -% -% except that p, q, and r will differ. Both return an upper block triangular -% form with a zero-free diagonal. The number and sizes of the blocks will be -% identical, but the order of the blocks, and the ordering within the blocks, -% can be different. If the matrix is structurally singular, both strongcomp -% and maxtrans return a vector q containing negative entries. abs(q) is a -% permutation of 1:n, and find(q<0) gives a list of the indices of the -% diagonal of A(p,q) that are zero. -% -% See also btf, maxtrans, dmperm - -% Copyright 2004-2007, University of Florida - -error ('strongcomp mexFunction not found') ; diff --git a/deps/AMICI/ThirdParty/SuiteSparse/BTF/Makefile b/deps/AMICI/ThirdParty/SuiteSparse/BTF/Makefile index 8cff0a1a8..f86ddabfd 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/BTF/Makefile +++ b/deps/AMICI/ThirdParty/SuiteSparse/BTF/Makefile @@ -1,35 +1,82 @@ -#------------------------------------------------------------------------------ -# BTF Makefile -#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------- +# SuiteSparse/BTF/Makefile +#------------------------------------------------------------------------------- -SUITESPARSE ?= $(realpath $(CURDIR)/..) -export SUITESPARSE +# BTF: Copyright (c) 2004-2022, University of Florida. All Rights Reserved. +# Authors: Timothy A. Davis and Ekanathan Palamadai. +# SPDX-License-Identifier: LGPL-2.1+ -default: library +#------------------------------------------------------------------------------- + +# A simple Makefile for BTF, which relies on cmake to do the +# actual build. All the work is done in cmake so this Makefile is just for +# convenience. + +# To compile with an alternate compiler: +# +# make CC=gcc CXX=g++ +# +# To compile/install for system-wide usage: +# +# make +# sudo make install +# +# To compile/install for local usage (SuiteSparse/lib and SuiteSparse/include): +# +# make local +# make install +# +# To clean up the files: +# +# make clean + +JOBS ?= 8 -include ../SuiteSparse_config/SuiteSparse_config.mk +default: library +# default is to install only in /usr/local library: - ( cd Lib ; $(MAKE) ) + ( cd build && cmake $(CMAKE_OPTIONS) .. && cmake --build . -j${JOBS} ) -# compile the static libraries only -static: - ( cd Lib ; $(MAKE) static ) +# install only in SuiteSparse/lib and SuiteSparse/include +local: + ( cd build && cmake $(CMAKE_OPTIONS) -DLOCAL_INSTALL=1 .. && cmake --build . -j${JOBS} ) -clean: - ( cd Lib ; $(MAKE) clean ) - ( cd MATLAB ; $(RM) $(CLEAN) ) +# install only in /usr/local (default) +global: + ( cd build && cmake $(CMAKE_OPTIONS) -DLOCAL_INSTALL=0 .. && cmake --build . -j${JOBS} ) + +debug: + ( cd build && cmake $(CMAKE_OPTIONS) -DCMAKE_BUILD_TYPE=Debug .. && cmake --build . -j${JOBS} ) + +all: library -distclean: - ( cd Lib ; $(MAKE) distclean ) - ( cd MATLAB ; $(RM) $(CLEAN) ; $(RM) *.mex* ) +# there is no BTF demo +demos: library -purge: distclean +# just compile after running cmake; do not run cmake again +remake: + ( cd build && cmake --build . -j${JOBS} ) + +# just run cmake to set things up +setup: + ( cd build && cmake $(CMAKE_OPTIONS) .. ) -# install BTF install: - ( cd Lib ; $(MAKE) install ) + ( cd build && cmake --install . ) -# uninstall BTF +# remove any installed libraries and #include files uninstall: - ( cd Lib ; $(MAKE) uninstall ) + - xargs rm < build/install_manifest.txt + +# remove all files not in the distribution +clean: + - $(RM) -rf build/* Config/*.tmp MATLAB/*.o MATLAB/*.mex* + +purge: clean + +distclean: clean + +docs: + + diff --git a/deps/AMICI/ThirdParty/SuiteSparse/BTF/README.txt b/deps/AMICI/ThirdParty/SuiteSparse/BTF/README.txt index e1032544e..110db4bb1 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/BTF/README.txt +++ b/deps/AMICI/ThirdParty/SuiteSparse/BTF/README.txt @@ -17,9 +17,10 @@ this directory. KLU relies on the BTF package to permute the matrix prior to factorization. To compile the libbtf.a and libbtf.so library (*.dylib on the Mac), type -"make". The compiled library is located in BTF/Lib. Compile code that uses -BTF with -IBTF/Include. Type "make install" to install the library in -/usr/local/lib and /usr/local/include, and "make uninstall" to remove it. +"make". Type "make install" to install the library in /usr/local/lib and +/usr/local/include, and "make uninstall" to remove it. +"make local ; make install" will install only in SuiteSparse/lib and +SuiteSparse/include. Type "make clean" to remove all but the compiled library, and "make distclean" to remove all files not in the original distribution. @@ -41,11 +42,12 @@ Files and directories in the BTF package: Doc documentation and license Include include files - Lib compiled BTF library Makefile Makefile for C and MATLAB versions MATLAB MATLAB interface README.txt this file Source BTF source code + CMakeLists.txt cmake script for compiling BTF + Config source for bft.h ./Doc: diff --git a/deps/AMICI/ThirdParty/SuiteSparse/BTF/Source/btf_l_maxtrans.c b/deps/AMICI/ThirdParty/SuiteSparse/BTF/Source/btf_l_maxtrans.c new file mode 100644 index 000000000..8cdddf250 --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/BTF/Source/btf_l_maxtrans.c @@ -0,0 +1,13 @@ +//------------------------------------------------------------------------------ +// BTF/Source/btf_l_maxtrans.c: int64_t version of btf_maxtrans +//------------------------------------------------------------------------------ + +// BTF, Copyright (c) 2004-2022, University of Florida. All Rights Reserved. +// Author: Timothy A. Davis. +// SPDX-License-Identifier: LGPL-2.1+ + +//------------------------------------------------------------------------------ + +#define DLONG +#include "btf_maxtrans.c" + diff --git a/deps/AMICI/ThirdParty/SuiteSparse/BTF/Source/btf_l_order.c b/deps/AMICI/ThirdParty/SuiteSparse/BTF/Source/btf_l_order.c new file mode 100644 index 000000000..1f9e0bfd1 --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/BTF/Source/btf_l_order.c @@ -0,0 +1,13 @@ +//------------------------------------------------------------------------------ +// BTF/Source/btf_l_order.c: int64_t version of btf_order +//------------------------------------------------------------------------------ + +// BTF, Copyright (c) 2004-2022, University of Florida. All Rights Reserved. +// Author: Timothy A. Davis. +// SPDX-License-Identifier: LGPL-2.1+ + +//------------------------------------------------------------------------------ + +#define DLONG +#include "btf_order.c" + diff --git a/deps/AMICI/ThirdParty/SuiteSparse/BTF/Source/btf_l_strongcomp.c b/deps/AMICI/ThirdParty/SuiteSparse/BTF/Source/btf_l_strongcomp.c new file mode 100644 index 000000000..b08d0ec39 --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/BTF/Source/btf_l_strongcomp.c @@ -0,0 +1,13 @@ +//------------------------------------------------------------------------------ +// BTF/Source/btf_l_strongcomp.c: int64_t version of btf_strongcomp +//------------------------------------------------------------------------------ + +// BTF, Copyright (c) 2004-2022, University of Florida. All Rights Reserved. +// Author: Timothy A. Davis. +// SPDX-License-Identifier: LGPL-2.1+ + +//------------------------------------------------------------------------------ + +#define DLONG +#include "btf_strongcomp.c" + diff --git a/deps/AMICI/ThirdParty/SuiteSparse/BTF/Source/btf_maxtrans.c b/deps/AMICI/ThirdParty/SuiteSparse/BTF/Source/btf_maxtrans.c index 3f44b2616..460fad4d2 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/BTF/Source/btf_maxtrans.c +++ b/deps/AMICI/ThirdParty/SuiteSparse/BTF/Source/btf_maxtrans.c @@ -1,6 +1,12 @@ -/* ========================================================================== */ -/* === BTF_MAXTRANS ========================================================= */ -/* ========================================================================== */ +//------------------------------------------------------------------------------ +// BTF/Source/btf_maxtrans: maximum transversal +//------------------------------------------------------------------------------ + +// BTF, Copyright (c) 2004-2022, University of Florida. All Rights Reserved. +// Author: Timothy A. Davis. +// SPDX-License-Identifier: LGPL-2.1+ + +//------------------------------------------------------------------------------ /* Finds a column permutation that maximizes the number of entries on the * diagonal of a sparse matrix. See btf.h for more information. @@ -40,9 +46,6 @@ * Thus, for general usage, cs_maxtrans is preferred. For square matrices that * are typically structurally non-singular, maxtrans is preferred. A partial * maxtrans can still be very useful when solving a sparse linear system. - * - * By Tim Davis. Copyright (c) 2004-2007, University of Florida. - * with support from Sandia National Laboratories. All Rights Reserved. */ #include "btf.h" @@ -130,7 +133,7 @@ * for (p = head ; ...) DO 90 K=1,JORD */ -static Int augment +static int augment ( Int k, /* which stage of the main loop we're in */ Int Ap [ ], /* column pointers, size n+1 */ @@ -314,7 +317,8 @@ Int BTF(maxtrans) /* returns # of columns in the matching */ ) { Int *Cheap, *Flag, *Istack, *Jstack, *Pstack ; - Int i, j, k, nmatch, work_limit_reached, result ; + Int i, j, k, nmatch, work_limit_reached ; + int result ; /* ---------------------------------------------------------------------- */ /* get workspace and initialize */ diff --git a/deps/AMICI/ThirdParty/SuiteSparse/BTF/Source/btf_order.c b/deps/AMICI/ThirdParty/SuiteSparse/BTF/Source/btf_order.c index b0bdb294b..0039d7fcf 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/BTF/Source/btf_order.c +++ b/deps/AMICI/ThirdParty/SuiteSparse/BTF/Source/btf_order.c @@ -1,6 +1,12 @@ -/* ========================================================================== */ -/* === BTF_ORDER ============================================================ */ -/* ========================================================================== */ +//------------------------------------------------------------------------------ +// BTF/Source/btf_order: permute a matrix to block triangular form +//------------------------------------------------------------------------------ + +// BTF, Copyright (c) 2004-2022, University of Florida. All Rights Reserved. +// Author: Timothy A. Davis. +// SPDX-License-Identifier: LGPL-2.1+ + +//------------------------------------------------------------------------------ /* Find a permutation P and Q to permute a square sparse matrix into upper block * triangular form. A(P,Q) will contain a zero-free diagonal if A has @@ -21,9 +27,6 @@ * might not be equal to the structural rank. * * See btf.h for more details. - * - * By Tim Davis. Copyright (c) 2004-2007, University of Florida. - * with support from Sandia National Laboratories. All Rights Reserved. */ #include "btf.h" diff --git a/deps/AMICI/ThirdParty/SuiteSparse/BTF/Source/btf_strongcomp.c b/deps/AMICI/ThirdParty/SuiteSparse/BTF/Source/btf_strongcomp.c index 36ec3e28f..2b9047e8d 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/BTF/Source/btf_strongcomp.c +++ b/deps/AMICI/ThirdParty/SuiteSparse/BTF/Source/btf_strongcomp.c @@ -1,13 +1,16 @@ -/* ========================================================================== */ -/* === BTF_STRONGCOMP ======================================================= */ -/* ========================================================================== */ +//------------------------------------------------------------------------------ +// BTF/Source/btf_strongcomp: strongly connected components +//------------------------------------------------------------------------------ + +// BTF, Copyright (c) 2004-2022, University of Florida. All Rights Reserved. +// Author: Timothy A. Davis. +// SPDX-License-Identifier: LGPL-2.1+ + +//------------------------------------------------------------------------------ /* Finds the strongly connected components of a graph, or equivalently, permutes * the matrix into upper block triangular form. See btf.h for more details. * Input matrix and Q are not checked on input. - * - * By Tim Davis. Copyright (c) 2004-2007, University of Florida. - * with support from Sandia National Laboratories. All Rights Reserved. */ #include "btf.h" diff --git a/deps/AMICI/ThirdParty/SuiteSparse/BTF/build/.gitignore b/deps/AMICI/ThirdParty/SuiteSparse/BTF/build/.gitignore new file mode 100644 index 000000000..52e15321b --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/BTF/build/.gitignore @@ -0,0 +1,4 @@ +# Ignore all files except this file. +* +*/ +!.gitignore diff --git a/deps/AMICI/ThirdParty/SuiteSparse/BTF/cmake_modules/FindBTF.cmake b/deps/AMICI/ThirdParty/SuiteSparse/BTF/cmake_modules/FindBTF.cmake new file mode 100644 index 000000000..b5e6153ed --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/BTF/cmake_modules/FindBTF.cmake @@ -0,0 +1,129 @@ +#------------------------------------------------------------------------------- +# 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/CAMD/Demo/Makefile b/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Demo/Makefile deleted file mode 100644 index f16c162e5..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Demo/Makefile +++ /dev/null @@ -1,58 +0,0 @@ -#----------------------------------------------------------------------------- -# compile the CAMD demo -#----------------------------------------------------------------------------- - -default: camd_simple camd_demo camd_demo2 camd_l_demo - -include ../../SuiteSparse_config/SuiteSparse_config.mk - -C = $(CC) $(CF) -I../../include - -LIB2 = $(LDFLAGS) -L../../lib -lcamd -lsuitesparseconfig $(LDLIBS) - -library: - ( cd ../../SuiteSparse_config ; $(MAKE) ) - ( cd ../Lib ; $(MAKE) ) - -#------------------------------------------------------------------------------ -# Create the demo program, run it, and compare the output -#------------------------------------------------------------------------------ - -dist: - -camd_demo: camd_demo.c library - $(C) -o camd_demo camd_demo.c $(LIB2) - ./camd_demo > my_camd_demo.out - - diff camd_demo.out my_camd_demo.out - -camd_l_demo: camd_l_demo.c library - $(C) -o camd_l_demo camd_l_demo.c $(LIB2) - ./camd_l_demo > my_camd_l_demo.out - - diff camd_l_demo.out my_camd_l_demo.out - -camd_demo2: camd_demo2.c library - $(C) -o camd_demo2 camd_demo2.c $(LIB2) - ./camd_demo2 > my_camd_demo2.out - - diff camd_demo2.out my_camd_demo2.out - -camd_simple: camd_simple.c library - $(C) -o camd_simple camd_simple.c $(LIB2) - ./camd_simple > my_camd_simple.out - - diff camd_simple.out my_camd_simple.out - -#------------------------------------------------------------------------------ -# Remove all but the files in the original distribution -#------------------------------------------------------------------------------ - -clean: - - $(RM) -r $(CLEAN) - -purge: distclean - -distclean: clean - - $(RM) camd_demo my_camd_demo.out - - $(RM) camd_l_demo my_camd_l_demo.out - - $(RM) camd_demo2 my_camd_demo2.out - - $(RM) camd_simple my_camd_simple.out - - $(RM) -r $(PURGE) - diff --git a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Demo/camd_demo.c b/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Demo/camd_demo.c deleted file mode 100644 index 0fd909a2c..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Demo/camd_demo.c +++ /dev/null @@ -1,172 +0,0 @@ -/* ========================================================================= */ -/* === CAMD demo main program ============================================== */ -/* ========================================================================= */ - -/* ------------------------------------------------------------------------- */ -/* CAMD, Copyright (c) Timothy A. Davis, Yanqing Chen, */ -/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ -/* email: DrTimothyAldenDavis@gmail.com */ -/* ------------------------------------------------------------------------- */ - -/* A simple C main program that illustrates the use of the ANSI C interface - * to CAMD. - */ - -#include "camd.h" -#include -#include - -int main (void) -{ - /* The symmetric can_24 Harwell/Boeing matrix, including upper and lower - * triangular parts, and the diagonal entries. Note that this matrix is - * 0-based, with row and column indices in the range 0 to n-1. */ - int n = 24, nz, - Ap [ ] = { 0, 9, 15, 21, 27, 33, 39, 48, 57, 61, 70, 76, 82, 88, 94, 100, - 106, 110, 119, 128, 137, 143, 152, 156, 160 }, - Ai [ ] = { - /* column 0: */ 0, 5, 6, 12, 13, 17, 18, 19, 21, - /* column 1: */ 1, 8, 9, 13, 14, 17, - /* column 2: */ 2, 6, 11, 20, 21, 22, - /* column 3: */ 3, 7, 10, 15, 18, 19, - /* column 4: */ 4, 7, 9, 14, 15, 16, - /* column 5: */ 0, 5, 6, 12, 13, 17, - /* column 6: */ 0, 2, 5, 6, 11, 12, 19, 21, 23, - /* column 7: */ 3, 4, 7, 9, 14, 15, 16, 17, 18, - /* column 8: */ 1, 8, 9, 14, - /* column 9: */ 1, 4, 7, 8, 9, 13, 14, 17, 18, - /* column 10: */ 3, 10, 18, 19, 20, 21, - /* column 11: */ 2, 6, 11, 12, 21, 23, - /* column 12: */ 0, 5, 6, 11, 12, 23, - /* column 13: */ 0, 1, 5, 9, 13, 17, - /* column 14: */ 1, 4, 7, 8, 9, 14, - /* column 15: */ 3, 4, 7, 15, 16, 18, - /* column 16: */ 4, 7, 15, 16, - /* column 17: */ 0, 1, 5, 7, 9, 13, 17, 18, 19, - /* column 18: */ 0, 3, 7, 9, 10, 15, 17, 18, 19, - /* column 19: */ 0, 3, 6, 10, 17, 18, 19, 20, 21, - /* column 20: */ 2, 10, 19, 20, 21, 22, - /* column 21: */ 0, 2, 6, 10, 11, 19, 20, 21, 22, - /* column 22: */ 2, 20, 21, 22, - /* column 23: */ 6, 11, 12, 23 } ; - - int P [24], Pinv [24], i, j, k, jnew, p, inew, result ; - double Control [CAMD_CONTROL], Info [CAMD_INFO] ; - char A [24][24] ; - int C [ ] = { 0, 0, 4, 0, 1, 0, 2, 2, 1, 1, 3, 4, 5, 5, 3, 4, - 5, 2, 5, 3, 4, 2, 1, 0 } ; - - printf ("CAMD version %d.%d, date: %s\n", CAMD_MAIN_VERSION, - CAMD_SUB_VERSION, CAMD_DATE) ; - printf ("CAMD demo, with the 24-by-24 Harwell/Boeing matrix, can_24:\n") ; - - /* get the default parameters, and print them */ - camd_defaults (Control) ; - camd_control (Control) ; - - /* print the input matrix */ - nz = Ap [n] ; - printf ("\nInput matrix: %d-by-%d, with %d entries.\n" - " Note that for a symmetric matrix such as this one, only the\n" - " strictly lower or upper triangular parts would need to be\n" - " passed to CAMD, since CAMD computes the ordering of A+A'. The\n" - " diagonal entries are also not needed, since CAMD ignores them.\n" - , n, n, nz) ; - for (j = 0 ; j < n ; j++) - { - printf ("\nColumn: %d, number of entries: %d, with row indices in" - " Ai [%d ... %d]:\n row indices:", - j, Ap [j+1] - Ap [j], Ap [j], Ap [j+1]-1) ; - for (p = Ap [j] ; p < Ap [j+1] ; p++) - { - i = Ai [p] ; - printf (" %d", i) ; - } - printf ("\n") ; - } - - /* print a character plot of the input matrix. This is only reasonable - * because the matrix is small. */ - printf ("\nPlot of input matrix pattern:\n") ; - for (j = 0 ; j < n ; j++) - { - for (i = 0 ; i < n ; i++) A [i][j] = '.' ; - for (p = Ap [j] ; p < Ap [j+1] ; p++) - { - i = Ai [p] ; - A [i][j] = 'X' ; - } - } - printf (" ") ; - for (j = 0 ; j < n ; j++) printf (" %1d", j % 10) ; - printf ("\n") ; - for (i = 0 ; i < n ; i++) - { - printf ("%2d: ", i) ; - for (j = 0 ; j < n ; j++) - { - printf (" %c", A [i][j]) ; - } - printf ("\n") ; - } - - /* order the matrix */ - result = camd_order (n, Ap, Ai, P, Control, Info, C) ; - printf ("return value from camd_order: %d (should be %d)\n", - result, CAMD_OK) ; - - /* print the statistics */ - camd_info (Info) ; - - if (result != CAMD_OK) - { - printf ("CAMD failed\n") ; - exit (1) ; - } - - /* print the permutation vector, P, and compute the inverse permutation */ - printf ("Permutation vector:\n") ; - for (k = 0 ; k < n ; k++) - { - /* row/column j is the kth row/column in the permuted matrix */ - j = P [k] ; - Pinv [j] = k ; - printf (" %2d", j) ; - } - printf ("\n\n") ; - - printf ("Inverse permutation vector:\n") ; - for (j = 0 ; j < n ; j++) - { - k = Pinv [j] ; - printf (" %2d", k) ; - } - printf ("\n\n") ; - - /* print a character plot of the permuted matrix. */ - printf ("\nPlot of permuted matrix pattern:\n") ; - for (jnew = 0 ; jnew < n ; jnew++) - { - j = P [jnew] ; - for (inew = 0 ; inew < n ; inew++) A [inew][jnew] = '.' ; - for (p = Ap [j] ; p < Ap [j+1] ; p++) - { - inew = Pinv [Ai [p]] ; - A [inew][jnew] = 'X' ; - } - } - printf (" ") ; - for (j = 0 ; j < n ; j++) printf (" %1d", j % 10) ; - printf ("\n") ; - for (i = 0 ; i < n ; i++) - { - printf ("%2d: ", i) ; - for (j = 0 ; j < n ; j++) - { - printf (" %c", A [i][j]) ; - } - printf ("\n") ; - } - - return (0) ; -} diff --git a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Demo/camd_demo.out b/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Demo/camd_demo.out deleted file mode 100644 index 2c9ea9876..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Demo/camd_demo.out +++ /dev/null @@ -1,179 +0,0 @@ -CAMD version 2.4, date: May 4, 2016 -CAMD demo, with the 24-by-24 Harwell/Boeing matrix, can_24: - -camd version 2.4, May 4, 2016: approximate minimum degree ordering: - dense row parameter: 10 - (rows with more than max (10 * sqrt (n), 16) entries are - considered "dense", and placed last in output permutation) - aggressive absorption: yes - size of CAMD integer: 4 - - -Input matrix: 24-by-24, with 160 entries. - Note that for a symmetric matrix such as this one, only the - strictly lower or upper triangular parts would need to be - passed to CAMD, since CAMD computes the ordering of A+A'. The - diagonal entries are also not needed, since CAMD ignores them. - -Column: 0, number of entries: 9, with row indices in Ai [0 ... 8]: - row indices: 0 5 6 12 13 17 18 19 21 - -Column: 1, number of entries: 6, with row indices in Ai [9 ... 14]: - row indices: 1 8 9 13 14 17 - -Column: 2, number of entries: 6, with row indices in Ai [15 ... 20]: - row indices: 2 6 11 20 21 22 - -Column: 3, number of entries: 6, with row indices in Ai [21 ... 26]: - row indices: 3 7 10 15 18 19 - -Column: 4, number of entries: 6, with row indices in Ai [27 ... 32]: - row indices: 4 7 9 14 15 16 - -Column: 5, number of entries: 6, with row indices in Ai [33 ... 38]: - row indices: 0 5 6 12 13 17 - -Column: 6, number of entries: 9, with row indices in Ai [39 ... 47]: - row indices: 0 2 5 6 11 12 19 21 23 - -Column: 7, number of entries: 9, with row indices in Ai [48 ... 56]: - row indices: 3 4 7 9 14 15 16 17 18 - -Column: 8, number of entries: 4, with row indices in Ai [57 ... 60]: - row indices: 1 8 9 14 - -Column: 9, number of entries: 9, with row indices in Ai [61 ... 69]: - row indices: 1 4 7 8 9 13 14 17 18 - -Column: 10, number of entries: 6, with row indices in Ai [70 ... 75]: - row indices: 3 10 18 19 20 21 - -Column: 11, number of entries: 6, with row indices in Ai [76 ... 81]: - row indices: 2 6 11 12 21 23 - -Column: 12, number of entries: 6, with row indices in Ai [82 ... 87]: - row indices: 0 5 6 11 12 23 - -Column: 13, number of entries: 6, with row indices in Ai [88 ... 93]: - row indices: 0 1 5 9 13 17 - -Column: 14, number of entries: 6, with row indices in Ai [94 ... 99]: - row indices: 1 4 7 8 9 14 - -Column: 15, number of entries: 6, with row indices in Ai [100 ... 105]: - row indices: 3 4 7 15 16 18 - -Column: 16, number of entries: 4, with row indices in Ai [106 ... 109]: - row indices: 4 7 15 16 - -Column: 17, number of entries: 9, with row indices in Ai [110 ... 118]: - row indices: 0 1 5 7 9 13 17 18 19 - -Column: 18, number of entries: 9, with row indices in Ai [119 ... 127]: - row indices: 0 3 7 9 10 15 17 18 19 - -Column: 19, number of entries: 9, with row indices in Ai [128 ... 136]: - row indices: 0 3 6 10 17 18 19 20 21 - -Column: 20, number of entries: 6, with row indices in Ai [137 ... 142]: - row indices: 2 10 19 20 21 22 - -Column: 21, number of entries: 9, with row indices in Ai [143 ... 151]: - row indices: 0 2 6 10 11 19 20 21 22 - -Column: 22, number of entries: 4, with row indices in Ai [152 ... 155]: - row indices: 2 20 21 22 - -Column: 23, number of entries: 4, with row indices in Ai [156 ... 159]: - row indices: 6 11 12 23 - -Plot of input matrix pattern: - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 - 0: X . . . . X X . . . . . X X . . . X X X . X . . - 1: . X . . . . . . X X . . . X X . . X . . . . . . - 2: . . X . . . X . . . . X . . . . . . . . X X X . - 3: . . . X . . . X . . X . . . . X . . X X . . . . - 4: . . . . X . . X . X . . . . X X X . . . . . . . - 5: X . . . . X X . . . . . X X . . . X . . . . . . - 6: X . X . . X X . . . . X X . . . . . . X . X . X - 7: . . . X X . . X . X . . . . X X X X X . . . . . - 8: . X . . . . . . X X . . . . X . . . . . . . . . - 9: . X . . X . . X X X . . . X X . . X X . . . . . -10: . . . X . . . . . . X . . . . . . . X X X X . . -11: . . X . . . X . . . . X X . . . . . . . . X . X -12: X . . . . X X . . . . X X . . . . . . . . . . X -13: X X . . . X . . . X . . . X . . . X . . . . . . -14: . X . . X . . X X X . . . . X . . . . . . . . . -15: . . . X X . . X . . . . . . . X X . X . . . . . -16: . . . . X . . X . . . . . . . X X . . . . . . . -17: X X . . . X . X . X . . . X . . . X X X . . . . -18: X . . X . . . X . X X . . . . X . X X X . . . . -19: X . . X . . X . . . X . . . . . . X X X X X . . -20: . . X . . . . . . . X . . . . . . . . X X X X . -21: X . X . . . X . . . X X . . . . . . . X X X X . -22: . . X . . . . . . . . . . . . . . . . . X X X . -23: . . . . . . X . . . . X X . . . . . . . . . . X -return value from camd_order: 0 (should be 0) - -CAMD version 2.4.6, May 4, 2016, results: - status: OK - n, dimension of A: 24 - nz, number of nonzeros in A: 160 - symmetry of A: 1.0000 - number of nonzeros on diagonal: 24 - nonzeros in pattern of A+A' (excl. diagonal): 136 - # dense rows/columns of A+A': 0 - memory used, in bytes: 1644 - # of memory compactions: 0 - - The following approximate statistics are for a subsequent - factorization of A(P,P) + A(P,P)'. They are slight upper - bounds if there are no dense rows/columns in A+A', and become - looser if dense rows/columns exist. - - nonzeros in L (excluding diagonal): 135 - nonzeros in L (including diagonal): 159 - # divide operations for LDL' or LU: 135 - # multiply-subtract operations for LDL': 541 - # multiply-subtract operations for LU: 947 - max nz. in any column of L (incl. diagonal): 12 - - chol flop count for real A, sqrt counted as 1 flop: 1241 - LDL' flop count for real A: 1217 - LDL' flop count for complex A: 5543 - LU flop count for real A (with no pivoting): 2029 - LU flop count for complex A (with no pivoting): 8791 - -Permutation vector: - 23 3 1 5 0 22 4 8 9 7 6 21 17 19 10 14 2 11 20 15 12 13 18 16 - -Inverse permutation vector: - 4 2 16 1 6 3 10 9 7 8 14 17 20 21 15 19 23 12 22 13 18 11 5 0 - - -Plot of permuted matrix pattern: - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 - 0: X . . . . . . . . . X . . . . . . X . . X . . . - 1: . X . . . . . . . X . . . X X . . . . X . . X . - 2: . . X . . . . X X . . . X . . X . . . . . X . . - 3: . . . X X . . . . . X . X . . . . . . . X X . . - 4: . . . X X . . . . . X X X X . . . . . . X X X . - 5: . . . . . X . . . . . X . . . . X . X . . . . . - 6: . . . . . . X . X X . . . . . X . . . X . . . X - 7: . . X . . . . X X . . . . . . X . . . . . . . . - 8: . . X . . . X X X X . . X . . X . . . . . X X . - 9: . X . . . . X . X X . . X . . X . . . X . . X X -10: X . . X X . . . . . X X . X . . X X . . X . . . -11: . . . . X X . . . . X X . X X . X X X . . . . . -12: . . X X X . . . X X . . X X . . . . . . . X X . -13: . X . . X . . . . . X X X X X . . . X . . . X . -14: . X . . . . . . . . . X . X X . . . X . . . X . -15: . . X . . . X X X X . . . . . X . . . . . . . . -16: . . . . . X . . . . X X . . . . X X X . . . . . -17: X . . . . . . . . . X X . . . . X X . . X . . . -18: . . . . . X . . . . . X . X X . X . X . . . . . -19: . X . . . . X . . X . . . . . . . . . X . . X X -20: X . . X X . . . . . X . . . . . . X . . X . . . -21: . . X X X . . . X . . . X . . . . . . . . X . . -22: . X . . X . . . X X . . X X X . . . . X . . X . -23: . . . . . . X . . X . . . . . . . . . X . . . X diff --git a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Demo/camd_demo2.c b/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Demo/camd_demo2.c deleted file mode 100644 index 8dd0b6205..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Demo/camd_demo2.c +++ /dev/null @@ -1,216 +0,0 @@ -/* ========================================================================= */ -/* === CAMD demo main program (jumbled matrix version) ===================== */ -/* ========================================================================= */ - -/* ------------------------------------------------------------------------- */ -/* CAMD, Copyright (c) Timothy A. Davis, Yanqing Chen, */ -/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ -/* email: DrTimothyAldenDavis@gmail.com */ -/* ------------------------------------------------------------------------- */ - -/* A simple C main program that illustrates the use of the ANSI C interface - * to CAMD. - * - * Identical to camd_demo.c, except that it operates on an input matrix that has - * unsorted columns and duplicate entries. - */ - -#include "camd.h" -#include -#include - -int main (void) -{ - /* The symmetric can_24 Harwell/Boeing matrix (jumbled, and not symmetric). - * Since CAMD operates on A+A', only A(i,j) or A(j,i) need to be specified, - * or both. The diagonal entries are optional (some are missing). - * There are many duplicate entries, which must be removed. */ - int n = 24, nz, - Ap [ ] = { 0, 9, 14, 20, 28, 33, 37, 44, 53, 58, 63, 63, 66, 69, 72, 75, - 78, 82, 86, 91, 97, 101, 112, 112, 116 }, - Ai [ ] = { - /* column 0: */ 0, 17, 18, 21, 5, 12, 5, 0, 13, - /* column 1: */ 14, 1, 8, 13, 17, - /* column 2: */ 2, 20, 11, 6, 11, 22, - /* column 3: */ 3, 3, 10, 7, 18, 18, 15, 19, - /* column 4: */ 7, 9, 15, 14, 16, - /* column 5: */ 5, 13, 6, 17, - /* column 6: */ 5, 0, 11, 6, 12, 6, 23, - /* column 7: */ 3, 4, 9, 7, 14, 16, 15, 17, 18, - /* column 8: */ 1, 9, 14, 14, 14, - /* column 9: */ 7, 13, 8, 1, 17, - /* column 10: */ - /* column 11: */ 2, 12, 23, - /* column 12: */ 5, 11, 12, - /* column 13: */ 0, 13, 17, - /* column 14: */ 1, 9, 14, - /* column 15: */ 3, 15, 16, - /* column 16: */ 16, 4, 4, 15, - /* column 17: */ 13, 17, 19, 17, - /* column 18: */ 15, 17, 19, 9, 10, - /* column 19: */ 17, 19, 20, 0, 6, 10, - /* column 20: */ 22, 10, 20, 21, - /* column 21: */ 6, 2, 10, 19, 20, 11, 21, 22, 22, 22, 22, - /* column 22: */ - /* column 23: */ 12, 11, 12, 23 } ; - - int P [24], Pinv [24], i, j, k, jnew, p, inew, result ; - double Control [CAMD_CONTROL], Info [CAMD_INFO] ; - char A [24][24] ; - int C [ ] = { 3, 0, 4, 0, 1, 1, 2, 2, 2, 2, 3, 4, 5, 5, 3, 4, 5, 2, - 8, 10, 4, 2, 2, 0 } ; - - printf ("CAMD demo, with a jumbled version of the 24-by-24\n") ; - printf ("Harwell/Boeing matrix, can_24:\n") ; - - /* get the default parameters, and print them */ - camd_defaults (Control) ; - camd_control (Control) ; - - /* print the input matrix */ - nz = Ap [n] ; - printf ("\nJumbled input matrix: %d-by-%d, with %d entries.\n" - " Note that for a symmetric matrix such as this one, only the\n" - " strictly lower or upper triangular parts would need to be\n" - " passed to CAMD, since CAMD computes the ordering of A+A'. The\n" - " diagonal entries are also not needed, since CAMD ignores them.\n" - " This version of the matrix has jumbled columns and duplicate\n" - " row indices.\n", n, n, nz) ; - for (j = 0 ; j < n ; j++) - { - printf ("\nColumn: %d, number of entries: %d, with row indices in" - " Ai [%d ... %d]:\n row indices:", - j, Ap [j+1] - Ap [j], Ap [j], Ap [j+1]-1) ; - for (p = Ap [j] ; p < Ap [j+1] ; p++) - { - i = Ai [p] ; - printf (" %d", i) ; - } - printf ("\n") ; - } - - /* print a character plot of the input matrix. This is only reasonable - * because the matrix is small. */ - printf ("\nPlot of (jumbled) input matrix pattern:\n") ; - for (j = 0 ; j < n ; j++) - { - for (i = 0 ; i < n ; i++) A [i][j] = '.' ; - for (p = Ap [j] ; p < Ap [j+1] ; p++) - { - i = Ai [p] ; - A [i][j] = 'X' ; - } - } - printf (" ") ; - for (j = 0 ; j < n ; j++) printf (" %1d", j % 10) ; - printf ("\n") ; - for (i = 0 ; i < n ; i++) - { - printf ("%2d: ", i) ; - for (j = 0 ; j < n ; j++) - { - printf (" %c", A [i][j]) ; - } - printf ("\n") ; - } - - /* print a character plot of the matrix A+A'. */ - printf ("\nPlot of symmetric matrix to be ordered by camd_order:\n") ; - for (j = 0 ; j < n ; j++) - { - for (i = 0 ; i < n ; i++) A [i][j] = '.' ; - } - for (j = 0 ; j < n ; j++) - { - A [j][j] = 'X' ; - for (p = Ap [j] ; p < Ap [j+1] ; p++) - { - i = Ai [p] ; - A [i][j] = 'X' ; - A [j][i] = 'X' ; - } - } - printf (" ") ; - for (j = 0 ; j < n ; j++) printf (" %1d", j % 10) ; - printf ("\n") ; - for (i = 0 ; i < n ; i++) - { - printf ("%2d: ", i) ; - for (j = 0 ; j < n ; j++) - { - printf (" %c", A [i][j]) ; - } - printf ("\n") ; - } - - /* order the matrix */ - result = camd_order (n, Ap, Ai, P, Control, Info, C) ; - printf ("return value from camd_order: %d (should be %d)\n", - result, CAMD_OK_BUT_JUMBLED) ; - - /* print the statistics */ - camd_info (Info) ; - - if (result != CAMD_OK_BUT_JUMBLED) - { - printf ("CAMD failed\n") ; - exit (1) ; - } - - /* print the permutation vector, P, and compute the inverse permutation */ - printf ("Permutation vector:\n") ; - for (k = 0 ; k < n ; k++) - { - /* row/column j is the kth row/column in the permuted matrix */ - j = P [k] ; - Pinv [j] = k ; - printf (" %2d", j) ; - } - printf ("\nPermuted constraints:\n") ; - for (k = 0 ; k < n ; k++) - { - /* row/column j is the kth row/column in the permuted matrix */ - printf (" %2d", C [P [k]]) ; - } - printf ("\n\n") ; - - printf ("Inverse permutation vector:\n") ; - for (j = 0 ; j < n ; j++) - { - k = Pinv [j] ; - printf (" %2d", k) ; - } - printf ("\n\n") ; - - /* print a character plot of the permuted matrix. */ - printf ("\nPlot of (symmetrized) permuted matrix pattern:\n") ; - for (j = 0 ; j < n ; j++) - { - for (i = 0 ; i < n ; i++) A [i][j] = '.' ; - } - for (jnew = 0 ; jnew < n ; jnew++) - { - j = P [jnew] ; - A [jnew][jnew] = 'X' ; - for (p = Ap [j] ; p < Ap [j+1] ; p++) - { - inew = Pinv [Ai [p]] ; - A [inew][jnew] = 'X' ; - A [jnew][inew] = 'X' ; - } - } - printf (" ") ; - for (j = 0 ; j < n ; j++) printf (" %1d", j % 10) ; - printf ("\n") ; - for (i = 0 ; i < n ; i++) - { - printf ("%2d: ", i) ; - for (j = 0 ; j < n ; j++) - { - printf (" %c", A [i][j]) ; - } - printf ("\n") ; - } - - return (0) ; -} diff --git a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Demo/camd_demo2.out b/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Demo/camd_demo2.out deleted file mode 100644 index 5f2b44e48..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Demo/camd_demo2.out +++ /dev/null @@ -1,210 +0,0 @@ -CAMD demo, with a jumbled version of the 24-by-24 -Harwell/Boeing matrix, can_24: - -camd version 2.4, May 4, 2016: approximate minimum degree ordering: - dense row parameter: 10 - (rows with more than max (10 * sqrt (n), 16) entries are - considered "dense", and placed last in output permutation) - aggressive absorption: yes - size of CAMD integer: 4 - - -Jumbled input matrix: 24-by-24, with 116 entries. - Note that for a symmetric matrix such as this one, only the - strictly lower or upper triangular parts would need to be - passed to CAMD, since CAMD computes the ordering of A+A'. The - diagonal entries are also not needed, since CAMD ignores them. - This version of the matrix has jumbled columns and duplicate - row indices. - -Column: 0, number of entries: 9, with row indices in Ai [0 ... 8]: - row indices: 0 17 18 21 5 12 5 0 13 - -Column: 1, number of entries: 5, with row indices in Ai [9 ... 13]: - row indices: 14 1 8 13 17 - -Column: 2, number of entries: 6, with row indices in Ai [14 ... 19]: - row indices: 2 20 11 6 11 22 - -Column: 3, number of entries: 8, with row indices in Ai [20 ... 27]: - row indices: 3 3 10 7 18 18 15 19 - -Column: 4, number of entries: 5, with row indices in Ai [28 ... 32]: - row indices: 7 9 15 14 16 - -Column: 5, number of entries: 4, with row indices in Ai [33 ... 36]: - row indices: 5 13 6 17 - -Column: 6, number of entries: 7, with row indices in Ai [37 ... 43]: - row indices: 5 0 11 6 12 6 23 - -Column: 7, number of entries: 9, with row indices in Ai [44 ... 52]: - row indices: 3 4 9 7 14 16 15 17 18 - -Column: 8, number of entries: 5, with row indices in Ai [53 ... 57]: - row indices: 1 9 14 14 14 - -Column: 9, number of entries: 5, with row indices in Ai [58 ... 62]: - row indices: 7 13 8 1 17 - -Column: 10, number of entries: 0, with row indices in Ai [63 ... 62]: - row indices: - -Column: 11, number of entries: 3, with row indices in Ai [63 ... 65]: - row indices: 2 12 23 - -Column: 12, number of entries: 3, with row indices in Ai [66 ... 68]: - row indices: 5 11 12 - -Column: 13, number of entries: 3, with row indices in Ai [69 ... 71]: - row indices: 0 13 17 - -Column: 14, number of entries: 3, with row indices in Ai [72 ... 74]: - row indices: 1 9 14 - -Column: 15, number of entries: 3, with row indices in Ai [75 ... 77]: - row indices: 3 15 16 - -Column: 16, number of entries: 4, with row indices in Ai [78 ... 81]: - row indices: 16 4 4 15 - -Column: 17, number of entries: 4, with row indices in Ai [82 ... 85]: - row indices: 13 17 19 17 - -Column: 18, number of entries: 5, with row indices in Ai [86 ... 90]: - row indices: 15 17 19 9 10 - -Column: 19, number of entries: 6, with row indices in Ai [91 ... 96]: - row indices: 17 19 20 0 6 10 - -Column: 20, number of entries: 4, with row indices in Ai [97 ... 100]: - row indices: 22 10 20 21 - -Column: 21, number of entries: 11, with row indices in Ai [101 ... 111]: - row indices: 6 2 10 19 20 11 21 22 22 22 22 - -Column: 22, number of entries: 0, with row indices in Ai [112 ... 111]: - row indices: - -Column: 23, number of entries: 4, with row indices in Ai [112 ... 115]: - row indices: 12 11 12 23 - -Plot of (jumbled) input matrix pattern: - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 - 0: X . . . . . X . . . . . . X . . . . . X . . . . - 1: . X . . . . . . X X . . . . X . . . . . . . . . - 2: . . X . . . . . . . . X . . . . . . . . . X . . - 3: . . . X . . . X . . . . . . . X . . . . . . . . - 4: . . . . . . . X . . . . . . . . X . . . . . . . - 5: X . . . . X X . . . . . X . . . . . . . . . . . - 6: . . X . . X X . . . . . . . . . . . . X . X . . - 7: . . . X X . . X . X . . . . . . . . . . . . . . - 8: . X . . . . . . . X . . . . . . . . . . . . . . - 9: . . . . X . . X X . . . . . X . . . X . . . . . -10: . . . X . . . . . . . . . . . . . . X X X X . . -11: . . X . . . X . . . . . X . . . . . . . . X . X -12: X . . . . . X . . . . X X . . . . . . . . . . X -13: X X . . . X . . . X . . . X . . . X . . . . . . -14: . X . . X . . X X . . . . . X . . . . . . . . . -15: . . . X X . . X . . . . . . . X X . X . . . . . -16: . . . . X . . X . . . . . . . X X . . . . . . . -17: X X . . . X . X . X . . . X . . . X X X . . . . -18: X . . X . . . X . . . . . . . . . . . . . . . . -19: . . . X . . . . . . . . . . . . . X X X . X . . -20: . . X . . . . . . . . . . . . . . . . X X X . . -21: X . . . . . . . . . . . . . . . . . . . X X . . -22: . . X . . . . . . . . . . . . . . . . . X X . . -23: . . . . . . X . . . . X . . . . . . . . . . . X - -Plot of symmetric matrix to be ordered by camd_order: - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 - 0: X . . . . X X . . . . . X X . . . X X X . X . . - 1: . X . . . . . . X X . . . X X . . X . . . . . . - 2: . . X . . . X . . . . X . . . . . . . . X X X . - 3: . . . X . . . X . . X . . . . X . . X X . . . . - 4: . . . . X . . X . X . . . . X X X . . . . . . . - 5: X . . . . X X . . . . . X X . . . X . . . . . . - 6: X . X . . X X . . . . X X . . . . . . X . X . X - 7: . . . X X . . X . X . . . . X X X X X . . . . . - 8: . X . . . . . . X X . . . . X . . . . . . . . . - 9: . X . . X . . X X X . . . X X . . X X . . . . . -10: . . . X . . . . . . X . . . . . . . X X X X . . -11: . . X . . . X . . . . X X . . . . . . . . X . X -12: X . . . . X X . . . . X X . . . . . . . . . . X -13: X X . . . X . . . X . . . X . . . X . . . . . . -14: . X . . X . . X X X . . . . X . . . . . . . . . -15: . . . X X . . X . . . . . . . X X . X . . . . . -16: . . . . X . . X . . . . . . . X X . . . . . . . -17: X X . . . X . X . X . . . X . . . X X X . . . . -18: X . . X . . . X . X X . . . . X . X X X . . . . -19: X . . X . . X . . . X . . . . . . X X X X X . . -20: . . X . . . . . . . X . . . . . . . . X X X X . -21: X . X . . . X . . . X X . . . . . . . X X X X . -22: . . X . . . . . . . . . . . . . . . . . X X X . -23: . . . . . . X . . . . X X . . . . . . . . . . X -return value from camd_order: 1 (should be 1) - -CAMD version 2.4.6, May 4, 2016, results: - status: OK, but jumbled - n, dimension of A: 24 - nz, number of nonzeros in A: 102 - symmetry of A: 0.4000 - number of nonzeros on diagonal: 17 - nonzeros in pattern of A+A' (excl. diagonal): 136 - # dense rows/columns of A+A': 0 - memory used, in bytes: 2208 - # of memory compactions: 0 - - The following approximate statistics are for a subsequent - factorization of A(P,P) + A(P,P)'. They are slight upper - bounds if there are no dense rows/columns in A+A', and become - looser if dense rows/columns exist. - - nonzeros in L (excluding diagonal): 140 - nonzeros in L (including diagonal): 164 - # divide operations for LDL' or LU: 140 - # multiply-subtract operations for LDL': 593 - # multiply-subtract operations for LU: 1046 - max nz. in any column of L (incl. diagonal): 13 - - chol flop count for real A, sqrt counted as 1 flop: 1350 - LDL' flop count for real A: 1326 - LDL' flop count for complex A: 6004 - LU flop count for real A (with no pivoting): 2232 - LU flop count for complex A (with no pivoting): 9628 - -Permutation vector: - 23 3 1 5 4 8 9 7 17 22 21 6 10 14 0 2 11 15 20 13 16 12 18 19 -Permuted constraints: - 0 0 0 1 1 2 2 2 2 2 2 2 3 3 3 4 4 4 4 5 5 5 8 10 - -Inverse permutation vector: - 14 2 15 1 4 3 11 7 5 6 12 16 21 19 13 17 20 8 22 23 18 10 9 0 - - -Plot of (symmetrized) permuted matrix pattern: - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 - 0: X . . . . . . . . . . X . . . . X . . . . X . . - 1: . X . . . . . X . . . . X . . . . X . . . . X X - 2: . . X . . X X . X . . . . X . . . . . X . . . . - 3: . . . X . . . . X . . X . . X . . . . X . X . . - 4: . . . . X . X X . . . . . X . . . X . . X . . . - 5: . . X . . X X . . . . . . X . . . . . . . . . . - 6: . . X . X X X X X . . . . X . . . . . X . . X . - 7: . X . . X . X X X . . . . X . . . X . . X . X . - 8: . . X X . . X X X . . . . . X . . . . X . . X X - 9: . . . . . . . . . X X . . . . X . . X . . . . . -10: . . . . . . . . . X X X X . X X X . X . . . . X -11: X . . X . . . . . . X X . . X X X . . . . X . X -12: . X . . . . . . . . X . X . . . . . X . . . X X -13: . . X . X X X X . . . . . X . . . . . . . . . . -14: . . . X . . . . X . X X . . X . . . . X . X X X -15: . . . . . . . . . X X X . . . X X . X . . . . . -16: X . . . . . . . . . X X . . . X X . . . . X . . -17: . X . . X . . X . . . . . . . . . X . . X . X . -18: . . . . . . . . . X X . X . . X . . X . . . . X -19: . . X X . . X . X . . . . . X . . . . X . . . . -20: . . . . X . . X . . . . . . . . . X . . X . . . -21: X . . X . . . . . . . X . . X . X . . . . X . . -22: . X . . . . X X X . . . X . X . . X . . . . X X -23: . X . . . . . . X . X X X . X . . . X . . . X X diff --git a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Demo/camd_l_demo.c b/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Demo/camd_l_demo.c deleted file mode 100644 index 1c127d05e..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Demo/camd_l_demo.c +++ /dev/null @@ -1,173 +0,0 @@ -/* ========================================================================= */ -/* === CAMD demo main program (long integer version) ======================= */ -/* ========================================================================= */ - -/* ------------------------------------------------------------------------- */ -/* CAMD, Copyright (c) Timothy A. Davis, Yanqing Chen, */ -/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ -/* email: DrTimothyAldenDavis@gmail.com */ -/* ------------------------------------------------------------------------- */ - -/* A simple C main program that illustrates the use of the ANSI C interface - * to CAMD. - */ - -#include "camd.h" -#include -#include -#define Long SuiteSparse_long - -int main (void) -{ - /* The symmetric can_24 Harwell/Boeing matrix, including upper and lower - * triangular parts, and the diagonal entries. Note that this matrix is - * 0-based, with row and column indices in the range 0 to n-1. */ - Long n = 24, nz, - Ap [ ] = { 0, 9, 15, 21, 27, 33, 39, 48, 57, 61, 70, 76, 82, 88, 94, 100, - 106, 110, 119, 128, 137, 143, 152, 156, 160 }, - Ai [ ] = { - /* column 0: */ 0, 5, 6, 12, 13, 17, 18, 19, 21, - /* column 1: */ 1, 8, 9, 13, 14, 17, - /* column 2: */ 2, 6, 11, 20, 21, 22, - /* column 3: */ 3, 7, 10, 15, 18, 19, - /* column 4: */ 4, 7, 9, 14, 15, 16, - /* column 5: */ 0, 5, 6, 12, 13, 17, - /* column 6: */ 0, 2, 5, 6, 11, 12, 19, 21, 23, - /* column 7: */ 3, 4, 7, 9, 14, 15, 16, 17, 18, - /* column 8: */ 1, 8, 9, 14, - /* column 9: */ 1, 4, 7, 8, 9, 13, 14, 17, 18, - /* column 10: */ 3, 10, 18, 19, 20, 21, - /* column 11: */ 2, 6, 11, 12, 21, 23, - /* column 12: */ 0, 5, 6, 11, 12, 23, - /* column 13: */ 0, 1, 5, 9, 13, 17, - /* column 14: */ 1, 4, 7, 8, 9, 14, - /* column 15: */ 3, 4, 7, 15, 16, 18, - /* column 16: */ 4, 7, 15, 16, - /* column 17: */ 0, 1, 5, 7, 9, 13, 17, 18, 19, - /* column 18: */ 0, 3, 7, 9, 10, 15, 17, 18, 19, - /* column 19: */ 0, 3, 6, 10, 17, 18, 19, 20, 21, - /* column 20: */ 2, 10, 19, 20, 21, 22, - /* column 21: */ 0, 2, 6, 10, 11, 19, 20, 21, 22, - /* column 22: */ 2, 20, 21, 22, - /* column 23: */ 6, 11, 12, 23 } ; - - Long P [24], Pinv [24], i, j, k, jnew, p, inew, result ; - double Control [CAMD_CONTROL], Info [CAMD_INFO] ; - char A [24][24] ; - Long C [ ] = { 0, 0, 4, 0, 1, 0, 2, 2, 1, 1, 3, 4, 5, 5, 3, 4, - 5, 2, 5, 3, 4, 2, 1, 0 }; - - printf ("CAMD version %d.%d, date: %s\n", CAMD_MAIN_VERSION, - CAMD_SUB_VERSION, CAMD_DATE) ; - printf ("CAMD demo, with the 24-by-24 Harwell/Boeing matrix, can_24:\n") ; - - /* get the default parameters, and print them */ - camd_l_defaults (Control) ; - camd_l_control (Control) ; - - /* print the input matrix */ - nz = Ap [n] ; - printf ("\nInput matrix: %ld-by-%ld, with %ld entries.\n" - " Note that for a symmetric matrix such as this one, only the\n" - " strictly lower or upper triangular parts would need to be\n" - " passed to CAMD, since CAMD computes the ordering of A+A'. The\n" - " diagonal entries are also not needed, since CAMD ignores them.\n" - , n, n, nz) ; - for (j = 0 ; j < n ; j++) - { - printf ("\nColumn: %ld, number of entries: %ld, with row indices in" - " Ai [%ld ... %ld]:\n row indices:", - j, Ap [j+1] - Ap [j], Ap [j], Ap [j+1]-1) ; - for (p = Ap [j] ; p < Ap [j+1] ; p++) - { - i = Ai [p] ; - printf (" %ld", i) ; - } - printf ("\n") ; - } - - /* print a character plot of the input matrix. This is only reasonable - * because the matrix is small. */ - printf ("\nPlot of input matrix pattern:\n") ; - for (j = 0 ; j < n ; j++) - { - for (i = 0 ; i < n ; i++) A [i][j] = '.' ; - for (p = Ap [j] ; p < Ap [j+1] ; p++) - { - i = Ai [p] ; - A [i][j] = 'X' ; - } - } - printf (" ") ; - for (j = 0 ; j < n ; j++) printf (" %1ld", j % 10) ; - printf ("\n") ; - for (i = 0 ; i < n ; i++) - { - printf ("%2ld: ", i) ; - for (j = 0 ; j < n ; j++) - { - printf (" %c", A [i][j]) ; - } - printf ("\n") ; - } - - /* order the matrix */ - result = camd_l_order (n, Ap, Ai, P, Control, Info, C) ; - printf ("return value from camd_l_order: %ld (should be %d)\n", - result, CAMD_OK) ; - - /* print the statistics */ - camd_l_info (Info) ; - - if (result != CAMD_OK) - { - printf ("CAMD failed\n") ; - exit (1) ; - } - - /* print the permutation vector, P, and compute the inverse permutation */ - printf ("Permutation vector:\n") ; - for (k = 0 ; k < n ; k++) - { - /* row/column j is the kth row/column in the permuted matrix */ - j = P [k] ; - Pinv [j] = k ; - printf (" %2ld", j) ; - } - printf ("\n\n") ; - - printf ("Inverse permutation vector:\n") ; - for (j = 0 ; j < n ; j++) - { - k = Pinv [j] ; - printf (" %2ld", k) ; - } - printf ("\n\n") ; - - /* print a character plot of the permuted matrix. */ - printf ("\nPlot of permuted matrix pattern:\n") ; - for (jnew = 0 ; jnew < n ; jnew++) - { - j = P [jnew] ; - for (inew = 0 ; inew < n ; inew++) A [inew][jnew] = '.' ; - for (p = Ap [j] ; p < Ap [j+1] ; p++) - { - inew = Pinv [Ai [p]] ; - A [inew][jnew] = 'X' ; - } - } - printf (" ") ; - for (j = 0 ; j < n ; j++) printf (" %1ld", j % 10) ; - printf ("\n") ; - for (i = 0 ; i < n ; i++) - { - printf ("%2ld: ", i) ; - for (j = 0 ; j < n ; j++) - { - printf (" %c", A [i][j]) ; - } - printf ("\n") ; - } - - return (0) ; -} diff --git a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Demo/camd_l_demo.out b/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Demo/camd_l_demo.out deleted file mode 100644 index 0e875dafc..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Demo/camd_l_demo.out +++ /dev/null @@ -1,179 +0,0 @@ -CAMD version 2.4, date: May 4, 2016 -CAMD demo, with the 24-by-24 Harwell/Boeing matrix, can_24: - -camd version 2.4, May 4, 2016: approximate minimum degree ordering: - dense row parameter: 10 - (rows with more than max (10 * sqrt (n), 16) entries are - considered "dense", and placed last in output permutation) - aggressive absorption: yes - size of CAMD integer: 8 - - -Input matrix: 24-by-24, with 160 entries. - Note that for a symmetric matrix such as this one, only the - strictly lower or upper triangular parts would need to be - passed to CAMD, since CAMD computes the ordering of A+A'. The - diagonal entries are also not needed, since CAMD ignores them. - -Column: 0, number of entries: 9, with row indices in Ai [0 ... 8]: - row indices: 0 5 6 12 13 17 18 19 21 - -Column: 1, number of entries: 6, with row indices in Ai [9 ... 14]: - row indices: 1 8 9 13 14 17 - -Column: 2, number of entries: 6, with row indices in Ai [15 ... 20]: - row indices: 2 6 11 20 21 22 - -Column: 3, number of entries: 6, with row indices in Ai [21 ... 26]: - row indices: 3 7 10 15 18 19 - -Column: 4, number of entries: 6, with row indices in Ai [27 ... 32]: - row indices: 4 7 9 14 15 16 - -Column: 5, number of entries: 6, with row indices in Ai [33 ... 38]: - row indices: 0 5 6 12 13 17 - -Column: 6, number of entries: 9, with row indices in Ai [39 ... 47]: - row indices: 0 2 5 6 11 12 19 21 23 - -Column: 7, number of entries: 9, with row indices in Ai [48 ... 56]: - row indices: 3 4 7 9 14 15 16 17 18 - -Column: 8, number of entries: 4, with row indices in Ai [57 ... 60]: - row indices: 1 8 9 14 - -Column: 9, number of entries: 9, with row indices in Ai [61 ... 69]: - row indices: 1 4 7 8 9 13 14 17 18 - -Column: 10, number of entries: 6, with row indices in Ai [70 ... 75]: - row indices: 3 10 18 19 20 21 - -Column: 11, number of entries: 6, with row indices in Ai [76 ... 81]: - row indices: 2 6 11 12 21 23 - -Column: 12, number of entries: 6, with row indices in Ai [82 ... 87]: - row indices: 0 5 6 11 12 23 - -Column: 13, number of entries: 6, with row indices in Ai [88 ... 93]: - row indices: 0 1 5 9 13 17 - -Column: 14, number of entries: 6, with row indices in Ai [94 ... 99]: - row indices: 1 4 7 8 9 14 - -Column: 15, number of entries: 6, with row indices in Ai [100 ... 105]: - row indices: 3 4 7 15 16 18 - -Column: 16, number of entries: 4, with row indices in Ai [106 ... 109]: - row indices: 4 7 15 16 - -Column: 17, number of entries: 9, with row indices in Ai [110 ... 118]: - row indices: 0 1 5 7 9 13 17 18 19 - -Column: 18, number of entries: 9, with row indices in Ai [119 ... 127]: - row indices: 0 3 7 9 10 15 17 18 19 - -Column: 19, number of entries: 9, with row indices in Ai [128 ... 136]: - row indices: 0 3 6 10 17 18 19 20 21 - -Column: 20, number of entries: 6, with row indices in Ai [137 ... 142]: - row indices: 2 10 19 20 21 22 - -Column: 21, number of entries: 9, with row indices in Ai [143 ... 151]: - row indices: 0 2 6 10 11 19 20 21 22 - -Column: 22, number of entries: 4, with row indices in Ai [152 ... 155]: - row indices: 2 20 21 22 - -Column: 23, number of entries: 4, with row indices in Ai [156 ... 159]: - row indices: 6 11 12 23 - -Plot of input matrix pattern: - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 - 0: X . . . . X X . . . . . X X . . . X X X . X . . - 1: . X . . . . . . X X . . . X X . . X . . . . . . - 2: . . X . . . X . . . . X . . . . . . . . X X X . - 3: . . . X . . . X . . X . . . . X . . X X . . . . - 4: . . . . X . . X . X . . . . X X X . . . . . . . - 5: X . . . . X X . . . . . X X . . . X . . . . . . - 6: X . X . . X X . . . . X X . . . . . . X . X . X - 7: . . . X X . . X . X . . . . X X X X X . . . . . - 8: . X . . . . . . X X . . . . X . . . . . . . . . - 9: . X . . X . . X X X . . . X X . . X X . . . . . -10: . . . X . . . . . . X . . . . . . . X X X X . . -11: . . X . . . X . . . . X X . . . . . . . . X . X -12: X . . . . X X . . . . X X . . . . . . . . . . X -13: X X . . . X . . . X . . . X . . . X . . . . . . -14: . X . . X . . X X X . . . . X . . . . . . . . . -15: . . . X X . . X . . . . . . . X X . X . . . . . -16: . . . . X . . X . . . . . . . X X . . . . . . . -17: X X . . . X . X . X . . . X . . . X X X . . . . -18: X . . X . . . X . X X . . . . X . X X X . . . . -19: X . . X . . X . . . X . . . . . . X X X X X . . -20: . . X . . . . . . . X . . . . . . . . X X X X . -21: X . X . . . X . . . X X . . . . . . . X X X X . -22: . . X . . . . . . . . . . . . . . . . . X X X . -23: . . . . . . X . . . . X X . . . . . . . . . . X -return value from camd_l_order: 0 (should be 0) - -CAMD version 2.4.6, May 4, 2016, results: - status: OK - n, dimension of A: 24 - nz, number of nonzeros in A: 160 - symmetry of A: 1.0000 - number of nonzeros on diagonal: 24 - nonzeros in pattern of A+A' (excl. diagonal): 136 - # dense rows/columns of A+A': 0 - memory used, in bytes: 3288 - # of memory compactions: 0 - - The following approximate statistics are for a subsequent - factorization of A(P,P) + A(P,P)'. They are slight upper - bounds if there are no dense rows/columns in A+A', and become - looser if dense rows/columns exist. - - nonzeros in L (excluding diagonal): 135 - nonzeros in L (including diagonal): 159 - # divide operations for LDL' or LU: 135 - # multiply-subtract operations for LDL': 541 - # multiply-subtract operations for LU: 947 - max nz. in any column of L (incl. diagonal): 12 - - chol flop count for real A, sqrt counted as 1 flop: 1241 - LDL' flop count for real A: 1217 - LDL' flop count for complex A: 5543 - LU flop count for real A (with no pivoting): 2029 - LU flop count for complex A (with no pivoting): 8791 - -Permutation vector: - 23 3 1 5 0 22 4 8 9 7 6 21 17 19 10 14 2 11 20 15 12 13 18 16 - -Inverse permutation vector: - 4 2 16 1 6 3 10 9 7 8 14 17 20 21 15 19 23 12 22 13 18 11 5 0 - - -Plot of permuted matrix pattern: - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 - 0: X . . . . . . . . . X . . . . . . X . . X . . . - 1: . X . . . . . . . X . . . X X . . . . X . . X . - 2: . . X . . . . X X . . . X . . X . . . . . X . . - 3: . . . X X . . . . . X . X . . . . . . . X X . . - 4: . . . X X . . . . . X X X X . . . . . . X X X . - 5: . . . . . X . . . . . X . . . . X . X . . . . . - 6: . . . . . . X . X X . . . . . X . . . X . . . X - 7: . . X . . . . X X . . . . . . X . . . . . . . . - 8: . . X . . . X X X X . . X . . X . . . . . X X . - 9: . X . . . . X . X X . . X . . X . . . X . . X X -10: X . . X X . . . . . X X . X . . X X . . X . . . -11: . . . . X X . . . . X X . X X . X X X . . . . . -12: . . X X X . . . X X . . X X . . . . . . . X X . -13: . X . . X . . . . . X X X X X . . . X . . . X . -14: . X . . . . . . . . . X . X X . . . X . . . X . -15: . . X . . . X X X X . . . . . X . . . . . . . . -16: . . . . . X . . . . X X . . . . X X X . . . . . -17: X . . . . . . . . . X X . . . . X X . . X . . . -18: . . . . . X . . . . . X . X X . X . X . . . . . -19: . X . . . . X . . X . . . . . . . . . X . . X X -20: X . . X X . . . . . X . . . . . . X . . X . . . -21: . . X X X . . . X . . . X . . . . . . . . X . . -22: . X . . X . . . X X . . X X X . . . . X . . X . -23: . . . . . . X . . X . . . . . . . . . X . . . X diff --git a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Demo/camd_simple.c b/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Demo/camd_simple.c deleted file mode 100644 index 87dbdd5b3..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Demo/camd_simple.c +++ /dev/null @@ -1,23 +0,0 @@ -/* ------------------------------------------------------------------------- */ -/* CAMD, Copyright (c) Timothy A. Davis, Yanqing Chen, */ -/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ -/* email: DrTimothyAldenDavis@gmail.com */ -/* ------------------------------------------------------------------------- */ - -#include -#include "camd.h" - -int n = 5 ; -int Ap [ ] = { 0, 2, 6, 10, 12, 14} ; -int Ai [ ] = { 0,1, 0,1,2,4, 1,2,3,4, 2,3, 1,4 } ; -int C [ ] = { 2, 0, 0, 0, 1 } ; -int P [5] ; - -int main (void) -{ - int k ; - (void) camd_order (n, Ap, Ai, P, (double *) NULL, (double *) NULL, C) ; - for (k = 0 ; k < n ; k++) printf ("P [%d] = %d\n", k, P [k]) ; - return (0) ; -} - diff --git a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Demo/camd_simple.out b/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Demo/camd_simple.out deleted file mode 100644 index c94ebcd9c..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Demo/camd_simple.out +++ /dev/null @@ -1,5 +0,0 @@ -P [0] = 3 -P [1] = 2 -P [2] = 1 -P [3] = 4 -P [4] = 0 diff --git a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Doc/CAMD_UserGuide.bib b/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Doc/CAMD_UserGuide.bib deleted file mode 100644 index 03fbfa8f7..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Doc/CAMD_UserGuide.bib +++ /dev/null @@ -1,98 +0,0 @@ -@string{SIREV = "{SIAM} Review"} -@string{SIMAX = "{SIAM} J. Matrix Anal. Applic."} -@string{SIAMJSC = "{SIAM} J. Sci. Comput."} -@string{TOMS = "{ACM} Trans. Math. Softw."} - -@article{schu:01, - author = {J. Schulze}, - title = {Towards a tighter coupling of bottom-up and top-down sparse matrix ordering methods}, - journal = {BIT}, - volume = {41}, - number = {4}, - pages = "800--841", - year = {2001} - } - -@article{GeorgeLiu89, - author={George, A. and Liu, J. W. H.}, - year={1989}, - title={The Evolution of the Minimum Degree Ordering Algorithm}, - journal=SIREV, - volume={31}, - number={1}, - pages={1--19}} - -@article{AmestoyDavisDuff96, - author={Amestoy, P. R. and Davis, T. A. and Duff, I. S.}, - title={An approximate minimum degree ordering algorithm}, - journal=SIMAX, - year={1996} - ,volume={17} - ,number={4} - ,pages={886-905} - } - -@article{AmestoyDavisDuff04, - author={Amestoy, P. R. and Davis, T. A. and Duff, I. S.}, - title={Algorithm 837: An approximate minimum degree ordering algorithm}, - journal=TOMS, - year={2004} - ,volume={30} - ,number={3} - ,pages={381-388} - } - -@misc{hsl:2002, - author = {HSL}, - title = "{HSL} 2002: {A} collection of {F}ortran codes for large - scale scientific computation", - note = {{\tt www.cse.clrc.ac.uk/nag/hsl}}, - year = 2002} - - -@article{RothbergEisenstat98, - author={Rothberg, E. and Eisenstat, S. C.}, - title={Node selection strategies for bottom-up sparse matrix orderings}, - journal=SIMAX, - year={1998} - ,volume={19} - ,number={3} - ,pages={682-695} - } - -@article{KarypisKumar98e, - author={Karypis, G. and Kumar, V.}, - title={A fast and high quality multilevel scheme for partitioning irregular graphs}, - journal=SIAMJSC, - year={1998} - ,volume={20} - ,pages={359-392} - } - -@article{Chaco, - author={B. Hendrickson and E. Rothberg}, - title={Improving the runtime and quality of nested dissection ordering}, - journal=SIAMJSC, - year={1999} - ,volume={20} - ,pages={468--489} - } - -@article{PellegriniRomanAmestoy00, - author={Pellegrini, F. and Roman, J. and Amestoy, P.}, - title={Hybridizing nested dissection and halo approximate minimum degree for efficient sparse matrix ordering}, - journal={Concurrency: Practice and Experience}, - year={2000} - ,volume={12} - ,pages={68-84} - } - -@article{DavisGilbertLarimoreNg04, - author={Davis, T. A. and Gilbert, J. R. and Larimore, S. I. and Ng, E. G.}, - title={A column approximate minimum degree ordering algorithm}, - journal=TOMS, - year={2004} - ,volume={30} - ,pages={353-376} - } - diff --git a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Doc/CAMD_UserGuide.pdf b/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Doc/CAMD_UserGuide.pdf deleted file mode 100644 index 11958b9a2..000000000 Binary files a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Doc/CAMD_UserGuide.pdf and /dev/null differ diff --git a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Doc/CAMD_UserGuide.tex b/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Doc/CAMD_UserGuide.tex deleted file mode 100644 index 00396ebe6..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Doc/CAMD_UserGuide.tex +++ /dev/null @@ -1,610 +0,0 @@ -\documentclass[11pt]{article} - -\newcommand{\m}[1]{{\bf{#1}}} % for matrices and vectors -\newcommand{\tr}{^{\sf T}} % transpose - -\topmargin 0in -\textheight 9in -\oddsidemargin 0pt -\evensidemargin 0pt -\textwidth 6.5in - -%------------------------------------------------------------------------------ -\begin{document} -%------------------------------------------------------------------------------ - -\title{CAMD User Guide} -\author{Patrick R. Amestoy\thanks{ENSEEIHT-IRIT, -2 rue Camichel 31017 Toulouse, France. -email: amestoy@enseeiht.fr. http://www.enseeiht.fr/$\sim$amestoy.} -\and Yanqing (Morris) Chen -\and Timothy A. Davis\thanks{ -email: DrTimothyAldenDavis@gmail.com, -http://www.suitesparse.com. -This work was supported by the National -Science Foundation, under grants ASC-9111263, DMS-9223088, and CCR-0203270. -Portions of the work were done while on sabbatical at Stanford University -and Lawrence Berkeley National Laboratory (with funding from Stanford -University and the SciDAC program). Ordering constraints added with -support from Sandia National Laboratory (Dept. of Energy). -} -\and Iain S. Duff\thanks{Rutherford Appleton Laboratory, Chilton, Didcot, -Oxon OX11 0QX, England. email: i.s.duff@rl.ac.uk. -http://www.numerical.rl.ac.uk/people/isd/isd.html. -This work was supported by the EPSRC under grant GR/R46441. -}} - -\date{VERSION 2.4.6, May 4, 2016} -\maketitle - -%------------------------------------------------------------------------------ -\begin{abstract} -CAMD is a set of ANSI C routines that implements the approximate minimum degree -ordering algorithm to permute sparse matrices prior to -numerical factorization. Ordering constraints can be optionally provided. -A MATLAB interface is included. -\end{abstract} -%------------------------------------------------------------------------------ - -CAMD Copyright\copyright 2013 by Timothy A. Davis, -Yanqing (Morris) Chen, -Patrick R. Amestoy, and Iain S. Duff. All Rights Reserved. -CAMD is available under alternate licences; contact T. Davis for details. - -{\bf CAMD License:} Refer to CAMD/Doc/License.txt for the license. - -{\bf Availability:} - http://www.suitesparse.com. - -{\bf Acknowledgments:} - - This work was supported by the National Science Foundation, under - grants ASC-9111263 and DMS-9223088 and CCR-0203270, and by Sandia - National Labs (a grant from DOE). - The conversion to C, the addition of the elimination tree - post-ordering, and the handling of dense rows and columns - were done while Davis was on sabbatical at - Stanford University and Lawrence Berkeley National Laboratory. - The ordering constraints were added by Chen and Davis. - -%------------------------------------------------------------------------------ -\newpage -\section{Overview} -%------------------------------------------------------------------------------ - -CAMD is a set of routines for preordering a sparse matrix prior to -numerical factorization. It uses an approximate minimum degree ordering -algorithm \cite{AmestoyDavisDuff96,AmestoyDavisDuff04} -to find a permutation matrix $\m{P}$ -so that the Cholesky factorization $\m{PAP}\tr=\m{LL}\tr$ has fewer -(often much fewer) nonzero entries than the Cholesky factorization of $\m{A}$. -The algorithm is typically much faster than other ordering methods -and minimum degree ordering -algorithms that compute an exact degree \cite{GeorgeLiu89}. -Some methods, such as approximate deficiency -\cite{RothbergEisenstat98} and graph-partitioning based methods -\cite{Chaco,KarypisKumar98e,PellegriniRomanAmestoy00,schu:01} -can produce better orderings, depending on the matrix. - -The algorithm starts with an undirected graph representation of a -symmetric sparse matrix $\m{A}$. Node $i$ in the graph corresponds to row -and column $i$ of the matrix, and there is an edge $(i,j)$ in the graph if -$a_{ij}$ is nonzero. -The degree of a node is initialized to the number of off-diagonal nonzeros -in row $i$, which is the size of the set of nodes -adjacent to $i$ in the graph. - -The selection of a pivot $a_{ii}$ from the diagonal of $\m{A}$ and the first -step of Gaussian elimination corresponds to one step of graph elimination. -Numerical fill-in causes new nonzero entries in the matrix -(fill-in refers to -nonzeros in $\m{L}$ that are not in $\m{A}$). -Node $i$ is eliminated and edges are added to its neighbors -so that they form a clique (or {\em element}). To reduce fill-in, -node $i$ is selected as the node of least degree in the graph. -This process repeats until the graph is eliminated. - -The clique is represented implicitly. Rather than listing all the -new edges in the graph, a single list of nodes is kept which represents -the clique. This list corresponds to the nonzero pattern of the first -column of $\m{L}$. As the elimination proceeds, some of these cliques -become subsets of subsequent cliques, and are removed. This graph -can be stored in place, that is -using the same amount of memory as the original graph. - -The most costly part of the minimum degree algorithm is the recomputation -of the degrees of nodes adjacent to the current pivot element. -Rather than keep track of the exact degree, the approximate minimum degree -algorithm finds an upper bound on the degree that is easier to compute. -For nodes of least degree, this bound tends to be tight. Using the -approximate degree instead of the exact degree leads to a substantial savings -in run time, particularly for very irregularly structured matrices. -It has no effect on the quality of the ordering. - -The elimination phase is followed by an -elimination tree post-ordering. This has no effect on fill-in, but -reorganizes the ordering so that the subsequent numerical factorization is -more efficient. It also includes a pre-processing phase in which nodes of -very high degree are removed (without causing fill-in), and placed last in the -permutation $\m{P}$ (subject to the constraints). -This reduces the run time substantially if the matrix -has a few rows with many nonzero entries, and has little effect on the quality -of the ordering. -CAMD operates on the -symmetric nonzero pattern of $\m{A}+\m{A}\tr$, so it can be given -an unsymmetric matrix, or either the lower or upper triangular part of -a symmetric matrix. - -CAMD has the ability to order the matrix with constraints. Each -node $i$ in the graph (row/column $i$ in the matrix) has a constraint, -{\tt C[i]}, which is in the range {\tt 0} to {\tt n-1}. All nodes with -{\tt C[i] = 0} are -ordered first, followed by all nodes with constraint {\tt 1}, and so on. -That is, {\tt C[P[k]]} is monotonically non-decreasing as {\tt k} varies from -{\tt 0} to {\tt n-1}. If {\tt C} is NULL, no -constraints are used (the ordering will be similar to AMD's ordering, -except that the postordering is different). -The optional {\tt C} parameter is also provided in the MATLAB interface, -({\tt p = camd (A,Control,C)}). - -For a discussion of the long history of the minimum degree algorithm, -see \cite{GeorgeLiu89}. - -%------------------------------------------------------------------------------ -\section{Availability} -%------------------------------------------------------------------------------ - -CAMD is available at http://www.suitesparse.com. -The Fortran version is available as the routine {\tt MC47} in HSL -(formerly the Harwell Subroutine Library) \cite{hsl:2002}. {\tt MC47} does -not include ordering constraints. - -%------------------------------------------------------------------------------ -\section{Using CAMD in MATLAB} -%------------------------------------------------------------------------------ - -To use CAMD in MATLAB, you must first compile the CAMD mexFunction. -Just type {\tt make} in the Unix system shell, while in the {\tt CAMD} -directory. You can also type {\tt camd\_make} in MATLAB, while in the -{\tt CAMD/MATLAB} directory. Place the {\tt CAMD/MATLAB} directory in your -MATLAB path. This can be done on any system with MATLAB, including Windows. -See Section~\ref{Install} for more details on how to install CAMD. - -The MATLAB statement {\tt p=camd(A)} finds a permutation vector {\tt p} such -that the Cholesky factorization {\tt chol(A(p,p))} is typically sparser than -{\tt chol(A)}. -If {\tt A} is unsymmetric, {\tt camd(A)} is identical to {\tt camd(A+A')} -(ignoring numerical cancellation). -If {\tt A} is not symmetric positive definite, -but has substantial diagonal entries and a mostly symmetric nonzero pattern, -then this ordering is also suitable for LU factorization. A partial pivoting -threshold may be required to prevent pivots from being selected off the -diagonal, such as the statement {\tt [L,U,P] = lu (A (p,p), 0.1)}. -Type {\tt help lu} for more details. -The statement {\tt [L,U,P,Q] = lu (A (p,p))} in MATLAB 6.5 is -not suitable, however, because it uses UMFPACK Version 4.0 and thus -does not attempt to select pivots from the diagonal. -UMFPACK Version 4.1 in MATLAB 7.0 and later -uses several strategies, including a symmetric pivoting strategy, and -will give you better results if you want to factorize an unsymmetric matrix -of this type. Refer to the UMFPACK User Guide for more details, at -http://www.suitesparse.com. - -The CAMD mexFunction is much faster than the built-in MATLAB symmetric minimum -degree ordering methods, SYMAMD and SYMMMD. Its ordering quality is -essentially identical to AMD, comparable to SYMAMD, and better than SYMMMD -\cite{DavisGilbertLarimoreNg04}. - -An optional input argument can be used to modify the control parameters for -CAMD (aggressive absorption, dense row/column handling, and printing of -statistics). An optional output -argument provides statistics on the ordering, including an analysis of the -fill-in and the floating-point operation count for a subsequent factorization. -For more details (once CAMD is installed), -type {\tt help camd} in the MATLAB command window. - -%------------------------------------------------------------------------------ -\section{Using CAMD in a C program} -\label{Cversion} -%------------------------------------------------------------------------------ - -The C-callable CAMD library consists of seven user-callable routines and one -include file. There are two versions of each of the routines, with -{\tt int} and {\tt long} integers. -The routines with prefix -{\tt camd\_l\_} use {\tt long} integer arguments; the others use -{\tt int} integer arguments. If you compile CAMD in the standard -ILP32 mode (32-bit {\tt int}'s, {\tt long}'s, and pointers) then the versions -are essentially identical. You will be able to solve problems using up to 2GB -of memory. If you compile CAMD in the standard LP64 mode, the size of an -{\tt int} remains 32-bits, but the size of a {\tt long} and a pointer both get -promoted to 64-bits. - -The following routines are fully described in Section~\ref{Primary}: - -\begin{itemize} -\item {\tt camd\_order} -({\tt long} version: {\tt camd\_l\_order}) - {\footnotesize - \begin{verbatim} - #include "camd.h" - int n, Ap [n+1], Ai [nz], P [n], C [n] ; - double Control [CAMD_CONTROL], Info [CAMD_INFO] ; - int result = camd_order (n, Ap, Ai, P, Control, Info, C) ; - \end{verbatim} - } - Computes the approximate minimum degree ordering of an $n$-by-$n$ matrix - $\m{A}$. Returns a permutation vector {\tt P} of size {\tt n}, where - {\tt P[k] = i} if row and column {\tt i} are the {\tt k}th row and - column in the permuted matrix. - This routine allocates its own memory of size $1.2e+9n$ integers, - where $e$ is the number of nonzeros in $\m{A}+\m{A}\tr$. - It computes statistics about the matrix $\m{A}$, such as the symmetry of - its nonzero pattern, the number of nonzeros in $\m{L}$, - and the number of floating-point operations required for Cholesky and LU - factorizations (which are returned in the {\tt Info} array). - The user's input matrix is not modified. - It returns {\tt CAMD\_OK} if successful, - {\tt CAMD\_OK\_BUT\_JUMBLED} if successful (but the matrix had unsorted - and/or duplicate row indices), - {\tt CAMD\_INVALID} if the matrix is invalid, - {\tt CAMD\_OUT\_OF\_MEMORY} if out of memory. - - The array {\tt C} provides the ordering constraints. - On input, {\tt C} may be null (to denote no constraints); - otherwise, it must be an array size {\tt n}, with entries in the range - {\tt 0} to {\tt n-1}. - On output, {\tt C[P[0..n-1]]} is monotonically non-descreasing. - -\item {\tt camd\_defaults} -({\tt long} version: {\tt camd\_l\_defaults}) - {\footnotesize - \begin{verbatim} - #include "camd.h" - double Control [CAMD_CONTROL] ; - camd_defaults (Control) ; - \end{verbatim} - } - Sets the default control parameters in the {\tt Control} array. These can - then be modified as desired before passing the array to the other CAMD - routines. - -\item {\tt camd\_control} -({\tt long} version: {\tt camd\_l\_control}) - {\footnotesize - \begin{verbatim} - #include "camd.h" - double Control [CAMD_CONTROL] ; - camd_control (Control) ; - \end{verbatim} - } - Prints a description of the control parameters, and their values. - -\item {\tt camd\_info} -({\tt long} version: {\tt camd\_l\_info}) - {\footnotesize - \begin{verbatim} - #include "camd.h" - double Info [CAMD_INFO] ; - camd_info (Info) ; - \end{verbatim} - } - Prints a description of the statistics computed by CAMD, and their values. - -\item {\tt camd\_valid} -({\tt long} version: {\tt camd\_valid}) - {\footnotesize - \begin{verbatim} - #include "camd.h" - int n, Ap [n+1], Ai [nz] ; - int result = camd_valid (n, n, Ap, Ai) ; - \end{verbatim} - } - Returns {\tt CAMD\_OK} or {\tt CAMD\_OK\_BUT\_JUMBLED} - if the matrix is valid as input to {\tt camd\_order}; - the latter is returned if the matrix has unsorted and/or duplicate - row indices in one or more columns. - Returns {\tt CAMD\_INVALID} if the matrix cannot be passed to - {\tt camd\_order}. - For {\tt camd\_order}, the matrix must - also be square. The first two arguments are the number of rows and the - number of columns of the matrix. For its use in CAMD, these must both - equal {\tt n}. - -\item {\tt camd\_2} -({\tt long} version: {\tt camd\_l2}) - CAMD ordering kernel. It is faster than {\tt camd\_order}, and - can be called by the user, but it is difficult to use. - It does not check its inputs for errors. - It does not require the columns of its input matrix to be sorted, - but it destroys the matrix on output. Additional workspace must be passed. - Refer to the source file {\tt CAMD/Source/camd\_2.c} for a description. - -\end{itemize} - -The nonzero pattern of the matrix $\m{A}$ is represented in compressed column -form. -For an $n$-by-$n$ matrix $\m{A}$ with {\tt nz} nonzero entries, the -representation consists of two arrays: {\tt Ap} of size {\tt n+1} and {\tt Ai} -of size {\tt nz}. The row indices of entries in column {\tt j} are stored in - {\tt Ai[Ap[j]} $\ldots$ {\tt Ap[j+1]-1]}. -For {\tt camd\_order}, -if duplicate row indices are present, or if the row indices in any given -column are not sorted in ascending order, then {\tt camd\_order} creates -an internal copy of the matrix with sorted rows and no duplicate entries, -and orders the copy. This adds slightly to the time and memory usage of -{\tt camd\_order}, but is not an error condition. - -The matrix is 0-based, and thus -row indices must be in the range {\tt 0} to {\tt n-1}. -The first entry {\tt Ap[0]} must be zero. -The total number of entries in the matrix is thus {\tt nz = Ap[n]}. - -The matrix must be square, but it does not need to be symmetric. -The {\tt camd\_order} routine constructs the nonzero pattern of -$\m{B} = \m{A}+\m{A}\tr$ (without forming $\m{A}\tr$ explicitly if -$\m{A}$ has sorted columns and no duplicate entries), -and then orders the matrix $\m{B}$. Thus, either the -lower triangular part of $\m{A}$, the upper triangular part, -or any combination may be passed. The transpose $\m{A}\tr$ may also be -passed to {\tt camd\_order}. -The diagonal entries may be present, but are ignored. - -%------------------------------------------------------------------------------ -\subsection{Control parameters} -\label{control_param} -%------------------------------------------------------------------------------ - -Control parameters are set in an optional {\tt Control} array. -It is optional in the sense that if -a {\tt NULL} pointer is passed for the {\tt Control} input argument, -then default control parameters are used. -% -\begin{itemize} -\item {\tt Control[CAMD\_DENSE]} (or {\tt Control(1)} in MATLAB): -controls the threshold for ``dense'' -rows/columns. A dense row/column in $\m{A}+\m{A}\tr$ -can cause CAMD to spend significant time -in ordering the matrix. If {\tt Control[CAMD\_DENSE]} $\ge 0$, -rows/columns with -more than {\tt Control[CAMD\_DENSE]} $\sqrt{n}$ entries are ignored during -the ordering, and placed last in the output order. The default -value of {\tt Control[CAMD\_DENSE]} is 10. If negative, no rows/columns -are treated as ``dense.'' Rows/columns with 16 or fewer off-diagonal -entries are never considered ``dense.'' -% -\item {\tt Control[CAMD\_AGGRESSIVE]} (or {\tt Control(2)} in MATLAB): -controls whether or not to use -aggressive absorption, in which a prior element is absorbed into the current -element if it is a subset of the current element, even if it is not -adjacent to the current pivot element (refer -to \cite{AmestoyDavisDuff96,AmestoyDavisDuff04} -for more details). The default value is nonzero, -which means that aggressive absorption will be performed. This nearly always -leads to a better ordering (because the approximate degrees are more -accurate) and a lower execution time. There are cases where it can -lead to a slightly worse ordering, however. To turn it off, set -{\tt Control[CAMD\_AGGRESSIVE]} to 0. -% -\end{itemize} - -Statistics are returned in the {\tt Info} array -(if {\tt Info} is {\tt NULL}, then no statistics are returned). -Refer to {\tt camd.h} file, for more details -(14 different statistics are returned, so the list is not included here). - -%------------------------------------------------------------------------------ -\subsection{Sample C program} -%------------------------------------------------------------------------------ - -The following program, {\tt camd\_demo.c}, illustrates the basic use of CAMD. -See Section~\ref{Synopsis} for a short description -of each calling sequence. - -{\footnotesize -\begin{verbatim} -#include -#include "camd.h" - -int n = 5 ; -int Ap [ ] = { 0, 2, 6, 10, 12, 14} ; -int Ai [ ] = { 0,1, 0,1,2,4, 1,2,3,4, 2,3, 1,4 } ; -int C [ ] = { 2, 0, 0, 0, 1 } ; -int P [5] ; - -int main (void) -{ - int k ; - (void) camd_order (n, Ap, Ai, P, (double *) NULL, (double *) NULL, C) ; - for (k = 0 ; k < n ; k++) printf ("P [%d] = %d\n", k, P [k]) ; - return (0) ; -} - -\end{verbatim} -} - -The {\tt Ap} and {\tt Ai} arrays represent the binary matrix -\[ -\m{A} = \left[ -\begin{array}{rrrrr} - 1 & 1 & 0 & 0 & 0 \\ - 1 & 1 & 1 & 0 & 1 \\ - 0 & 1 & 1 & 1 & 0 \\ - 0 & 0 & 1 & 1 & 0 \\ - 0 & 1 & 1 & 0 & 1 \\ -\end{array} -\right]. -\] -The diagonal entries are ignored. -% -CAMD constructs the pattern of $\m{A}+\m{A}\tr$, -and returns a permutation vector of $(3, 2, 1, 4, 0)$. -Note that nodes 1, 2, and 3 appear first (they are in the constraint set 0), -node 4 appears next (since {\tt C[4] = 1}), and node 0 appears last. -% -Since the matrix is unsymmetric but with a mostly symmetric nonzero -pattern, this would be a suitable permutation for an LU factorization of a -matrix with this nonzero pattern and whose diagonal entries are not too small. -The program uses default control settings and does not return any statistics -about the ordering, factorization, or solution ({\tt Control} and {\tt Info} -are both {\tt (double *) NULL}). It also ignores the status value returned by -{\tt camd\_order}. - -More example programs are included with the CAMD package. -The {\tt camd\_demo.c} program provides a more detailed demo of CAMD. -Another example is the CAMD mexFunction, {\tt camd\_mex.c}. - -%------------------------------------------------------------------------------ -\subsection{A note about zero-sized arrays} -%------------------------------------------------------------------------------ - -CAMD uses several user-provided arrays of size {\tt n} or {\tt nz}. -Either {\tt n} or {\tt nz} can be zero. -If you attempt to {\tt malloc} an array of size zero, -however, {\tt malloc} will return a null pointer which CAMD will report -as invalid. If you {\tt malloc} an array of -size {\tt n} or {\tt nz} to pass to CAMD, make sure that you handle the -{\tt n} = 0 and {\tt nz = 0} cases correctly. - -%------------------------------------------------------------------------------ -\section{Synopsis of C-callable routines} -\label{Synopsis} -%------------------------------------------------------------------------------ - -The matrix $\m{A}$ is {\tt n}-by-{\tt n} with {\tt nz} entries. - -{\footnotesize -\begin{verbatim} -#include "camd.h" -int n, status, Ap [n+1], Ai [nz], P [n], C [n] ; -double Control [CAMD_CONTROL], Info [CAMD_INFO] ; -camd_defaults (Control) ; -status = camd_order (n, Ap, Ai, P, Control, Info, C) ; -camd_control (Control) ; -camd_info (Info) ; -status = camd_valid (n, n, Ap, Ai) ; -\end{verbatim} -} - -The {\tt camd\_l\_*} routines are identical, except that all {\tt int} -arguments become {\tt long}: - -{\footnotesize -\begin{verbatim} -#include "camd.h" -long n, status, Ap [n+1], Ai [nz], P [n], C [n] ; -double Control [CAMD_CONTROL], Info [CAMD_INFO] ; -camd_l_defaults (Control) ; -status = camd_l_order (n, Ap, Ai, P, Control, Info, C) ; -camd_l_control (Control) ; -camd_l_info (Info) ; -status = camd_l_valid (n, n, Ap, Ai) ; -\end{verbatim} -} - -%------------------------------------------------------------------------------ -\section{Installation} -\label{Install} -%------------------------------------------------------------------------------ - -The following discussion assumes you have the {\tt make} program, either in -Unix, or in Windows with Cygwin. - -System-dependent configurations are in the -{\tt ../SuiteSparse\_config/SuiteSparse\_config.mk} -file. You can edit that file to customize the compilation. The default -settings will work on most systems. -Sample configuration files are provided -for Mac, Linux, Sun Solaris, and IBM AIX. -The system you are on is detected automatically. - -To compile and install the C-callable CAMD library, -go to the {\tt CAMD} directory and type {\tt make}. -A dynamic library is placed in -in {\tt AMD/Lib/libcamd.so.*}, ({\tt *.dylib} for the Mac). -Three demo programs of the AMD ordering routine will be compiled and tested in -the {\tt CAMD/Demo} directory. -The outputs of these demo programs will then be compared with output -files in the distribution. - -Typing {\tt make clean} will remove all but the final compiled libraries -and demo programs. Typing {\tt make purge} or {\tt make distclean} -removes all files not in the original distribution. -If you compile CAMD and then later change the -{\tt ../SuiteSparse\_config/SuiteSparse\_config.mk} file -then you should type {\tt make purge} and then {\tt make} to recompile. - -When you compile your program that uses the C-callable CAMD library, -you need to add the {\tt CAMD/Lib/libcamd.*} library -and you need to tell your compiler to look in the -{\tt CAMD/Include} directory for include -files. -See {\tt CAMD/Demo/Makefile} for an example. - -By doing {\tt make}, all compiled dynamic libraries are also copied into {\tt -SuiteSparse/lib} the include files are coped into {\tt SuiteSparse/include}, -and documentation is copied into {\tt SuiteSparse/doc}. - -Alternatively, you can install the shared library -and include files in /usr/local/lib and /usr/local/include, and -the documenation in /usr/local/doc. -Just do {\tt make} then {\tt make install}. -Now you can simply use {\tt -lcamd} when you compile your own program. - -If all you want to use is the CAMD mexFunction in MATLAB, you can skip -the use of the {\tt make} command entirely. Simply type -{\tt camd\_make} in MATLAB while in the {\tt CAMD/MATLAB} directory. -This works on any system with MATLAB, including Windows. -Alternately, type {\tt make} in the {\tt CAMD/MATLAB} directory. - -If you are including CAMD as a subset of a larger library and do not want -to link the C standard I/O library, or if you simply do not need to use -them, you can safely remove the {\tt camd\_control.c} and {\tt camd\_info.c} -files. Similarly, if you use default parameters (or define your -own {\tt Control} array), then you can exclude the {\tt camd\_defaults.c} -file. -Each of these files contains the user-callable routines of the same -name. None of these auxiliary routines are directly called by -{\tt camd\_order}. -The {\tt camd\_dump.c} file contains debugging routines -that are neither used nor compiled unless debugging is enabled. -The {\tt camd\_internal.h} file must be edited to enable debugging; -refer to the instructions in that file. -The bare minimum files required to use just {\tt camd\_order} are -{\tt camd.h} and {\tt camd\_internal.h} -in the {\tt Include} directory, -and -{\tt camd\_1.c}, -{\tt camd\_2.c}, -{\tt camd\_aat.c}, -{\tt camd\_global.c}, -{\tt and\_order.c}, -{\tt camd\_postorder.c}, -{\tt camd\_preprocess.c}, -and -{\tt camd\_valid.c} -in the {\tt Source} directory. - -%------------------------------------------------------------------------------ -\newpage -\section{The CAMD routines} -\label{Primary} -%------------------------------------------------------------------------------ - -The file {\tt CAMD/Include/camd.h} listed below -describes each user-callable routine in CAMD, -and gives details on their use. - -{\footnotesize -\input{camd_h.tex} -} - -%------------------------------------------------------------------------------ -\newpage -% References -%------------------------------------------------------------------------------ - -\bibliographystyle{plain} -\bibliography{CAMD_UserGuide} - -\end{document} diff --git a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Doc/ChangeLog b/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Doc/ChangeLog deleted file mode 100644 index 3309a935b..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Doc/ChangeLog +++ /dev/null @@ -1,107 +0,0 @@ -May 4, 2016: version 2.4.6 - - * minor changes to Makefile - -Apr 1, 2016: version 2.4.5 - - * licensing simplified (no other change); refer to CAMD/Doc/License.txt - -Feb 1, 2016: version 2.4.4 - - * update to Makefiles - -Jan 30, 2016: version 2.4.3 - - * modifications to Makefiles - -Jan 1, 2016: version 2.4.2 - - * modified Makefile to create shared libraries - No change to C code except version number - -Oct 10, 2014: version 2.4.1 - - modified MATLAB/camd_make.m. No change to C code except version number. - -July 31, 2013: version 2.4.0 - - * changed malloc and printf pointers to use SuiteSparse_config - -Jun 20, 2012: verison 2.3.1 - - * minor update for Windows (removed filesep) - -Jun 1, 2012: version 2.3.0 - - * changed from UFconfig to SuiteSparse_config - -May 15, 2012: version 2.2.4 - - * minor fix to SIZE_T_MAX definition (finicky compiler workaround) - -Dec 7, 2011: version 2.2.3 - - * fixed the Makefile to better align with CFLAGS and other standards - -Jan 25, 2011: version 2.2.2 - - * minor fix to "make install" - -Nov 30, 2009: version 2.2.1 - - * added "make install" and "make uninstall" - -May 31, 2007: version 2.2.0 - - * port to 64-bit MATLAB - - * Makefile moved from Source/ to Lib/ - -Dec 12, 2006, v2.1.3 - - * minor MATLAB cleanup - -Sept 28, 2006, v2.1.2 - - * #define SIZE_T_MAX not done if already defined (Mac OSX). - -Aug 31, 2006: v2.1.1 - - * trivial change to comments in camd.m - -June 27, 2006: CAMD Version 2.1 - - * bug fix. Ordering constraints not always met if dense and/or empty - nodes are present in the matrix. - -Apr. 30, 2006: CAMD Version 2.0 - - * CAMD released, based on AMD v2.0. To compare the two codes, type the - command ./docdiff in this directory (the "CAMD" and "camd" strings - are replaced with "AMD" and "amd" when the two packages are compared, - to make more evident the substantive differences between the packages). - - Primary differences with AMD v2.0: - - CAMD adds the ability to order the matrix with constraints. Each - node i in the graph (row/column i in the matrix) has a constraint, - C[i], which is in the range 0 to n-1. All nodes with C[i] = 0 are - ordered first, followed by all nodes with constraint 1, and so on. - That is, C[P[k]] is monotonically non-decreasing as k varies from 0 - to n-1. camd_order has an additional C parameter; if NULL, no - constraints are used (the ordering will be similar to AMD's ordering). - The optional C parameter is also added to the MATLAB interface, - p = camd (A,Control,C). - - Since the C parameter is optional, CAMD can replace AMD in any - application that uses AMD. Just pass C = NULL (or omit C in the MATLAB - interface). There is no Fortran version of CAMD, however. - - The postordering is different, and there is no camd_post_tree.c file. - - A new routine, camd_cvalid, has been added to check the validity of C. - - CAMD requires more workspace than AMD (n+1 integers). - - All user-visible names AMD* and amd* replaced with CAMD* and camd*. - diff --git a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Doc/License.txt b/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Doc/License.txt deleted file mode 100644 index 5329cec2b..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Doc/License.txt +++ /dev/null @@ -1,35 +0,0 @@ -CAMD, Copyright (c) by Timothy A. Davis, -Yanqing Chen, -Patrick R. Amestoy, and Iain S. Duff. All Rights Reserved. -CAMD is available under alternate licenses, contact T. Davis for details. - -CAMD License: BSD 3-clause - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * 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. - * Neither the name of the organizations to which the authors are - affiliated, nor the names of its contributors may be used to endorse - or promote products derived from this software without specific prior - written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH - DAMAGE. - -Availability: - - http://www.suitesparse.com - diff --git a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Doc/Makefile b/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Doc/Makefile deleted file mode 100644 index 8d177e61e..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Doc/Makefile +++ /dev/null @@ -1,37 +0,0 @@ -#------------------------------------------------------------------------------ -# CAMD Makefile for compiling on Unix systems -#------------------------------------------------------------------------------ - -default: dist - -include ../../SuiteSparse_config/SuiteSparse_config.mk - -#------------------------------------------------------------------------------ -# Remove all but the files in the original distribution -#------------------------------------------------------------------------------ - -clean: - - $(RM) -r camd_temp $(CLEAN) - -purge: distclean - -distclean: clean - - $(RM) -r $(PURGE) - -#------------------------------------------------------------------------------ -# Create the User Guide and Quick Start Guide -#------------------------------------------------------------------------------ - -CAMD_UserGuide.pdf: CAMD_UserGuide.tex CAMD_UserGuide.bib ../Include/camd.h - echo '\\begin{verbatim}' > camd_h.tex - expand -8 ../Include/camd.h >> camd_h.tex - echo '\\end{verbatim}' >> camd_h.tex - pdflatex CAMD_UserGuide - bibtex CAMD_UserGuide - pdflatex CAMD_UserGuide - pdflatex CAMD_UserGuide - -dist: CAMD_UserGuide.pdf - - $(RM) *.aux *.bbl *.blg *.log *.toc camd_h.tex - - $(RM) camd_temp - diff --git a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Doc/camd.sed b/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Doc/camd.sed deleted file mode 100644 index 877e3f068..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Doc/camd.sed +++ /dev/null @@ -1,2 +0,0 @@ -s/CAMD/AMD/g -s/camd/amd/g diff --git a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Doc/cdiff b/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Doc/cdiff deleted file mode 100755 index 7d477e213..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Doc/cdiff +++ /dev/null @@ -1,4 +0,0 @@ -# -echo diff $1 $2 -sed -f camd.sed < $1 > camd_temp -diff camd_temp $2 diff --git a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Doc/docdiff b/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Doc/docdiff deleted file mode 100755 index 539522c35..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Doc/docdiff +++ /dev/null @@ -1,35 +0,0 @@ -./cdiff ../Demo/camd_demo2.c ../../AMD/Demo/amd_demo2.c -./cdiff ../Demo/camd_demo.c ../../AMD/Demo/amd_demo.c -./cdiff ../Demo/camd_l_demo.c ../../AMD/Demo/amd_l_demo.c -./cdiff ../Demo/camd_simple.c ../../AMD/Demo/amd_simple.c -./cdiff ../Demo/Makefile ../../AMD/Demo/Makefile -./cdiff ../Doc/CAMD_UserGuide.bib ../../AMD/Doc/AMD_UserGuide.bib -./cdiff ../Doc/CAMD_UserGuide.tex ../../AMD/Doc/AMD_UserGuide.tex -./cdiff ../Doc/License.txt ../../AMD/Doc/License.txt -./cdiff ../Doc/Makefile ../../AMD/Doc/Makefile -./cdiff ../Include/camd.h ../../AMD/Include/amd.h -./cdiff ../Include/camd_internal.h ../../AMD/Include/amd_internal.h -./cdiff ../Lib/libcamd.def ../../AMD/Lib/libamd.def -./cdiff ../MATLAB/camd_demo.m ../../AMD/MATLAB/amd_demo.m -./cdiff ../MATLAB/camd.m ../../AMD/MATLAB/amd2.m -./cdiff ../MATLAB/camd_make.m ../../AMD/MATLAB/amd_make.m -./cdiff ../MATLAB/camd_mex.c ../../AMD/MATLAB/amd_mex.c -./cdiff ../MATLAB/can_24 ../../AMD/MATLAB/can_24 -./cdiff ../MATLAB/Contents.m ../../AMD/MATLAB/Contents.m -./cdiff ../MATLAB/Makefile ../../AMD/MATLAB/Makefile -./cdiff ../Source/camd_1.c ../../AMD/Source/amd_1.c -./cdiff ../Source/camd_2.c ../../AMD/Source/amd_2.c -./cdiff ../Source/camd_aat.c ../../AMD/Source/amd_aat.c -./cdiff ../Source/camd_control.c ../../AMD/Source/amd_control.c -./cdiff ../Source/camd_defaults.c ../../AMD/Source/amd_defaults.c -./cdiff ../Source/camd_dump.c ../../AMD/Source/amd_dump.c -./cdiff ../Source/camd_global.c ../../AMD/Source/amd_global.c -./cdiff ../Source/camd_info.c ../../AMD/Source/amd_info.c -./cdiff ../Source/camd_order.c ../../AMD/Source/amd_order.c -./cdiff ../Source/camd_postorder.c ../../AMD/Source/amd_postorder.c -# ./cdiff ../Source/camd_post_tree.c ../../AMD/Source/amd_post_tree.c -./cdiff ../Source/camd_preprocess.c ../../AMD/Source/amd_preprocess.c -./cdiff ../Source/camd_valid.c ../../AMD/Source/amd_valid.c -./cdiff ../Source/Makefile ../../AMD/Source/Makefile -./cdiff ../Makefile ../../AMD/Makefile -./cdiff ../README.txt ../../AMD/README.txt diff --git a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Doc/lesser.txt b/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Doc/lesser.txt deleted file mode 100644 index 8add30ad5..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Doc/lesser.txt +++ /dev/null @@ -1,504 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - - Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - - When we speak of free software, we are referring to freedom of use, -not price. Our General Public Licenses are designed to make sure that -you have the freedom to distribute copies of free software (and charge -for this service if you wish); that you receive source code or can get -it if you want it; that you can change the software and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Libraries - - If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms of the -ordinary General Public License). - - To apply these terms, attach the following notices to the library. It is -safest to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random Hacker. - - , 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! - - diff --git a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Include/camd.h b/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Include/camd.h deleted file mode 100644 index 21898e017..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Include/camd.h +++ /dev/null @@ -1,407 +0,0 @@ -/* ========================================================================= */ -/* === CAMD: approximate minimum degree ordering ========================== */ -/* ========================================================================= */ - -/* ------------------------------------------------------------------------- */ -/* CAMD Version 2.4, Copyright (c) 2013 by Timothy A. Davis, Yanqing Chen, */ -/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ -/* email: DrTimothyAldenDavis@gmail.com */ -/* ------------------------------------------------------------------------- */ - -/* CAMD finds a symmetric ordering P of a matrix A so that the Cholesky - * factorization of P*A*P' has fewer nonzeros and takes less work than the - * Cholesky factorization of A. If A is not symmetric, then it performs its - * ordering on the matrix A+A'. Two sets of user-callable routines are - * provided, one for int integers and the other for SuiteSparse_long integers. - * - * The method is based on the approximate minimum degree algorithm, discussed - * in Amestoy, Davis, and Duff, "An approximate degree ordering algorithm", - * SIAM Journal of Matrix Analysis and Applications, vol. 17, no. 4, pp. - * 886-905, 1996. - */ - -#ifndef CAMD_H -#define CAMD_H - -/* make it easy for C++ programs to include CAMD */ -#ifdef __cplusplus -extern "C" { -#endif - -/* get the definition of size_t: */ -#include - -#include "SuiteSparse_config.h" - -int camd_order /* returns CAMD_OK, CAMD_OK_BUT_JUMBLED, - * CAMD_INVALID, or CAMD_OUT_OF_MEMORY */ -( - int n, /* A is n-by-n. n must be >= 0. */ - const int Ap [ ], /* column pointers for A, of size n+1 */ - const int Ai [ ], /* row indices of A, of size nz = Ap [n] */ - int P [ ], /* output permutation, of size n */ - double Control [ ], /* input Control settings, of size CAMD_CONTROL */ - double Info [ ], /* output Info statistics, of size CAMD_INFO */ - const int C [ ] /* Constraint set of A, of size n; can be NULL */ -) ; - -SuiteSparse_long camd_l_order /* see above for description of arguments */ -( - SuiteSparse_long n, - const SuiteSparse_long Ap [ ], - const SuiteSparse_long Ai [ ], - SuiteSparse_long P [ ], - double Control [ ], - double Info [ ], - const SuiteSparse_long C [ ] -) ; - -/* Input arguments (not modified): - * - * n: the matrix A is n-by-n. - * Ap: an int/SuiteSparse_long array of size n+1, containing column - * pointers of A. - * Ai: an int/SuiteSparse_long array of size nz, containing the row - * indices of A, where nz = Ap [n]. - * Control: a double array of size CAMD_CONTROL, containing control - * parameters. Defaults are used if Control is NULL. - * - * Output arguments (not defined on input): - * - * P: an int/SuiteSparse_long array of size n, containing the output - * permutation. If row i is the kth pivot row, then P [k] = i. In - * MATLAB notation, the reordered matrix is A (P,P). - * Info: a double array of size CAMD_INFO, containing statistical - * information. Ignored if Info is NULL. - * - * On input, the matrix A is stored in column-oriented form. The row indices - * of nonzero entries in column j are stored in Ai [Ap [j] ... Ap [j+1]-1]. - * - * If the row indices appear in ascending order in each column, and there - * are no duplicate entries, then camd_order is slightly more efficient in - * terms of time and memory usage. If this condition does not hold, a copy - * of the matrix is created (where these conditions do hold), and the copy is - * ordered. - * - * Row indices must be in the range 0 to - * n-1. Ap [0] must be zero, and thus nz = Ap [n] is the number of nonzeros - * in A. The array Ap is of size n+1, and the array Ai is of size nz = Ap [n]. - * The matrix does not need to be symmetric, and the diagonal does not need to - * be present (if diagonal entries are present, they are ignored except for - * the output statistic Info [CAMD_NZDIAG]). The arrays Ai and Ap are not - * modified. This form of the Ap and Ai arrays to represent the nonzero - * pattern of the matrix A is the same as that used internally by MATLAB. - * If you wish to use a more flexible input structure, please see the - * umfpack_*_triplet_to_col routines in the UMFPACK package, at - * http://www.suitesparse.com. - * - * Restrictions: n >= 0. Ap [0] = 0. Ap [j] <= Ap [j+1] for all j in the - * range 0 to n-1. nz = Ap [n] >= 0. Ai [0..nz-1] must be in the range 0 - * to n-1. Finally, Ai, Ap, and P must not be NULL. If any of these - * restrictions are not met, CAMD returns CAMD_INVALID. - * - * CAMD returns: - * - * CAMD_OK if the matrix is valid and sufficient memory can be allocated to - * perform the ordering. - * - * CAMD_OUT_OF_MEMORY if not enough memory can be allocated. - * - * CAMD_INVALID if the input arguments n, Ap, Ai are invalid, or if P is - * NULL. - * - * CAMD_OK_BUT_JUMBLED if the matrix had unsorted columns, and/or duplicate - * entries, but was otherwise valid. - * - * The CAMD routine first forms the pattern of the matrix A+A', and then - * computes a fill-reducing ordering, P. If P [k] = i, then row/column i of - * the original is the kth pivotal row. In MATLAB notation, the permuted - * matrix is A (P,P), except that 0-based indexing is used instead of the - * 1-based indexing in MATLAB. - * - * The Control array is used to set various parameters for CAMD. If a NULL - * pointer is passed, default values are used. The Control array is not - * modified. - * - * Control [CAMD_DENSE]: controls the threshold for "dense" rows/columns. - * A dense row/column in A+A' can cause CAMD to spend a lot of time in - * ordering the matrix. If Control [CAMD_DENSE] >= 0, rows/columns - * with more than Control [CAMD_DENSE] * sqrt (n) entries are ignored - * during the ordering, and placed last in the output order. The - * default value of Control [CAMD_DENSE] is 10. If negative, no - * rows/columns are treated as "dense". Rows/columns with 16 or - * fewer off-diagonal entries are never considered "dense". - * - * Control [CAMD_AGGRESSIVE]: controls whether or not to use aggressive - * absorption, in which a prior element is absorbed into the current - * element if is a subset of the current element, even if it is not - * adjacent to the current pivot element (refer to Amestoy, Davis, - * & Duff, 1996, for more details). The default value is nonzero, - * which means to perform aggressive absorption. This nearly always - * leads to a better ordering (because the approximate degrees are - * more accurate) and a lower execution time. There are cases where - * it can lead to a slightly worse ordering, however. To turn it off, - * set Control [CAMD_AGGRESSIVE] to 0. - * - * Control [2..4] are not used in the current version, but may be used in - * future versions. - * - * The Info array provides statistics about the ordering on output. If it is - * not present, the statistics are not returned. This is not an error - * condition. - * - * Info [CAMD_STATUS]: the return value of CAMD, either CAMD_OK, - * CAMD_OK_BUT_JUMBLED, CAMD_OUT_OF_MEMORY, or CAMD_INVALID. - * - * Info [CAMD_N]: n, the size of the input matrix - * - * Info [CAMD_NZ]: the number of nonzeros in A, nz = Ap [n] - * - * Info [CAMD_SYMMETRY]: the symmetry of the matrix A. It is the number - * of "matched" off-diagonal entries divided by the total number of - * off-diagonal entries. An entry A(i,j) is matched if A(j,i) is also - * an entry, for any pair (i,j) for which i != j. In MATLAB notation, - * S = spones (A) ; - * B = tril (S, -1) + triu (S, 1) ; - * symmetry = nnz (B & B') / nnz (B) ; - * - * Info [CAMD_NZDIAG]: the number of entries on the diagonal of A. - * - * Info [CAMD_NZ_A_PLUS_AT]: the number of nonzeros in A+A', excluding the - * diagonal. If A is perfectly symmetric (Info [CAMD_SYMMETRY] = 1) - * with a fully nonzero diagonal, then Info [CAMD_NZ_A_PLUS_AT] = nz-n - * (the smallest possible value). If A is perfectly unsymmetric - * (Info [CAMD_SYMMETRY] = 0, for an upper triangular matrix, for - * example) with no diagonal, then Info [CAMD_NZ_A_PLUS_AT] = 2*nz - * (the largest possible value). - * - * Info [CAMD_NDENSE]: the number of "dense" rows/columns of A+A' that were - * removed from A prior to ordering. These are placed last in the - * output order P. - * - * Info [CAMD_MEMORY]: the amount of memory used by CAMD, in bytes. In the - * current version, this is 1.2 * Info [CAMD_NZ_A_PLUS_AT] + 9*n - * times the size of an integer. This is at most 2.4nz + 9n. This - * excludes the size of the input arguments Ai, Ap, and P, which have - * a total size of nz + 2*n + 1 integers. - * - * Info [CAMD_NCMPA]: the number of garbage collections performed. - * - * Info [CAMD_LNZ]: the number of nonzeros in L (excluding the diagonal). - * This is a slight upper bound because mass elimination is combined - * with the approximate degree update. It is a rough upper bound if - * there are many "dense" rows/columns. The rest of the statistics, - * below, are also slight or rough upper bounds, for the same reasons. - * The post-ordering of the assembly tree might also not exactly - * correspond to a true elimination tree postordering. - * - * Info [CAMD_NDIV]: the number of divide operations for a subsequent LDL' - * or LU factorization of the permuted matrix A (P,P). - * - * Info [CAMD_NMULTSUBS_LDL]: the number of multiply-subtract pairs for a - * subsequent LDL' factorization of A (P,P). - * - * Info [CAMD_NMULTSUBS_LU]: the number of multiply-subtract pairs for a - * subsequent LU factorization of A (P,P), assuming that no numerical - * pivoting is required. - * - * Info [CAMD_DMAX]: the maximum number of nonzeros in any column of L, - * including the diagonal. - * - * Info [14..19] are not used in the current version, but may be used in - * future versions. - */ - -/* ------------------------------------------------------------------------- */ -/* direct interface to CAMD */ -/* ------------------------------------------------------------------------- */ - -/* camd_2 is the primary CAMD ordering routine. It is not meant to be - * user-callable because of its restrictive inputs and because it destroys - * the user's input matrix. It does not check its inputs for errors, either. - * However, if you can work with these restrictions it can be faster than - * camd_order and use less memory (assuming that you can create your own copy - * of the matrix for CAMD to destroy). Refer to CAMD/Source/camd_2.c for a - * description of each parameter. */ - -void camd_2 -( - int n, - int Pe [ ], - int Iw [ ], - int Len [ ], - int iwlen, - int pfree, - int Nv [ ], - int Next [ ], - int Last [ ], - int Head [ ], - int Elen [ ], - int Degree [ ], - int W [ ], - double Control [ ], - double Info [ ], - const int C [ ], - int BucketSet [ ] -) ; - -void camd_l2 -( - SuiteSparse_long n, - SuiteSparse_long Pe [ ], - SuiteSparse_long Iw [ ], - SuiteSparse_long Len [ ], - SuiteSparse_long iwlen, - SuiteSparse_long pfree, - SuiteSparse_long Nv [ ], - SuiteSparse_long Next [ ], - SuiteSparse_long Last [ ], - SuiteSparse_long Head [ ], - SuiteSparse_long Elen [ ], - SuiteSparse_long Degree [ ], - SuiteSparse_long W [ ], - double Control [ ], - double Info [ ], - const SuiteSparse_long C [ ], - SuiteSparse_long BucketSet [ ] - -) ; - -/* ------------------------------------------------------------------------- */ -/* camd_valid */ -/* ------------------------------------------------------------------------- */ - -/* Returns CAMD_OK or CAMD_OK_BUT_JUMBLED if the matrix is valid as input to - * camd_order; the latter is returned if the matrix has unsorted and/or - * duplicate row indices in one or more columns. Returns CAMD_INVALID if the - * matrix cannot be passed to camd_order. For camd_order, the matrix must also - * be square. The first two arguments are the number of rows and the number - * of columns of the matrix. For its use in CAMD, these must both equal n. - */ - -int camd_valid -( - int n_row, /* # of rows */ - int n_col, /* # of columns */ - const int Ap [ ], /* column pointers, of size n_col+1 */ - const int Ai [ ] /* row indices, of size Ap [n_col] */ -) ; - -SuiteSparse_long camd_l_valid -( - SuiteSparse_long n_row, - SuiteSparse_long n_col, - const SuiteSparse_long Ap [ ], - const SuiteSparse_long Ai [ ] -) ; - -/* ------------------------------------------------------------------------- */ -/* camd_cvalid */ -/* ------------------------------------------------------------------------- */ - -/* Returns TRUE if the constraint set is valid as input to camd_order, - * FALSE otherwise. */ - -int camd_cvalid -( - int n, - const int C [ ] -) ; - -SuiteSparse_long camd_l_cvalid -( - SuiteSparse_long n, - const SuiteSparse_long C [ ] -) ; - -/* ------------------------------------------------------------------------- */ -/* CAMD memory manager and printf routines */ -/* ------------------------------------------------------------------------- */ - - /* moved to SuiteSparse_config.c */ - -/* ------------------------------------------------------------------------- */ -/* CAMD Control and Info arrays */ -/* ------------------------------------------------------------------------- */ - -/* camd_defaults: sets the default control settings */ -void camd_defaults (double Control [ ]) ; -void camd_l_defaults (double Control [ ]) ; - -/* camd_control: prints the control settings */ -void camd_control (double Control [ ]) ; -void camd_l_control (double Control [ ]) ; - -/* camd_info: prints the statistics */ -void camd_info (double Info [ ]) ; -void camd_l_info (double Info [ ]) ; - -#define CAMD_CONTROL 5 /* size of Control array */ -#define CAMD_INFO 20 /* size of Info array */ - -/* contents of Control */ -#define CAMD_DENSE 0 /* "dense" if degree > Control [0] * sqrt (n) */ -#define CAMD_AGGRESSIVE 1 /* do aggressive absorption if Control [1] != 0 */ - -/* default Control settings */ -#define CAMD_DEFAULT_DENSE 10.0 /* default "dense" degree 10*sqrt(n) */ -#define CAMD_DEFAULT_AGGRESSIVE 1 /* do aggressive absorption by default */ - -/* contents of Info */ -#define CAMD_STATUS 0 /* return value of camd_order and camd_l_order */ -#define CAMD_N 1 /* A is n-by-n */ -#define CAMD_NZ 2 /* number of nonzeros in A */ -#define CAMD_SYMMETRY 3 /* symmetry of pattern (1 is sym., 0 is unsym.) */ -#define CAMD_NZDIAG 4 /* # of entries on diagonal */ -#define CAMD_NZ_A_PLUS_AT 5 /* nz in A+A' */ -#define CAMD_NDENSE 6 /* number of "dense" rows/columns in A */ -#define CAMD_MEMORY 7 /* amount of memory used by CAMD */ -#define CAMD_NCMPA 8 /* number of garbage collections in CAMD */ -#define CAMD_LNZ 9 /* approx. nz in L, excluding the diagonal */ -#define CAMD_NDIV 10 /* number of fl. point divides for LU and LDL' */ -#define CAMD_NMULTSUBS_LDL 11 /* number of fl. point (*,-) pairs for LDL' */ -#define CAMD_NMULTSUBS_LU 12 /* number of fl. point (*,-) pairs for LU */ -#define CAMD_DMAX 13 /* max nz. in any column of L, incl. diagonal */ - -/* ------------------------------------------------------------------------- */ -/* return values of CAMD */ -/* ------------------------------------------------------------------------- */ - -#define CAMD_OK 0 /* success */ -#define CAMD_OUT_OF_MEMORY -1 /* malloc failed, or problem too large */ -#define CAMD_INVALID -2 /* input arguments are not valid */ -#define CAMD_OK_BUT_JUMBLED 1 /* input matrix is OK for camd_order, but - * columns were not sorted, and/or duplicate entries were present. CAMD had - * to do extra work before ordering the matrix. This is a warning, not an - * error. */ - -/* ========================================================================== */ -/* === CAMD version ========================================================= */ -/* ========================================================================== */ - -/* - * As an example, to test if the version you are using is 1.2 or later: - * - * if (CAMD_VERSION >= CAMD_VERSION_CODE (1,2)) ... - * - * This also works during compile-time: - * - * #if (CAMD_VERSION >= CAMD_VERSION_CODE (1,2)) - * printf ("This is version 1.2 or later\n") ; - * #else - * printf ("This is an early version\n") ; - * #endif - */ - -#define CAMD_DATE "May 4, 2016" -#define CAMD_VERSION_CODE(main,sub) ((main) * 1000 + (sub)) -#define CAMD_MAIN_VERSION 2 -#define CAMD_SUB_VERSION 4 -#define CAMD_SUBSUB_VERSION 6 -#define CAMD_VERSION CAMD_VERSION_CODE(CAMD_MAIN_VERSION,CAMD_SUB_VERSION) - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Include/camd_internal.h b/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Include/camd_internal.h deleted file mode 100644 index 92407c808..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Include/camd_internal.h +++ /dev/null @@ -1,317 +0,0 @@ -/* ========================================================================= */ -/* === camd_internal.h ===================================================== */ -/* ========================================================================= */ - -/* ------------------------------------------------------------------------- */ -/* CAMD, Copyright (c) Timothy A. Davis, Yanqing Chen, */ -/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ -/* email: DrTimothyAldenDavis@gmail.com */ -/* ------------------------------------------------------------------------- */ - -/* This file is for internal use in CAMD itself, and does not normally need to - * be included in user code (it is included in UMFPACK, however). All others - * should use camd.h instead. - */ - -/* ========================================================================= */ -/* === NDEBUG ============================================================== */ -/* ========================================================================= */ - -/* - * Turning on debugging takes some work (see below). If you do not edit this - * file, then debugging is always turned off, regardless of whether or not - * -DNDEBUG is specified in your compiler options. - * - * If CAMD is being compiled as a mexFunction, then MATLAB_MEX_FILE is defined, - * and mxAssert is used instead of assert. If debugging is not enabled, no - * MATLAB include files or functions are used. Thus, the CAMD library libcamd.a - * can be safely used in either a stand-alone C program or in another - * mexFunction, without any change. - */ - -/* - CAMD will be exceedingly slow when running in debug mode. The next three - lines ensure that debugging is turned off. -*/ -#ifndef NDEBUG -#define NDEBUG -#endif - -/* - To enable debugging, uncomment the following line: -#undef NDEBUG -*/ - - -/* ------------------------------------------------------------------------- */ -/* ANSI include files */ -/* ------------------------------------------------------------------------- */ - -/* from stdlib.h: size_t, malloc, free, realloc, and calloc */ -#include - -#if !defined(NPRINT) || !defined(NDEBUG) -/* from stdio.h: printf. Not included if NPRINT is defined at compile time. - * fopen and fscanf are used when debugging. */ -#include -#endif - -/* from limits.h: INT_MAX and LONG_MAX */ -#include - -/* from math.h: sqrt */ -#include - -/* ------------------------------------------------------------------------- */ -/* MATLAB include files (only if being used in or via MATLAB) */ -/* ------------------------------------------------------------------------- */ - -#ifdef MATLAB_MEX_FILE -#include "matrix.h" -#include "mex.h" -#endif - -/* ------------------------------------------------------------------------- */ -/* basic definitions */ -/* ------------------------------------------------------------------------- */ - -#ifdef FLIP -#undef FLIP -#endif - -#ifdef MAX -#undef MAX -#endif - -#ifdef MIN -#undef MIN -#endif - -#ifdef EMPTY -#undef EMPTY -#endif - -#ifdef GLOBAL -#undef GLOBAL -#endif - -#ifdef PRIVATE -#undef PRIVATE -#endif - -/* FLIP is a "negation about -1", and is used to mark an integer i that is - * normally non-negative. FLIP (EMPTY) is EMPTY. FLIP of a number > EMPTY - * is negative, and FLIP of a number < EMTPY is positive. FLIP (FLIP (i)) = i - * for all integers i. UNFLIP (i) is >= EMPTY. */ -#define EMPTY (-1) -#define FLIP(i) (-(i)-2) -#define UNFLIP(i) ((i < EMPTY) ? FLIP (i) : (i)) - -/* for integer MAX/MIN, or for doubles when we don't care how NaN's behave: */ -#define MAX(a,b) (((a) > (b)) ? (a) : (b)) -#define MIN(a,b) (((a) < (b)) ? (a) : (b)) - -/* logical expression of p implies q: */ -#define IMPLIES(p,q) (!(p) || (q)) - -/* Note that the IBM RS 6000 xlc predefines TRUE and FALSE in . */ -/* The Compaq Alpha also predefines TRUE and FALSE. */ -#ifdef TRUE -#undef TRUE -#endif -#ifdef FALSE -#undef FALSE -#endif - -#define TRUE (1) -#define FALSE (0) -#define PRIVATE static -#define GLOBAL -#define EMPTY (-1) - -/* Note that Linux's gcc 2.96 defines NULL as ((void *) 0), but other */ -/* compilers (even gcc 2.95.2 on Solaris) define NULL as 0 or (0). We */ -/* need to use the ANSI standard value of 0. */ -#ifdef NULL -#undef NULL -#endif - -#define NULL 0 - -/* largest value of size_t */ -#ifndef SIZE_T_MAX -#ifdef SIZE_MAX -/* C99 only */ -#define SIZE_T_MAX SIZE_MAX -#else -#define SIZE_T_MAX ((size_t) (-1)) -#endif -#endif - -/* ------------------------------------------------------------------------- */ -/* integer type for CAMD: int or SuiteSparse_long */ -/* ------------------------------------------------------------------------- */ - -#include "camd.h" - -#if defined (DLONG) || defined (ZLONG) - -#define Int SuiteSparse_long -#define ID SuiteSparse_long_id -#define Int_MAX SuiteSparse_long_max - -#define CAMD_order camd_l_order -#define CAMD_defaults camd_l_defaults -#define CAMD_control camd_l_control -#define CAMD_info camd_l_info -#define CAMD_1 camd_l1 -#define CAMD_2 camd_l2 -#define CAMD_valid camd_l_valid -#define CAMD_cvalid camd_l_cvalid -#define CAMD_aat camd_l_aat -#define CAMD_postorder camd_l_postorder -#define CAMD_post_tree camd_l_post_tree -#define CAMD_dump camd_l_dump -#define CAMD_debug camd_l_debug -#define CAMD_debug_init camd_l_debug_init -#define CAMD_preprocess camd_l_preprocess - -#else - -#define Int int -#define ID "%d" -#define Int_MAX INT_MAX - -#define CAMD_order camd_order -#define CAMD_defaults camd_defaults -#define CAMD_control camd_control -#define CAMD_info camd_info -#define CAMD_1 camd_1 -#define CAMD_2 camd_2 -#define CAMD_valid camd_valid -#define CAMD_cvalid camd_cvalid -#define CAMD_aat camd_aat -#define CAMD_postorder camd_postorder -#define CAMD_post_tree camd_post_tree -#define CAMD_dump camd_dump -#define CAMD_debug camd_debug -#define CAMD_debug_init camd_debug_init -#define CAMD_preprocess camd_preprocess - -#endif - -/* ------------------------------------------------------------------------- */ -/* CAMD routine definitions (not user-callable) */ -/* ------------------------------------------------------------------------- */ - -GLOBAL size_t CAMD_aat -( - Int n, - const Int Ap [ ], - const Int Ai [ ], - Int Len [ ], - Int Tp [ ], - double Info [ ] -) ; - -GLOBAL void CAMD_1 -( - Int n, - const Int Ap [ ], - const Int Ai [ ], - Int P [ ], - Int Pinv [ ], - Int Len [ ], - Int slen, - Int S [ ], - double Control [ ], - double Info [ ], - const Int C [ ] -) ; - -GLOBAL Int CAMD_postorder -( - Int j, Int k, Int n, Int head [], Int next [], Int post [], Int stack [] -) ; - -GLOBAL void CAMD_preprocess -( - Int n, - const Int Ap [ ], - const Int Ai [ ], - Int Rp [ ], - Int Ri [ ], - Int W [ ], - Int Flag [ ] -) ; - -/* ------------------------------------------------------------------------- */ -/* debugging definitions */ -/* ------------------------------------------------------------------------- */ - -#ifndef NDEBUG - -/* from assert.h: assert macro */ -#include - -#ifndef EXTERN -#define EXTERN extern -#endif - -EXTERN Int CAMD_debug ; - -GLOBAL void CAMD_debug_init ( char *s ) ; - -GLOBAL void CAMD_dump -( - Int n, - Int Pe [ ], - Int Iw [ ], - Int Len [ ], - Int iwlen, - Int pfree, - Int Nv [ ], - Int Next [ ], - Int Last [ ], - Int Head [ ], - Int Elen [ ], - Int Degree [ ], - Int W [ ], - Int nel, - Int BucketSet [], - const Int C [], - Int Curc -) ; - -#ifdef ASSERT -#undef ASSERT -#endif - -/* Use mxAssert if CAMD is compiled into a mexFunction */ -#ifdef MATLAB_MEX_FILE -#define ASSERT(expression) (mxAssert ((expression), "")) -#else -#define ASSERT(expression) (assert (expression)) -#endif - -#define CAMD_DEBUG0(params) { SUITESPARSE_PRINTF (params) ; } -#define CAMD_DEBUG1(params) \ - { if (CAMD_debug >= 1) SUITESPARSE_PRINTF (params) ; } -#define CAMD_DEBUG2(params) \ - { if (CAMD_debug >= 2) SUITESPARSE_PRINTF (params) ; } -#define CAMD_DEBUG3(params) \ - { if (CAMD_debug >= 3) SUITESPARSE_PRINTF (params) ; } -#define CAMD_DEBUG4(params) \ - { if (CAMD_debug >= 4) SUITESPARSE_PRINTF (params) ; } - -#else - -/* no debugging */ -#define ASSERT(expression) -#define CAMD_DEBUG0(params) -#define CAMD_DEBUG1(params) -#define CAMD_DEBUG2(params) -#define CAMD_DEBUG3(params) -#define CAMD_DEBUG4(params) - -#endif diff --git a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Lib/Makefile b/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Lib/Makefile deleted file mode 100644 index b3ae159ef..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Lib/Makefile +++ /dev/null @@ -1,102 +0,0 @@ -#------------------------------------------------------------------------------- -# CAMD Lib/Makefile -#------------------------------------------------------------------------------- - -LIBRARY = libcamd -VERSION = 2.4.6 -SO_VERSION = 2 - -default: library - -include ../../SuiteSparse_config/SuiteSparse_config.mk - -# CAMD depends on SuiteSparse_config -LDLIBS += -lsuitesparseconfig - -# compile and install in SuiteSparse/lib -library: - $(MAKE) install INSTALL=$(SUITESPARSE) - -C = $(CC) $(CF) -I../Include -I../../SuiteSparse_config - -#------------------------------------------------------------------------------- -# source files -#------------------------------------------------------------------------------- - -CAMD = camd_aat camd_1 camd_2 camd_dump camd_postorder camd_defaults \ - camd_order camd_control camd_info camd_valid camd_preprocess - -INC = ../Include/camd.h ../Include/camd_internal.h \ - ../../SuiteSparse_config/SuiteSparse_config.h - -#------------------------------------------------------------------------------- -# object files for each version -#------------------------------------------------------------------------------- - -CAMDI = $(addsuffix .o, $(subst camd_,camd_i_,$(CAMD))) -CAMDL = $(addsuffix .o, $(subst camd_,camd_l_,$(CAMD))) -OBJ = $(CAMDI) $(CAMDL) - -#------------------------------------------------------------------------------- -# compile each int and long routine (with no real/complex version) -#------------------------------------------------------------------------------- - -camd_i_%.o: ../Source/camd_%.c $(INC) - $(C) -DDINT -c $< -o $@ - -camd_l_%.o: ../Source/camd_%.c $(INC) - $(C) -DDLONG -c $< -o $@ - -#------------------------------------------------------------------------------- -# Create the static and shared libraries (C versions only) -#------------------------------------------------------------------------------- - -static: $(AR_TARGET) - -$(AR_TARGET): $(OBJ) - $(ARCHIVE) $@ $^ - - $(RANLIB) $@ - -#------------------------------------------------------------------------------- -# install (shared C version only) -#------------------------------------------------------------------------------- - -# install CAMD -install: $(AR_TARGET) $(INSTALL_LIB)/$(SO_TARGET) - -$(INSTALL_LIB)/$(SO_TARGET): $(OBJ) - @mkdir -p $(INSTALL_LIB) - @mkdir -p $(INSTALL_INCLUDE) - @mkdir -p $(INSTALL_DOC) - $(CC) $(SO_OPTS) $^ -o $@ $(LDLIBS) - ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_PLAIN) ) - ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_MAIN) ) - $(CP) ../Include/camd.h $(INSTALL_INCLUDE) - $(CP) ../Doc/CAMD_UserGuide.pdf $(INSTALL_DOC) - $(CP) ../README.txt $(INSTALL_DOC)/CAMD_README.txt - chmod 755 $(INSTALL_LIB)/$(SO_TARGET) - chmod 644 $(INSTALL_INCLUDE)/camd.h - chmod 644 $(INSTALL_DOC)/CAMD_UserGuide.pdf - chmod 644 $(INSTALL_DOC)/CAMD_README.txt - -# uninstall CAMD -uninstall: - $(RM) $(INSTALL_LIB)/$(SO_TARGET) - $(RM) $(INSTALL_LIB)/$(SO_PLAIN) - $(RM) $(INSTALL_LIB)/$(SO_MAIN) - $(RM) $(INSTALL_INCLUDE)/camd.h - $(RM) $(INSTALL_DOC)/CAMD_UserGuide.pdf - $(RM) $(INSTALL_DOC)/CAMD_README.txt - -#------------------------------------------------------------------------------- -# Remove all but the files in the original distribution -#------------------------------------------------------------------------------- - -clean: - - $(RM) -r $(CLEAN) - -purge: distclean - -distclean: clean - - $(RM) -r $(PURGE) - diff --git a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/MATLAB/Contents.m b/deps/AMICI/ThirdParty/SuiteSparse/CAMD/MATLAB/Contents.m deleted file mode 100644 index ba35d1bd1..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/MATLAB/Contents.m +++ /dev/null @@ -1,14 +0,0 @@ -%Contents of the CAMD sparse matrix ordering package: -% -% camd - p = camd (A), the approximate minimum degree ordering of A -% camd_demo - a demo of camd, using the can_24 matrix -% camd_make - to compile camd for use in MATLAB -% -% Example: -% p = camd(A) -% -% See also: camd, amd, colamd, symamd, colmmd, symmmd, umfpack - -% Copyright 1994-2007, Tim Davis, Patrick R. Amestoy, Iain S. Duff, and Y. Chen. - -help Contents diff --git a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/MATLAB/camd.m b/deps/AMICI/ThirdParty/SuiteSparse/CAMD/MATLAB/camd.m deleted file mode 100644 index de430967b..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/MATLAB/camd.m +++ /dev/null @@ -1,79 +0,0 @@ -function [p, Info] = camd (A, Control, C) %#ok -%CAMD p = camd (A), the approximate minimum degree ordering of A -% P = CAMD (S) returns the approximate minimum degree permutation vector for -% the sparse matrix C = S+S'. The Cholesky factorization of C (P,P), or -% S (P,P), tends to be sparser than that of C or S. CAMD tends to be faster -% than SYMMMD and SYMAMD, and tends to return better orderings than SYMMMD. -% S must be square. If S is full, camd(S) is equivalent to camd(sparse(S)). -% -% Usage: P = camd (S) ; % finds the ordering -% [P, Info] = camd (S,Control,C) ; % optional parameters & statistics -% Control = camd ; % returns default parameters -% camd ; % prints default parameters. -% -% Control (1); If S is n-by-n, then rows/columns with more than -% max (16, (Control (1))* sqrt(n)) entries in S+S' are considered -% "dense", and ignored during ordering. They are placed last in the -% output permutation. The default is 10.0 if Control is not present. -% Control (2): If nonzero, then aggressive absorption is performed. -% This is the default if Control is not present. -% Control (3): If nonzero, print statistics about the ordering. -% -% Info (1): status (0: ok, -1: out of memory, -2: matrix invalid) -% Info (2): n = size (A,1) -% Info (3): nnz (A) -% Info (4): the symmetry of the matrix S (0.0 means purely unsymmetric, -% 1.0 means purely symmetric). Computed as: -% B = tril (S, -1) + triu (S, 1) ; symmetry = nnz (B & B') / nnz (B); -% Info (5): nnz (diag (S)) -% Info (6): nnz in S+S', excluding the diagonal (= nnz (B+B')) -% Info (7): number "dense" rows/columns in S+S' -% Info (8): the amount of memory used by CAMD, in bytes -% Info (9): the number of memory compactions performed by CAMD -% -% The following statistics are slight upper bounds because of the -% approximate degree in CAMD. The bounds are looser if "dense" rows/columns -% are ignored during ordering (Info (7) > 0). The statistics are for a -% subsequent factorization of the matrix C (P,P). The LU factorization -% statistics assume no pivoting. -% -% Info (10): the number of nonzeros in L, excluding the diagonal -% Info (11): the number of divide operations for LL', LDL', or LU -% Info (12): the number of multiply-subtract pairs for LL' or LDL' -% Info (13): the number of multiply-subtract pairs for LU -% Info (14): the max # of nonzeros in any column of L (incl. diagonal) -% Info (15:20): unused, reserved for future use -% -% An assembly tree post-ordering is performed, which is typically the same -% as an elimination tree post-ordering. It is not always identical because -% of the approximate degree update used, and because "dense" rows/columns -% do not take part in the post-order. It well-suited for a subsequent -% "chol", however. If you require a precise elimination tree post-ordering, -% then do the following: -% -% Example: -% P = camd (S) ; -% C = spones (S) + spones (S') ; % skip this if S already symmetric -% [ignore, Q] = etree (C (P,P)) ; -% P = P (Q) ; -% -% CAMD has the ability to order the matrix with constraints. Each -% node i in the graph (row/column i in the matrix) has a constraint, -% C(i), which is in the range 1 to n. All nodes with C(i) = 1 are -% ordered first, followed by all nodes with constraint 2, and so on. -% That is, C(P) is monotonically non-decreasing. If C is not provided, -% no constraints are used (the ordering will be similar to AMD's ordering, -% except that the postordering is different). -% -% See also AMD, COLMMD, COLAMD, COLPERM, SYMAMD, SYMMMD, SYMRCM. - - -% Copyright 1994-2007, Tim Davis, Patrick R. Amestoy, Iain S. Duff, and Y. Chen. -% -% Acknowledgements: This work was supported by the National Science -% Foundation, under grants ASC-9111263, DMS-9223088, and CCR-0203270, -% and by Sandia National Laboratories. -% - -help camd -error ('camd mexFunction not found! Type "camd_make" in MATLAB to compile camd'); diff --git a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/MATLAB/camd_demo.m b/deps/AMICI/ThirdParty/SuiteSparse/CAMD/MATLAB/camd_demo.m deleted file mode 100644 index 5b5c177cb..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/MATLAB/camd_demo.m +++ /dev/null @@ -1,114 +0,0 @@ -function camd_demo -%CAMD_DEMO a demo of camd, using the can_24 matrix -% -% A demo of CAMD for MATLAB. -% -% Example: -% camd_demo -% -% See also: camd, camd_make - -% Copyright 1994-2007, Tim Davis, Patrick R. Amestoy, Iain S. Duff, and Y. Chen. - -% This orders the same matrix as the ANSI C demo, camd_demo.c. It includes an -% additional analysis of the matrix via MATLAB's symbfact routine. - -% First, print the help information for CAMD -help camd - -% Get the Harwell/Boeing can_24 matrix. - -load can_24 -A = spconvert (can_24) ; - -n = size (A,1) ; - -rand ('state', 0) ; -C = irand (6, n) ; - -clf -hold off -subplot (2,2,1) ; -spy (A) -title ('HB/can24 matrix') ; - -% print the details during CAMD ordering and SYMBFACT -% spparms ('spumoni', 1) ; - -% order the matrix. Note that the Info argument is optional. -fprintf ('\nIf the next step fails, then you have\n') ; -fprintf ('not yet compiled the CAMD mexFunction.\n') ; -[p, Info] = camd (A) ; %#ok - -% order again, but this time print some statistics -[p, camd_Info] = camd (A, [10 1 1], C) ; - -fprintf ('Permutation vector:\n') ; -fprintf (' %2d', p) ; - -fprintf ('\n\n') ; -fprintf ('Corresponding constraint sets:\n') ; - -if (any (sort (C (p)) ~= C (p))) - error ('Error!') ; -end - -for j = 1:n - fprintf (' %2d', C (p (j))) ; -end -fprintf ('\n\n\n') ; - -subplot (2,2,2) ; -spy (A (p,p)) ; -title ('Permuted matrix') ; - -% The camd_demo.c program stops here. - -fprintf ('Analyze A(p,p) with MATLAB symbfact routine:\n') ; -[cn, height, parent, post, R] = symbfact (A(p,p)) ; - -subplot (2,2,3) ; -spy (R') ; -title ('Cholesky factor L') ; - -subplot (2,2,4) ; -treeplot (parent) ; -title ('etree') ; - -% results from symbfact -lnz = sum (cn) ; % number of nonzeros in L, incl. diagonal -cn = cn - 1 ; % get the count of off-diagonal entries -fl = n + sum (cn.^2 + 2*cn) ; % flop count for chol (A (p,p) -fprintf ('number of nonzeros in L (including diagonal): %d\n', lnz) ; -fprintf ('floating point operation count for chol (A (p,p)): %d\n', fl) ; - -% approximations from camd: -lnz2 = n + camd_Info (10) ; -fl2 = n + camd_Info (11) + 2 * camd_Info (12) ; -fprintf ('\nResults from CAMD''s approximate analysis:\n') ; -fprintf ('number of nonzeros in L (including diagonal): %d\n', lnz2) ; -fprintf ('floating point operation count for chol (A (p,p)): %d\n\n', fl2) ; - -fprintf ('\nNote that the ordering quality is not as good as p=amd(A).\n') ; -fprintf ('This is only because the ordering constraints, C, have been\n') ; -fprintf ('randomly selected.\n') ; - -if (lnz2 ~= lnz | fl ~= fl2) %#ok - fprintf ('Note that the nonzero and flop counts from CAMD are slight\n') ; - fprintf ('upper bounds. This is due to the approximate minimum degree\n'); - fprintf ('method used, in conjunction with "mass elimination".\n') ; - fprintf ('See the discussion about mass elimination in camd.h and\n') ; - fprintf ('camd_2.c for more details.\n') ; -end - -% turn off diagnostic output in MATLAB's sparse matrix routines -% spparms ('spumoni', 0) ; - -%------------------------------------------------------------------------------- - -function i = irand (n,s) -% irand: return a random vector of size s, with values between 1 and n -if (nargin == 1) - s = 1 ; -end -i = min (n, 1 + floor (rand (1,s) * n)) ; diff --git a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/MATLAB/camd_demo.m.out b/deps/AMICI/ThirdParty/SuiteSparse/CAMD/MATLAB/camd_demo.m.out deleted file mode 100644 index 9cedeb522..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/MATLAB/camd_demo.m.out +++ /dev/null @@ -1,131 +0,0 @@ -camd_demo - CAMD p = camd (A), the approximate minimum degree ordering of A - P = CAMD (S) returns the approximate minimum degree permutation vector for - the sparse matrix C = S+S'. The Cholesky factorization of C (P,P), or - S (P,P), tends to be sparser than that of C or S. CAMD tends to be faster - than SYMMMD and SYMAMD, and tends to return better orderings than SYMMMD. - S must be square. If S is full, camd(S) is equivalent to camd(sparse(S)). - - Usage: P = camd (S) ; % finds the ordering - [P, Info] = camd (S,Control,C) ; % optional parameters & statistics - Control = camd ; % returns default parameters - camd ; % prints default parameters. - - Control (1); If S is n-by-n, then rows/columns with more than - max (16, (Control (1))* sqrt(n)) entries in S+S' are considered - "dense", and ignored during ordering. They are placed last in the - output permutation. The default is 10.0 if Control is not present. - Control (2): If nonzero, then aggressive absorption is performed. - This is the default if Control is not present. - Control (3): If nonzero, print statistics about the ordering. - - Info (1): status (0: ok, -1: out of memory, -2: matrix invalid) - Info (2): n = size (A,1) - Info (3): nnz (A) - Info (4): the symmetry of the matrix S (0.0 means purely unsymmetric, - 1.0 means purely symmetric). Computed as: - B = tril (S, -1) + triu (S, 1) ; symmetry = nnz (B & B') / nnz (B); - Info (5): nnz (diag (S)) - Info (6): nnz in S+S', excluding the diagonal (= nnz (B+B')) - Info (7): number "dense" rows/columns in S+S' - Info (8): the amount of memory used by CAMD, in bytes - Info (9): the number of memory compactions performed by CAMD - - The following statistics are slight upper bounds because of the - approximate degree in CAMD. The bounds are looser if "dense" rows/columns - are ignored during ordering (Info (7) > 0). The statistics are for a - subsequent factorization of the matrix C (P,P). The LU factorization - statistics assume no pivoting. - - Info (10): the number of nonzeros in L, excluding the diagonal - Info (11): the number of divide operations for LL', LDL', or LU - Info (12): the number of multiply-subtract pairs for LL' or LDL' - Info (13): the number of multiply-subtract pairs for LU - Info (14): the max # of nonzeros in any column of L (incl. diagonal) - Info (15:20): unused, reserved for future use - - An assembly tree post-ordering is performed, which is typically the same - as an elimination tree post-ordering. It is not always identical because - of the approximate degree update used, and because "dense" rows/columns - do not take part in the post-order. It well-suited for a subsequent - "chol", however. If you require a precise elimination tree post-ordering, - then do the following: - - Example: - P = camd (S) ; - C = spones (S) + spones (S') ; % skip this if S already symmetric - [ignore, Q] = etree (C (P,P)) ; - P = P (Q) ; - - CAMD has the ability to order the matrix with constraints. Each - node i in the graph (row/column i in the matrix) has a constraint, - C(i), which is in the range 1 to n. All nodes with C(i) = 1 are - ordered first, followed by all nodes with constraint 2, and so on. - That is, C(P) is monotonically non-decreasing. If C is not provided, - no constraints are used (the ordering will be similar to AMD's ordering, - except that the postordering is different). - - See also AMD, COLMMD, COLAMD, COLPERM, SYMAMD, SYMMMD, SYMRCM. - - -If the next step fails, then you have -not yet compiled the CAMD mexFunction. - -camd version 2.2, May 31, 2007: approximate minimum degree ordering: - dense row parameter: 10 - (rows with more than max (10 * sqrt (n), 16) entries are - considered "dense", and placed last in output permutation) - aggressive absorption: yes - - input matrix A is 24-by-24 - input matrix A has 160 nonzero entries - -camd: approximate minimum degree ordering, results: - status: OK - n, dimension of A: 24 - nz, number of nonzeros in A: 160 - symmetry of A: 1.0000 - number of nonzeros on diagonal: 24 - nonzeros in pattern of A+A' (excl. diagonal): 136 - # dense rows/columns of A+A': 0 - memory used, in bytes: 1644 - # of memory compactions: 0 - - The following approximate statistics are for a subsequent - factorization of A(P,P) + A(P,P)'. They are slight upper - bounds if there are no dense rows/columns in A+A', and become - looser if dense rows/columns exist. - - nonzeros in L (excluding diagonal): 149 - nonzeros in L (including diagonal): 173 - # divide operations for LDL' or LU: 149 - # multiply-subtract operations for LDL': 631 - # multiply-subtract operations for LU: 1113 - max nz. in any column of L (incl. diagonal): 12 - - chol flop count for real A, sqrt counted as 1 flop: 1435 - LDL' flop count for real A: 1411 - LDL' flop count for complex A: 6389 - LU flop count for real A (with no pivoting): 2375 - LU flop count for complex A (with no pivoting): 10245 - -Permutation vector: - 24 21 8 2 15 10 16 4 19 22 7 3 11 6 9 23 12 14 20 1 5 13 18 17 - -Corresponding constraint sets: - 1 1 1 2 2 3 3 3 3 3 3 4 4 5 5 5 5 5 6 6 6 6 6 6 - - -Analyze A(p,p) with MATLAB symbfact routine: -number of nonzeros in L (including diagonal): 173 -floating point operation count for chol (A (p,p)): 1435 - -Results from CAMD's approximate analysis: -number of nonzeros in L (including diagonal): 173 -floating point operation count for chol (A (p,p)): 1435 - - -Note that the ordering quality is not as good as p=amd(A). -This is only because the ordering constraints, C, have been -randomly selected. -diary off diff --git a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/MATLAB/camd_make.m b/deps/AMICI/ThirdParty/SuiteSparse/CAMD/MATLAB/camd_make.m deleted file mode 100644 index 7b959bfc0..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/MATLAB/camd_make.m +++ /dev/null @@ -1,43 +0,0 @@ -function camd_make -%CAMD_MAKE to compile camd for use in MATLAB -% -% Example: -% camd_make -% -% See also camd. - -% Copyright 1994-2007, Tim Davis, Patrick R. Amestoy, Iain S. Duff, and Y. Chen. - -details = 0 ; % 1 if details of each command are to be printed - -d = '' ; -if (~isempty (strfind (computer, '64'))) - d = '-largeArrayDims' ; -end - -% MATLAB 8.3.0 now has a -silent option to keep 'mex' from burbling too much -if (~verLessThan ('matlab', '8.3.0')) - d = ['-silent ' d] ; -end - -i = sprintf ('-I../Include -I../../SuiteSparse_config') ; -cmd = sprintf ('mex -O %s -DDLONG -output camd %s camd_mex.c %s', d, i, ... - '../../SuiteSparse_config/SuiteSparse_config.c') ; -files = {'camd_order', 'camd_dump', 'camd_postorder', ... - 'camd_aat', 'camd_2', 'camd_1', 'camd_defaults', 'camd_control', ... - 'camd_info', 'camd_valid', 'camd_preprocess' } ; -for i = 1 : length (files) - cmd = sprintf ('%s ../Source/%s.c', cmd, files {i}) ; -end -if (details) - fprintf ('%s\n', cmd) ; -end - -if (~(ispc || ismac)) - % for POSIX timing routine - cmd = [cmd ' -lrt'] ; -end - -eval (cmd) ; - -fprintf ('CAMD successfully compiled.\n') ; diff --git a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/MATLAB/camd_mex.c b/deps/AMICI/ThirdParty/SuiteSparse/CAMD/MATLAB/camd_mex.c deleted file mode 100644 index 2be61209e..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/MATLAB/camd_mex.c +++ /dev/null @@ -1,213 +0,0 @@ -/* ========================================================================= */ -/* === CAMD mexFunction ==================================================== */ -/* ========================================================================= */ - -/* ------------------------------------------------------------------------- */ -/* CAMD, Copyright (c) Timothy A. Davis, Yanqing Chen, */ -/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ -/* email: DrTimothyAldenDavis@gmail.com */ -/* ------------------------------------------------------------------------- */ - -/* - * Usage: - * p = camd (A) - * p = camd (A, Control) - * [p, Info] = camd (A) - * [p, Info] = camd (A, Control, C) - * Control = camd ; % return the default Control settings for CAMD - * camd ; % print the default Control settings for CAMD - * - * Given a square matrix A, compute a permutation P suitable for a Cholesky - * factorization of the matrix B (P,P), where B = spones (A) + spones (A'). - * The method used is the approximate minimum degree ordering method. See - * camd.m and camd.h for more information. - * - * The input matrix need not have sorted columns, and can have duplicate - * entries. - */ - -#include "camd.h" -#include "mex.h" -#include "matrix.h" -#define Long SuiteSparse_long - -void mexFunction -( - int nargout, - mxArray *pargout [ ], - int nargin, - const mxArray *pargin [ ] -) -{ - Long i, m, n, *Ap, *Ai, *P, nc, result, spumoni, full, *C, Clen ; - double *Pout, *InfoOut, Control [CAMD_CONTROL], Info [CAMD_INFO], - *ControlIn, *Cin ; - mxArray *A ; - - /* --------------------------------------------------------------------- */ - /* get control parameters */ - /* --------------------------------------------------------------------- */ - - spumoni = 0 ; - if (nargin == 0) - { - /* get the default control parameters, and return */ - pargout [0] = mxCreateDoubleMatrix (CAMD_CONTROL, 1, mxREAL) ; - camd_l_defaults (mxGetPr (pargout [0])) ; - if (nargout == 0) - { - camd_l_control (mxGetPr (pargout [0])) ; - } - return ; - } - - camd_l_defaults (Control) ; - if (nargin > 1) - { - ControlIn = mxGetPr (pargin [1]) ; - nc = mxGetM (pargin [1]) * mxGetN (pargin [1]) ; - Control [CAMD_DENSE] - = (nc > 0) ? ControlIn [CAMD_DENSE] : CAMD_DEFAULT_DENSE ; - Control [CAMD_AGGRESSIVE] - = (nc > 1) ? ControlIn [CAMD_AGGRESSIVE] : CAMD_DEFAULT_AGGRESSIVE ; - spumoni = (nc > 2) ? (ControlIn [2] != 0) : 0 ; - } - - if (spumoni > 0) - { - camd_l_control (Control) ; - } - - /* --------------------------------------------------------------------- */ - /* get inputs */ - /* --------------------------------------------------------------------- */ - - if (nargout > 2 || nargin > 3) - { - mexErrMsgTxt ("Usage: p = camd (A)\n" - "or [p, Info] = camd (A, Control, C)") ; - } - - Clen = 0 ; - C = NULL ; - if (nargin > 2) - { - Cin = mxGetPr (pargin [2]) ; - Clen = mxGetNumberOfElements (pargin [2]) ; - if (Clen != 0) - { - C = (Long *) mxCalloc (Clen, sizeof (Long)) ; - for (i = 0 ; i < Clen ; i++) - { - /* convert c from 1-based to 0-based */ - C [i] = (Long) Cin [i] - 1 ; - } - } - } - - A = (mxArray *) pargin [0] ; - n = mxGetN (A) ; - m = mxGetM (A) ; - if (spumoni > 0) - { - mexPrintf (" input matrix A is %d-by-%d\n", m, n) ; - } - - if (mxGetNumberOfDimensions (A) != 2) - { - mexErrMsgTxt ("camd: A must be 2-dimensional") ; - } - if (m != n) - { - mexErrMsgTxt ("camd: A must be square") ; - } - - /* --------------------------------------------------------------------- */ - /* allocate workspace for output permutation */ - /* --------------------------------------------------------------------- */ - - P = mxMalloc ((n+1) * sizeof (Long)) ; - - /* --------------------------------------------------------------------- */ - /* if A is full, convert to a sparse matrix */ - /* --------------------------------------------------------------------- */ - - full = !mxIsSparse (A) ; - if (full) - { - if (spumoni > 0) - { - mexPrintf ( - " input matrix A is full (sparse copy of A will be created)\n"); - } - mexCallMATLAB (1, &A, 1, (mxArray **) pargin, "sparse") ; - } - Ap = (Long *) mxGetJc (A) ; - Ai = (Long *) mxGetIr (A) ; - if (spumoni > 0) - { - mexPrintf (" input matrix A has %d nonzero entries\n", Ap [n]) ; - } - - /* --------------------------------------------------------------------- */ - /* order the matrix */ - /* --------------------------------------------------------------------- */ - - result = camd_l_order (n, Ap, Ai, P, Control, Info, C) ; - - /* --------------------------------------------------------------------- */ - /* if A is full, free the sparse copy of A */ - /* --------------------------------------------------------------------- */ - - if (full) - { - mxDestroyArray (A) ; - } - - /* --------------------------------------------------------------------- */ - /* print results (including return value) */ - /* --------------------------------------------------------------------- */ - - if (spumoni > 0) - { - camd_l_info (Info) ; - } - - /* --------------------------------------------------------------------- */ - /* check error conditions */ - /* --------------------------------------------------------------------- */ - - if (result == CAMD_OUT_OF_MEMORY) - { - mexErrMsgTxt ("camd: out of memory") ; - } - else if (result == CAMD_INVALID) - { - mexErrMsgTxt ("camd: input matrix A is corrupted") ; - } - - /* --------------------------------------------------------------------- */ - /* copy the outputs to MATLAB */ - /* --------------------------------------------------------------------- */ - - /* output permutation, P */ - pargout [0] = mxCreateDoubleMatrix (1, n, mxREAL) ; - Pout = mxGetPr (pargout [0]) ; - for (i = 0 ; i < n ; i++) - { - Pout [i] = P [i] + 1 ; /* change to 1-based indexing for MATLAB */ - } - mxFree (P) ; - if (nargin > 2) mxFree (C) ; - - /* Info */ - if (nargout > 1) - { - pargout [1] = mxCreateDoubleMatrix (CAMD_INFO, 1, mxREAL) ; - InfoOut = mxGetPr (pargout [1]) ; - for (i = 0 ; i < CAMD_INFO ; i++) - { - InfoOut [i] = Info [i] ; - } - } -} diff --git a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/MATLAB/can_24 b/deps/AMICI/ThirdParty/SuiteSparse/CAMD/MATLAB/can_24 deleted file mode 100644 index 38158a159..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/MATLAB/can_24 +++ /dev/null @@ -1,160 +0,0 @@ - 1 1 1 - 6 1 1 - 7 1 1 -13 1 1 -14 1 1 -18 1 1 -19 1 1 -20 1 1 -22 1 1 - 2 2 1 - 9 2 1 -10 2 1 -14 2 1 -15 2 1 -18 2 1 - 3 3 1 - 7 3 1 -12 3 1 -21 3 1 -22 3 1 -23 3 1 - 4 4 1 - 8 4 1 -11 4 1 -16 4 1 -19 4 1 -20 4 1 - 5 5 1 - 8 5 1 -10 5 1 -15 5 1 -16 5 1 -17 5 1 - 1 6 1 - 6 6 1 - 7 6 1 -13 6 1 -14 6 1 -18 6 1 - 1 7 1 - 3 7 1 - 6 7 1 - 7 7 1 -12 7 1 -13 7 1 -20 7 1 -22 7 1 -24 7 1 - 4 8 1 - 5 8 1 - 8 8 1 -10 8 1 -15 8 1 -16 8 1 -17 8 1 -18 8 1 -19 8 1 - 2 9 1 - 9 9 1 -10 9 1 -15 9 1 - 2 10 1 - 5 10 1 - 8 10 1 - 9 10 1 -10 10 1 -14 10 1 -15 10 1 -18 10 1 -19 10 1 - 4 11 1 -11 11 1 -19 11 1 -20 11 1 -21 11 1 -22 11 1 - 3 12 1 - 7 12 1 -12 12 1 -13 12 1 -22 12 1 -24 12 1 - 1 13 1 - 6 13 1 - 7 13 1 -12 13 1 -13 13 1 -24 13 1 - 1 14 1 - 2 14 1 - 6 14 1 -10 14 1 -14 14 1 -18 14 1 - 2 15 1 - 5 15 1 - 8 15 1 - 9 15 1 -10 15 1 -15 15 1 - 4 16 1 - 5 16 1 - 8 16 1 -16 16 1 -17 16 1 -19 16 1 - 5 17 1 - 8 17 1 -16 17 1 -17 17 1 - 1 18 1 - 2 18 1 - 6 18 1 - 8 18 1 -10 18 1 -14 18 1 -18 18 1 -19 18 1 -20 18 1 - 1 19 1 - 4 19 1 - 8 19 1 -10 19 1 -11 19 1 -16 19 1 -18 19 1 -19 19 1 -20 19 1 - 1 20 1 - 4 20 1 - 7 20 1 -11 20 1 -18 20 1 -19 20 1 -20 20 1 -21 20 1 -22 20 1 - 3 21 1 -11 21 1 -20 21 1 -21 21 1 -22 21 1 -23 21 1 - 1 22 1 - 3 22 1 - 7 22 1 -11 22 1 -12 22 1 -20 22 1 -21 22 1 -22 22 1 -23 22 1 - 3 23 1 -21 23 1 -22 23 1 -23 23 1 - 7 24 1 -12 24 1 -13 24 1 -24 24 1 diff --git a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Makefile b/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Makefile deleted file mode 100644 index 335116199..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Makefile +++ /dev/null @@ -1,63 +0,0 @@ -#------------------------------------------------------------------------------ -# CAMD Makefile -#------------------------------------------------------------------------------ - -SUITESPARSE ?= $(realpath $(CURDIR)/..) -export SUITESPARSE - -default: all - -include ../SuiteSparse_config/SuiteSparse_config.mk - -demos: all - -# Compile all C code. Do not compile the FORTRAN versions. -all: - ( cd Lib ; $(MAKE) ) - ( cd Demo ; $(MAKE) ) - -# compile just the C-callable libraries (not Demos) -library: - ( cd Lib ; $(MAKE) ) - -# compile the static libraries only -static: - ( cd Lib ; $(MAKE) static ) - -# remove object files, but keep the compiled programs and library archives -clean: - ( cd Lib ; $(MAKE) clean ) - ( cd Demo ; $(MAKE) clean ) - ( cd MATLAB ; $(RM) $(CLEAN) ) - ( cd Doc ; $(MAKE) clean ) - -# clean, and then remove compiled programs and library archives -purge: - ( cd Lib ; $(MAKE) purge ) - ( cd Demo ; $(MAKE) purge ) - ( cd MATLAB ; $(RM) $(CLEAN) ; $(RM) *.mex* ) - ( cd Doc ; $(MAKE) purge ) - -distclean: purge - -# create PDF documents for the original distribution -docs: - ( cd Doc ; $(MAKE) ) - -# get ready for distribution -dist: purge - ( cd Demo ; $(MAKE) dist ) - ( cd Doc ; $(MAKE) ) - -ccode: library - -lib: library - -# install CAMD -install: - ( cd Lib ; $(MAKE) install ) - -# uninstall CAMD -uninstall: - ( cd Lib ; $(MAKE) uninstall ) - diff --git a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/README.txt b/deps/AMICI/ThirdParty/SuiteSparse/CAMD/README.txt deleted file mode 100644 index 68c033497..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/README.txt +++ /dev/null @@ -1,171 +0,0 @@ -CAMD, Copyright (c) 2007-2013, University of Florida. -Written by Timothy A. Davis (http://www.suitesparse.com), Yanqing Chen, Patrick -R. Amestoy, and Iain S. Duff. All Rights Reserved. CAMD is available under -alternate licences; contact T. Davis for details. - -CAMD: a set of routines for permuting sparse matrices prior to - factorization. Includes a version in C, a version in Fortran, and a MATLAB - mexFunction. - -Requires SuiteSparse_config, in the ../SuiteSparse_config directory relative to -this directory. - -Quick start (Unix, or Windows with Cygwin): - - To compile, test, and install CAMD, you may wish to first configure the - installation by editting the ../SuiteSparse_config/SuiteSparse_config.mk - file. Next, cd to this directory (CAMD) and type "make" (or "make lib" if - you do not have MATLAB). When done, type "make clean" to remove unused *.o - files (keeps the compiled libraries and demo programs). See the User Guide - (Doc/CAMD_UserGuide.pdf), or ../SuiteSparse_config/SuiteSparse_config.mk - for more details. To install do "make install". - -Quick start (for MATLAB users); - - To compile, test, and install the CAMD mexFunction, cd to the - CAMD/MATLAB directory and type camd_make at the MATLAB prompt. - -------------------------------------------------------------------------------- - -CAMD License: refer to CAMD/Doc/License.txt - -Availability: - - http://www.suitesparse.com - -------------------------------------------------------------------------------- - -This is the CAMD README file. It is a terse overview of CAMD. -Refer to the User Guide (Doc/CAMD_UserGuide.pdf) for how to install -and use CAMD. - -Description: - - CAMD is a set of routines for pre-ordering sparse matrices prior to Cholesky - or LU factorization, using the approximate minimum degree ordering - algorithm with optional ordering constraints. Written in ANSI/ISO C with - a MATLAB interface. - -Authors: - - Timothy A. Davis (DrTimothyAldenDavis@gmail.com) - Patrick R. Amestory, ENSEEIHT, Toulouse, France. - Iain S. Duff, Rutherford Appleton Laboratory, UK. - -Acknowledgements: - - This work was supported by the National Science Foundation, under - grants DMS-9504974, DMS-9803599, and CCR-0203270. - - Portions of this work were done while on sabbatical at Stanford University - and Lawrence Berkeley National Laboratory (with funding from the SciDAC - program). I would like to thank Gene Golub, Esmond Ng, and Horst Simon - for making this sabbatical possible. - -------------------------------------------------------------------------------- -Files and directories in the CAMD distribution: -------------------------------------------------------------------------------- - - --------------------------------------------------------------------------- - Subdirectories of the CAMD directory: - --------------------------------------------------------------------------- - - Doc documentation - Source primary source code - Include include file for use in your code that calls CAMD - Demo demo programs. also serves as test of the CAMD installation. - MATLAB CAMD mexFunction for MATLAB, and supporting m-files - Lib where the compiled C-callable and Fortran-callable - CAMD libraries placed. - - --------------------------------------------------------------------------- - Files in the CAMD directory: - --------------------------------------------------------------------------- - - Makefile top-level Makefile. - Windows users would require Cygwin to use "make" - - README.txt this file - - --------------------------------------------------------------------------- - Doc directory: documentation - --------------------------------------------------------------------------- - - ChangeLog change log - License.txt the CAMD License - Makefile for creating the documentation - CAMD_UserGuide.bib CAMD User Guide (references) - CAMD_UserGuide.tex CAMD User Guide (LaTeX) - CAMD_UserGuide.pdf CAMD User Guide (PDF) - - docdiff tools for comparing CAMD with AMD - cdiff - camd.sed - - --------------------------------------------------------------------------- - Source directory: - --------------------------------------------------------------------------- - - camd_order.c user-callable, primary CAMD ordering routine - camd_control.c user-callable, prints the control parameters - camd_defaults.c user-callable, sets default control parameters - camd_info.c user-callable, prints the statistics from CAMD - - camd_1.c non-user-callable, construct A+A' - camd_2.c user-callable, primary ordering kernel - (a C version of camd.f and camdbar.f, with - post-ordering added) - camd_aat.c non-user-callable, computes nnz (A+A') - camd_dump.c non-user-callable, debugging routines - camd_postorder.c non-user-callable, postorder - camd_valid.c non-user-callable, verifies a matrix - camd_preprocess.c non-user-callable, computes A', removes duplic - - --------------------------------------------------------------------------- - Include directory: - --------------------------------------------------------------------------- - - camd.h include file for C programs that use CAMD - camd_internal.h non-user-callable, include file for CAMD - - --------------------------------------------------------------------------- - Demo directory: - --------------------------------------------------------------------------- - - Makefile to compile the demos - - camd_demo.c C demo program for CAMD - camd_demo.out output of camd_demo.c - - camd_demo2.c C demo program for CAMD, jumbled matrix - camd_demo2.out output of camd_demo2.c - - camd_l_demo.c C demo program for CAMD ("long" version) - camd_l_demo.out output of camd_l_demo.c - - camd_simple.c simple C demo program for CAMD - camd_simple.out output of camd_simple.c - - --------------------------------------------------------------------------- - MATLAB directory: - --------------------------------------------------------------------------- - - Contents.m for "help camd" listing of toolbox contents - - camd.m MATLAB help file for CAMD - camd_make.m MATLAB m-file for compiling CAMD mexFunction - camd_install.m compile and install CAMD mexFunctions - - camd_mex.c CAMD mexFunction for MATLAB - - camd_demo.m MATLAB demo for CAMD - camd_demo.m.out diary output of camd_demo.m - can_24.mat input file for CAMD demo - - --------------------------------------------------------------------------- - Lib directory: libcamd.a and libcamd.so library placed here - --------------------------------------------------------------------------- - - Makefile Makefile for both shared and static libraries - old/ old version of Makefiles - diff --git a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Source/camd_1.c b/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Source/camd_1.c deleted file mode 100644 index 753f9651e..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Source/camd_1.c +++ /dev/null @@ -1,183 +0,0 @@ -/* ========================================================================= */ -/* === CAMD_1 ============================================================== */ -/* ========================================================================= */ - -/* ------------------------------------------------------------------------- */ -/* CAMD, Copyright (c) Timothy A. Davis, Yanqing Chen, */ -/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ -/* email: DrTimothyAldenDavis@gmail.com */ -/* ------------------------------------------------------------------------- */ - -/* CAMD_1: Construct A+A' for a sparse matrix A and perform the CAMD ordering. - * - * The n-by-n sparse matrix A can be unsymmetric. It is stored in MATLAB-style - * compressed-column form, with sorted row indices in each column, and no - * duplicate entries. Diagonal entries may be present, but they are ignored. - * Row indices of column j of A are stored in Ai [Ap [j] ... Ap [j+1]-1]. - * Ap [0] must be zero, and nz = Ap [n] is the number of entries in A. The - * size of the matrix, n, must be greater than or equal to zero. - * - * This routine must be preceded by a call to CAMD_aat, which computes the - * number of entries in each row/column in A+A', excluding the diagonal. - * Len [j], on input, is the number of entries in row/column j of A+A'. This - * routine constructs the matrix A+A' and then calls CAMD_2. No error checking - * is performed (this was done in CAMD_valid). - */ - -#include "camd_internal.h" - -GLOBAL void CAMD_1 -( - Int n, /* n > 0 */ - const Int Ap [ ], /* input of size n+1, not modified */ - const Int Ai [ ], /* input of size nz = Ap [n], not modified */ - Int P [ ], /* size n output permutation */ - Int Pinv [ ], /* size n output inverse permutation */ - Int Len [ ], /* size n input, undefined on output */ - Int slen, /* slen >= sum (Len [0..n-1]) + 7n+2, - * ideally slen = 1.2 * sum (Len) + 8n+2 */ - Int S [ ], /* size slen workspace */ - double Control [ ], /* input array of size CAMD_CONTROL */ - double Info [ ], /* output array of size CAMD_INFO */ - const Int C [ ] /* Constraint set of size n */ -) -{ - Int i, j, k, p, pfree, iwlen, pj, p1, p2, pj2, *Iw, *Pe, *Nv, *Head, - *Elen, *Degree, *s, *W, *Sp, *Tp, *BucketSet ; - - /* --------------------------------------------------------------------- */ - /* construct the matrix for CAMD_2 */ - /* --------------------------------------------------------------------- */ - - ASSERT (n > 0) ; - - iwlen = slen - (7*n+2) ; /* allocate 7*n+2 workspace from S */ - s = S ; - Pe = s ; s += n ; - Nv = s ; s += n ; - Head = s ; s += n+1 ; /* NOTE: was size n in AMD; size n+1 in CAMD */ - Elen = s ; s += n ; - Degree = s ; s += n ; - W = s ; s += n+1 ; /* NOTE: was size n in AMD; size n+1 in CAMD */ - BucketSet = s ; s += n ; - Iw = s ; s += iwlen ; - - ASSERT (CAMD_valid (n, n, Ap, Ai) == CAMD_OK) ; - ASSERT (CAMD_cvalid (n, C)) ; - - /* construct the pointers for A+A' */ - Sp = Nv ; /* use Nv and W as workspace for Sp and Tp [ */ - Tp = W ; - pfree = 0 ; - for (j = 0 ; j < n ; j++) - { - Pe [j] = pfree ; - Sp [j] = pfree ; - pfree += Len [j] ; - } - - /* Note that this restriction on iwlen is slightly more restrictive than - * what is strictly required in CAMD_2. CAMD_2 can operate with no elbow - * room at all, but it will be very slow. For better performance, at - * least size-n elbow room is enforced. */ - ASSERT (iwlen >= pfree + n) ; - -#ifndef NDEBUG - for (p = 0 ; p < iwlen ; p++) Iw [p] = EMPTY ; -#endif - - for (k = 0 ; k < n ; k++) - { - CAMD_DEBUG1 (("Construct row/column k= "ID" of A+A'\n", k)) ; - p1 = Ap [k] ; - p2 = Ap [k+1] ; - - /* construct A+A' */ - for (p = p1 ; p < p2 ; ) - { - /* scan the upper triangular part of A */ - j = Ai [p] ; - ASSERT (j >= 0 && j < n) ; - if (j < k) - { - /* entry A (j,k) in the strictly upper triangular part */ - ASSERT (Sp [j] < (j == n-1 ? pfree : Pe [j+1])) ; - ASSERT (Sp [k] < (k == n-1 ? pfree : Pe [k+1])) ; - Iw [Sp [j]++] = k ; - Iw [Sp [k]++] = j ; - p++ ; - } - else if (j == k) - { - /* skip the diagonal */ - p++ ; - break ; - } - else /* j > k */ - { - /* first entry below the diagonal */ - break ; - } - /* scan lower triangular part of A, in column j until reaching - * row k. Start where last scan left off. */ - ASSERT (Ap [j] <= Tp [j] && Tp [j] <= Ap [j+1]) ; - pj2 = Ap [j+1] ; - for (pj = Tp [j] ; pj < pj2 ; ) - { - i = Ai [pj] ; - ASSERT (i >= 0 && i < n) ; - if (i < k) - { - /* A (i,j) is only in the lower part, not in upper */ - ASSERT (Sp [i] < (i == n-1 ? pfree : Pe [i+1])) ; - ASSERT (Sp [j] < (j == n-1 ? pfree : Pe [j+1])) ; - Iw [Sp [i]++] = j ; - Iw [Sp [j]++] = i ; - pj++ ; - } - else if (i == k) - { - /* entry A (k,j) in lower part and A (j,k) in upper */ - pj++ ; - break ; - } - else /* i > k */ - { - /* consider this entry later, when k advances to i */ - break ; - } - } - Tp [j] = pj ; - } - Tp [k] = p ; - } - - /* clean up, for remaining mismatched entries */ - for (j = 0 ; j < n ; j++) - { - for (pj = Tp [j] ; pj < Ap [j+1] ; pj++) - { - i = Ai [pj] ; - ASSERT (i >= 0 && i < n) ; - /* A (i,j) is only in the lower part, not in upper */ - ASSERT (Sp [i] < (i == n-1 ? pfree : Pe [i+1])) ; - ASSERT (Sp [j] < (j == n-1 ? pfree : Pe [j+1])) ; - Iw [Sp [i]++] = j ; - Iw [Sp [j]++] = i ; - } - } - -#ifndef NDEBUG - for (j = 0 ; j < n-1 ; j++) ASSERT (Sp [j] == Pe [j+1]) ; - ASSERT (Sp [n-1] == pfree) ; -#endif - - /* Tp and Sp no longer needed ] */ - - /* --------------------------------------------------------------------- */ - /* order the matrix */ - /* --------------------------------------------------------------------- */ - - CAMD_2 (n, Pe, Iw, Len, iwlen, pfree, - Nv, Pinv, P, Head, Elen, Degree, W, Control, Info, C, BucketSet) ; -} diff --git a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Source/camd_2.c b/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Source/camd_2.c deleted file mode 100644 index ac8d63691..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Source/camd_2.c +++ /dev/null @@ -1,2012 +0,0 @@ -/* ========================================================================= */ -/* === CAMD_2 ============================================================== */ -/* ========================================================================= */ - -/* ------------------------------------------------------------------------- */ -/* CAMD, Copyright (c) Timothy A. Davis, Yanqing Chen, */ -/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ -/* email: DrTimothyAldenDavis@gmail.com */ -/* ------------------------------------------------------------------------- */ - -/* CAMD_2: performs the CAMD ordering on a symmetric sparse matrix A, followed - * by a postordering (via depth-first search) of the assembly tree using the - * CAMD_postorder routine. - */ - -/* ========================================================================= */ -/* === Macros and definitions ============================================== */ -/* ========================================================================= */ - -/* True if node i is in the current Constraint Set */ -#define IsInCurrentSet(C,i,curC) ((C == NULL) ? 1 : (C [i] == curC)) - -/* True if i and j are in the same Constraint Set */ -#define InSameConstraintSet(C,i,j) ((C == NULL) ? 1 : (C [i] == C [j])) - -#include "camd_internal.h" - -/* ========================================================================= */ -/* === clear_flag ========================================================== */ -/* ========================================================================= */ - -static Int clear_flag (Int wflg, Int wbig, Int W [ ], Int n) -{ - Int x ; - if (wflg < 2 || wflg >= wbig) - { - for (x = 0 ; x < n ; x++) - { - if (W [x] != 0) W [x] = 1 ; - } - wflg = 2 ; - } - /* at this point, W [0..n-1] < wflg holds */ - return (wflg) ; -} - - -/* ========================================================================= */ -/* === CAMD_2 ============================================================== */ -/* ========================================================================= */ - -GLOBAL void CAMD_2 -( - Int n, /* A is n-by-n, where n > 0 */ - Int Pe [ ], /* Pe [0..n-1]: index in Iw of row i on input */ - Int Iw [ ], /* workspace of size iwlen. Iw [0..pfree-1] - * holds the matrix on input */ - Int Len [ ], /* Len [0..n-1]: length for row/column i on input */ - Int iwlen, /* length of Iw. iwlen >= pfree + n */ - Int pfree, /* Iw [pfree ... iwlen-1] is empty on input */ - - /* 7 size-n or size-n+1 workspaces, not defined on input: */ - Int Nv [ ], /* size n, the size of each supernode on output */ - Int Next [ ], /* size n, the output inverse permutation */ - Int Last [ ], /* size n, the output permutation */ - Int Head [ ], /* size n+1 (Note: it was size n in AMD) */ - Int Elen [ ], /* size n, the size columns of L for each supernode */ - Int Degree [ ], /* size n */ - Int W [ ], /* size n+1 (Note: it was size n in AMD) */ - - /* control parameters and output statistics */ - double Control [ ], /* array of size CAMD_CONTROL */ - double Info [ ], /* array of size CAMD_INFO */ - - /* input, not modified: */ - const Int C [ ], /* size n, C [i] is the constraint set of node i */ - - /* size-n workspace, not defined on input or output: */ - Int BucketSet [ ] /* size n */ -) -{ - -/* - * Given a representation of the nonzero pattern of a symmetric matrix, A, - * (excluding the diagonal) perform an approximate minimum (UMFPACK/MA38-style) - * degree ordering to compute a pivot order such that the introduction of - * nonzeros (fill-in) in the Cholesky factors A = LL' is kept low. At each - * step, the pivot selected is the one with the minimum UMFAPACK/MA38-style - * upper-bound on the external degree. This routine can optionally perform - * aggresive absorption (as done by MC47B in the Harwell Subroutine - * Library). - * - * The approximate degree algorithm implemented here is the symmetric analog of - * the degree update algorithm in MA38 and UMFPACK (the Unsymmetric-pattern - * MultiFrontal PACKage, both by Davis and Duff). The routine is based on the - * MA27 minimum degree ordering algorithm by Iain Duff and John Reid. - * - * This routine is a translation of the original AMDBAR and MC47B routines, - * in Fortran, with the following modifications: - * - * (1) dense rows/columns are removed prior to ordering the matrix, and placed - * last in the output order. The presence of a dense row/column can - * increase the ordering time by up to O(n^2), unless they are removed - * prior to ordering. - * - * (2) the minimum degree ordering is followed by a postordering (depth-first - * search) of the assembly tree. Note that mass elimination (discussed - * below) combined with the approximate degree update can lead to the mass - * elimination of nodes with lower exact degree than the current pivot - * element. No additional fill-in is caused in the representation of the - * Schur complement. The mass-eliminated nodes merge with the current - * pivot element. They are ordered prior to the current pivot element. - * Because they can have lower exact degree than the current element, the - * merger of two or more of these nodes in the current pivot element can - * lead to a single element that is not a "fundamental supernode". The - * diagonal block can have zeros in it. Thus, the assembly tree used here - * is not guaranteed to be the precise supernodal elemination tree (with - * "funadmental" supernodes), and the postordering performed by this - * routine is not guaranteed to be a precise postordering of the - * elimination tree. - * - * (3) input parameters are added, to control aggressive absorption and the - * detection of "dense" rows/columns of A. - * - * (4) additional statistical information is returned, such as the number of - * nonzeros in L, and the flop counts for subsequent LDL' and LU - * factorizations. These are slight upper bounds, because of the mass - * elimination issue discussed above. - * - * (5) additional routines are added to interface this routine to MATLAB - * to provide a simple C-callable user-interface, to check inputs for - * errors, compute the symmetry of the pattern of A and the number of - * nonzeros in each row/column of A+A', to compute the pattern of A+A', - * to perform the assembly tree postordering, and to provide debugging - * ouput. Many of these functions are also provided by the Fortran - * Harwell Subroutine Library routine MC47A. - * - * (6) both "int" and "long" versions are provided. In the descriptions below - * and integer is and "int" or "long", depending on which version is - * being used. - - ********************************************************************** - ***** CAUTION: ARGUMENTS ARE NOT CHECKED FOR ERRORS ON INPUT. ****** - ********************************************************************** - ** If you want error checking, a more versatile input format, and a ** - ** simpler user interface, use camd_order or camd_l_order instead. ** - ** This routine is not meant to be user-callable. ** - ********************************************************************** - - * ---------------------------------------------------------------------------- - * References: - * ---------------------------------------------------------------------------- - * - * [1] Timothy A. Davis and Iain Duff, "An unsymmetric-pattern multifrontal - * method for sparse LU factorization", SIAM J. Matrix Analysis and - * Applications, vol. 18, no. 1, pp. 140-158. Discusses UMFPACK / MA38, - * which first introduced the approximate minimum degree used by this - * routine. - * - * [2] Patrick Amestoy, Timothy A. Davis, and Iain S. Duff, "An approximate - * minimum degree ordering algorithm," SIAM J. Matrix Analysis and - * Applications, vol. 17, no. 4, pp. 886-905, 1996. Discusses AMDBAR and - * MC47B, which are the Fortran versions of this routine. - * - * [3] Alan George and Joseph Liu, "The evolution of the minimum degree - * ordering algorithm," SIAM Review, vol. 31, no. 1, pp. 1-19, 1989. - * We list below the features mentioned in that paper that this code - * includes: - * - * mass elimination: - * Yes. MA27 relied on supervariable detection for mass elimination. - * - * indistinguishable nodes: - * Yes (we call these "supervariables"). This was also in the MA27 - * code - although we modified the method of detecting them (the - * previous hash was the true degree, which we no longer keep track - * of). A supervariable is a set of rows with identical nonzero - * pattern. All variables in a supervariable are eliminated together. - * Each supervariable has as its numerical name that of one of its - * variables (its principal variable). - * - * quotient graph representation: - * Yes. We use the term "element" for the cliques formed during - * elimination. This was also in the MA27 code. The algorithm can - * operate in place, but it will work more efficiently if given some - * "elbow room." - * - * element absorption: - * Yes. This was also in the MA27 code. - * - * external degree: - * Yes. The MA27 code was based on the true degree. - * - * incomplete degree update and multiple elimination: - * No. This was not in MA27, either. Our method of degree update - * within MC47B is element-based, not variable-based. It is thus - * not well-suited for use with incomplete degree update or multiple - * elimination. - * - * AMD Authors, and Copyright (C) 2004 by: - * Timothy A. Davis, Patrick Amestoy, Iain S. Duff, John K. Reid. - * Modifications for CAMD authored by Davis and Yanqing "Morris" Chen. - * - * Acknowledgements: This work (and the UMFPACK package) was supported by the - * National Science Foundation (ASC-9111263, DMS-9223088, and CCR-0203270). - * The UMFPACK/MA38 approximate degree update algorithm, the unsymmetric analog - * which forms the basis of CAMD, was developed while Tim Davis was supported by - * CERFACS (Toulouse, France) in a post-doctoral position. This C version, and - * the etree postorder, were written while Tim Davis was on sabbatical at - * Stanford University and Lawrence Berkeley National Laboratory. - * Ordering constraints were added with support from Sandia National Labs (DOE). - - * ---------------------------------------------------------------------------- - * INPUT ARGUMENTS (unaltered): - * ---------------------------------------------------------------------------- - - * n: The matrix order. Restriction: n >= 1. - * - * iwlen: The size of the Iw array. On input, the matrix is stored in - * Iw [0..pfree-1]. However, Iw [0..iwlen-1] should be slightly larger - * than what is required to hold the matrix, at least iwlen >= pfree + n. - * Otherwise, excessive compressions will take place. The recommended - * value of iwlen is 1.2 * pfree + n, which is the value used in the - * user-callable interface to this routine (camd_order.c). The algorithm - * will not run at all if iwlen < pfree. Restriction: iwlen >= pfree + n. - * Note that this is slightly more restrictive than the actual minimum - * (iwlen >= pfree), but CAMD_2 will be very slow with no elbow room. - * Thus, this routine enforces a bare minimum elbow room of size n. - * - * pfree: On input the tail end of the array, Iw [pfree..iwlen-1], is empty, - * and the matrix is stored in Iw [0..pfree-1]. During execution, - * additional data is placed in Iw, and pfree is modified so that - * Iw [pfree..iwlen-1] is always the unused part of Iw. - * - * Control: A double array of size CAMD_CONTROL containing input parameters - * that affect how the ordering is computed. If NULL, then default - * settings are used. - * - * Control [CAMD_DENSE] is used to determine whether or not a given input - * row is "dense". A row is "dense" if the number of entries in the row - * exceeds Control [CAMD_DENSE] times sqrt (n), except that rows with 16 or - * fewer entries are never considered "dense". To turn off the detection - * of dense rows, set Control [CAMD_DENSE] to a negative number, or to a - * number larger than sqrt (n). The default value of Control [CAMD_DENSE] - * is CAMD_DEFAULT_DENSE, which is defined in camd.h as 10. - * - * Control [CAMD_AGGRESSIVE] is used to determine whether or not aggressive - * absorption is to be performed. If nonzero, then aggressive absorption - * is performed (this is the default). - * - * C: defines the ordering constraints. s = C [j] gives the constraint set s - * that contains the row/column j (Restriction: 0 <= s < n). - * In the output row permutation, all rows in set 0 appear first, followed - * by all rows in set 1, and so on. If NULL, all rows are treated as if - * they were in a single constraint set, and you will obtain a similar - * ordering as AMD (slightly different because of the different - * postordering used). - - * ---------------------------------------------------------------------------- - * INPUT/OUPUT ARGUMENTS: - * ---------------------------------------------------------------------------- - * - * Pe: An integer array of size n. On input, Pe [i] is the index in Iw of - * the start of row i. Pe [i] is ignored if row i has no off-diagonal - * entries. Thus Pe [i] must be in the range 0 to pfree-1 for non-empty - * rows. - * - * During execution, it is used for both supervariables and elements: - * - * Principal supervariable i: index into Iw of the description of - * supervariable i. A supervariable represents one or more rows of - * the matrix with identical nonzero pattern. In this case, - * Pe [i] >= 0. - * - * Non-principal supervariable i: if i has been absorbed into another - * supervariable j, then Pe [i] = FLIP (j), where FLIP (j) is defined - * as (-(j)-2). Row j has the same pattern as row i. Note that j - * might later be absorbed into another supervariable j2, in which - * case Pe [i] is still FLIP (j), and Pe [j] = FLIP (j2) which is - * < EMPTY, where EMPTY is defined as (-1) in camd_internal.h. - * - * Unabsorbed element e: the index into Iw of the description of element - * e, if e has not yet been absorbed by a subsequent element. Element - * e is created when the supervariable of the same name is selected as - * the pivot. In this case, Pe [i] >= 0. - * - * Absorbed element e: if element e is absorbed into element e2, then - * Pe [e] = FLIP (e2). This occurs when the pattern of e (which we - * refer to as Le) is found to be a subset of the pattern of e2 (that - * is, Le2). In this case, Pe [i] < EMPTY. If element e is "null" - * (it has no nonzeros outside its pivot block), then Pe [e] = EMPTY, - * and e is the root of an assembly subtree (or the whole tree if - * there is just one such root). - * - * Dense or empty variable i: if i is "dense" or empty (with zero degree), - * then Pe [i] = FLIP (n). - * - * On output, Pe holds the assembly tree/forest, which implicitly - * represents a pivot order with identical fill-in as the actual order - * (via a depth-first search of the tree), as follows. If Nv [i] > 0, - * then i represents a node in the assembly tree, and the parent of i is - * Pe [i], or EMPTY if i is a root. If Nv [i] = 0, then (i, Pe [i]) - * represents an edge in a subtree, the root of which is a node in the - * assembly tree. Note that i refers to a row/column in the original - * matrix, not the permuted matrix. - * - * Info: A double array of size CAMD_INFO. If present, (that is, not NULL), - * then statistics about the ordering are returned in the Info array. - * See camd.h for a description. - - * ---------------------------------------------------------------------------- - * INPUT/MODIFIED (undefined on output): - * ---------------------------------------------------------------------------- - * - * Len: An integer array of size n. On input, Len [i] holds the number of - * entries in row i of the matrix, excluding the diagonal. The contents - * of Len are undefined on output. Len also works as a temporary - * workspace in post ordering with dense nodes detected. - * - * Iw: An integer array of size iwlen. On input, Iw [0..pfree-1] holds the - * description of each row i in the matrix. The matrix must be symmetric, - * and both upper and lower triangular parts must be present. The - * diagonal must not be present. Row i is held as follows: - * - * Len [i]: the length of the row i data structure in the Iw array. - * Iw [Pe [i] ... Pe [i] + Len [i] - 1]: - * the list of column indices for nonzeros in row i (simple - * supervariables), excluding the diagonal. All supervariables - * start with one row/column each (supervariable i is just row i). - * If Len [i] is zero on input, then Pe [i] is ignored on input. - * - * Note that the rows need not be in any particular order, and there - * may be empty space between the rows. - * - * During execution, the supervariable i experiences fill-in. This is - * represented by placing in i a list of the elements that cause fill-in - * in supervariable i: - * - * Len [i]: the length of supervariable i in the Iw array. - * Iw [Pe [i] ... Pe [i] + Elen [i] - 1]: - * the list of elements that contain i. This list is kept short - * by removing absorbed elements. - * Iw [Pe [i] + Elen [i] ... Pe [i] + Len [i] - 1]: - * the list of supervariables in i. This list is kept short by - * removing nonprincipal variables, and any entry j that is also - * contained in at least one of the elements (j in Le) in the list - * for i (e in row i). - * - * When supervariable i is selected as pivot, we create an element e of - * the same name (e=i): - * - * Len [e]: the length of element e in the Iw array. - * Iw [Pe [e] ... Pe [e] + Len [e] - 1]: - * the list of supervariables in element e. - * - * An element represents the fill-in that occurs when supervariable i is - * selected as pivot (which represents the selection of row i and all - * non-principal variables whose principal variable is i). We use the - * term Le to denote the set of all supervariables in element e. Absorbed - * supervariables and elements are pruned from these lists when - * computationally convenient. - * - * CAUTION: THE INPUT MATRIX IS OVERWRITTEN DURING COMPUTATION. - * The contents of Iw are undefined on output. - - * ---------------------------------------------------------------------------- - * OUTPUT (need not be set on input): - * ---------------------------------------------------------------------------- - * - * - * Nv: An integer array of size n. During execution, ABS (Nv [i]) is equal to - * the number of rows that are represented by the principal supervariable - * i. If i is a nonprincipal or dense variable, then Nv [i] = 0. - * Initially, Nv [i] = 1 for all i. Nv [i] < 0 signifies that i is a - * principal variable in the pattern Lme of the current pivot element me. - * After element me is constructed, Nv [i] is set back to a positive - * value. - * - * On output, Nv [i] holds the number of pivots represented by super - * row/column i of the original matrix, or Nv [i] = 0 for non-principal - * rows/columns. Note that i refers to a row/column in the original - * matrix, not the permuted matrix. - * - * Nv also works as a temporary workspace in initializing the BucketSet - * array. - * - * Elen: An integer array of size n. See the description of Iw above. At the - * start of execution, Elen [i] is set to zero for all rows i. During - * execution, Elen [i] is the number of elements in the list for - * supervariable i. When e becomes an element, Elen [e] = FLIP (esize) is - * set, where esize is the size of the element (the number of pivots, plus - * the number of nonpivotal entries). Thus Elen [e] < EMPTY. - * Elen (i) = EMPTY set when variable i becomes nonprincipal. - * - * For variables, Elen (i) >= EMPTY holds until just before the - * postordering and permutation vectors are computed. For elements, - * Elen [e] < EMPTY holds. - * - * On output, Elen [i] is the degree of the row/column in the Cholesky - * factorization of the permuted matrix, corresponding to the original row - * i, if i is a super row/column. It is equal to EMPTY if i is - * non-principal. Note that i refers to a row/column in the original - * matrix, not the permuted matrix. - * - * Note that the contents of Elen on output differ from the Fortran - * version (Elen holds the inverse permutation in the Fortran version, - * which is instead returned in the Next array in this C version, - * described below). - * - * Last: In a degree list, Last [i] is the supervariable preceding i, or EMPTY - * if i is the head of the list. In a hash bucket, Last [i] is the hash - * key for i. - * - * Last [Head [hash]] is also used as the head of a hash bucket if - * Head [hash] contains a degree list (see the description of Head, - * below). - * - * On output, Last [0..n-1] holds the permutation. That is, if - * i = Last [k], then row i is the kth pivot row (where k ranges from 0 to - * n-1). Row Last [k] of A is the kth row in the permuted matrix, PAP'. - * - * Next: Next [i] is the supervariable following i in a link list, or EMPTY if - * i is the last in the list. Used for two kinds of lists: degree lists - * and hash buckets (a supervariable can be in only one kind of list at a - * time). - * - * On output Next [0..n-1] holds the inverse permutation. That is, if - * k = Next [i], then row i is the kth pivot row. Row i of A appears as - * the (Next[i])-th row in the permuted matrix, PAP'. - * - * Note that the contents of Next on output differ from the Fortran - * version (Next is undefined on output in the Fortran version). - - * ---------------------------------------------------------------------------- - * LOCAL WORKSPACE (not input or output - used only during execution): - * ---------------------------------------------------------------------------- - * - * Degree: An integer array of size n. If i is a supervariable, then - * Degree [i] holds the current approximation of the external degree of - * row i (an upper bound). The external degree is the number of nonzeros - * in row i, minus ABS (Nv [i]), the diagonal part. The bound is equal to - * the exact external degree if Elen [i] is less than or equal to two. - * - * We also use the term "external degree" for elements e to refer to - * |Le \ Lme|. If e is an element, then Degree [e] is |Le|, which is the - * degree of the off-diagonal part of the element e (not including the - * diagonal part). - * - * Head: An integer array of size n. Head is used for degree lists. - * Head [deg] is the first supervariable in a degree list. All - * supervariables i in a degree list Head [deg] have the same approximate - * degree, namely, deg = Degree [i]. If the list Head [deg] is empty then - * Head [deg] = EMPTY. - * - * During supervariable detection Head [hash] also serves as a pointer to - * a hash bucket. If Head [hash] >= 0, there is a degree list of degree - * hash. The hash bucket head pointer is Last [Head [hash]]. If - * Head [hash] = EMPTY, then the degree list and hash bucket are both - * empty. If Head [hash] < EMPTY, then the degree list is empty, and - * FLIP (Head [hash]) is the head of the hash bucket. After supervariable - * detection is complete, all hash buckets are empty, and the - * (Last [Head [hash]] = EMPTY) condition is restored for the non-empty - * degree lists. - * - * Head also workes as a temporary workspace in post ordering with dense - * nodes detected. - * - * W: An integer array of size n. The flag array W determines the status of - * elements and variables, and the external degree of elements. - * - * for elements: - * if W [e] = 0, then the element e is absorbed. - * if W [e] >= wflg, then W [e] - wflg is the size of the set - * |Le \ Lme|, in terms of nonzeros (the sum of ABS (Nv [i]) for - * each principal variable i that is both in the pattern of - * element e and NOT in the pattern of the current pivot element, - * me). - * if wflg > W [e] > 0, then e is not absorbed and has not yet been - * seen in the scan of the element lists in the computation of - * |Le\Lme| in Scan 1 below. - * - * for variables: - * during supervariable detection, if W [j] != wflg then j is - * not in the pattern of variable i. - * - * The W array is initialized by setting W [i] = 1 for all i, and by - * setting wflg = 2. It is reinitialized if wflg becomes too large (to - * ensure that wflg+n does not cause integer overflow). - * - * BucketSet: An integer array of size n. - * During execution it stores the rows that sorted in the ascending order - * based on C []. For instance: if C[]={0,2,1,0,1,0,2,1}, the - * Bucketset will be {0,3,5,2,4,7,1,6}. - * The elements in Bucketset are then modified, to maintain the order of - * roots (Pe[i]=-1) in each Constraint Set. - - * ---------------------------------------------------------------------------- - * LOCAL INTEGERS: - * ---------------------------------------------------------------------------- - */ - - Int deg, degme, dext, lemax, e, elenme, eln, i, ilast, inext, j, - jlast, k, knt1, knt2, knt3, lenj, ln, me, mindeg, nel, nleft, - nvi, nvj, nvpiv, slenme, wbig, we, wflg, wnvi, ok, ndense, ncmpa, nnull, - dense, aggressive ; - - unsigned Int hash ; /* unsigned, so that hash % n is well defined.*/ - -/* - * deg: the degree of a variable or element - * degme: size, |Lme|, of the current element, me (= Degree [me]) - * dext: external degree, |Le \ Lme|, of some element e - * lemax: largest |Le| seen so far (called dmax in Fortran version) - * e: an element - * elenme: the length, Elen [me], of element list of pivotal variable - * eln: the length, Elen [...], of an element list - * hash: the computed value of the hash function - * i: a supervariable - * ilast: the entry in a link list preceding i - * inext: the entry in a link list following i - * j: a supervariable - * jlast: the entry in a link list preceding j - * k: the pivot order of an element or variable - * knt1: loop counter used during element construction - * knt2: loop counter used during element construction - * knt3: loop counter used during compression - * lenj: Len [j] - * ln: length of a supervariable list - * me: current supervariable being eliminated, and the current - * element created by eliminating that supervariable - * mindeg: current minimum degree - * nel: number of pivots selected so far - * nleft: n - nel, the number of nonpivotal rows/columns remaining - * nvi: the number of variables in a supervariable i (= Nv [i]) - * nvj: the number of variables in a supervariable j (= Nv [j]) - * nvpiv: number of pivots in current element - * slenme: number of variables in variable list of pivotal variable - * wbig: = INT_MAX - n for the "int" version, LONG_MAX - n for the - * "long" version. wflg is not allowed to be >= wbig. - * we: W [e] - * wflg: used for flagging the W array. See description of Iw. - * wnvi: wflg - Nv [i] - * x: either a supervariable or an element - * - * ok: true if supervariable j can be absorbed into i - * ndense: number of "dense" rows/columns - * nnull: number of empty rows/columns - * dense: rows/columns with initial degree > dense are considered "dense" - * aggressive: true if aggressive absorption is being performed - * ncmpa: number of garbage collections - - * ---------------------------------------------------------------------------- - * LOCAL DOUBLES, used for statistical output only (except for alpha): - * ---------------------------------------------------------------------------- - */ - - double f, r, ndiv, s, nms_lu, nms_ldl, dmax, alpha, lnz, lnzme ; - -/* - * f: nvpiv - * r: degme + nvpiv - * ndiv: number of divisions for LU or LDL' factorizations - * s: number of multiply-subtract pairs for LU factorization, for the - * current element me - * nms_lu number of multiply-subtract pairs for LU factorization - * nms_ldl number of multiply-subtract pairs for LDL' factorization - * dmax: the largest number of entries in any column of L, including the - * diagonal - * alpha: "dense" degree ratio - * lnz: the number of nonzeros in L (excluding the diagonal) - * lnzme: the number of nonzeros in L (excl. the diagonal) for the - * current element me - - * ---------------------------------------------------------------------------- - * LOCAL "POINTERS" (indices into the Iw array) - * ---------------------------------------------------------------------------- -*/ - - Int p, p1, p2, p3, p4, pdst, pend, pj, pme, pme1, pme2, pn, psrc ; - -/* - * Any parameter (Pe [...] or pfree) or local variable starting with "p" (for - * Pointer) is an index into Iw, and all indices into Iw use variables starting - * with "p." The only exception to this rule is the iwlen input argument. - * - * p: pointer into lots of things - * p1: Pe [i] for some variable i (start of element list) - * p2: Pe [i] + Elen [i] - 1 for some variable i - * p3: index of first supervariable in clean list - * p4: - * pdst: destination pointer, for compression - * pend: end of memory to compress - * pj: pointer into an element or variable - * pme: pointer into the current element (pme1...pme2) - * pme1: the current element, me, is stored in Iw [pme1...pme2] - * pme2: the end of the current element - * pn: pointer into a "clean" variable, also used to compress - * psrc: source pointer, for compression -*/ - - Int curC, pBucket, pBucket2, degreeListCounter, c, cmax = 0, - ndense_or_null ; - Int *Bucket, *Perm ; - -/* - * curC: the current Constraint Set being ordered - * pBucket: pointer into Bucketset[] when building the degreelist for each - * Constraint Set - * pBucket2: pointer into Bucketset[] to tell the post ordering where to stop - * degreeListCounter: number of elements remaining in the - * degreelist of current Constraint Set - * Bucket: used to construct BucketSet - * Perm: permutation - */ - -/* ========================================================================= */ -/* INITIALIZATIONS */ -/* ========================================================================= */ - - /* Note that this restriction on iwlen is slightly more restrictive than - * what is actually required in CAMD_2. CAMD_2 can operate with no elbow - * room at all, but it will be slow. For better performance, at least - * size-n elbow room is enforced. */ - ASSERT (iwlen >= pfree + n) ; - ASSERT (n > 0) ; - - /* initialize output statistics */ - lnz = 0 ; - ndiv = 0 ; - nms_lu = 0 ; - nms_ldl = 0 ; - dmax = 1 ; - me = EMPTY ; - - mindeg = 0 ; - ncmpa = 0 ; - nel = 0 ; - lemax = 0 ; - curC = 0 ; - -/* camd work initBucketSet using CountingSort - * BucketSort the index Array BucketSet According to Contrains Array C, Using - * Nv[] as a temporary workspace - * Input: Index Array from 0 to n.(index of rows) - * Output: Index Array sorted according to C. worked as a bucket set. - * - * All the value in C must be 0 <= C[i] <= n-1 - * For instance: if C[]={0,2,1,0,1,0,2,1}, the output Bucketset should be - * {0,3,5,2,4,7,1,6} - */ - - -/* CountingSort BucketSet[] based on C[], It is O(n) linear time */ - - if (C == NULL) - { - /* store everything in bucket without changing order */ - for (j = 0 ; j < n ; j++) - { - BucketSet [j] = j ; - } - } - else - { - - Bucket = Nv ; - for (i = 0 ; i < n ; i++) - { - Bucket [i] = 0 ; - } - cmax = C [0] ; - for (j = 0 ; j < n ; j++) - { - c = C [j] ; - CAMD_DEBUG1 (("C [%d] = "ID"\n", j, c)) ; - Bucket [c]++ ; - cmax = MAX (cmax, c) ; - ASSERT (c >= 0 && c < n) ; - } - CAMD_DEBUG1 (("Max constraint set: "ID"\n", cmax)) ; - for (i = 1 ; i < n ; i++) - { - Bucket [i] += Bucket [i-1] ; - } - for (j = n-1 ; j >= 0 ; j--) - { - BucketSet [--Bucket [C [j]]] = j ; - } - -#ifndef NDEBUG - CAMD_DEBUG3 (("\nConstraint Set "ID" :", C [BucketSet [0]])); - for (i = 0 ; i < n ; i++) - { - CAMD_DEBUG3 ((ID" ", BucketSet [i])) ; - if (i == n-1) - { - CAMD_DEBUG3 (("\n")) ; - break ; - } - if (C [BucketSet [i+1]] != C [BucketSet [i]]) - { - CAMD_DEBUG3 (("\nConstraint Set "ID" :", C [BucketSet [i+1]])) ; - } - } -#endif - } - - /* get control parameters */ - if (Control != (double *) NULL) - { - alpha = Control [CAMD_DENSE] ; - aggressive = (Control [CAMD_AGGRESSIVE] != 0) ; - } - else - { - alpha = CAMD_DEFAULT_DENSE ; - aggressive = CAMD_DEFAULT_AGGRESSIVE ; - } - /* Note: if alpha is NaN, this is undefined: */ - if (alpha < 0) - { - /* only remove completely dense rows/columns */ - dense = n-2 ; - } - else - { - dense = alpha * sqrt ((double) n) ; - } - dense = MAX (16, dense) ; - dense = MIN (n, dense) ; - CAMD_DEBUG1 (("\n\nCAMD (debug), alpha %g, aggr. "ID"\n", - alpha, aggressive)) ; - - for (i = 0 ; i < n ; i++) - { - Last [i] = EMPTY ; - Head [i] = EMPTY ; - Next [i] = EMPTY ; - /* if separate Hhead array is used for hash buckets: * - Hhead [i] = EMPTY ; - */ - Nv [i] = 1 ; - W [i] = 1 ; - Elen [i] = 0 ; - Degree [i] = Len [i] ; - } - Head [n] = EMPTY ; - - /* initialize wflg */ - wbig = Int_MAX - n ; - wflg = clear_flag (0, wbig, W, n) ; - - /* --------------------------------------------------------------------- */ - /* eliminate dense and empty rows */ - /* --------------------------------------------------------------------- */ - - ndense = 0 ; - nnull = 0 ; - - for (j = 0 ; j < n ; j++) - { - i = BucketSet [j] ; - deg = Degree [i] ; - ASSERT (deg >= 0 && deg < n) ; - if (deg > dense || deg == 0) - { - - /* ------------------------------------------------------------- - * Dense or empty variables are treated as non-principal variables - * represented by node n. That is, i is absorbed into n, just like - * j is absorbed into i in supervariable detection (see "found it!" - * comment, below). - * ------------------------------------------------------------- */ - - if (deg > dense) - { - CAMD_DEBUG1 (("Dense node "ID" degree "ID" bucket "ID"\n", - i, deg, j)) ; - ndense++ ; - } - else - { - CAMD_DEBUG1 (("Empty node "ID" degree "ID" bucket "ID"\n", - i, deg, j)) ; - nnull++ ; - } - Pe [i] = FLIP (n) ; - Nv [i] = 0 ; /* do not postorder this node */ - Elen [i] = EMPTY ; - nel++ ; - } - } - - ndense_or_null = ndense + nnull ; - - pBucket = 0 ; - degreeListCounter = 0 ; - pBucket2 = 0 ; - -/* ========================================================================= */ -/* WHILE (selecting pivots) DO */ -/* ========================================================================= */ - - while (nel < n) - { - - /* ------------------------------------------------------------------ */ - /* if empty, fill the degree list with next non-empty constraint set */ - /* ------------------------------------------------------------------ */ - - while (degreeListCounter == 0) - { - mindeg = n ; - /* determine the new constraint set */ - curC = (C == NULL) ? 0 : C [BucketSet [pBucket]] ; - for ( ; pBucket < n ; pBucket++) - { - /* add i to the degree list, unless it's dead or not in curC */ - i = BucketSet [pBucket] ; - if (!IsInCurrentSet (C, i, curC)) break ; - deg = Degree [i] ; - ASSERT (deg >= 0 && deg < n) ; - if (Pe [i] >= 0) - { - - /* ------------------------------------------------------ - * place i in the degree list corresponding to its degree - * ------------------------------------------------------ */ - - inext = Head [deg] ; - ASSERT (inext >= EMPTY && inext < n) ; - if (inext != EMPTY) Last [inext] = i ; - Next [i] = inext ; - Head [deg] = i ; - degreeListCounter++ ; - Last [i] = EMPTY ; - mindeg = MIN (mindeg, deg) ; - } - } - } - -#ifndef NDEBUG - CAMD_DEBUG1 (("\n======Nel "ID"\n", nel)) ; - if (CAMD_debug >= 2) - { - CAMD_dump (n, Pe, Iw, Len, iwlen, pfree, Nv, Next, - Last, Head, Elen, Degree, W, nel, BucketSet, C, curC) ; - } -#endif - -/* ========================================================================= */ -/* GET PIVOT OF MINIMUM DEGREE */ -/* ========================================================================= */ - - /* ----------------------------------------------------------------- */ - /* find next supervariable for elimination */ - /* ----------------------------------------------------------------- */ - - ASSERT (mindeg >= 0 && mindeg < n) ; - for (deg = mindeg ; deg < n ; deg++) - { - me = Head [deg] ; - if (me != EMPTY) break ; - } - mindeg = deg ; - ASSERT (me >= 0 && me < n) ; - CAMD_DEBUG1 (("=================me: "ID"\n", me)) ; - - /* ----------------------------------------------------------------- */ - /* remove chosen variable from link list */ - /* ----------------------------------------------------------------- */ - - inext = Next [me] ; - ASSERT (inext >= EMPTY && inext < n) ; - if (inext != EMPTY) Last [inext] = EMPTY ; - Head [deg] = inext ; - degreeListCounter-- ; - - /* ----------------------------------------------------------------- */ - /* me represents the elimination of pivots nel to nel+Nv[me]-1. */ - /* place me itself as the first in this set. */ - /* ----------------------------------------------------------------- */ - - elenme = Elen [me] ; - nvpiv = Nv [me] ; - ASSERT (nvpiv > 0) ; - nel += nvpiv ; - CAMD_DEBUG1 (("nvpiv is initially "ID"\n", nvpiv)) ; - -/* ========================================================================= */ -/* CONSTRUCT NEW ELEMENT */ -/* ========================================================================= */ - - /* ----------------------------------------------------------------- - * At this point, me is the pivotal supervariable. It will be - * converted into the current element. Scan list of the pivotal - * supervariable, me, setting tree pointers and constructing new list - * of supervariables for the new element, me. p is a pointer to the - * current position in the old list. - * ----------------------------------------------------------------- */ - - /* flag the variable "me" as being in Lme by negating Nv [me] */ - Nv [me] = -nvpiv ; - degme = 0 ; - ASSERT (Pe [me] >= 0 && Pe [me] < iwlen) ; - - if (elenme == 0) - { - - /* ------------------------------------------------------------- */ - /* construct the new element in place */ - /* ------------------------------------------------------------- */ - - pme1 = Pe [me] ; - pme2 = pme1 - 1 ; - - for (p = pme1 ; p <= pme1 + Len [me] - 1 ; p++) - { - i = Iw [p] ; - ASSERT (i >= 0 && i < n && Nv [i] >= 0) ; - nvi = Nv [i] ; - if (nvi > 0) - { - - /* ----------------------------------------------------- */ - /* i is a principal variable not yet placed in Lme. */ - /* store i in new list */ - /* ----------------------------------------------------- */ - - /* flag i as being in Lme by negating Nv [i] */ - degme += nvi ; - Nv [i] = -nvi ; - Iw [++pme2] = i ; - - /* ----------------------------------------------------- */ - /* remove variable i from degree list. */ - /* ----------------------------------------------------- */ - - if (IsInCurrentSet (C, i, curC)) - { - ilast = Last [i] ; - inext = Next [i] ; - ASSERT (ilast >= EMPTY && ilast < n) ; - ASSERT (inext >= EMPTY && inext < n) ; - if (inext != EMPTY) Last [inext] = ilast ; - if (ilast != EMPTY) - { - Next [ilast] = inext ; - } - else - { - /* i is at the head of the degree list */ - ASSERT (Degree [i] >= 0 && Degree [i] < n) ; - Head [Degree [i]] = inext ; - } - degreeListCounter-- ; - } - } - } - } - else - { - - /* ------------------------------------------------------------- */ - /* construct the new element in empty space, Iw [pfree ...] */ - /* ------------------------------------------------------------- */ - - p = Pe [me] ; - pme1 = pfree ; - slenme = Len [me] - elenme ; - - for (knt1 = 1 ; knt1 <= elenme + 1 ; knt1++) - { - - if (knt1 > elenme) - { - /* search the supervariables in me. */ - e = me ; - pj = p ; - ln = slenme ; - CAMD_DEBUG2 (("Search sv: "ID" "ID" "ID"\n", me,pj,ln)) ; - } - else - { - /* search the elements in me. */ - e = Iw [p++] ; - ASSERT (e >= 0 && e < n) ; - pj = Pe [e] ; - ln = Len [e] ; - CAMD_DEBUG2 (("Search element e "ID" in me "ID"\n", e,me)) ; - ASSERT (Elen [e] < EMPTY && W [e] > 0 && pj >= 0) ; - } - ASSERT (ln >= 0 && (ln == 0 || (pj >= 0 && pj < iwlen))) ; - - /* --------------------------------------------------------- - * search for different supervariables and add them to the - * new list, compressing when necessary. this loop is - * executed once for each element in the list and once for - * all the supervariables in the list. - * --------------------------------------------------------- */ - - for (knt2 = 1 ; knt2 <= ln ; knt2++) - { - i = Iw [pj++] ; - ASSERT (i >= 0 && i < n && (i == me || Elen [i] >= EMPTY)); - nvi = Nv [i] ; - CAMD_DEBUG2 ((": "ID" "ID" "ID" "ID"\n", - i, Elen [i], Nv [i], wflg)) ; - - if (nvi > 0) - { - - /* ------------------------------------------------- */ - /* compress Iw, if necessary */ - /* ------------------------------------------------- */ - - if (pfree >= iwlen) - { - - CAMD_DEBUG1 (("GARBAGE COLLECTION\n")) ; - - /* prepare for compressing Iw by adjusting pointers - * and lengths so that the lists being searched in - * the inner and outer loops contain only the - * remaining entries. */ - - Pe [me] = p ; - Len [me] -= knt1 ; - /* check if nothing left of supervariable me */ - if (Len [me] == 0) Pe [me] = EMPTY ; - Pe [e] = pj ; - Len [e] = ln - knt2 ; - /* nothing left of element e */ - if (Len [e] == 0) Pe [e] = EMPTY ; - - ncmpa++ ; /* one more garbage collection */ - - /* store first entry of each object in Pe */ - /* FLIP the first entry in each object */ - for (j = 0 ; j < n ; j++) - { - pn = Pe [j] ; - if (pn >= 0) - { - ASSERT (pn >= 0 && pn < iwlen) ; - Pe [j] = Iw [pn] ; - Iw [pn] = FLIP (j) ; - } - } - - /* psrc/pdst point to source/destination */ - psrc = 0 ; - pdst = 0 ; - pend = pme1 - 1 ; - - while (psrc <= pend) - { - /* search for next FLIP'd entry */ - j = FLIP (Iw [psrc++]) ; - if (j >= 0) - { - CAMD_DEBUG2 (("Got object j: "ID"\n", j)) ; - Iw [pdst] = Pe [j] ; - Pe [j] = pdst++ ; - lenj = Len [j] ; - /* copy from source to destination */ - for (knt3 = 0 ; knt3 <= lenj - 2 ; knt3++) - { - Iw [pdst++] = Iw [psrc++] ; - } - } - } - - /* move the new partially-constructed element */ - p1 = pdst ; - for (psrc = pme1 ; psrc <= pfree-1 ; psrc++) - { - Iw [pdst++] = Iw [psrc] ; - } - pme1 = p1 ; - pfree = pdst ; - pj = Pe [e] ; - p = Pe [me] ; - - } - - /* ------------------------------------------------- */ - /* i is a principal variable not yet placed in Lme */ - /* store i in new list */ - /* ------------------------------------------------- */ - - /* flag i as being in Lme by negating Nv [i] */ - degme += nvi ; - Nv [i] = -nvi ; - Iw [pfree++] = i ; - CAMD_DEBUG2 ((" s: "ID" nv "ID"\n", i, Nv [i])); - - /* ------------------------------------------------- */ - /* remove variable i from degree link list */ - /* ------------------------------------------------- */ - - if (IsInCurrentSet (C, i, curC)) - { - ilast = Last [i] ; - inext = Next [i] ; - ASSERT (ilast >= EMPTY && ilast < n) ; - ASSERT (inext >= EMPTY && inext < n) ; - if (inext != EMPTY) Last [inext] = ilast ; - if (ilast != EMPTY) - { - Next [ilast] = inext ; - } - else - { - /* i is at the head of the degree list */ - ASSERT (Degree [i] >= 0 && Degree [i] < n) ; - Head [Degree [i]] = inext ; - } - degreeListCounter-- ; - } - } - } - - if (e != me) - { - if (IsInCurrentSet (C, e, curC)) - { - /* absorb element here if in same bucket */ - /* set tree pointer and flag to indicate element e is - * absorbed into new element me (the parent of e is me) - */ - CAMD_DEBUG1 ((" Element "ID" => "ID"\n", e, me)) ; - Pe [e] = FLIP (me) ; - W [e] = 0 ; - } - else - { - /* make element a root; kill it if not in same bucket */ - CAMD_DEBUG1 (("2 Element "ID" => "ID"\n", e, me)) ; - Pe [e] = EMPTY ; - W [e] = 0 ; - } - } - } - - pme2 = pfree - 1 ; - } - - /* ----------------------------------------------------------------- */ - /* me has now been converted into an element in Iw [pme1..pme2] */ - /* ----------------------------------------------------------------- */ - - /* degme holds the external degree of new element */ - Degree [me] = degme ; - Pe [me] = pme1 ; - Len [me] = pme2 - pme1 + 1 ; - ASSERT (Pe [me] >= 0 && Pe [me] < iwlen) ; - - Elen [me] = FLIP (nvpiv + degme) ; - /* FLIP (Elen (me)) is now the degree of pivot (including - * diagonal part). */ - -#ifndef NDEBUG - CAMD_DEBUG2 (("New element structure: length= "ID"\n", pme2-pme1+1)) ; - for (pme = pme1 ; pme <= pme2 ; pme++) CAMD_DEBUG3 ((" "ID"", Iw[pme])); - CAMD_DEBUG3 (("\n")) ; -#endif - - /* ----------------------------------------------------------------- */ - /* make sure that wflg is not too large. */ - /* ----------------------------------------------------------------- */ - - /* With the current value of wflg, wflg+n must not cause integer - * overflow */ - - wflg = clear_flag (wflg, wbig, W, n) ; - -/* ========================================================================= */ -/* COMPUTE (W [e] - wflg) = |Le\Lme| FOR ALL ELEMENTS */ -/* ========================================================================= */ - - /* ----------------------------------------------------------------- - * Scan 1: compute the external degrees of previous elements with - * respect to the current element. That is: - * (W [e] - wflg) = |Le \ Lme| - * for each element e that appears in any supervariable in Lme. The - * notation Le refers to the pattern (list of supervariables) of a - * previous element e, where e is not yet absorbed, stored in - * Iw [Pe [e] + 1 ... Pe [e] + Len [e]]. The notation Lme - * refers to the pattern of the current element (stored in - * Iw [pme1..pme2]). If aggressive absorption is enabled, and - * (W [e] - wflg) becomes zero, then the element e will be absorbed - * in Scan 2. - * ----------------------------------------------------------------- */ - - CAMD_DEBUG2 (("me: ")) ; - for (pme = pme1 ; pme <= pme2 ; pme++) - { - i = Iw [pme] ; - ASSERT (i >= 0 && i < n) ; - eln = Elen [i] ; - CAMD_DEBUG3 ((""ID" Elen "ID": \n", i, eln)) ; - if (eln > 0) - { - /* note that Nv [i] has been negated to denote i in Lme: */ - nvi = -Nv [i] ; - ASSERT (nvi > 0 && Pe [i] >= 0 && Pe [i] < iwlen) ; - wnvi = wflg - nvi ; - for (p = Pe [i] ; p <= Pe [i] + eln - 1 ; p++) - { - e = Iw [p] ; - ASSERT (e >= 0 && e < n) ; - we = W [e] ; - CAMD_DEBUG4 ((" e "ID" we "ID" ", e, we)) ; - if (we >= wflg) - { - /* unabsorbed element e has been seen in this loop */ - CAMD_DEBUG4 ((" unabsorbed, first time seen")) ; - we -= nvi ; - } - else if (we != 0) - { - /* e is an unabsorbed element */ - /* this is the first we have seen e in all of Scan 1 */ - CAMD_DEBUG4 ((" unabsorbed")) ; - we = Degree [e] + wnvi ; - } - CAMD_DEBUG4 (("\n")) ; - W [e] = we ; - } - } - } - CAMD_DEBUG2 (("\n")) ; - -/* ========================================================================= */ -/* DEGREE UPDATE AND ELEMENT ABSORPTION */ -/* ========================================================================= */ - - /* ----------------------------------------------------------------- - * Scan 2: for each i in Lme, sum up the degree of Lme (which is - * degme), plus the sum of the external degrees of each Le for the - * elements e appearing within i, plus the supervariables in i. - * Place i in hash list. - * ----------------------------------------------------------------- */ - - for (pme = pme1 ; pme <= pme2 ; pme++) - { - i = Iw [pme] ; - ASSERT (i >= 0 && i < n && Nv [i] < 0 && Elen [i] >= 0) ; - CAMD_DEBUG2 (("Updating: i "ID" "ID" "ID"\n", i, Elen[i], Len [i])); - p1 = Pe [i] ; - p2 = p1 + Elen [i] - 1 ; - pn = p1 ; - hash = 0 ; - deg = 0 ; - ASSERT (p1 >= 0 && p1 < iwlen && p2 >= -1 && p2 < iwlen) ; - - /* ------------------------------------------------------------- */ - /* scan the element list associated with supervariable i */ - /* ------------------------------------------------------------- */ - - /* UMFPACK/MA38-style approximate degree: */ - if (aggressive) - { - for (p = p1 ; p <= p2 ; p++) - { - e = Iw [p] ; - ASSERT (e >= 0 && e < n) ; - we = W [e] ; - if (we != 0) - { - /* e is an unabsorbed element */ - /* dext = | Le \ Lme | */ - dext = we - wflg ; - if (dext > 0) - { - deg += dext ; - Iw [pn++] = e ; - hash += e ; - CAMD_DEBUG4 ((" e: "ID" hash = "ID"\n",e,hash)) ; - } - else - { - if (IsInCurrentSet (C, e, curC)) - { - /* external degree of e is zero and if - * C[e] = curC; absorb e into me */ - CAMD_DEBUG1 ((" Element "ID" =>"ID" (aggr)\n", - e, me)) ; - ASSERT (dext == 0) ; - Pe [e] = FLIP (me) ; - W [e] = 0 ; - } - else - { - /* make element a root; kill it if not in same - * bucket */ - CAMD_DEBUG1 (("2 Element "ID" =>"ID" (aggr)\n", - e, me)) ; - ASSERT (dext == 0) ; - Pe [e] = EMPTY ; - W [e] = 0 ; - } - } - } - } - } - else - { - for (p = p1 ; p <= p2 ; p++) - { - e = Iw [p] ; - ASSERT (e >= 0 && e < n) ; - we = W [e] ; - if (we != 0) - { - /* e is an unabsorbed element */ - dext = we - wflg ; - ASSERT (dext >= 0) ; - deg += dext ; - Iw [pn++] = e ; - hash += e ; - CAMD_DEBUG4 ((" e: "ID" hash = "ID"\n",e,hash)) ; - } - } - } - - /* count the number of elements in i (including me): */ - Elen [i] = pn - p1 + 1 ; - - /* ------------------------------------------------------------- */ - /* scan the supervariables in the list associated with i */ - /* ------------------------------------------------------------- */ - - /* The bulk of the CAMD run time is typically spent in this loop, - * particularly if the matrix has many dense rows that are not - * removed prior to ordering. */ - p3 = pn ; - p4 = p1 + Len [i] ; - for (p = p2 + 1 ; p < p4 ; p++) - { - j = Iw [p] ; - ASSERT (j >= 0 && j < n) ; - nvj = Nv [j] ; - if (nvj > 0) - { - /* j is unabsorbed, and not in Lme. */ - /* add to degree and add to new list */ - deg += nvj ; - Iw [pn++] = j ; - hash += j ; - CAMD_DEBUG4 ((" s: "ID" hash "ID" Nv[j]= "ID"\n", - j, hash, nvj)) ; - } - } - - /* ------------------------------------------------------------- */ - /* update the degree and check for mass elimination */ - /* ------------------------------------------------------------- */ - - /* with aggressive absorption, deg==0 is identical to the - * Elen [i] == 1 && p3 == pn test, below. */ - ASSERT (IMPLIES (aggressive, (deg==0) == (Elen[i]==1 && p3==pn))) ; - - if (Elen [i] == 1 && p3 == pn && IsInCurrentSet (C, i, curC)) - { - - /* --------------------------------------------------------- */ - /* mass elimination */ - /* --------------------------------------------------------- */ - - /* There is nothing left of this node except for an edge to - * the current pivot element. Elen [i] is 1, and there are - * no variables adjacent to node i. Absorb i into the - * current pivot element, me. Note that if there are two or - * more mass eliminations, fillin due to mass elimination is - * possible within the nvpiv-by-nvpiv pivot block. It is this - * step that causes CAMD's analysis to be an upper bound. - * - * The reason is that the selected pivot has a lower - * approximate degree than the true degree of the two mass - * eliminated nodes. There is no edge between the two mass - * eliminated nodes. They are merged with the current pivot - * anyway. - * - * No fillin occurs in the Schur complement, in any case, - * and this effect does not decrease the quality of the - * ordering itself, just the quality of the nonzero and - * flop count analysis. It also means that the post-ordering - * is not an exact elimination tree post-ordering. */ - - CAMD_DEBUG1 ((" MASS i "ID" => parent e "ID"\n", i, me)) ; - Pe [i] = FLIP (me) ; - nvi = -Nv [i] ; - degme -= nvi ; - nvpiv += nvi ; - nel += nvi ; - Nv [i] = 0 ; - Elen [i] = EMPTY ; - - } - else - { - - /* --------------------------------------------------------- */ - /* update the upper-bound degree of i */ - /* --------------------------------------------------------- */ - - /* the following degree does not yet include the size - * of the current element, which is added later: */ - - Degree [i] = MIN (Degree [i], deg) ; - - /* --------------------------------------------------------- */ - /* add me to the list for i */ - /* --------------------------------------------------------- */ - - /* move first supervariable to end of list */ - Iw [pn] = Iw [p3] ; - /* move first element to end of element part of list */ - Iw [p3] = Iw [p1] ; - /* add new element, me, to front of list. */ - Iw [p1] = me ; - /* store the new length of the list in Len [i] */ - Len [i] = pn - p1 + 1 ; - - /* --------------------------------------------------------- */ - /* place in hash bucket. Save hash key of i in Last [i]. */ - /* --------------------------------------------------------- */ - - /* NOTE: this can fail if hash is negative, because the ANSI C - * standard does not define a % b when a and/or b are negative. - * That's why hash is defined as an unsigned Int, to avoid this - * problem. */ - hash = hash % n ; - ASSERT (((Int) hash) >= 0 && ((Int) hash) < n) ; - - /* if the Hhead array is not used: */ - j = Head [hash] ; - if (j <= EMPTY) - { - /* degree list is empty, hash head is FLIP (j) */ - Next [i] = FLIP (j) ; - Head [hash] = FLIP (i) ; - } - else - { - /* degree list is not empty, use Last [Head [hash]] as - * hash head. */ - Next [i] = Last [j] ; - Last [j] = i ; - } - - /* if a separate Hhead array is used: * - Next [i] = Hhead [hash] ; - Hhead [hash] = i ; - */ - - CAMD_DEBUG4 ((" s: "ID" hash "ID" \n", i, hash)) ; - Last [i] = hash ; - } - } - - Degree [me] = degme ; - - /* ----------------------------------------------------------------- */ - /* Clear the counter array, W [...], by incrementing wflg. */ - /* ----------------------------------------------------------------- */ - - /* make sure that wflg+n does not cause integer overflow */ - lemax = MAX (lemax, degme) ; - wflg += lemax ; - wflg = clear_flag (wflg, wbig, W, n) ; - /* at this point, W [0..n-1] < wflg holds */ - -/* ========================================================================= */ -/* SUPERVARIABLE DETECTION */ -/* ========================================================================= */ - - CAMD_DEBUG1 (("Detecting supervariables:\n")) ; - for (pme = pme1 ; pme <= pme2 ; pme++) - { - i = Iw [pme] ; - ASSERT (i >= 0 && i < n) ; - CAMD_DEBUG2 (("Consider i "ID" nv "ID"\n", i, Nv [i])) ; - if (Nv [i] < 0) - { - /* i is a principal variable in Lme */ - - /* --------------------------------------------------------- - * examine all hash buckets with 2 or more variables. We do - * this by examing all unique hash keys for supervariables in - * the pattern Lme of the current element, me - * --------------------------------------------------------- */ - - CAMD_DEBUG2 (("Last: "ID"\n", Last [i])) ; - - /* let i = head of hash bucket, and empty the hash bucket */ - ASSERT (Last [i] >= 0 && Last [i] < n) ; - hash = Last [i] ; - - /* if Hhead array is not used: */ - j = Head [hash] ; - if (j == EMPTY) - { - /* hash bucket and degree list are both empty */ - i = EMPTY ; - } - else if (j < EMPTY) - { - /* degree list is empty */ - i = FLIP (j) ; - Head [hash] = EMPTY ; - } - else - { - /* degree list is not empty, restore Last [j] of head j */ - i = Last [j] ; - Last [j] = EMPTY ; - } - - /* if separate Hhead array is used: * - i = Hhead [hash] ; - Hhead [hash] = EMPTY ; - */ - - ASSERT (i >= EMPTY && i < n) ; - CAMD_DEBUG2 (("----i "ID" hash "ID"\n", i, hash)) ; - - while (i != EMPTY && Next [i] != EMPTY) - { - - /* ----------------------------------------------------- - * this bucket has one or more variables following i. - * scan all of them to see if i can absorb any entries - * that follow i in hash bucket. Scatter i into w. - * ----------------------------------------------------- */ - - ln = Len [i] ; - eln = Elen [i] ; - ASSERT (ln >= 0 && eln >= 0) ; - ASSERT (Pe [i] >= 0 && Pe [i] < iwlen) ; - /* do not flag the first element in the list (me) */ - for (p = Pe [i] + 1 ; p <= Pe [i] + ln - 1 ; p++) - { - ASSERT (Iw [p] >= 0 && Iw [p] < n) ; - W [Iw [p]] = wflg ; - } - - /* ----------------------------------------------------- */ - /* scan every other entry j following i in bucket */ - /* ----------------------------------------------------- */ - - jlast = i ; - j = Next [i] ; - ASSERT (j >= EMPTY && j < n) ; - - while (j != EMPTY) - { - /* ------------------------------------------------- */ - /* check if j and i have identical nonzero pattern */ - /* ------------------------------------------------- */ - - CAMD_DEBUG3 (("compare i "ID" and j "ID"\n", i,j)) ; - - /* check if i and j have the same Len and Elen */ - /* and are in the same bucket */ - ASSERT (Len [j] >= 0 && Elen [j] >= 0) ; - ASSERT (Pe [j] >= 0 && Pe [j] < iwlen) ; - ok = (Len [j] == ln) && (Elen [j] == eln) ; - ok = ok && InSameConstraintSet (C,i,j) ; - - /* skip the first element in the list (me) */ - for (p = Pe [j] + 1 ; ok && p <= Pe [j] + ln - 1 ; p++) - { - ASSERT (Iw [p] >= 0 && Iw [p] < n) ; - if (W [Iw [p]] != wflg) ok = 0 ; - } - if (ok) - { - /* --------------------------------------------- */ - /* found it! j can be absorbed into i */ - /* --------------------------------------------- */ - - CAMD_DEBUG1 (("found it! j "ID" => i "ID"\n", j,i)); - Pe [j] = FLIP (i) ; - /* both Nv [i] and Nv [j] are negated since they */ - /* are in Lme, and the absolute values of each */ - /* are the number of variables in i and j: */ - Nv [i] += Nv [j] ; - Nv [j] = 0 ; - Elen [j] = EMPTY ; - /* delete j from hash bucket */ - ASSERT (j != Next [j]) ; - j = Next [j] ; - Next [jlast] = j ; - - } - else - { - /* j cannot be absorbed into i */ - jlast = j ; - ASSERT (j != Next [j]) ; - j = Next [j] ; - } - ASSERT (j >= EMPTY && j < n) ; - } - - /* ----------------------------------------------------- - * no more variables can be absorbed into i - * go to next i in bucket and clear flag array - * ----------------------------------------------------- */ - - wflg++ ; - i = Next [i] ; - ASSERT (i >= EMPTY && i < n) ; - - } - } - } - CAMD_DEBUG2 (("detect done\n")) ; - -/* ========================================================================= */ -/* RESTORE DEGREE LISTS AND REMOVE NONPRINCIPAL SUPERVARIABLES FROM ELEMENT */ -/* ========================================================================= */ - - p = pme1 ; - nleft = n - nel ; - for (pme = pme1 ; pme <= pme2 ; pme++) - { - i = Iw [pme] ; - ASSERT (i >= 0 && i < n) ; - nvi = -Nv [i] ; - CAMD_DEBUG3 (("Restore i "ID" "ID"\n", i, nvi)) ; - if (nvi > 0) - { - /* i is a principal variable in Lme */ - /* restore Nv [i] to signify that i is principal */ - Nv [i] = nvi ; - - /* --------------------------------------------------------- */ - /* compute the external degree (add size of current element) */ - /* --------------------------------------------------------- */ - - deg = Degree [i] + degme - nvi ; - deg = MIN (deg, nleft - nvi) ; - ASSERT (deg >= 0 && deg < n) ; - - /* --------------------------------------------------------- */ - /* place the supervariable at the head of the degree list */ - /* --------------------------------------------------------- */ - - if (IsInCurrentSet (C, i, curC)) - { - inext = Head [deg] ; - ASSERT (inext >= EMPTY && inext < n) ; - if (inext != EMPTY) Last [inext] = i ; - Next [i] = inext ; - Last [i] = EMPTY ; - Head [deg] = i ; - degreeListCounter++ ; - } - - /* --------------------------------------------------------- */ - /* save the new degree, and find the minimum degree */ - /* --------------------------------------------------------- */ - - mindeg = MIN (mindeg, deg) ; - Degree [i] = deg ; - - /* --------------------------------------------------------- */ - /* place the supervariable in the element pattern */ - /* --------------------------------------------------------- */ - - Iw [p++] = i ; - } - } - CAMD_DEBUG2 (("restore done\n")) ; - -/* ========================================================================= */ -/* FINALIZE THE NEW ELEMENT */ -/* ========================================================================= */ - - CAMD_DEBUG2 (("ME = "ID" DONE\n", me)) ; - Nv [me] = nvpiv ; - /* save the length of the list for the new element me */ - Len [me] = p - pme1 ; - if (Len [me] == 0) - { - /* there is nothing left of the current pivot element */ - /* it is a root of the assembly tree */ - Pe [me] = EMPTY ; - W [me] = 0 ; - } - if (elenme != 0) - { - /* element was not constructed in place: deallocate part of */ - /* it since newly nonprincipal variables may have been removed */ - pfree = p ; - } - - /* Store the element back into BucketSet. This is the way to maintain - * the order of roots (Pe[i]=-1) in each Constraint Set. */ - BucketSet [pBucket2++] = me ; - - /* The new element has nvpiv pivots and the size of the contribution - * block for a multifrontal method is degme-by-degme, not including - * the "dense" rows/columns. If the "dense" rows/columns are included, - * the frontal matrix is no larger than - * (degme+ndense)-by-(degme+ndense). - */ - - if (Info != (double *) NULL) - { - f = nvpiv ; - r = degme + ndense ; - dmax = MAX (dmax, f + r) ; - - /* number of nonzeros in L (excluding the diagonal) */ - lnzme = f*r + (f-1)*f/2 ; - lnz += lnzme ; - - /* number of divide operations for LDL' and for LU */ - ndiv += lnzme ; - - /* number of multiply-subtract pairs for LU */ - s = f*r*r + r*(f-1)*f + (f-1)*f*(2*f-1)/6 ; - nms_lu += s ; - - /* number of multiply-subtract pairs for LDL' */ - nms_ldl += (s + lnzme)/2 ; - } - -#ifndef NDEBUG - CAMD_DEBUG2 (("finalize done nel "ID" n "ID"\n ::::\n", nel, n)) ; - for (pme = Pe [me] ; pme <= Pe [me] + Len [me] - 1 ; pme++) - { - CAMD_DEBUG3 ((" "ID"", Iw [pme])) ; - } - CAMD_DEBUG3 (("\n")) ; -#endif - - } - -/* ========================================================================= */ -/* DONE SELECTING PIVOTS */ -/* ========================================================================= */ - - if (Info != (double *) NULL) - { - - /* count the work to factorize the ndense-by-ndense submatrix */ - f = ndense ; - dmax = MAX (dmax, (double) ndense) ; - - /* number of nonzeros in L (excluding the diagonal) */ - lnzme = (f-1)*f/2 ; - lnz += lnzme ; - - /* number of divide operations for LDL' and for LU */ - ndiv += lnzme ; - - /* number of multiply-subtract pairs for LU */ - s = (f-1)*f*(2*f-1)/6 ; - nms_lu += s ; - - /* number of multiply-subtract pairs for LDL' */ - nms_ldl += (s + lnzme)/2 ; - - /* number of nz's in L (excl. diagonal) */ - Info [CAMD_LNZ] = lnz ; - - /* number of divide ops for LU and LDL' */ - Info [CAMD_NDIV] = ndiv ; - - /* number of multiply-subtract pairs for LDL' */ - Info [CAMD_NMULTSUBS_LDL] = nms_ldl ; - - /* number of multiply-subtract pairs for LU */ - Info [CAMD_NMULTSUBS_LU] = nms_lu ; - - /* number of "dense" rows/columns */ - Info [CAMD_NDENSE] = ndense ; - - /* largest front is dmax-by-dmax */ - Info [CAMD_DMAX] = dmax ; - - /* number of garbage collections in CAMD */ - Info [CAMD_NCMPA] = ncmpa ; - - /* successful ordering */ - Info [CAMD_STATUS] = CAMD_OK ; - } - -/* ========================================================================= */ -/* POST-ORDERING */ -/* ========================================================================= */ - -/* ------------------------------------------------------------------------- - * Variables at this point: - * - * Pe: holds the elimination tree. The parent of j is FLIP (Pe [j]), - * or EMPTY if j is a root. The tree holds both elements and - * non-principal (unordered) variables absorbed into them. - * Dense and empty variables are non-principal and unordered. They are - * all represented by the fictitious node n (that is, Pe [i] = FLIP (n) - * and Elen [i] = EMPTY if i is a dense or empty node). - * - * Elen: holds the size of each element, including the diagonal part. - * FLIP (Elen [e]) > 0 if e is an element. For unordered - * variables i, Elen [i] is EMPTY. - * - * Nv: Nv [e] > 0 is the number of pivots represented by the element e. - * For unordered variables i, Nv [i] is zero. - * - * BucketSet: BucketSet [0.....pBucket2] holds all - * the elements that removed during the elimination, in eliminated order. - * - * - * Contents no longer needed: - * W, Iw, Len, Degree, Head, Next, Last. - * - * The matrix itself has been destroyed. - * - * n: the size of the matrix. - * ndense: the number of "dense" nodes. - * nnull: the number of empty nodes (zero degree) - * No other scalars needed (pfree, iwlen, etc.) - * ------------------------------------------------------------------------- */ - - - /* restore Pe */ - for (i = 0 ; i < n ; i++) - { - Pe [i] = FLIP (Pe [i]) ; - } - - /* restore Elen, for output information only */ - for (i = 0 ; i < n ; i++) - { - Elen [i] = FLIP (Elen [i]) ; - } - - /* Now, Pe [j] is the parent of j, or EMPTY if j is a root. - * Pe [j] = n if j is a dense/empty node */ - - /* place all variables in the list of children of their parents */ - for (j = n-1 ; j >= 0 ; j--) - { - if (Nv [j] > 0) continue ; /* skip if j is an element */ - ASSERT (Pe [j] >= 0 && Pe [j] <= n) ; - Next [j] = Head [Pe [j]] ; /* place j in list of its parent */ - Head [Pe [j]] = j ; - } - - /* place all elements in the list of children of their parents */ - for (e = n-1 ; e >= 0 ; e--) - { - if (Nv [e] <= 0) continue ; /* skip if e is a variable */ - if (Pe [e] == EMPTY) continue ; /* skip if e is a root */ - Next [e] = Head [Pe [e]] ; /* place e in list of its parent */ - Head [Pe [e]] = e ; - } - - /* determine where to put the postordering permutation */ - if (C != NULL && ndense_or_null > 0) - { - /* Perm needs to be computed in a temporary workspace, and then - * transformed and copied into the output permutation, in Last */ - Perm = Degree ; - } - else - { - /* the postorder computes the permutation directly, in Last */ - Perm = Last ; - } - - /* postorder the elements and their descendants (both elements and - * variables), but not (yet) the dense/empty nodes */ - for (k = 0 , i = 0 ; i < pBucket2 ; i++) - { - j = BucketSet [i] ; - ASSERT (j >= 0 && j < n) ; - if (Pe [j] == EMPTY) - { - k = CAMD_postorder (j, k, n, Head, Next, Perm, W) ; - } - } - - /* Perm [0..k-1] now contains a list of the nonempty/nondense nodes, - * ordered via minimum degree and following the constraints. */ - - CAMD_DEBUG1 (("before dense/empty, k = "ID"\n", k)) ; - fflush (stdout) ; - ASSERT (k + ndense_or_null == n) ; - - if (ndense_or_null > 0) - { - if (C == NULL) - { - /* postorder the dense/empty nodes (the parent of all these is n) */ - CAMD_postorder (n, k, n, Head, Next, Perm, W) ; - } - else - { - /* dense (or empty) nodes exist, AND C also exists. The dense/empty - * nodes are a link list whose head is Head[n], and Next[i] gives the - * next node after i in the list. They need to be sorted by their - * constraints, and then merged with Perm [0..k-1].*/ - - /* count how many dense/empty nodes are in each constraint set */ - - Bucket = W ; /* use W as workspace (it has size n+1) */ - - /* count the number of dense/empty nodes in each constraint set */ - for (c = 0 ; c <= cmax ; c++) - { - Bucket [c] = 0 ; - } - i = 0 ; - for (j = Head [n] ; j != EMPTY ; j = Next [j]) - { - CAMD_DEBUG1 (("Dense/empty node: "ID" : "ID" "ID"\n", j, - Pe [j], Elen [j])) ; - fflush (stdout) ; - ASSERT (Pe [j] == n && Elen [j] == EMPTY) ; - i++ ; - Bucket [C [j]]++ ; - } - ASSERT (i == ndense_or_null) ; - - /* find the cumulative sum of Bucket */ - knt1 = 0 ; - for (c = 0 ; c <= cmax ; c++) - { - i = Bucket [c] ; - Bucket [c] = knt1 ; - knt1 += i ; - } - CAMD_DEBUG1 (("knt1 "ID" dense/empty "ID"\n", knt1, ndense_or_null)); - ASSERT (knt1 == ndense_or_null) ; - - /* place dense/empty nodes in BucketSet, in constraint order, - * ties in natural order */ - for (j = Head [n] ; j != EMPTY ; j = Next [j]) - { - BucketSet [Bucket [C [j]]++] = j ; - } - -#ifndef NDEBUG - /* each set is in monotonically increasing order of constraints */ - for (i = 1 ; i < k ; i++) - { - ASSERT (C [Perm [i]] >= C [Perm [i-1]]) ; - } - for (i = 1 ; i < ndense_or_null ; i++) - { - /* in order of constraints, with ties in natural order */ - ASSERT ( - (C [BucketSet [i]] > C [BucketSet [i-1]]) || - (C [BucketSet [i]] == C [BucketSet [i-1]] - && (BucketSet [i] > BucketSet [i-1]))) ; - } -#endif - - /* merge Perm [0..k-1] and BucketSet [0..ndense+nnull] */ - p1 = 0 ; - p2 = 0 ; - p3 = 0 ; - while (p1 < k && p2 < ndense_or_null) - { - /* place the dense/empty nodes at the end of each constraint - * set, after the non-dense/non-empty nodes in the same set */ - if (C [Perm [p1]] <= C [BucketSet [p2]]) - { - /* non-dense/non-empty node */ - Last [p3++] = Perm [p1++] ; - } - else - { - /* dense/empty node */ - Last [p3++] = BucketSet [p2++] ; - } - } - /* wrap up; either Perm[0..k-1] or BucketSet[ ] is used up */ - while (p1 < k) - { - Last [p3++] = Perm [p1++] ; - } - while (p2 < ndense_or_null) - { - Last [p3++] = BucketSet [p2++] ; - } - } - } - -#ifndef NDEBUG - CAMD_DEBUG1 (("\nFinal constrained ordering:\n")) ; - i = 0 ; - CAMD_DEBUG1 (("Last ["ID"] = "ID", C["ID"] = "ID"\n", i, Last [i], - Last [i], C [Last [i]])) ; - for (i = 1 ; i < n ; i++) - { - CAMD_DEBUG1 (("Last ["ID"] = "ID", C["ID"] = "ID"\n", i, Last [i], - Last [i], C [Last [i]])) ; - - /* This is the critical assertion. It states that the permutation - * satisfies the constraints. */ - ASSERT (C [Last [i]] >= C [Last [i-1]]) ; - } -#endif -} diff --git a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Source/camd_aat.c b/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Source/camd_aat.c deleted file mode 100644 index b385fc7ac..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Source/camd_aat.c +++ /dev/null @@ -1,183 +0,0 @@ -/* ========================================================================= */ -/* === CAMD_aat ============================================================ */ -/* ========================================================================= */ - -/* ------------------------------------------------------------------------- */ -/* CAMD, Copyright (c) Timothy A. Davis, Yanqing Chen, */ -/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ -/* email: DrTimothyAldenDavis@gmail.com */ -/* ------------------------------------------------------------------------- */ - -/* CAMD_aat: compute the symmetry of the pattern of A, and count the number of - * nonzeros each column of A+A' (excluding the diagonal). Assumes the input - * matrix has no errors, with sorted columns and no duplicates - * (CAMD_valid (n, n, Ap, Ai) must be CAMD_OK, but this condition is not - * checked). - */ - -#include "camd_internal.h" - -GLOBAL size_t CAMD_aat /* returns nz in A+A' */ -( - Int n, - const Int Ap [ ], - const Int Ai [ ], - Int Len [ ], /* Len [j]: length of column j of A+A', excl diagonal*/ - Int Tp [ ], /* workspace of size n */ - double Info [ ] -) -{ - Int p1, p2, p, i, j, pj, pj2, k, nzdiag, nzboth, nz ; - double sym ; - size_t nzaat ; - -#ifndef NDEBUG - CAMD_debug_init ("CAMD AAT") ; - for (k = 0 ; k < n ; k++) Tp [k] = EMPTY ; - ASSERT (CAMD_valid (n, n, Ap, Ai) == CAMD_OK) ; -#endif - - if (Info != (double *) NULL) - { - /* clear the Info array, if it exists */ - for (i = 0 ; i < CAMD_INFO ; i++) - { - Info [i] = EMPTY ; - } - Info [CAMD_STATUS] = CAMD_OK ; - } - - for (k = 0 ; k < n ; k++) - { - Len [k] = 0 ; - } - - nzdiag = 0 ; - nzboth = 0 ; - nz = Ap [n] ; - - for (k = 0 ; k < n ; k++) - { - p1 = Ap [k] ; - p2 = Ap [k+1] ; - CAMD_DEBUG2 (("\nAAT Column: "ID" p1: "ID" p2: "ID"\n", k, p1, p2)) ; - - /* construct A+A' */ - for (p = p1 ; p < p2 ; ) - { - /* scan the upper triangular part of A */ - j = Ai [p] ; - if (j < k) - { - /* entry A (j,k) is in the strictly upper triangular part, - * add both A (j,k) and A (k,j) to the matrix A+A' */ - Len [j]++ ; - Len [k]++ ; - CAMD_DEBUG3 ((" upper ("ID","ID") ("ID","ID")\n", j,k, k,j)); - p++ ; - } - else if (j == k) - { - /* skip the diagonal */ - p++ ; - nzdiag++ ; - break ; - } - else /* j > k */ - { - /* first entry below the diagonal */ - break ; - } - /* scan lower triangular part of A, in column j until reaching - * row k. Start where last scan left off. */ - ASSERT (Tp [j] != EMPTY) ; - ASSERT (Ap [j] <= Tp [j] && Tp [j] <= Ap [j+1]) ; - pj2 = Ap [j+1] ; - for (pj = Tp [j] ; pj < pj2 ; ) - { - i = Ai [pj] ; - if (i < k) - { - /* A (i,j) is only in the lower part, not in upper. - * add both A (i,j) and A (j,i) to the matrix A+A' */ - Len [i]++ ; - Len [j]++ ; - CAMD_DEBUG3 ((" lower ("ID","ID") ("ID","ID")\n", - i,j, j,i)) ; - pj++ ; - } - else if (i == k) - { - /* entry A (k,j) in lower part and A (j,k) in upper */ - pj++ ; - nzboth++ ; - break ; - } - else /* i > k */ - { - /* consider this entry later, when k advances to i */ - break ; - } - } - Tp [j] = pj ; - } - /* Tp [k] points to the entry just below the diagonal in column k */ - Tp [k] = p ; - } - - /* clean up, for remaining mismatched entries */ - for (j = 0 ; j < n ; j++) - { - for (pj = Tp [j] ; pj < Ap [j+1] ; pj++) - { - i = Ai [pj] ; - /* A (i,j) is only in the lower part, not in upper. - * add both A (i,j) and A (j,i) to the matrix A+A' */ - Len [i]++ ; - Len [j]++ ; - CAMD_DEBUG3 ((" lower cleanup ("ID","ID") ("ID","ID")\n", - i,j, j,i)) ; - } - } - - /* --------------------------------------------------------------------- */ - /* compute the symmetry of the nonzero pattern of A */ - /* --------------------------------------------------------------------- */ - - /* Given a matrix A, the symmetry of A is: - * B = tril (spones (A), -1) + triu (spones (A), 1) ; - * sym = nnz (B & B') / nnz (B) ; - * or 1 if nnz (B) is zero. - */ - - if (nz == nzdiag) - { - sym = 1 ; - } - else - { - sym = (2 * (double) nzboth) / ((double) (nz - nzdiag)) ; - } - - nzaat = 0 ; - for (k = 0 ; k < n ; k++) - { - nzaat += Len [k] ; - } - CAMD_DEBUG1 (("CAMD nz in A+A', excluding diagonal (nzaat) = %g\n", - (double) nzaat)) ; - CAMD_DEBUG1 ((" nzboth: "ID" nz: "ID" nzdiag: "ID" symmetry: %g\n", - nzboth, nz, nzdiag, sym)) ; - - if (Info != (double *) NULL) - { - Info [CAMD_STATUS] = CAMD_OK ; - Info [CAMD_N] = n ; - Info [CAMD_NZ] = nz ; - Info [CAMD_SYMMETRY] = sym ; /* symmetry of pattern of A */ - Info [CAMD_NZDIAG] = nzdiag ; /* nonzeros on diagonal of A */ - Info [CAMD_NZ_A_PLUS_AT] = nzaat ; /* nonzeros in A+A' */ - } - - return (nzaat) ; -} diff --git a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Source/camd_control.c b/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Source/camd_control.c deleted file mode 100644 index c1c57541b..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Source/camd_control.c +++ /dev/null @@ -1,64 +0,0 @@ -/* ========================================================================= */ -/* === CAMD_control ======================================================== */ -/* ========================================================================= */ - -/* ------------------------------------------------------------------------- */ -/* CAMD, Copyright (c) Timothy A. Davis, Yanqing Chen, */ -/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ -/* email: DrTimothyAldenDavis@gmail.com */ -/* ------------------------------------------------------------------------- */ - -/* User-callable. Prints the control parameters for CAMD. See camd.h - * for details. If the Control array is not present, the defaults are - * printed instead. - */ - -#include "camd_internal.h" - -GLOBAL void CAMD_control -( - double Control [ ] -) -{ - double alpha ; - Int aggressive ; - - if (Control != (double *) NULL) - { - alpha = Control [CAMD_DENSE] ; - aggressive = Control [CAMD_AGGRESSIVE] != 0 ; - } - else - { - alpha = CAMD_DEFAULT_DENSE ; - aggressive = CAMD_DEFAULT_AGGRESSIVE ; - } - - SUITESPARSE_PRINTF (( - "\ncamd version %d.%d, %s: approximate minimum degree ordering:\n" - " dense row parameter: %g\n", CAMD_MAIN_VERSION, CAMD_SUB_VERSION, - CAMD_DATE, alpha)) ; - - if (alpha < 0) - { - SUITESPARSE_PRINTF ((" no rows treated as dense\n")) ; - } - else - { - SUITESPARSE_PRINTF (( - " (rows with more than max (%g * sqrt (n), 16) entries are\n" - " considered \"dense\", and placed last in output permutation)\n", - alpha)) ; - } - - if (aggressive) - { - SUITESPARSE_PRINTF ((" aggressive absorption: yes\n")) ; - } - else - { - SUITESPARSE_PRINTF ((" aggressive absorption: no\n")) ; - } - - SUITESPARSE_PRINTF ((" size of CAMD integer: %d\n\n", sizeof (Int))) ; -} diff --git a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Source/camd_defaults.c b/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Source/camd_defaults.c deleted file mode 100644 index 5ac59bff3..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Source/camd_defaults.c +++ /dev/null @@ -1,36 +0,0 @@ -/* ========================================================================= */ -/* === CAMD_defaults ======================================================= */ -/* ========================================================================= */ - -/* ------------------------------------------------------------------------- */ -/* CAMD, Copyright (c) Timothy A. Davis, Yanqing Chen, */ -/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ -/* email: DrTimothyAldenDavis@gmail.com */ -/* ------------------------------------------------------------------------- */ - -/* User-callable. Sets default control parameters for CAMD. See camd.h - * for details. - */ - -#include "camd_internal.h" - -/* ========================================================================= */ -/* === CAMD defaults ======================================================= */ -/* ========================================================================= */ - -GLOBAL void CAMD_defaults -( - double Control [ ] -) -{ - Int i ; - if (Control != (double *) NULL) - { - for (i = 0 ; i < CAMD_CONTROL ; i++) - { - Control [i] = 0 ; - } - Control [CAMD_DENSE] = CAMD_DEFAULT_DENSE ; - Control [CAMD_AGGRESSIVE] = CAMD_DEFAULT_AGGRESSIVE ; - } -} diff --git a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Source/camd_dump.c b/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Source/camd_dump.c deleted file mode 100644 index 6b0b495b2..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Source/camd_dump.c +++ /dev/null @@ -1,189 +0,0 @@ -/* ========================================================================= */ -/* === CAMD_dump =========================================================== */ -/* ========================================================================= */ - -/* ------------------------------------------------------------------------- */ -/* CAMD, Copyright (c) Timothy A. Davis, Yanqing Chen, */ -/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ -/* email: DrTimothyAldenDavis@gmail.com */ -/* ------------------------------------------------------------------------- */ - -/* Debugging routines for CAMD. Not used if NDEBUG is not defined at compile- - * time (the default). See comments in camd_internal.h on how to enable - * debugging. Not user-callable. - */ - -#include "camd_internal.h" - -#ifndef NDEBUG - -/* This global variable is present only when debugging */ -GLOBAL Int CAMD_debug = -999 ; /* default is no debug printing */ - -/* ========================================================================= */ -/* === CAMD_debug_init ===================================================== */ -/* ========================================================================= */ - -/* Sets the debug print level, by reading the file debug.camd (if it exists) */ - -GLOBAL void CAMD_debug_init ( char *s ) -{ - FILE *f ; - f = fopen ("debug.camd", "r") ; - if (f == (FILE *) NULL) - { - CAMD_debug = -999 ; - } - else - { - fscanf (f, ID, &CAMD_debug) ; - fclose (f) ; - } - if (CAMD_debug >= 0) - { - printf ("%s: CAMD_debug_init, D= "ID"\n", s, CAMD_debug) ; - } -} - -/* ========================================================================= */ -/* === CAMD_dump =========================================================== */ -/* ========================================================================= */ - -/* Dump CAMD's data structure, except for the hash buckets. This routine - * cannot be called when the hash buckets are non-empty. - */ - -GLOBAL void CAMD_dump ( - Int n, /* A is n-by-n */ - Int Pe [ ], /* pe [0..n-1]: index in iw of start of row i */ - Int Iw [ ], /* workspace of size iwlen, iwlen [0..pfree-1] - * holds the matrix on input */ - Int Len [ ], /* len [0..n-1]: length for row i */ - Int iwlen, /* length of iw */ - Int pfree, /* iw [pfree ... iwlen-1] is empty on input */ - Int Nv [ ], /* nv [0..n-1] */ - Int Next [ ], /* next [0..n-1] */ - Int Last [ ], /* last [0..n-1] */ - Int Head [ ], /* head [0..n-1] */ - Int Elen [ ], /* size n */ - Int Degree [ ], /* size n */ - Int W [ ], /* size n */ - Int nel, - Int BucketSet [ ], - const Int C [ ], - Int CurC -) -{ - Int i, pe, elen, nv, len, e, p, k, j, deg, w, cnt, ilast ; - - if (CAMD_debug < 0) return ; - ASSERT (pfree <= iwlen) ; - CAMD_DEBUG3 (("\nCAMD dump, pfree: "ID"\n", pfree)) ; - for (i = 0 ; i < n ; i++) - { - pe = Pe [i] ; - elen = Elen [i] ; - nv = Nv [i] ; - len = Len [i] ; - w = W [i] ; - - if (elen >= EMPTY) - { - if (nv == 0) - { - CAMD_DEBUG4 (("\nI "ID": nonprincipal: ", i)) ; - ASSERT (elen == EMPTY) ; - if (pe == FLIP(n)) - { - CAMD_DEBUG4 ((" dense node\n")) ; - ASSERT (w == 1) ; - } - else - { - ASSERT (pe < EMPTY) ; - CAMD_DEBUG4 ((" i "ID" -> parent "ID"\n", i, FLIP (Pe[i]))); - } - } - else - { - CAMD_DEBUG4 (("\nI "ID": active principal supervariable:\n",i)); - CAMD_DEBUG4 ((" nv(i): "ID" Flag: %d\n", nv, (nv < 0))) ; - ASSERT (elen >= 0) ; - ASSERT (nv > 0 && pe >= 0) ; - p = pe ; - CAMD_DEBUG4 ((" e/s: ")) ; - if (elen == 0) CAMD_DEBUG4 ((" : ")) ; - ASSERT (pe + len <= pfree) ; - for (k = 0 ; k < len ; k++) - { - j = Iw [p] ; - CAMD_DEBUG4 ((" "ID"", j)) ; - ASSERT (j >= 0 && j < n) ; - if (k == elen-1) CAMD_DEBUG4 ((" : ")) ; - p++ ; - } - CAMD_DEBUG4 (("\n")) ; - } - } - else - { - e = i ; - if (w == 0) - { - CAMD_DEBUG4 (("\nE "ID": absorbed element: w "ID"\n", e, w)) ; - ASSERT (nv > 0 && pe < 0) ; - CAMD_DEBUG4 ((" e "ID" -> parent "ID"\n", e, FLIP (Pe [e]))) ; - } - else - { - CAMD_DEBUG4 (("\nE "ID": unabsorbed element: w "ID"\n", e, w)) ; - ASSERT (nv > 0 && pe >= 0) ; - p = pe ; - CAMD_DEBUG4 ((" : ")) ; - ASSERT (pe + len <= pfree) ; - for (k = 0 ; k < len ; k++) - { - j = Iw [p] ; - CAMD_DEBUG4 ((" "ID"", j)) ; - ASSERT (j >= 0 && j < n) ; - p++ ; - } - CAMD_DEBUG4 (("\n")) ; - } - } - CAMD_DEBUG4 (("C[i] is :"ID"\n", (C == NULL) ? 0 : C [i])); - } - - /* this routine cannot be called when the hash buckets are non-empty */ - CAMD_DEBUG4 (("\nDegree lists:\n")) ; - if (nel >= 0) - { - cnt = 0 ; - for (deg = 0 ; deg < n ; deg++) - { - if (Head [deg] == EMPTY) continue ; - ilast = EMPTY ; - CAMD_DEBUG4 ((ID": \n", deg)) ; - for (i = Head [deg] ; i != EMPTY ; i = Next [i]) - { - CAMD_DEBUG4 ((" "ID" : next "ID" last "ID" deg "ID"\n", - i, Next [i], Last [i], Degree [i])) ; - ASSERT (i >= 0 && i < n && ilast == Last [i] && - deg == Degree [i]) ; - cnt += Nv [i] ; - ilast = i ; - } - CAMD_DEBUG4 (("\n")) ; - } - } - - CAMD_DEBUG4(("\nCurrent C[i] is "ID". current Buckets are:\n", CurC)) ; - for (i = 0 ; i < n ; i++) - { - if ((C == NULL) ? 1 : (C [BucketSet [i]] <= CurC)) - CAMD_DEBUG4((ID",",BucketSet [i])); - } - CAMD_DEBUG4 (("\n")) ; -} - -#endif diff --git a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Source/camd_global.c b/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Source/camd_global.c deleted file mode 100644 index eef93ee48..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Source/camd_global.c +++ /dev/null @@ -1,14 +0,0 @@ -/* ========================================================================= */ -/* === camd_global ========================================================= */ -/* ========================================================================= */ - -/* ------------------------------------------------------------------------- */ -/* CAMD, Copyright (c) Timothy A. Davis, Yanqing Chen, */ -/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ -/* email: DrTimothyAldenDavis@gmail.com */ -/* ------------------------------------------------------------------------- */ - -/* In prior versions of CAMD, this file declared the camd_malloc, camd_free, - camd_realloc, camd_calloc, and camd_printf functions. They are now replaced - by functions defined in SuiteSparse_config/SuiteSparse_config.c. - */ diff --git a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Source/camd_info.c b/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Source/camd_info.c deleted file mode 100644 index 96547e686..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Source/camd_info.c +++ /dev/null @@ -1,119 +0,0 @@ -/* ========================================================================= */ -/* === CAMD_info =========================================================== */ -/* ========================================================================= */ - -/* ------------------------------------------------------------------------- */ -/* CAMD, Copyright (c) Timothy A. Davis, Yanqing Chen, */ -/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ -/* email: DrTimothyAldenDavis@gmail.com */ -/* ------------------------------------------------------------------------- */ - -/* User-callable. Prints the output statistics for CAMD. See camd.h - * for details. If the Info array is not present, nothing is printed. - */ - -#include "camd_internal.h" - -#define PRI(format,x) { if (x >= 0) { SUITESPARSE_PRINTF ((format, x)) ; }} - -GLOBAL void CAMD_info -( - double Info [ ] -) -{ - double n, ndiv, nmultsubs_ldl, nmultsubs_lu, lnz, lnzd ; - - SUITESPARSE_PRINTF (("\nCAMD version %d.%d.%d, %s, results:\n", - CAMD_MAIN_VERSION, CAMD_SUB_VERSION, CAMD_SUBSUB_VERSION, CAMD_DATE)) ; - - if (!Info) - { - return ; - } - - n = Info [CAMD_N] ; - ndiv = Info [CAMD_NDIV] ; - nmultsubs_ldl = Info [CAMD_NMULTSUBS_LDL] ; - nmultsubs_lu = Info [CAMD_NMULTSUBS_LU] ; - lnz = Info [CAMD_LNZ] ; - lnzd = (n >= 0 && lnz >= 0) ? (n + lnz) : (-1) ; - - /* CAMD return status */ - SUITESPARSE_PRINTF ((" status: ")) ; - if (Info [CAMD_STATUS] == CAMD_OK) - { - SUITESPARSE_PRINTF (("OK\n")) ; - } - else if (Info [CAMD_STATUS] == CAMD_OUT_OF_MEMORY) - { - SUITESPARSE_PRINTF (("out of memory\n")) ; - } - else if (Info [CAMD_STATUS] == CAMD_INVALID) - { - SUITESPARSE_PRINTF (("invalid matrix\n")) ; - } - else if (Info [CAMD_STATUS] == CAMD_OK_BUT_JUMBLED) - { - SUITESPARSE_PRINTF (("OK, but jumbled\n")) ; - } - else - { - SUITESPARSE_PRINTF (("unknown\n")) ; - } - - /* statistics about the input matrix */ - PRI (" n, dimension of A: %.20g\n", n); - PRI (" nz, number of nonzeros in A: %.20g\n", - Info [CAMD_NZ]) ; - PRI (" symmetry of A: %.4f\n", - Info [CAMD_SYMMETRY]) ; - PRI (" number of nonzeros on diagonal: %.20g\n", - Info [CAMD_NZDIAG]) ; - PRI (" nonzeros in pattern of A+A' (excl. diagonal): %.20g\n", - Info [CAMD_NZ_A_PLUS_AT]) ; - PRI (" # dense rows/columns of A+A': %.20g\n", - Info [CAMD_NDENSE]) ; - - /* statistics about CAMD's behavior */ - PRI (" memory used, in bytes: %.20g\n", - Info [CAMD_MEMORY]) ; - PRI (" # of memory compactions: %.20g\n", - Info [CAMD_NCMPA]) ; - - /* statistics about the ordering quality */ - SUITESPARSE_PRINTF (("\n" - " The following approximate statistics are for a subsequent\n" - " factorization of A(P,P) + A(P,P)'. They are slight upper\n" - " bounds if there are no dense rows/columns in A+A', and become\n" - " looser if dense rows/columns exist.\n\n")) ; - - PRI (" nonzeros in L (excluding diagonal): %.20g\n", - lnz) ; - PRI (" nonzeros in L (including diagonal): %.20g\n", - lnzd) ; - PRI (" # divide operations for LDL' or LU: %.20g\n", - ndiv) ; - PRI (" # multiply-subtract operations for LDL': %.20g\n", - nmultsubs_ldl) ; - PRI (" # multiply-subtract operations for LU: %.20g\n", - nmultsubs_lu) ; - PRI (" max nz. in any column of L (incl. diagonal): %.20g\n", - Info [CAMD_DMAX]) ; - - /* total flop counts for various factorizations */ - - if (n >= 0 && ndiv >= 0 && nmultsubs_ldl >= 0 && nmultsubs_lu >= 0) - { - SUITESPARSE_PRINTF (("\n" - " chol flop count for real A, sqrt counted as 1 flop: %.20g\n" - " LDL' flop count for real A: %.20g\n" - " LDL' flop count for complex A: %.20g\n" - " LU flop count for real A (with no pivoting): %.20g\n" - " LU flop count for complex A (with no pivoting): %.20g\n\n", - n + ndiv + 2*nmultsubs_ldl, - ndiv + 2*nmultsubs_ldl, - 9*ndiv + 8*nmultsubs_ldl, - ndiv + 2*nmultsubs_lu, - 9*ndiv + 8*nmultsubs_lu)) ; - } -} diff --git a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Source/camd_order.c b/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Source/camd_order.c deleted file mode 100644 index 67eb1fdaf..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Source/camd_order.c +++ /dev/null @@ -1,200 +0,0 @@ -/* ========================================================================= */ -/* === CAMD_order ========================================================== */ -/* ========================================================================= */ - -/* ------------------------------------------------------------------------- */ -/* CAMD, Copyright (c) Timothy A. Davis, Yanqing Chen, */ -/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ -/* email: DrTimothyAldenDavis@gmail.com */ -/* ------------------------------------------------------------------------- */ - -/* User-callable CAMD minimum degree ordering routine. See camd.h for - * documentation. - */ - -#include "camd_internal.h" - -/* ========================================================================= */ -/* === CAMD_order ========================================================== */ -/* ========================================================================= */ - -GLOBAL Int CAMD_order -( - Int n, - const Int Ap [ ], - const Int Ai [ ], - Int P [ ], - double Control [ ], - double Info [ ], - const Int C [ ] -) -{ - Int *Len, *S, nz, i, *Pinv, info, status, *Rp, *Ri, *Cp, *Ci, ok ; - size_t nzaat, slen ; - double mem = 0 ; - -#ifndef NDEBUG - CAMD_debug_init ("camd") ; -#endif - - /* clear the Info array, if it exists */ - info = Info != (double *) NULL ; - if (info) - { - for (i = 0 ; i < CAMD_INFO ; i++) - { - Info [i] = EMPTY ; - } - Info [CAMD_N] = n ; - Info [CAMD_STATUS] = CAMD_OK ; - } - - /* make sure inputs exist and n is >= 0 */ - if (Ai == (Int *) NULL || Ap == (Int *) NULL || P == (Int *) NULL || n < 0) - { - if (info) Info [CAMD_STATUS] = CAMD_INVALID ; - return (CAMD_INVALID) ; /* arguments are invalid */ - } - - if (n == 0) - { - return (CAMD_OK) ; /* n is 0 so there's nothing to do */ - } - - nz = Ap [n] ; - if (info) - { - Info [CAMD_NZ] = nz ; - } - if (nz < 0) - { - if (info) Info [CAMD_STATUS] = CAMD_INVALID ; - return (CAMD_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)) - { - if (info) Info [CAMD_STATUS] = CAMD_OUT_OF_MEMORY ; - return (CAMD_OUT_OF_MEMORY) ; /* problem too large */ - } - - /* check the input matrix: CAMD_OK, CAMD_INVALID, or CAMD_OK_BUT_JUMBLED */ - status = CAMD_valid (n, n, Ap, Ai) ; - - if (status == CAMD_INVALID) - { - if (info) Info [CAMD_STATUS] = CAMD_INVALID ; - return (CAMD_INVALID) ; /* matrix is invalid */ - } - - /* allocate two size-n integer workspaces */ - Len = SuiteSparse_malloc (n, sizeof (Int)) ; - Pinv = SuiteSparse_malloc (n, sizeof (Int)) ; - mem += n ; - mem += n ; - if (!Len || !Pinv) - { - /* :: out of memory :: */ - SuiteSparse_free (Len) ; - SuiteSparse_free (Pinv) ; - if (info) Info [CAMD_STATUS] = CAMD_OUT_OF_MEMORY ; - return (CAMD_OUT_OF_MEMORY) ; - } - - if (status == CAMD_OK_BUT_JUMBLED) - { - /* sort the input matrix and remove duplicate entries */ - CAMD_DEBUG1 (("Matrix is jumbled\n")) ; - Rp = SuiteSparse_malloc (n+1, sizeof (Int)) ; - Ri = SuiteSparse_malloc (nz, sizeof (Int)) ; - mem += (n+1) ; - mem += MAX (nz,1) ; - if (!Rp || !Ri) - { - /* :: out of memory :: */ - SuiteSparse_free (Rp) ; - SuiteSparse_free (Ri) ; - SuiteSparse_free (Len) ; - SuiteSparse_free (Pinv) ; - if (info) Info [CAMD_STATUS] = CAMD_OUT_OF_MEMORY ; - return (CAMD_OUT_OF_MEMORY) ; - } - /* use Len and Pinv as workspace to create R = A' */ - CAMD_preprocess (n, Ap, Ai, Rp, Ri, Len, Pinv) ; - Cp = Rp ; - Ci = Ri ; - } - else - { - /* order the input matrix as-is. No need to compute R = A' first */ - Rp = NULL ; - Ri = NULL ; - Cp = (Int *) Ap ; - Ci = (Int *) Ai ; - } - - /* --------------------------------------------------------------------- */ - /* determine the symmetry and count off-diagonal nonzeros in A+A' */ - /* --------------------------------------------------------------------- */ - - nzaat = CAMD_aat (n, Cp, Ci, Len, P, Info) ; - CAMD_DEBUG1 (("nzaat: %g\n", (double) nzaat)) ; - ASSERT ((MAX (nz-n, 0) <= nzaat) && (nzaat <= 2 * (size_t) nz)) ; - - /* --------------------------------------------------------------------- */ - /* allocate workspace for matrix, elbow room, and 7 size-n vectors */ - /* --------------------------------------------------------------------- */ - - S = NULL ; - slen = nzaat ; /* space for matrix */ - ok = ((slen + nzaat/5) >= slen) ; /* check for size_t overflow */ - slen += nzaat/5 ; /* add elbow room */ - for (i = 0 ; ok && i < 8 ; i++) - { - ok = ((slen + n+1) > slen) ; /* check for size_t overflow */ - slen += (n+1) ; /* size-n elbow room, 7 size-(n+1) workspace */ - } - mem += slen ; - ok = ok && (slen < SIZE_T_MAX / sizeof (Int)) ; /* check for overflow */ - ok = ok && (slen < Int_MAX) ; /* S[i] for Int i must be OK */ - if (ok) - { - S = SuiteSparse_malloc (slen, sizeof (Int)) ; - } - CAMD_DEBUG1 (("slen %g\n", (double) slen)) ; - if (!S) - { - /* :: out of memory :: (or problem too large) */ - SuiteSparse_free (Rp) ; - SuiteSparse_free (Ri) ; - SuiteSparse_free (Len) ; - SuiteSparse_free (Pinv) ; - if (info) Info [CAMD_STATUS] = CAMD_OUT_OF_MEMORY ; - return (CAMD_OUT_OF_MEMORY) ; - } - if (info) - { - /* memory usage, in bytes. */ - Info [CAMD_MEMORY] = mem * sizeof (Int) ; - } - - /* --------------------------------------------------------------------- */ - /* order the matrix */ - /* --------------------------------------------------------------------- */ - - CAMD_1 (n, Cp, Ci, P, Pinv, Len, slen, S, Control, Info, C) ; - - /* --------------------------------------------------------------------- */ - /* free the workspace */ - /* --------------------------------------------------------------------- */ - - SuiteSparse_free (Rp) ; - SuiteSparse_free (Ri) ; - SuiteSparse_free (Len) ; - SuiteSparse_free (Pinv) ; - SuiteSparse_free (S) ; - if (info) Info [CAMD_STATUS] = status ; - return (status) ; /* successful ordering */ -} diff --git a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Source/camd_postorder.c b/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Source/camd_postorder.c deleted file mode 100644 index 4af03e11e..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Source/camd_postorder.c +++ /dev/null @@ -1,50 +0,0 @@ -/* ========================================================================= */ -/* === CAMD_postorder ====================================================== */ -/* ========================================================================= */ - -/* ------------------------------------------------------------------------- */ -/* CAMD, Copyright (c) Timothy A. Davis, Yanqing Chen, */ -/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ -/* email: DrTimothyAldenDavis@gmail.com */ -/* ------------------------------------------------------------------------- */ - -/* Perform a postordering (via depth-first search) of an assembly tree. */ - -#include "camd_internal.h" - -GLOBAL Int CAMD_postorder -( - Int j, /* start at node j, a root of the assembly tree */ - Int k, /* on input, next node is the kth node */ - Int n, /* normal nodes 0 to n-1, place-holder node n */ - Int head [], /* head of link list of children of each node */ - Int next [], /* next[i] is the next child after i in link list */ - Int post [], /* postordering, post [k] = p if p is the kth node */ - Int stack [] /* recursion stack */ -) -{ - int i, p, top = 0 ; - stack [0] = j ; /* place j on the stack, maybe place-holder node n */ - while (top >= 0) /* while (stack is not empty) */ - { - p = stack [top] ; /* p = top of stack */ - i = head [p] ; /* i = youngest child of p */ - if (i == -1) - { - top-- ; /* p has no unordered children left */ - if (p != n) - { - /* node p is the kth postordered node. Do not postorder the - * place-holder node n, which is the root of a subtree - * containing all dense and empty nodes. */ - post [k++] = p ; - } - } - else - { - head [p] = next [i] ; /* remove i from children of p */ - stack [++top] = i ; /* start dfs on child node i */ - } - } - return (k) ; -} diff --git a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Source/camd_preprocess.c b/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Source/camd_preprocess.c deleted file mode 100644 index aa399c36e..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Source/camd_preprocess.c +++ /dev/null @@ -1,118 +0,0 @@ -/* ========================================================================= */ -/* === CAMD_preprocess ===================================================== */ -/* ========================================================================= */ - -/* ------------------------------------------------------------------------- */ -/* CAMD, Copyright (c) Timothy A. Davis, Yanqing Chen, */ -/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ -/* email: DrTimothyAldenDavis@gmail.com */ -/* ------------------------------------------------------------------------- */ - -/* Sorts, removes duplicate entries, and transposes from the nonzero pattern of - * a column-form matrix A, to obtain the matrix R. The input matrix can have - * duplicate entries and/or unsorted columns (CAMD_valid (n,Ap,Ai) must not be - * CAMD_INVALID). - * - * This input condition is NOT checked. This routine is not user-callable. - */ - -#include "camd_internal.h" - -/* ========================================================================= */ -/* === CAMD_preprocess ===================================================== */ -/* ========================================================================= */ - -/* CAMD_preprocess does not check its input for errors or allocate workspace. - * On input, the condition (CAMD_valid (n,n,Ap,Ai) != CAMD_INVALID) must hold. - */ - -GLOBAL void CAMD_preprocess -( - Int n, /* input matrix: A is n-by-n */ - const Int Ap [ ], /* size n+1 */ - const Int Ai [ ], /* size nz = Ap [n] */ - - /* output matrix R: */ - Int Rp [ ], /* size n+1 */ - Int Ri [ ], /* size nz (or less, if duplicates present) */ - - Int W [ ], /* workspace of size n */ - Int Flag [ ] /* workspace of size n */ -) -{ - - /* --------------------------------------------------------------------- */ - /* local variables */ - /* --------------------------------------------------------------------- */ - - Int i, j, p, p2 ; - - ASSERT (CAMD_valid (n, n, Ap, Ai) != CAMD_INVALID) ; - - /* --------------------------------------------------------------------- */ - /* count the entries in each row of A (excluding duplicates) */ - /* --------------------------------------------------------------------- */ - - for (i = 0 ; i < n ; i++) - { - W [i] = 0 ; /* # of nonzeros in row i (excl duplicates) */ - Flag [i] = EMPTY ; /* Flag [i] = j if i appears in column j */ - } - for (j = 0 ; j < n ; j++) - { - p2 = Ap [j+1] ; - for (p = Ap [j] ; p < p2 ; p++) - { - i = Ai [p] ; - if (Flag [i] != j) - { - /* row index i has not yet appeared in column j */ - W [i]++ ; /* one more entry in row i */ - Flag [i] = j ; /* flag row index i as appearing in col j*/ - } - } - } - - /* --------------------------------------------------------------------- */ - /* compute the row pointers for R */ - /* --------------------------------------------------------------------- */ - - Rp [0] = 0 ; - for (i = 0 ; i < n ; i++) - { - Rp [i+1] = Rp [i] + W [i] ; - } - for (i = 0 ; i < n ; i++) - { - W [i] = Rp [i] ; - Flag [i] = EMPTY ; - } - - /* --------------------------------------------------------------------- */ - /* construct the row form matrix R */ - /* --------------------------------------------------------------------- */ - - /* R = row form of pattern of A */ - for (j = 0 ; j < n ; j++) - { - p2 = Ap [j+1] ; - for (p = Ap [j] ; p < p2 ; p++) - { - i = Ai [p] ; - if (Flag [i] != j) - { - /* row index i has not yet appeared in column j */ - Ri [W [i]++] = j ; /* put col j in row i */ - Flag [i] = j ; /* flag row index i as appearing in col j*/ - } - } - } - -#ifndef NDEBUG - ASSERT (CAMD_valid (n, n, Rp, Ri) == CAMD_OK) ; - for (j = 0 ; j < n ; j++) - { - ASSERT (W [j] == Rp [j+1]) ; - } -#endif -} diff --git a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Source/camd_valid.c b/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Source/camd_valid.c deleted file mode 100644 index a1da203d8..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/CAMD/Source/camd_valid.c +++ /dev/null @@ -1,112 +0,0 @@ -/* ========================================================================= */ -/* === CAMD_valid ========================================================== */ -/* ========================================================================= */ - -/* ------------------------------------------------------------------------- */ -/* CAMD, Copyright (c) Timothy A. Davis, Yanqing Chen, */ -/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ -/* email: DrTimothyAldenDavis@gmail.com */ -/* ------------------------------------------------------------------------- */ - -/* Check if a column-form matrix is valid or not. The matrix A is - * n_row-by-n_col. The row indices of entries in column j are in - * Ai [Ap [j] ... Ap [j+1]-1]. Required conditions are: - * - * n_row >= 0 - * n_col >= 0 - * nz = Ap [n_col] >= 0 number of entries in the matrix - * Ap [0] == 0 - * Ap [j] <= Ap [j+1] for all j in the range 0 to n_col. - * Ai [0 ... nz-1] must be in the range 0 to n_row-1. - * - * If any of the above conditions hold, CAMD_INVALID is returned. If the - * following condition holds, CAMD_OK_BUT_JUMBLED is returned (a warning, - * not an error): - * - * row indices in Ai [Ap [j] ... Ap [j+1]-1] are not sorted in ascending - * order, and/or duplicate entries exist. - * - * Otherwise, CAMD_OK is returned. - */ - -#include "camd_internal.h" - -GLOBAL Int CAMD_valid -( - /* inputs, not modified on output: */ - Int n_row, /* A is n_row-by-n_col */ - Int n_col, - const Int Ap [ ], /* column pointers of A, of size n_col+1 */ - const Int Ai [ ] /* row indices of A, of size nz = Ap [n_col] */ -) -{ - Int nz, j, p1, p2, ilast, i, p, result = CAMD_OK ; - if (n_row < 0 || n_col < 0 || Ap == NULL || Ai == NULL) - { - return (CAMD_INVALID) ; - } - nz = Ap [n_col] ; - if (Ap [0] != 0 || nz < 0) - { - /* column pointers must start at Ap [0] = 0, and Ap [n] must be >= 0 */ - CAMD_DEBUG0 (("column 0 pointer bad or nz < 0\n")) ; - return (CAMD_INVALID) ; - } - for (j = 0 ; j < n_col ; j++) - { - p1 = Ap [j] ; - p2 = Ap [j+1] ; - CAMD_DEBUG2 (("\nColumn: "ID" p1: "ID" p2: "ID"\n", j, p1, p2)) ; - if (p1 > p2) - { - /* column pointers must be ascending */ - CAMD_DEBUG0 (("column "ID" pointer bad\n", j)) ; - return (CAMD_INVALID) ; - } - ilast = EMPTY ; - for (p = p1 ; p < p2 ; p++) - { - i = Ai [p] ; - CAMD_DEBUG3 (("row: "ID"\n", i)) ; - if (i < 0 || i >= n_row) - { - /* row index out of range */ - CAMD_DEBUG0 (("index out of range, col "ID" row "ID"\n", j, i)); - return (CAMD_INVALID) ; - } - if (i <= ilast) - { - /* row index unsorted, or duplicate entry present */ - CAMD_DEBUG1 (("index unsorted/dupl col "ID" row "ID"\n", j, i)); - result = CAMD_OK_BUT_JUMBLED ; - } - ilast = i ; - } - } - return (result) ; -} - - -GLOBAL Int CAMD_cvalid /* return TRUE if the Constraint set is valid, - * FALSE otherwise */ -( - /* inputs, not modified on output: */ - Int n, /* the length of constraint set */ - const Int C [ ] /* constraint set */ -) -{ - Int i ; - if (C != NULL) - { - for (i = 0 ; i < n ; i++) - { - if (C [i] < 0 || C [i] > n - 1) - { - CAMD_DEBUG0 (("C["ID"] = "ID" invalid\n", i, C [i])) ; - return (FALSE) ; - } - } - } - return (TRUE) ; -} - diff --git a/deps/AMICI/ThirdParty/SuiteSparse/CITATION.bib b/deps/AMICI/ThirdParty/SuiteSparse/CITATION.bib new file mode 100644 index 000000000..13b13d93a --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/CITATION.bib @@ -0,0 +1,490 @@ + +@article{10.1145/3322125, +author = {Davis, Timothy A.}, +title = {Algorithm 1000: SuiteSparse:GraphBLAS: Graph Algorithms in the Language of Sparse Linear Algebra}, +year = {2019}, +issue_date = {December 2019}, +publisher = {Association for Computing Machinery}, +address = {New York, NY, USA}, +volume = {45}, +number = {4}, +issn = {0098-3500}, +url = {https://doi.org/10.1145/3322125}, +doi = {10.1145/3322125}, +abstract = {SuiteSparse:GraphBLAS is a full implementation of the GraphBLAS standard, which defines a set of sparse matrix operations on an extended algebra of semirings using an almost unlimited variety of operators and types. When applied to sparse adjacency matrices, these algebraic operations are equivalent to computations on graphs. GraphBLAS provides a powerful and expressive framework for creating graph algorithms based on the elegant mathematics of sparse matrix operations on a semiring. An overview of the GraphBLAS specification is given, followed by a description of the key features and performance of its implementation in the SuiteSparse:GraphBLAS package.}, +journal = {ACM Trans. Math. Softw.}, +month = {dec}, +articleno = {44}, +numpages = {25}, +keywords = {GraphBLAS, Graph algorithms, sparse matrices} +} + +@book{FA02, + author={T. A. Davis}, + title={Direct Methods for Sparse Linear Systems}, + publisher={SIAM}, + address={Philadelphia, PA}, + year={2006}, + url={https://doi.org/10.1137/1.9780898718881}, + doi={10.1137/1.9780898718881}} + + +@article{10.1145/2049662.2049670, +author = {Davis, Timothy A.}, +title = {Algorithm 915, SuiteSparseQR: Multifrontal Multithreaded Rank-Revealing Sparse QR Factorization}, +year = {2011}, +issue_date = {November 2011}, +publisher = {Association for Computing Machinery}, +address = {New York, NY, USA}, +volume = {38}, +number = {1}, +issn = {0098-3500}, +url = {https://doi.org/10.1145/2049662.2049670}, +doi = {10.1145/2049662.2049670}, +abstract = {SuiteSparseQR is a sparse QR factorization package based on the multifrontal method. Within each frontal matrix, LAPACK and the multithreaded BLAS enable the method to obtain high performance on multicore architectures. Parallelism across different frontal matrices is handled with Intel's Threading Building Blocks library. The symbolic analysis and ordering phase pre-eliminates singletons by permuting the input matrix A into the form [R11 R12; 0 A22] where R11 is upper triangular with diagonal entries above a given tolerance. Next, the fill-reducing ordering, column elimination tree, and frontal matrix structures are found without requiring the formation of the pattern of ATA. Approximate rank-detection is performed within each frontal matrix using Heath's method. While Heath's method is not always exact, it has the advantage of not requiring column pivoting and thus does not interfere with the fill-reducing ordering. For sufficiently large problems, the resulting sparse QR factorization obtains a substantial fraction of the theoretical peak performance of a multicore computer.}, +journal = {ACM Trans. Math. Softw.}, +month = {dec}, +articleno = {8}, +numpages = {22}, +keywords = {least-square problems, sparse matrices, QR factorization} +} + +@article{10.1145/3065870, +author = {Yeralan, Sencer Nuri and Davis, Timothy A. and Sid-Lakhdar, Wissam M. and Ranka, Sanjay}, +title = {Algorithm 980: Sparse QR Factorization on the GPU}, +year = {2017}, +issue_date = {June 2018}, +publisher = {Association for Computing Machinery}, +address = {New York, NY, USA}, +volume = {44}, +number = {2}, +issn = {0098-3500}, +url = {https://doi.org/10.1145/3065870}, +doi = {10.1145/3065870}, +abstract = {Sparse matrix factorization involves a mix of regular and irregular computation, which is a particular challenge when trying to obtain high-performance on the highly parallel general-purpose computing cores available on graphics processing units (GPUs). We present a sparse multifrontal QR factorization method that meets this challenge and is significantly faster than a highly optimized method on a multicore CPU. Our method factorizes many frontal matrices in parallel and keeps all the data transmitted between frontal matrices on the GPU. A novel bucket scheduler algorithm extends the communication-avoiding QR factorization for dense matrices by exploiting more parallelism and by exploiting the staircase form present in the frontal matrices of a sparse multifrontal method.}, +journal = {ACM Trans. Math. Softw.}, +month = {aug}, +articleno = {17}, +numpages = {29}, +keywords = {GPU, QR factorization, least-square problems, sparse matrices} +} + +@article{10.1145/1391989.1391995, +author = {Chen, Yanqing and Davis, Timothy A. and Hager, William W. and Rajamanickam, Sivasankaran}, +title = {Algorithm 887: CHOLMOD, Supernodal Sparse Cholesky Factorization and Update/Downdate}, +year = {2008}, +issue_date = {October 2008}, +publisher = {Association for Computing Machinery}, +address = {New York, NY, USA}, +volume = {35}, +number = {3}, +issn = {0098-3500}, +url = {https://doi.org/10.1145/1391989.1391995}, +doi = {10.1145/1391989.1391995}, +abstract = {CHOLMOD is a set of routines for factorizing sparse symmetric positive definite matrices of the form A or AAT, updating/downdating a sparse Cholesky factorization, solving linear systems, updating/downdating the solution to the triangular system Lx = b, and many other sparse matrix functions for both symmetric and unsymmetric matrices. Its supernodal Cholesky factorization relies on LAPACK and the Level-3 BLAS, and obtains a substantial fraction of the peak performance of the BLAS. Both real and complex matrices are supported. CHOLMOD is written in ANSI/ISO C, with both C and MATLABTM interfaces. It appears in MATLAB 7.2 as x = Ab when A is sparse symmetric positive definite, as well as in several other sparse matrix functions.}, +journal = {ACM Trans. Math. Softw.}, +month = {oct}, +articleno = {22}, +numpages = {14}, +keywords = {sparse matrices, linear equations, Cholesky factorization} +} + +@article{10.1145/1462173.1462176, +author = {Davis, Timothy A. and Hager, William W.}, +title = {Dynamic Supernodes in Sparse Cholesky Update/Downdate and Triangular Solves}, +year = {2009}, +issue_date = {February 2009}, +publisher = {Association for Computing Machinery}, +address = {New York, NY, USA}, +volume = {35}, +number = {4}, +issn = {0098-3500}, +url = {https://doi.org/10.1145/1462173.1462176}, +doi = {10.1145/1462173.1462176}, +abstract = {The supernodal method for sparse Cholesky factorization represents the factor L as a set of supernodes, each consisting of a contiguous set of columns of L with identical nonzero pattern. A conventional supernode is stored as a dense submatrix. While this is suitable for sparse Cholesky factorization where the nonzero pattern of L does not change, it is not suitable for methods that modify a sparse Cholesky factorization after a low-rank change to A (an update/downdate, undefined = A ± WWT). Supernodes merge and split apart during an update/downdate. Dynamic supernodes are introduced which allow a sparse Cholesky update/downdate to obtain performance competitive with conventional supernodal methods. A dynamic supernodal solver is shown to exceed the performance of the conventional (BLAS-based) supernodal method for solving triangular systems. These methods are incorporated into CHOLMOD, a sparse Cholesky factorization and update/downdate package which forms the basis of x = Ab MATLAB when A is sparse and symmetric positive definite.}, +journal = {ACM Trans. Math. Softw.}, +month = {feb}, +articleno = {27}, +numpages = {23}, +keywords = {sparse matrices, linear equations, Cholesky factorization} +} + + +@article{doi:10.1137/S089547980343641X, +author = {Davis, Timothy A. and Hager, William W.}, +title = {Row Modifications of a Sparse Cholesky Factorization}, +journal = {SIAM Journal on Matrix Analysis and Applications}, +volume = {26}, +number = {3}, +pages = {621-639}, +year = {2005}, +doi = {10.1137/S089547980343641X}, +URL = {https://doi.org/10.1137/S089547980343641X }, +eprint = { https://doi.org/10.1137/S089547980343641X } , + abstract = { Given a sparse, symmetric positive definite matrix C and an associated sparse Cholesky factorization LDL\$\tr\$, we develop sparse techniques for updating the factorization after a symmetric modification of a row and column of C. We show how the modification in the Cholesky factorization associated with this rank-2 modification of C can be computed efficiently using a sparse rank-1 technique developed in [T. A. Davis and W. W. Hager, SIAM J. Matrix Anal. Appl., 20 (1999), pp. 606--627]. We also determine how the solution of a linear system Lx = b changes after changing a row and column of C or after a rank-r change in C. } +} + + +@article{doi:10.1137/S0895479899357346, +author = {Davis, Timothy A. and Hager, William W.}, +title = {Multiple-Rank Modifications of a Sparse Cholesky Factorization}, +journal = {SIAM Journal on Matrix Analysis and Applications}, +volume = {22}, +number = {4}, +pages = {997-1013}, +year = {2001}, +doi = {10.1137/S0895479899357346}, +URL = { https://doi.org/10.1137/S0895479899357346 }, +eprint = { https://doi.org/10.1137/S0895479899357346 } , + abstract = { Given a sparse symmetric positive definite matrix \$\mathbf{AA}\tr\$ and an associated sparse Cholesky factorization \$\mathbf{LDL}\tr\$ or \$\mathbf{LL}\tr\$, we develop sparse techniques for updating the factorization after either adding a collection of columns to A or deleting a collection of columns from A. Our techniques are based on an analysis and manipulation of the underlying graph structure, using the framework developed in an earlier paper on rank-1 modifications [T. A. Davis and W. W. Hager, SIAM J. Matrix Anal. Appl., 20 (1999), pp. 606--627]. Computationally, the multiple-rank update has better memory traffic and executes much faster than an equivalent series of rank-1 updates since the multiple-rank update makes one pass through L computing the new entries, while a series of rank-1 updates requires multiple passes through L. } +} + + + +@article{doi:10.1137/S0895479897321076, +author = {Davis, Timothy A. and Hager, William W.}, +title = {Modifying a Sparse Cholesky Factorization}, +journal = {SIAM Journal on Matrix Analysis and Applications}, +volume = {20}, +number = {3}, +pages = {606-627}, +year = {1999}, +doi = {10.1137/S0895479897321076}, +URL = { https://doi.org/10.1137/S0895479897321076 }, +eprint = { https://doi.org/10.1137/S0895479897321076 } , + abstract = { Given a sparse symmetric positive definite matrix \${\bf AA}^{\sf T}\$ and an associated sparse Cholesky factorization \${\bf LDL}^{\sf T}\$ or \${\bf LL}^{\sf T}\$, we develop sparse techniques for obtaining the new factorization associated with either adding a column to \${\bf A}\$ or deleting a column from \${\bf A}\$. Our techniques are based on an analysis and manipulation of the underlying graph structure and on ideas of Gill et al.\ [ Math. Comp., 28 (1974), pp. 505--535] for modifying a dense Cholesky factorization. We show that our methods extend to the general case where an arbitrary sparse symmetric positive definite matrix is modified. Our methods are optimal in the sense that they take time proportional to the number of nonzero entries in \${\bf L}\$ and \${\bf D}\$ that change. } +} + + +@article{RENNICH2016140, +title = {Accelerating sparse Cholesky factorization on GPUs}, +journal = {Parallel Computing}, +volume = {59}, +pages = {140-150}, +year = {2016}, +note = {Theory and Practice of Irregular Applications}, +issn = {0167-8191}, +doi = {https://doi.org/10.1016/j.parco.2016.06.004}, +url = {https://www.sciencedirect.com/science/article/pii/S016781911630059X}, +author = {Steven C. Rennich and Darko Stosic and Timothy A. Davis}, +keywords = {Sparse, Cholesky, Factorization, GPU, Parallel}, +abstract = {Sparse factorization is a fundamental tool in scientific computing. As the major component of a sparse direct solver, it represents the dominant computational cost for many analyses. For factorizations which involve sufficient dense math, the substantial computational capability provided by GPUs (Graphics Processing Units) can help alleviate this cost. However, for many other cases, the prevalence of small/irregular dense math and the relatively slow communication between the host and device over the PCIe bus, make it challenging to significantly accelerate sparse factorization using the GPU. In this paper we describe a left-looking supernodal Cholesky factorization algorithm which permits improved utilization of the GPU when factoring sparse matrices. The central idea is to stream subtrees of the elimination tree through the GPU and perform the factorization of each subtree entirely on the GPU. This avoids the majority of the PCIe communication without the need for a complex task scheduler. Importantly, within these subtrees, many independent, small, dense operations are batched to minimize kernel launch overhead and many of these batched kernels are executed concurrently to maximize device utilization. Performance results for commonly studied matrices are presented along with suggested actions for further optimization.} +} + +@article{10.1145/1024074.1024081, +author = {Amestoy, Patrick R. and Davis, Timothy A. and Duff, Iain S.}, +title = {Algorithm 837: AMD, an Approximate Minimum Degree Ordering Algorithm}, +year = {2004}, +issue_date = {September 2004}, +publisher = {Association for Computing Machinery}, +address = {New York, NY, USA}, +volume = {30}, +number = {3}, +issn = {0098-3500}, +url = {https://doi.org/10.1145/1024074.1024081}, +doi = {10.1145/1024074.1024081}, +abstract = {AMD is a set of routines that implements the approximate minimum degree ordering algorithm to permute sparse matrices prior to numerical factorization. There are versions written in both C and Fortran 77. A MATLAB interface is included.}, +journal = {ACM Trans. Math. Softw.}, +month = {sep}, +pages = {381–388}, +numpages = {8}, +keywords = {sparse matrices, Linear equations, ordering methods, minimum degree} +} + + + + + + + +@article{doi:10.1137/S0895479894278952, +author = {Amestoy, Patrick R. and Davis, Timothy A. and Duff, Iain S.}, +title = {An Approximate Minimum Degree Ordering Algorithm}, +journal = {SIAM Journal on Matrix Analysis and Applications}, +volume = {17}, +number = {4}, +pages = {886-905}, +year = {1996}, +doi = {10.1137/S0895479894278952}, +URL = { https://doi.org/10.1137/S0895479894278952 }, +eprint = { https://doi.org/10.1137/S0895479894278952 } , + abstract = { Abstract. An approximate minimum degree (AMD), ordering algorithm for preordering a symmetric sparse matrix prior to numerical factorization is presented. We use techniques based on the quotient graph for matrix factorization that allow us to obtain computationally cheap bounds for the minimum degree. We show that these bounds are often equal to the actual degree. The resulting algorithm is typically much faster than previous minimum degree ordering algorithms and produces results that are comparable in quality with the best orderings from other minimum degree algorithms. } +} + + +@article{10.1145/1024074.1024080, +author = {Davis, Timothy A. and Gilbert, John R. and Larimore, Stefan I. and Ng, Esmond G.}, +title = {Algorithm 836: COLAMD, a Column Approximate Minimum Degree Ordering Algorithm}, +year = {2004}, +issue_date = {September 2004}, +publisher = {Association for Computing Machinery}, +address = {New York, NY, USA}, +volume = {30}, +number = {3}, +issn = {0098-3500}, +url = {https://doi.org/10.1145/1024074.1024080}, +doi = {10.1145/1024074.1024080}, +abstract = {Two codes are discussed, COLAMD and SYMAMD, that compute approximate minimum degree orderings for sparse matrices in two contexts: (1) sparse partial pivoting, which requires a sparsity preserving column pre-ordering prior to numerical factorization, and (2) sparse Cholesky factorization, which requires a symmetric permutation of both the rows and columns of the matrix being factorized. These orderings are computed by COLAMD and SYMAMD, respectively. The ordering from COLAMD is also suitable for sparse QR factorization, and the factorization of matrices of the form ATA and AAT, such as those that arise in least-squares problems and interior point methods for linear programming problems. The two routines are available both in MATLAB and C-callable forms. They appear as built-in routines in MATLAB Version 6.0.}, +journal = {ACM Trans. Math. Softw.}, +month = {sep}, +pages = {377–380}, +numpages = {4}, +keywords = {sparse nonsymmetric matrices, ordering methods, Linear equations} +} + +@article{10.1145/1024074.1024079, +author = {Davis, Timothy A. and Gilbert, John R. and Larimore, Stefan I. and Ng, Esmond G.}, +title = {A Column Approximate Minimum Degree Ordering Algorithm}, +year = {2004}, +issue_date = {September 2004}, +publisher = {Association for Computing Machinery}, +address = {New York, NY, USA}, +volume = {30}, +number = {3}, +issn = {0098-3500}, +url = {https://doi.org/10.1145/1024074.1024079}, +doi = {10.1145/1024074.1024079}, +abstract = {Sparse Gaussian elimination with partial pivoting computes the factorization PAQ = LU of a sparse matrix A, where the row ordering P is selected during factorization using standard partial pivoting with row interchanges. The goal is to select a column preordering, Q, based solely on the nonzero pattern of A, that limits the worst-case number of nonzeros in the factorization. The fill-in also depends on P, but Q is selected to reduce an upper bound on the fill-in for any subsequent choice of P. The choice of Q can have a dramatic impact on the number of nonzeros in L and U. One scheme for determining a good column ordering for A is to compute a symmetric ordering that reduces fill-in in the Cholesky factorization of ATA. A conventional minimum degree ordering algorithm would require the sparsity structure of ATA to be computed, which can be expensive both in terms of space and time since ATA may be much denser than A. An alternative is to compute Q directly from the sparsity structure of A; this strategy is used by MATLAB's COLMMD preordering algorithm. A new ordering algorithm, COLAMD, is presented. It is based on the same strategy but uses a better ordering heuristic. COLAMD is faster and computes better orderings, with fewer nonzeros in the factors of the matrix.}, +journal = {ACM Trans. Math. Softw.}, +month = {sep}, +pages = {353–376}, +numpages = {24}, +keywords = {linear equations, Sparse nonsymmetric matrices, ordering methods} +} + +@article{10.1145/992200.992206, +author = {Davis, Timothy A.}, +title = {Algorithm 832: UMFPACK V4.3---an Unsymmetric-Pattern Multifrontal Method}, +year = {2004}, +issue_date = {June 2004}, +publisher = {Association for Computing Machinery}, +address = {New York, NY, USA}, +volume = {30}, +number = {2}, +issn = {0098-3500}, +url = {https://doi.org/10.1145/992200.992206}, +doi = {10.1145/992200.992206}, +abstract = {An ANSI C code for sparse LU factorization is presented that combines a column pre-ordering strategy with a right-looking unsymmetric-pattern multifrontal numerical factorization. The pre-ordering and symbolic analysis phase computes an upper bound on fill-in, work, and memory usage during the subsequent numerical factorization. User-callable routines are provided for ordering and analyzing a sparse matrix, computing the numerical factorization, solving a system with the LU factors, transposing and permuting a sparse matrix, and converting between sparse matrix representations. The simple user interface shields the user from the details of the complex sparse factorization data structures by returning simple handles to opaque objects. Additional user-callable routines are provided for printing and extracting the contents of these opaque objects. An even simpler way to use the package is through its MATLAB interface. UMFPACK is incorporated as a built-in operator in MATLAB 6.5 as x = Ab when A is sparse and unsymmetric.}, +journal = {ACM Trans. Math. Softw.}, +month = {jun}, +pages = {196–199}, +numpages = {4}, +keywords = {ordering methods, multifrontal method, sparse nonsymmetric matrices, linear equations} +} + + +@article{10.1145/992200.992205, +author = {Davis, Timothy A.}, +title = {A Column Pre-Ordering Strategy for the Unsymmetric-Pattern Multifrontal Method}, +year = {2004}, +issue_date = {June 2004}, +publisher = {Association for Computing Machinery}, +address = {New York, NY, USA}, +volume = {30}, +number = {2}, +issn = {0098-3500}, +url = {https://doi.org/10.1145/992200.992205}, +doi = {10.1145/992200.992205}, +abstract = {A new method for sparse LU factorization is presented that combines a column pre-ordering strategy with a right-looking unsymmetric-pattern multifrontal numerical factorization. The column ordering is selected to give a good a priori upper bound on fill-in and then refined during numerical factorization (while preserving the bound). Pivot rows are selected to maintain numerical stability and to preserve sparsity. The method analyzes the matrix and automatically selects one of three pre-ordering and pivoting strategies. The number of nonzeros in the LU factors computed by the method is typically less than or equal to those found by a wide range of unsymmetric sparse LU factorization methods, including left-looking methods and prior multifrontal methods.}, +journal = {ACM Trans. Math. Softw.}, +month = {jun}, +pages = {165–195}, +numpages = {31}, +keywords = {linear equations, multifrontal method, sparse nonsymmetric matrices, ordering methods} +} + +@article{10.1145/305658.287640, +author = {Davis, Timothy A. and Duff, Iain S.}, +title = {A Combined Unifrontal/Multifrontal Method for Unsymmetric Sparse Matrices}, +year = {1999}, +issue_date = {March 1999}, +publisher = {Association for Computing Machinery}, +address = {New York, NY, USA}, +volume = {25}, +number = {1}, +issn = {0098-3500}, +url = {https://doi.org/10.1145/305658.287640}, +doi = {10.1145/305658.287640}, +abstract = {We discuss the organization of frontal matrices in multifrontal methods for the solution of large sparse sets of unsymmetric linear equations. In the multifrontal method, work on a frontal matrix can be suspended, the frontal matrix can be stored for later reuse, and a new frontal matrix can be generated. There are thus several frontal matrices stored during the factorization, and one or more of these are assembled (summed) when creating a new frontal matrix. Although this means that arbitrary sparsity patterns can be handled efficiently, extra work is required to sum the frontal matrices together and can be costly because indirect addressing is requred. The (uni)frontal method avoids this extra work by factorizing the matrix with a single frontal matrix. Rows and columns are added to the frontal matrix, and pivot rows and columns are removed. Data movement is simpler, but higher fill-in can result if the matrix cannot be permuted into a variable-band form with small profile. We consider a combined unifrontal/multifrontal algorithm to enable general fill-in reduction orderings to be applied without the data movement of previous multifrontal approaches. We discuss this technique in the context of a code designed for the solution of sparse systems with unsymmetric pattern.}, +journal = {ACM Trans. Math. Softw.}, +month = {mar}, +pages = {1–20}, +numpages = {20}, +keywords = {linear equations, frontal methods, sparse unsymmetric matrices, multifrontal methods} +} + + + + + + + + +@article{doi:10.1137/S0895479894246905, +author = {Davis, Timothy A. and Duff, Iain S.}, +title = {An Unsymmetric-Pattern Multifrontal Method for Sparse LU Factorization}, +journal = {SIAM Journal on Matrix Analysis and Applications}, +volume = {18}, +number = {1}, +pages = {140-158}, +year = {1997}, +doi = {10.1137/S0895479894246905}, +URL = { https://doi.org/10.1137/S0895479894246905 }, +eprint = { https://doi.org/10.1137/S0895479894246905 } , + abstract = { Sparse matrix factorization algorithms for general problems are typically characterized by irregular memory access patterns that limit their performance on parallel-vector supercomputers. For symmetric problems, methods such as the multifrontal method avoid indirect addressing in the innermost loops by using dense matrix kernels. However, no efficient LU factorization algorithm based primarily on dense matrix kernels exists for matrices whose pattern is very unsymmetric. We address this deficiency and present a new unsymmetric-pattern multifrontal method based on dense matrix kernels. As in the classical multifrontal method, advantage is taken of repetitive structure in the matrix by factorizing more than one pivot in each frontal matrix, thus enabling the use of Level 2 and Level 3 BLAS. The performance is compared with the classical multifrontal method and other unsymmetric solvers on a CRAY C-98. } +} + + +@article{10.1145/2491491.2491498, +author = {Davis, Timothy A.}, +title = {Algorithm 930: FACTORIZE: An Object-Oriented Linear System Solver for MATLAB}, +year = {2013}, +issue_date = {July 2013}, +publisher = {Association for Computing Machinery}, +address = {New York, NY, USA}, +volume = {39}, +number = {4}, +issn = {0098-3500}, +url = {https://doi.org/10.1145/2491491.2491498}, +doi = {10.1145/2491491.2491498}, +abstract = {The MATLAB™ backslash (x=Ab) is an elegant and powerful interface to a suite of high-performance factorization methods for the direct solution of the linear system Ax = b and the least-squares problem minx ‖b - Ax‖. It is a meta-algorithm that selects the best factorization method for a particular matrix, whether sparse or dense. However, the simplicity and elegance of its single-character interface prohibits the reuse of its factorization for subsequent systems. Requiring MATLAB users to find the best factorization method on their own can lead to suboptimal choices; even MATLAB experts can make the wrong choice. Furthermore, naive MATLAB users have a tendency to translate mathematical expressions from linear algebra directly into MATLAB, so that x = A-1b becomes the inferior yet all-to-prevalent x=inv(A)*b. To address these issues, an object-oriented FACTORIZE method is presented. Via simple-to-use operator overloading, solving two linear systems can be written as F=factorize(A); x=Fb; y=Fc, where A is factorized only once. The selection of the best factorization method (LU, Cholesky, LDLT, QR, or a complete orthogonal decomposition for rank-deficient matrices) is hidden from the user. The mathematical expression x = A-1b directly translates into the MATLAB expression x=inverse(A)*b, which does not compute the inverse at all, but does the right thing by factorizing A and solving the corresponding triangular systems.}, +journal = {ACM Trans. Math. Softw.}, +month = {jul}, +articleno = {28}, +numpages = {18}, +keywords = {object-oriented methods, least-square problems, matrix factorization, Linear systems} +} + + +@article{10.1145/1824801.1824814, +author = {Davis, Timothy A. and Palamadai Natarajan, Ekanathan}, +title = {Algorithm 907: KLU, A Direct Sparse Solver for Circuit Simulation Problems}, +year = {2010}, +issue_date = {September 2010}, +publisher = {Association for Computing Machinery}, +address = {New York, NY, USA}, +volume = {37}, +number = {3}, +issn = {0098-3500}, +url = {https://doi.org/10.1145/1824801.1824814}, +doi = {10.1145/1824801.1824814}, +abstract = {KLU is a software package for solving sparse unsymmetric linear systems of equations that arise in circuit simulation applications. It relies on a permutation to Block Triangular Form (BTF), several methods for finding a fill-reducing ordering (variants of approximate minimum degree and nested dissection), and Gilbert/Peierls’ sparse left-looking LU factorization algorithm to factorize each block. The package is written in C and includes a MATLAB interface. Performance results comparing KLU with SuperLU, Sparse 1.3, and UMFPACK on circuit simulation matrices are presented. KLU is the default sparse direct solver in the XyceTMcircuit simulation package developed by Sandia National Laboratories.}, +journal = {ACM Trans. Math. Softw.}, +month = {sep}, +articleno = {36}, +numpages = {17}, +keywords = {LU factorization, circuit simulation, sparse matrices} +} + +@article{10.1145/1114268.1114277, +author = {Davis, Timothy A.}, +title = {Algorithm 849: A Concise Sparse Cholesky Factorization Package}, +year = {2005}, +issue_date = {December 2005}, +publisher = {Association for Computing Machinery}, +address = {New York, NY, USA}, +volume = {31}, +number = {4}, +issn = {0098-3500}, +url = {https://doi.org/10.1145/1114268.1114277}, +doi = {10.1145/1114268.1114277}, +abstract = {The LDL software package is a set of short, concise routines for factorizing symmetric positive-definite sparse matrices, with some applicability to symmetric indefinite matrices. Its primary purpose is to illustrate much of the basic theory of sparse matrix algorithms in as concise a code as possible, including an elegant method of sparse symmetric factorization that computes the factorization row-by-row but stores it column-by-column. The entire symbolic and numeric factorization consists of less than 50 executable lines of code. The package is written in C, and includes a MATLAB interface.}, +journal = {ACM Trans. Math. Softw.}, +month = {dec}, +pages = {587–591}, +numpages = {5}, +keywords = {Cholesky factorization, linear equations, sparse matrices} +} + +@article{10.1145/2049662.2049663, +author = {Davis, Timothy A. and Hu, Yifan}, +title = {The University of Florida Sparse Matrix Collection}, +year = {2011}, +issue_date = {November 2011}, +publisher = {Association for Computing Machinery}, +address = {New York, NY, USA}, +volume = {38}, +number = {1}, +issn = {0098-3500}, +url = {https://doi.org/10.1145/2049662.2049663}, +doi = {10.1145/2049662.2049663}, +abstract = {We describe the University of Florida Sparse Matrix Collection, a large and actively growing set of sparse matrices that arise in real applications. The Collection is widely used by the numerical linear algebra community for the development and performance evaluation of sparse matrix algorithms. It allows for robust and repeatable experiments: robust because performance results with artificially generated matrices can be misleading, and repeatable because matrices are curated and made publicly available in many formats. Its matrices cover a wide spectrum of domains, include those arising from problems with underlying 2D or 3D geometry (as structural engineering, computational fluid dynamics, model reduction, electromagnetics, semiconductor devices, thermodynamics, materials, acoustics, computer graphics/vision, robotics/kinematics, and other discretizations) and those that typically do not have such geometry (optimization, circuit simulation, economic and financial modeling, theoretical and quantum chemistry, chemical process simulation, mathematics and statistics, power networks, and other networks and graphs). We provide software for accessing and managing the Collection, from MATLAB™, Mathematica™, Fortran, and C, as well as an online search capability. Graph visualization of the matrices is provided, and a new multilevel coarsening scheme is proposed to facilitate this task.}, +journal = {ACM Trans. Math. Softw.}, +month = {dec}, +articleno = {1}, +numpages = {25}, +keywords = {sparse matrices, Graph drawing, performance evaluation, multilevel algorithms} +} + + +@article{Kolodziej2019, + doi = {10.21105/joss.01244}, + url = {https://doi.org/10.21105/joss.01244}, + year = {2019}, + publisher = {The Open Journal}, + volume = {4}, + number = {35}, + pages = {1244}, + author = {Scott P. Kolodziej and Mohsen Aznaveh and Matthew Bullock and Jarrett David and Timothy A. Davis and Matthew Henderson and Yifan Hu and Read Sandstrom}, + title = {The SuiteSparse Matrix Collection Website Interface}, + journal = {Journal of Open Source Software} +} + + +@article{10.1145/2513109.2513116, +author = {Foster, Leslie V. and Davis, Timothy A.}, +title = {Algorithm 933: Reliable Calculation of Numerical Rank, Null Space Bases, Pseudoinverse Solutions, and Basic Solutions Using SuitesparseQR}, +year = {2013}, +issue_date = {September 2013}, +publisher = {Association for Computing Machinery}, +address = {New York, NY, USA}, +volume = {40}, +number = {1}, +issn = {0098-3500}, +url = {https://doi.org/10.1145/2513109.2513116}, +doi = {10.1145/2513109.2513116}, +abstract = {The SPQR_RANK package contains routines that calculate the numerical rank of large, sparse, numerically rank-deficient matrices. The routines can also calculate orthonormal bases for numerical null spaces, approximate pseudoinverse solutions to least squares problems involving rank-deficient matrices, and basic solutions to these problems. The algorithms are based on SPQR from SuiteSparseQR (ACM Transactions on Mathematical Software 38, Article 8, 2011). SPQR is a high-performance routine for forming QR factorizations of large, sparse matrices. It returns an estimate for the numerical rank that is usually, but not always, correct. The new routines improve the accuracy of the numerical rank calculated by SPQR and reliably determine the numerical rank in the sense that, based on extensive testing with matrices from applications, the numerical rank is almost always accurately determined when our methods report that the numerical rank should be correct. Reliable determination of numerical rank is critical to the other calculations in the package. The routines work well for matrices with either small or large null space dimensions.}, +journal = {ACM Trans. Math. Softw.}, +month = {oct}, +articleno = {7}, +numpages = {23}, +keywords = {QR factorization, pseudoinverse, Numerical rank, null space, rank revealing, sparse matrices} +} + +@article{10.1145/3337792, +author = {Davis, Timothy A. and Hager, William W. and Kolodziej, Scott P. and Yeralan, S. Nuri}, +title = {Algorithm 1003: Mongoose, a Graph Coarsening and Partitioning Library}, +year = {2020}, +issue_date = {March 2020}, +publisher = {Association for Computing Machinery}, +address = {New York, NY, USA}, +volume = {46}, +number = {1}, +issn = {0098-3500}, +url = {https://doi.org/10.1145/3337792}, +doi = {10.1145/3337792}, +abstract = {Partitioning graphs is a common and useful operation in many areas, from parallel computing to VLSI design to sparse matrix algorithms. In this article, we introduce Mongoose, a multilevel hybrid graph partitioning algorithm and library. Building on previous work in multilevel partitioning frameworks and combinatoric approaches, we introduce novel stall-reducing and stall-free coarsening strategies, as well as an efficient hybrid algorithm leveraging (1) traditional combinatoric methods and (2) continuous quadratic programming formulations. We demonstrate how this new hybrid algorithm outperforms either strategy in isolation, and we also compare Mongoose to METIS and demonstrate its effectiveness on large and social networking (power law) graphs.}, +journal = {ACM Trans. Math. Softw.}, +month = {mar}, +articleno = {7}, +numpages = {18}, +keywords = {vertex matching, Graph partitioning, graph coarsening} +} + + +@article{10.1145/3519024, +author = {Lourenco, Christopher and Chen, Jinhao and Moreno-Centeno, Erick and Davis, Timothy A.}, +title = {Algorithm 1XXX: SPEX Left LU, Exactly Solving Sparse Linear Systems via a Sparse Left-Looking Integer-Preserving LU Factorization}, +year = {2022}, +publisher = {Association for Computing Machinery}, +address = {New York, NY, USA}, +issn = {0098-3500}, +url = {https://doi.org/10.1145/3519024}, +doi = {10.1145/3519024}, +abstract = {SPEX Left LU is a software package for exactly solving unsymmetric sparse linear systems. As a component of the sparse exact (SPEX) software package, SPEX Left LU can be applied to any input matrix, A, whose entries are integral, rational, or decimal, and provides a solution to the system Ax = b which is either exact or accurate to user-specified precision. SPEX Left LU preorders the matrix A with a user-specified fill-reducing ordering and computes a left-looking LU factorization with the special property that each operation used to compute the L and U matrices is integral. Notable additional applications of this package include benchmarking the stability and accuracy of state-of-the-art linear solvers, and determining whether singular-to-double-precision matrices are indeed singular. Computationally, this paper evaluates the impact of several novel pivoting schemes in exact arithmetic, benchmarks the exact iterative solvers within Linbox, and benchmarks the accuracy of MATLAB sparse backslash. Most importantly, it is shown that SPEX Left LU outperforms the exact iterative solvers in run time on easy instances and in stability as the iterative solver fails on a sizeable subset of the tested (both easy and hard) instances. The SPEX Left LU package is written in ANSI C, comes with a MATLAB interface, and is distributed via GitHub, as a component of the SPEX software package, and as a component of SuiteSparse.}, +note = {Just Accepted}, +journal = {ACM Trans. Math. Softw.}, +month = {feb}, +keywords = {exact matrix factorization, sparse linear systems, sparse matrix algorithms, exactly solving linear systems, roundoff errors} +} + diff --git a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/CMakeLists.txt b/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/CMakeLists.txt new file mode 100644 index 000000000..0f9603c95 --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/CMakeLists.txt @@ -0,0 +1,165 @@ +#------------------------------------------------------------------------------- +# SuiteSparse/COLAMD/CMakeLists.txt: cmake for COLAMD +#------------------------------------------------------------------------------- + +# Copyright (c) 1998-2023, Timothy A. Davis. All Rights Reserved. +# SPDX-License-Identifier: BSD-3-clause + +#------------------------------------------------------------------------------- +# get the version +#------------------------------------------------------------------------------- + +cmake_minimum_required ( VERSION 3.19 ) + +set ( COLAMD_DATE "Jan 17, 2023" ) +set ( COLAMD_VERSION_MAJOR 3 ) +set ( COLAMD_VERSION_MINOR 0 ) +set ( COLAMD_VERSION_SUB 3 ) + +message ( STATUS "Building COLAMD version: v" + ${COLAMD_VERSION_MAJOR}. + ${COLAMD_VERSION_MINOR}. + ${COLAMD_VERSION_SUB} " (" ${COLAMD_DATE} ")" ) + +#------------------------------------------------------------------------------- +# SuiteSparse policies +#------------------------------------------------------------------------------- + +set ( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} + ${CMAKE_SOURCE_DIR}/cmake_modules + ${CMAKE_SOURCE_DIR}/../SuiteSparse_config/cmake_modules ) + +include ( SuiteSparsePolicy ) + +#------------------------------------------------------------------------------- +# define the project +#------------------------------------------------------------------------------- + +project ( colamd + VERSION "${COLAMD_VERSION_MAJOR}.${COLAMD_VERSION_MINOR}.${COLAMD_VERSION_SUB}" + LANGUAGES C ) + +#------------------------------------------------------------------------------- +# find library dependencies +#------------------------------------------------------------------------------- + +find_package ( SuiteSparse_config 7.0.0 REQUIRED ) + +#------------------------------------------------------------------------------- +# configure files +#------------------------------------------------------------------------------- + +configure_file ( "Config/colamd.h.in" + "${PROJECT_SOURCE_DIR}/Include/colamd.h" + NEWLINE_STYLE LF ) + +#------------------------------------------------------------------------------- +# include directories +#------------------------------------------------------------------------------- + +include_directories ( Source Include ${SUITESPARSE_CONFIG_INCLUDE_DIR} ) + +#------------------------------------------------------------------------------- +# dynamic colamd library properties +#------------------------------------------------------------------------------- + +file ( GLOB COLAMD_SOURCES "Source/*.c" ) + +add_library ( colamd SHARED ${COLAMD_SOURCES} ) + +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 ) + +#------------------------------------------------------------------------------- +# static colamd library properties +#------------------------------------------------------------------------------- + +if ( NOT NSTATIC ) + add_library ( colamd_static STATIC ${COLAMD_SOURCES} ) + + set_target_properties ( colamd_static PROPERTIES + VERSION ${COLAMD_VERSION_MAJOR}.${COLAMD_VERSION_MINOR}.${COLAMD_VERSION_SUB} + OUTPUT_NAME colamd + C_STANDARD_REQUIRED 11 + SOVERSION ${COLAMD_VERSION_MAJOR} ) + + if ( MSVC ) + set_target_properties ( colamd_static PROPERTIES + OUTPUT_NAME colamd_static ) + endif ( ) +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} ) +endif ( ) + +# libm: +if ( NOT WIN32 ) + target_link_libraries ( colamd PUBLIC m ) + if ( NOT NSTATIC ) + target_link_libraries ( colamd_static PUBLIC m ) + endif ( ) +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} ) +endif ( ) + +#------------------------------------------------------------------------------- +# Demo library and programs +#------------------------------------------------------------------------------- + +option ( DEMO "ON: Build the demo programs. OFF (default): do not build the demo programs." off ) +if ( DEMO ) + + #--------------------------------------------------------------------------- + # demo library + #--------------------------------------------------------------------------- + + message ( STATUS "Also compiling the demos in COLAMD/Demo" ) + + #--------------------------------------------------------------------------- + # Demo programs + #--------------------------------------------------------------------------- + + add_executable ( colamd_example "Demo/colamd_example.c" ) + 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 ) + +else ( ) + + message ( STATUS "Skipping the demos in COLAMD/Demo" ) + +endif ( ) + +#------------------------------------------------------------------------------- +# report status +#------------------------------------------------------------------------------- + +include ( SuiteSparseReport ) + diff --git a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Config/colamd.h.in b/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Config/colamd.h.in new file mode 100644 index 000000000..fb0450ac4 --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Config/colamd.h.in @@ -0,0 +1,236 @@ +//------------------------------------------------------------------------------ +// COLAMD/Source/colamd.h: include file for COLAMD +//------------------------------------------------------------------------------ + +// COLAMD, Copyright (c) 1998-2022, Timothy A. Davis and Stefan Larimore, +// All Rights Reserved. +// SPDX-License-Identifier: BSD-3-clause + +//------------------------------------------------------------------------------ + +/* COLAMD / SYMAMD include file + + You must include this file (colamd.h) in any routine that uses colamd, + symamd, or the related macros and definitions. + + Authors: + + The authors of the code itself are Stefan I. Larimore and Timothy A. + Davis (DrTimothyAldenDavis@gmail.com). The algorithm was + developed in collaboration with John Gilbert, Xerox PARC, and Esmond + Ng, Oak Ridge National Laboratory. + + Acknowledgements: + + This work was supported by the National Science Foundation, under + grants DMS-9504974 and DMS-9803599. + + Availability: + + The colamd/symamd library is available at http://www.suitesparse.com + This file is required by the colamd.c, colamdmex.c, and symamdmex.c + files, and by any C code that calls the routines whose prototypes are + listed below, or that uses the colamd/symamd definitions listed below. + +*/ + +#ifndef COLAMD_H +#define COLAMD_H + +/* make it easy for C++ programs to include COLAMD */ +#ifdef __cplusplus +extern "C" { +#endif + +/* ========================================================================== */ +/* === Include files ======================================================== */ +/* ========================================================================== */ + +#include "SuiteSparse_config.h" + +/* ========================================================================== */ +/* === COLAMD version ======================================================= */ +/* ========================================================================== */ + +/* COLAMD Version 2.4 and later will include the following definitions. + * As an example, to test if the version you are using is 2.4 or later: + * + * #ifdef COLAMD_VERSION + * if (COLAMD_VERSION >= COLAMD_VERSION_CODE (2,4)) ... + * #endif + * + * This also works during compile-time: + * + * #if defined(COLAMD_VERSION) && (COLAMD_VERSION >= COLAMD_VERSION_CODE (2,4)) + * printf ("This is version 2.4 or later\n") ; + * #else + * printf ("This is an early version\n") ; + * #endif + * + * Versions 2.3 and earlier of COLAMD do not include a #define'd version number. + */ + +#define COLAMD_DATE "@COLAMD_DATE@" +#define COLAMD_MAIN_VERSION @COLAMD_VERSION_MAJOR@ +#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) + +/* ========================================================================== */ +/* === Knob and statistics definitions ====================================== */ +/* ========================================================================== */ + +/* size of the knobs [ ] array. Only knobs [0..1] are currently used. */ +#define COLAMD_KNOBS 20 + +/* number of output statistics. Only stats [0..6] are currently used. */ +#define COLAMD_STATS 20 + +/* knobs [0] and stats [0]: dense row knob and output statistic. */ +#define COLAMD_DENSE_ROW 0 + +/* knobs [1] and stats [1]: dense column knob and output statistic. */ +#define COLAMD_DENSE_COL 1 + +/* knobs [2]: aggressive absorption */ +#define COLAMD_AGGRESSIVE 2 + +/* stats [2]: memory defragmentation count output statistic */ +#define COLAMD_DEFRAG_COUNT 2 + +/* stats [3]: colamd status: zero OK, > 0 warning or notice, < 0 error */ +#define COLAMD_STATUS 3 + +/* stats [4..6]: error info, or info on jumbled columns */ +#define COLAMD_INFO1 4 +#define COLAMD_INFO2 5 +#define COLAMD_INFO3 6 + +/* error codes returned in stats [3]: */ +#define COLAMD_OK (0) +#define COLAMD_OK_BUT_JUMBLED (1) +#define COLAMD_ERROR_A_not_present (-1) +#define COLAMD_ERROR_p_not_present (-2) +#define COLAMD_ERROR_nrow_negative (-3) +#define COLAMD_ERROR_ncol_negative (-4) +#define COLAMD_ERROR_nnz_negative (-5) +#define COLAMD_ERROR_p0_nonzero (-6) +#define COLAMD_ERROR_A_too_small (-7) +#define COLAMD_ERROR_col_length_negative (-8) +#define COLAMD_ERROR_row_index_out_of_bounds (-9) +#define COLAMD_ERROR_out_of_memory (-10) +#define COLAMD_ERROR_internal_error (-999) + + +/* ========================================================================== */ +/* === Prototypes of user-callable routines ================================= */ +/* ========================================================================== */ + +size_t colamd_recommended /* returns recommended value of Alen, */ + /* or 0 if input arguments are erroneous */ +( + int32_t nnz, /* nonzeros in A */ + int32_t n_row, /* number of rows in A */ + int32_t n_col /* number of columns in A */ +) ; + +size_t colamd_l_recommended /* returns recommended value of Alen, */ + /* or 0 if input arguments are erroneous */ +( + int64_t nnz, /* nonzeros in A */ + int64_t n_row, /* number of rows in A */ + int64_t n_col /* number of columns in A */ +) ; + +void colamd_set_defaults /* sets default parameters */ +( /* knobs argument is modified on output */ + double knobs [COLAMD_KNOBS] /* parameter settings for colamd */ +) ; + +void colamd_l_set_defaults /* sets default parameters */ +( /* knobs argument is modified on output */ + double knobs [COLAMD_KNOBS] /* parameter settings for colamd */ +) ; + +int colamd /* returns (1) if successful, (0) otherwise*/ +( /* A and p arguments are modified on output */ + int32_t n_row, /* number of rows in A */ + int32_t n_col, /* number of columns in A */ + int32_t Alen, /* size of the array A */ + int32_t A [], /* row indices of A, of size Alen */ + int32_t p [], /* column pointers of A, of size n_col+1 */ + double knobs [COLAMD_KNOBS], /* parameter settings for colamd */ + int32_t stats [COLAMD_STATS] /* colamd output stats and error codes */ +) ; + +int colamd_l /* returns (1) if successful, (0) otherwise*/ +( /* A and p arguments are modified on output */ + int64_t n_row, /* number of rows in A */ + int64_t n_col, /* number of columns in A */ + int64_t Alen, /* size of the array A */ + int64_t A [], /* row indices of A, of size Alen */ + int64_t p [], /* column pointers of A, of size n_col+1 */ + double knobs [COLAMD_KNOBS], /* parameter settings for colamd */ + int64_t stats [COLAMD_STATS] /* colamd output stats and error codes */ +) ; + +int symamd /* return (1) if OK, (0) otherwise */ +( + int32_t n, /* number of rows and columns of A */ + int32_t A [], /* row indices of A */ + int32_t p [], /* column pointers of A */ + int32_t perm [], /* output permutation, size n_col+1 */ + double knobs [COLAMD_KNOBS], /* parameters (uses defaults if NULL) */ + int32_t stats [COLAMD_STATS], /* output stats and error codes */ + void * (*allocate) (size_t, size_t), + /* pointer to calloc (ANSI C) or */ + /* mxCalloc (for MATLAB mexFunction) */ + void (*release) (void *) + /* pointer to free (ANSI C) or */ + /* mxFree (for MATLAB mexFunction) */ +) ; + +int symamd_l /* return (1) if OK, (0) otherwise */ +( + int64_t n, /* number of rows and columns of A */ + int64_t A [], /* row indices of A */ + int64_t p [], /* column pointers of A */ + int64_t perm [], /* output permutation, size n_col+1 */ + double knobs [COLAMD_KNOBS], /* parameters (uses defaults if NULL) */ + int64_t stats [COLAMD_STATS], /* output stats and error codes */ + void * (*allocate) (size_t, size_t), + /* pointer to calloc (ANSI C) or */ + /* mxCalloc (for MATLAB mexFunction) */ + void (*release) (void *) + /* pointer to free (ANSI C) or */ + /* mxFree (for MATLAB mexFunction) */ +) ; + +void colamd_report +( + int32_t stats [COLAMD_STATS] +) ; + +void colamd_l_report +( + int64_t stats [COLAMD_STATS] +) ; + +void symamd_report +( + int32_t stats [COLAMD_STATS] +) ; + +void symamd_l_report +( + int64_t stats [COLAMD_STATS] +) ; + +#ifdef __cplusplus +} +#endif + +#endif /* COLAMD_H */ diff --git a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Demo/Makefile b/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Demo/Makefile deleted file mode 100644 index 5a1f9e544..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Demo/Makefile +++ /dev/null @@ -1,49 +0,0 @@ -#----------------------------------------------------------------------------- -# compile the COLAMD demo -#----------------------------------------------------------------------------- - -default: all - -include ../../SuiteSparse_config/SuiteSparse_config.mk - -I = -I../../include - -C = $(CC) $(CF) $(I) - -LIB2 = $(LDFLAGS) -L../../lib -lcolamd -lsuitesparseconfig $(LDLIBS) - -all: library colamd_example colamd_l_example - -library: - ( cd ../../SuiteSparse_config ; $(MAKE) ) - ( cd ../Lib ; $(MAKE) ) - -#------------------------------------------------------------------------------ -# Create the demo program, run it, and compare the output -#------------------------------------------------------------------------------ - -dist: - -colamd_example: colamd_example.c - $(C) -o colamd_example colamd_example.c $(LIB2) - - ./colamd_example > my_colamd_example.out - - diff colamd_example.out my_colamd_example.out - -colamd_l_example: colamd_l_example.c - $(C) -o colamd_l_example colamd_l_example.c $(LIB2) - - ./colamd_l_example > my_colamd_l_example.out - - diff colamd_l_example.out my_colamd_l_example.out - -#------------------------------------------------------------------------------ -# Remove all but the files in the original distribution -#------------------------------------------------------------------------------ - -clean: - - $(RM) -r $(CLEAN) - -purge: distclean - -distclean: clean - - $(RM) colamd_example colamd_l_example - - $(RM) my_colamd_example.out my_colamd_l_example.out - - $(RM) -r $(PURGE) diff --git a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Demo/colamd_example.c b/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Demo/colamd_example.c deleted file mode 100644 index 02fa3697e..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Demo/colamd_example.c +++ /dev/null @@ -1,178 +0,0 @@ -/* ========================================================================== */ -/* === colamd and symamd example ============================================ */ -/* ========================================================================== */ - -/* COLAMD / SYMAMD example - - colamd example of use, to order the columns of a 5-by-4 matrix with - 11 nonzero entries in the following nonzero pattern, with default knobs. - - x 0 x 0 - x 0 x x - 0 x x 0 - 0 0 x x - x x 0 0 - - symamd example of use, to order the rows and columns of a 5-by-5 - matrix with 13 nonzero entries in the following nonzero pattern, - with default knobs. - - x x 0 0 0 - x x x x 0 - 0 x x 0 0 - 0 x 0 x x - 0 0 0 x x - - (where x denotes a nonzero value). -*/ - -/* ========================================================================== */ - -#include -#include "colamd.h" - -#define A_NNZ 11 -#define A_NROW 5 -#define A_NCOL 4 -#define ALEN 150 - -#define B_NNZ 4 -#define B_N 5 - -int main (void) -{ - - /* ====================================================================== */ - /* input matrix A definition */ - /* ====================================================================== */ - - int A [ALEN] = { - - 0, 1, 4, /* row indices of nonzeros in column 0 */ - 2, 4, /* row indices of nonzeros in column 1 */ - 0, 1, 2, 3, /* row indices of nonzeros in column 2 */ - 1, 3} ; /* row indices of nonzeros in column 3 */ - - int p [ ] = { - - 0, /* column 0 is in A [0..2] */ - 3, /* column 1 is in A [3..4] */ - 5, /* column 2 is in A [5..8] */ - 9, /* column 3 is in A [9..10] */ - A_NNZ} ; /* number of nonzeros in A */ - - /* ====================================================================== */ - /* input matrix B definition */ - /* ====================================================================== */ - - int B [ ] = { /* Note: only strictly lower triangular part */ - /* is included, since symamd ignores the */ - /* diagonal and upper triangular part of B. */ - - 1, /* row indices of nonzeros in column 0 */ - 2, 3, /* row indices of nonzeros in column 1 */ - /* row indices of nonzeros in column 2 (none) */ - 4 /* row indices of nonzeros in column 3 */ - } ; /* row indices of nonzeros in column 4 (none) */ - - int q [ ] = { - - 0, /* column 0 is in B [0] */ - 1, /* column 1 is in B [1..2] */ - 3, /* column 2 is empty */ - 3, /* column 3 is in B [3] */ - 4, /* column 4 is empty */ - B_NNZ} ; /* number of nonzeros in strictly lower B */ - - /* ====================================================================== */ - /* other variable definitions */ - /* ====================================================================== */ - - int perm [B_N+1] ; /* note the size is N+1 */ - int stats [COLAMD_STATS] ; /* for colamd and symamd output statistics */ - - int row, col, pp, length, ok ; - - /* ====================================================================== */ - /* dump the input matrix A */ - /* ====================================================================== */ - - printf ("colamd %d-by-%d input matrix:\n", A_NROW, A_NCOL) ; - for (col = 0 ; col < A_NCOL ; col++) - { - length = p [col+1] - p [col] ; - printf ("Column %d, with %d entries:\n", col, length) ; - for (pp = p [col] ; pp < p [col+1] ; pp++) - { - row = A [pp] ; - printf (" row %d\n", row) ; - } - } - - /* ====================================================================== */ - /* order the matrix. Note that this destroys A and overwrites p */ - /* ====================================================================== */ - - ok = colamd (A_NROW, A_NCOL, ALEN, A, p, (double *) NULL, stats) ; - colamd_report (stats) ; - - if (!ok) - { - printf ("colamd error!\n") ; - exit (1) ; - } - - /* ====================================================================== */ - /* print the column ordering */ - /* ====================================================================== */ - - printf ("colamd column ordering:\n") ; - printf ("1st column: %d\n", p [0]) ; - printf ("2nd column: %d\n", p [1]) ; - printf ("3rd column: %d\n", p [2]) ; - printf ("4th column: %d\n", p [3]) ; - - /* ====================================================================== */ - /* dump the strictly lower triangular part of symmetric input matrix B */ - /* ====================================================================== */ - - printf ("\n\nsymamd %d-by-%d input matrix:\n", B_N, B_N) ; - printf ("Entries in strictly lower triangular part:\n") ; - for (col = 0 ; col < B_N ; col++) - { - length = q [col+1] - q [col] ; - printf ("Column %d, with %d entries:\n", col, length) ; - for (pp = q [col] ; pp < q [col+1] ; pp++) - { - row = B [pp] ; - printf (" row %d\n", row) ; - } - } - - /* ====================================================================== */ - /* order the matrix B. Note that this does not modify B or q. */ - /* ====================================================================== */ - - ok = symamd (B_N, B, q, perm, (double *) NULL, stats, &calloc, &free) ; - symamd_report (stats) ; - - if (!ok) - { - printf ("symamd error!\n") ; - exit (1) ; - } - - /* ====================================================================== */ - /* print the symmetric ordering */ - /* ====================================================================== */ - - printf ("symamd column ordering:\n") ; - printf ("1st row/column: %d\n", perm [0]) ; - printf ("2nd row/column: %d\n", perm [1]) ; - printf ("3rd row/column: %d\n", perm [2]) ; - printf ("4th row/column: %d\n", perm [3]) ; - printf ("5th row/column: %d\n", perm [4]) ; - - exit (0) ; -} - diff --git a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Demo/colamd_example.out b/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Demo/colamd_example.out deleted file mode 100644 index a2b6b0ae8..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Demo/colamd_example.out +++ /dev/null @@ -1,50 +0,0 @@ -colamd 5-by-4 input matrix: -Column 0, with 3 entries: - row 0 - row 1 - row 4 -Column 1, with 2 entries: - row 2 - row 4 -Column 2, with 4 entries: - row 0 - row 1 - row 2 - row 3 -Column 3, with 2 entries: - row 1 - row 3 - -colamd version 2.9, May 4, 2016: OK. -colamd: number of dense or empty rows ignored: 0 -colamd: number of dense or empty columns ignored: 0 -colamd: number of garbage collections performed: 0 -colamd column ordering: -1st column: 1 -2nd column: 0 -3rd column: 2 -4th column: 3 - - -symamd 5-by-5 input matrix: -Entries in strictly lower triangular part: -Column 0, with 1 entries: - row 1 -Column 1, with 2 entries: - row 2 - row 3 -Column 2, with 0 entries: -Column 3, with 1 entries: - row 4 -Column 4, with 0 entries: - -symamd version 2.9, May 4, 2016: OK. -symamd: number of dense or empty rows ignored: 0 -symamd: number of dense or empty columns ignored: 0 -symamd: number of garbage collections performed: 0 -symamd column ordering: -1st row/column: 0 -2nd row/column: 2 -3rd row/column: 1 -4th row/column: 3 -5th row/column: 4 diff --git a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Demo/colamd_l_example.c b/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Demo/colamd_l_example.c deleted file mode 100644 index 657a9a73b..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Demo/colamd_l_example.c +++ /dev/null @@ -1,179 +0,0 @@ -/* ========================================================================== */ -/* === colamd and symamd example ============================================ */ -/* ========================================================================== */ - -/* COLAMD / SYMAMD example - - colamd example of use, to order the columns of a 5-by-4 matrix with - 11 nonzero entries in the following nonzero pattern, with default knobs. - - x 0 x 0 - x 0 x x - 0 x x 0 - 0 0 x x - x x 0 0 - - symamd example of use, to order the rows and columns of a 5-by-5 - matrix with 13 nonzero entries in the following nonzero pattern, - with default knobs. - - x x 0 0 0 - x x x x 0 - 0 x x 0 0 - 0 x 0 x x - 0 0 0 x x - - (where x denotes a nonzero value). -*/ - -/* ========================================================================== */ - -#include -#include "colamd.h" -#define Long SuiteSparse_long - -#define A_NNZ 11 -#define A_NROW 5 -#define A_NCOL 4 -#define ALEN 150 - -#define B_NNZ 4 -#define B_N 5 - -int main (void) -{ - - /* ====================================================================== */ - /* input matrix A definition */ - /* ====================================================================== */ - - Long A [ALEN] = { - - 0, 1, 4, /* row indices of nonzeros in column 0 */ - 2, 4, /* row indices of nonzeros in column 1 */ - 0, 1, 2, 3, /* row indices of nonzeros in column 2 */ - 1, 3} ; /* row indices of nonzeros in column 3 */ - - Long p [ ] = { - - 0, /* column 0 is in A [0..2] */ - 3, /* column 1 is in A [3..4] */ - 5, /* column 2 is in A [5..8] */ - 9, /* column 3 is in A [9..10] */ - A_NNZ} ; /* number of nonzeros in A */ - - /* ====================================================================== */ - /* input matrix B definition */ - /* ====================================================================== */ - - Long B [ ] = { /* Note: only strictly lower triangular part */ - /* is included, since symamd ignores the */ - /* diagonal and upper triangular part of B. */ - - 1, /* row indices of nonzeros in column 0 */ - 2, 3, /* row indices of nonzeros in column 1 */ - /* row indices of nonzeros in column 2 (none) */ - 4 /* row indices of nonzeros in column 3 */ - } ; /* row indices of nonzeros in column 4 (none) */ - - Long q [ ] = { - - 0, /* column 0 is in B [0] */ - 1, /* column 1 is in B [1..2] */ - 3, /* column 2 is empty */ - 3, /* column 3 is in B [3] */ - 4, /* column 4 is empty */ - B_NNZ} ; /* number of nonzeros in strictly lower B */ - - /* ====================================================================== */ - /* other variable definitions */ - /* ====================================================================== */ - - Long perm [B_N+1] ; /* note the size is N+1 */ - Long stats [COLAMD_STATS] ; /* for colamd and symamd output statistics */ - - Long row, col, pp, length, ok ; - - /* ====================================================================== */ - /* dump the input matrix A */ - /* ====================================================================== */ - - printf ("colamd %d-by-%d input matrix:\n", A_NROW, A_NCOL) ; - for (col = 0 ; col < A_NCOL ; col++) - { - length = p [col+1] - p [col] ; - printf ("Column %ld, with %ld entries:\n", col, length) ; - for (pp = p [col] ; pp < p [col+1] ; pp++) - { - row = A [pp] ; - printf (" row %ld\n", row) ; - } - } - - /* ====================================================================== */ - /* order the matrix. Note that this destroys A and overwrites p */ - /* ====================================================================== */ - - ok = colamd_l (A_NROW, A_NCOL, ALEN, A, p, (double *) NULL, stats) ; - colamd_l_report (stats) ; - - if (!ok) - { - printf ("colamd error!\n") ; - exit (1) ; - } - - /* ====================================================================== */ - /* print the column ordering */ - /* ====================================================================== */ - - printf ("colamd_l column ordering:\n") ; - printf ("1st column: %ld\n", p [0]) ; - printf ("2nd column: %ld\n", p [1]) ; - printf ("3rd column: %ld\n", p [2]) ; - printf ("4th column: %ld\n", p [3]) ; - - /* ====================================================================== */ - /* dump the strictly lower triangular part of symmetric input matrix B */ - /* ====================================================================== */ - - printf ("\n\nsymamd_l %d-by-%d input matrix:\n", B_N, B_N) ; - printf ("Entries in strictly lower triangular part:\n") ; - for (col = 0 ; col < B_N ; col++) - { - length = q [col+1] - q [col] ; - printf ("Column %ld, with %ld entries:\n", col, length) ; - for (pp = q [col] ; pp < q [col+1] ; pp++) - { - row = B [pp] ; - printf (" row %ld\n", row) ; - } - } - - /* ====================================================================== */ - /* order the matrix B. Note that this does not modify B or q. */ - /* ====================================================================== */ - - ok = symamd_l (B_N, B, q, perm, (double *) NULL, stats, &calloc, &free) ; - symamd_l_report (stats) ; - - if (!ok) - { - printf ("symamd error!\n") ; - exit (1) ; - } - - /* ====================================================================== */ - /* print the symmetric ordering */ - /* ====================================================================== */ - - printf ("symamd_l column ordering:\n") ; - printf ("1st row/column: %ld\n", perm [0]) ; - printf ("2nd row/column: %ld\n", perm [1]) ; - printf ("3rd row/column: %ld\n", perm [2]) ; - printf ("4th row/column: %ld\n", perm [3]) ; - printf ("5th row/column: %ld\n", perm [4]) ; - - exit (0) ; -} - diff --git a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Demo/colamd_l_example.out b/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Demo/colamd_l_example.out deleted file mode 100644 index 2f1035c5c..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Demo/colamd_l_example.out +++ /dev/null @@ -1,50 +0,0 @@ -colamd 5-by-4 input matrix: -Column 0, with 3 entries: - row 0 - row 1 - row 4 -Column 1, with 2 entries: - row 2 - row 4 -Column 2, with 4 entries: - row 0 - row 1 - row 2 - row 3 -Column 3, with 2 entries: - row 1 - row 3 - -colamd version 2.9, May 4, 2016: OK. -colamd: number of dense or empty rows ignored: 0 -colamd: number of dense or empty columns ignored: 0 -colamd: number of garbage collections performed: 0 -colamd_l column ordering: -1st column: 1 -2nd column: 0 -3rd column: 2 -4th column: 3 - - -symamd_l 5-by-5 input matrix: -Entries in strictly lower triangular part: -Column 0, with 1 entries: - row 1 -Column 1, with 2 entries: - row 2 - row 3 -Column 2, with 0 entries: -Column 3, with 1 entries: - row 4 -Column 4, with 0 entries: - -symamd version 2.9, May 4, 2016: OK. -symamd: number of dense or empty rows ignored: 0 -symamd: number of dense or empty columns ignored: 0 -symamd: number of garbage collections performed: 0 -symamd_l column ordering: -1st row/column: 0 -2nd row/column: 2 -3rd row/column: 1 -4th row/column: 3 -5th row/column: 4 diff --git a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Doc/ChangeLog b/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Doc/ChangeLog index 7640c626b..f7a3dcf3c 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Doc/ChangeLog +++ b/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Doc/ChangeLog @@ -1,3 +1,17 @@ +Jan 17, 2023: version 3.0.3 + + * SuiteSparse_config: now v7.0.0 + +Dec 9, 2022: version 3.0.2 + + * minor change to build system + +Nov 12, 2022: version 3.0.0 + + * using CMake build system + * integers: int (32-bit) and SuiteSparse_long (nominally 64-bit) replaced + with int32_t and int64_t. + May 4, 2016: version 2.9.6 * minor changes to Makefile diff --git a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Doc/License.txt b/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Doc/License.txt index 4fff9e977..6abd8a41c 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Doc/License.txt +++ b/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Doc/License.txt @@ -1,4 +1,4 @@ -COLAMD, Copyright 1998-2016, Timothy A. Davis. http://www.suitesparse.com +COLAMD, Copyright 1998-2022, Timothy A. Davis. http://www.suitesparse.com http://www.suitesparse.com -------------------------------------------------------------------------------- diff --git a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Doc/lesser.txt b/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Doc/lesser.txt deleted file mode 100644 index 8add30ad5..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Doc/lesser.txt +++ /dev/null @@ -1,504 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - - Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - - When we speak of free software, we are referring to freedom of use, -not price. Our General Public Licenses are designed to make sure that -you have the freedom to distribute copies of free software (and charge -for this service if you wish); that you receive source code or can get -it if you want it; that you can change the software and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Libraries - - If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms of the -ordinary General Public License). - - To apply these terms, attach the following notices to the library. It is -safest to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random Hacker. - - , 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! - - diff --git a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Include/colamd.h b/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Include/colamd.h index fbe959308..f258ef68e 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Include/colamd.h +++ b/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Include/colamd.h @@ -1,6 +1,12 @@ -/* ========================================================================== */ -/* === colamd/symamd prototypes and definitions ============================= */ -/* ========================================================================== */ +//------------------------------------------------------------------------------ +// COLAMD/Source/colamd.h: include file for COLAMD +//------------------------------------------------------------------------------ + +// COLAMD, Copyright (c) 1998-2022, Timothy A. Davis and Stefan Larimore, +// All Rights Reserved. +// SPDX-License-Identifier: BSD-3-clause + +//------------------------------------------------------------------------------ /* COLAMD / SYMAMD include file @@ -9,27 +15,22 @@ Authors: - The authors of the code itself are Stefan I. Larimore and Timothy A. - Davis (DrTimothyAldenDavis@gmail.com). The algorithm was - developed in collaboration with John Gilbert, Xerox PARC, and Esmond - Ng, Oak Ridge National Laboratory. + The authors of the code itself are Stefan I. Larimore and Timothy A. + Davis (DrTimothyAldenDavis@gmail.com). The algorithm was + developed in collaboration with John Gilbert, Xerox PARC, and Esmond + Ng, Oak Ridge National Laboratory. Acknowledgements: - This work was supported by the National Science Foundation, under - grants DMS-9504974 and DMS-9803599. - - Notice: - - Copyright (c) 1998-2007, Timothy A. Davis, All Rights Reserved. - See COLAMD/Doc/License.txt for the license. + This work was supported by the National Science Foundation, under + grants DMS-9504974 and DMS-9803599. Availability: - The colamd/symamd library is available at http://www.suitesparse.com - This file is required by the colamd.c, colamdmex.c, and symamdmex.c - files, and by any C code that calls the routines whose prototypes are - listed below, or that uses the colamd/symamd definitions listed below. + The colamd/symamd library is available at http://www.suitesparse.com + This file is required by the colamd.c, colamdmex.c, and symamdmex.c + files, and by any C code that calls the routines whose prototypes are + listed below, or that uses the colamd/symamd definitions listed below. */ @@ -45,7 +46,7 @@ extern "C" { /* === Include files ======================================================== */ /* ========================================================================== */ -#include +#include "SuiteSparse_config.h" /* ========================================================================== */ /* === COLAMD version ======================================================= */ @@ -55,7 +56,7 @@ extern "C" { * As an example, to test if the version you are using is 2.4 or later: * * #ifdef COLAMD_VERSION - * if (COLAMD_VERSION >= COLAMD_VERSION_CODE (2,4)) ... + * if (COLAMD_VERSION >= COLAMD_VERSION_CODE (2,4)) ... * #endif * * This also works during compile-time: @@ -69,13 +70,14 @@ extern "C" { * Versions 2.3 and earlier of COLAMD do not include a #define'd version number. */ -#define COLAMD_DATE "May 4, 2016" +#define COLAMD_DATE "Jan 17, 2023" +#define COLAMD_MAIN_VERSION 3 +#define COLAMD_SUB_VERSION 0 +#define COLAMD_SUBSUB_VERSION 3 + #define COLAMD_VERSION_CODE(main,sub) ((main) * 1000 + (sub)) -#define COLAMD_MAIN_VERSION 2 -#define COLAMD_SUB_VERSION 9 -#define COLAMD_SUBSUB_VERSION 6 #define COLAMD_VERSION \ - COLAMD_VERSION_CODE(COLAMD_MAIN_VERSION,COLAMD_SUB_VERSION) + COLAMD_VERSION_CODE(COLAMD_MAIN_VERSION,COLAMD_SUB_VERSION) /* ========================================================================== */ /* === Knob and statistics definitions ====================================== */ @@ -108,126 +110,123 @@ extern "C" { #define COLAMD_INFO3 6 /* error codes returned in stats [3]: */ -#define COLAMD_OK (0) -#define COLAMD_OK_BUT_JUMBLED (1) -#define COLAMD_ERROR_A_not_present (-1) -#define COLAMD_ERROR_p_not_present (-2) -#define COLAMD_ERROR_nrow_negative (-3) -#define COLAMD_ERROR_ncol_negative (-4) -#define COLAMD_ERROR_nnz_negative (-5) -#define COLAMD_ERROR_p0_nonzero (-6) -#define COLAMD_ERROR_A_too_small (-7) -#define COLAMD_ERROR_col_length_negative (-8) -#define COLAMD_ERROR_row_index_out_of_bounds (-9) -#define COLAMD_ERROR_out_of_memory (-10) -#define COLAMD_ERROR_internal_error (-999) +#define COLAMD_OK (0) +#define COLAMD_OK_BUT_JUMBLED (1) +#define COLAMD_ERROR_A_not_present (-1) +#define COLAMD_ERROR_p_not_present (-2) +#define COLAMD_ERROR_nrow_negative (-3) +#define COLAMD_ERROR_ncol_negative (-4) +#define COLAMD_ERROR_nnz_negative (-5) +#define COLAMD_ERROR_p0_nonzero (-6) +#define COLAMD_ERROR_A_too_small (-7) +#define COLAMD_ERROR_col_length_negative (-8) +#define COLAMD_ERROR_row_index_out_of_bounds (-9) +#define COLAMD_ERROR_out_of_memory (-10) +#define COLAMD_ERROR_internal_error (-999) /* ========================================================================== */ /* === Prototypes of user-callable routines ================================= */ /* ========================================================================== */ -#include "SuiteSparse_config.h" - -size_t colamd_recommended /* returns recommended value of Alen, */ - /* or 0 if input arguments are erroneous */ +size_t colamd_recommended /* returns recommended value of Alen, */ + /* or 0 if input arguments are erroneous */ ( - int nnz, /* nonzeros in A */ - int n_row, /* number of rows in A */ - int n_col /* number of columns in A */ + int32_t nnz, /* nonzeros in A */ + int32_t n_row, /* number of rows in A */ + int32_t n_col /* number of columns in A */ ) ; -size_t colamd_l_recommended /* returns recommended value of Alen, */ - /* or 0 if input arguments are erroneous */ +size_t colamd_l_recommended /* returns recommended value of Alen, */ + /* or 0 if input arguments are erroneous */ ( - SuiteSparse_long nnz, /* nonzeros in A */ - SuiteSparse_long n_row, /* number of rows in A */ - SuiteSparse_long n_col /* number of columns in A */ + int64_t nnz, /* nonzeros in A */ + int64_t n_row, /* number of rows in A */ + int64_t n_col /* number of columns in A */ ) ; -void colamd_set_defaults /* sets default parameters */ -( /* knobs argument is modified on output */ - double knobs [COLAMD_KNOBS] /* parameter settings for colamd */ +void colamd_set_defaults /* sets default parameters */ +( /* knobs argument is modified on output */ + double knobs [COLAMD_KNOBS] /* parameter settings for colamd */ ) ; -void colamd_l_set_defaults /* sets default parameters */ -( /* knobs argument is modified on output */ - double knobs [COLAMD_KNOBS] /* parameter settings for colamd */ +void colamd_l_set_defaults /* sets default parameters */ +( /* knobs argument is modified on output */ + double knobs [COLAMD_KNOBS] /* parameter settings for colamd */ ) ; -int colamd /* returns (1) if successful, (0) otherwise*/ -( /* A and p arguments are modified on output */ - int n_row, /* number of rows in A */ - int n_col, /* number of columns in A */ - int Alen, /* size of the array A */ - int A [], /* row indices of A, of size Alen */ - int p [], /* column pointers of A, of size n_col+1 */ - double knobs [COLAMD_KNOBS],/* parameter settings for colamd */ - int stats [COLAMD_STATS] /* colamd output statistics and error codes */ +int colamd /* returns (1) if successful, (0) otherwise*/ +( /* A and p arguments are modified on output */ + int32_t n_row, /* number of rows in A */ + int32_t n_col, /* number of columns in A */ + int32_t Alen, /* size of the array A */ + int32_t A [], /* row indices of A, of size Alen */ + int32_t p [], /* column pointers of A, of size n_col+1 */ + double knobs [COLAMD_KNOBS], /* parameter settings for colamd */ + int32_t stats [COLAMD_STATS] /* colamd output stats and error codes */ ) ; -SuiteSparse_long colamd_l /* returns (1) if successful, (0) otherwise*/ -( /* A and p arguments are modified on output */ - SuiteSparse_long n_row, /* number of rows in A */ - SuiteSparse_long n_col, /* number of columns in A */ - SuiteSparse_long Alen, /* size of the array A */ - SuiteSparse_long A [], /* row indices of A, of size Alen */ - SuiteSparse_long p [], /* column pointers of A, of size n_col+1 */ - double knobs [COLAMD_KNOBS],/* parameter settings for colamd */ - SuiteSparse_long stats [COLAMD_STATS] /* colamd output statistics - * and error codes */ +int colamd_l /* returns (1) if successful, (0) otherwise*/ +( /* A and p arguments are modified on output */ + int64_t n_row, /* number of rows in A */ + int64_t n_col, /* number of columns in A */ + int64_t Alen, /* size of the array A */ + int64_t A [], /* row indices of A, of size Alen */ + int64_t p [], /* column pointers of A, of size n_col+1 */ + double knobs [COLAMD_KNOBS], /* parameter settings for colamd */ + int64_t stats [COLAMD_STATS] /* colamd output stats and error codes */ ) ; -int symamd /* return (1) if OK, (0) otherwise */ +int symamd /* return (1) if OK, (0) otherwise */ ( - int n, /* number of rows and columns of A */ - int A [], /* row indices of A */ - int p [], /* column pointers of A */ - int perm [], /* output permutation, size n_col+1 */ - double knobs [COLAMD_KNOBS], /* parameters (uses defaults if NULL) */ - int stats [COLAMD_STATS], /* output statistics and error codes */ + int32_t n, /* number of rows and columns of A */ + int32_t A [], /* row indices of A */ + int32_t p [], /* column pointers of A */ + int32_t perm [], /* output permutation, size n_col+1 */ + double knobs [COLAMD_KNOBS], /* parameters (uses defaults if NULL) */ + int32_t stats [COLAMD_STATS], /* output stats and error codes */ void * (*allocate) (size_t, size_t), - /* pointer to calloc (ANSI C) or */ - /* mxCalloc (for MATLAB mexFunction) */ + /* pointer to calloc (ANSI C) or */ + /* mxCalloc (for MATLAB mexFunction) */ void (*release) (void *) - /* pointer to free (ANSI C) or */ - /* mxFree (for MATLAB mexFunction) */ + /* pointer to free (ANSI C) or */ + /* mxFree (for MATLAB mexFunction) */ ) ; -SuiteSparse_long symamd_l /* return (1) if OK, (0) otherwise */ +int symamd_l /* return (1) if OK, (0) otherwise */ ( - SuiteSparse_long n, /* number of rows and columns of A */ - SuiteSparse_long A [], /* row indices of A */ - SuiteSparse_long p [], /* column pointers of A */ - SuiteSparse_long perm [], /* output permutation, size n_col+1 */ - double knobs [COLAMD_KNOBS], /* parameters (uses defaults if NULL) */ - SuiteSparse_long stats [COLAMD_STATS], /* output stats and error codes */ + int64_t n, /* number of rows and columns of A */ + int64_t A [], /* row indices of A */ + int64_t p [], /* column pointers of A */ + int64_t perm [], /* output permutation, size n_col+1 */ + double knobs [COLAMD_KNOBS], /* parameters (uses defaults if NULL) */ + int64_t stats [COLAMD_STATS], /* output stats and error codes */ void * (*allocate) (size_t, size_t), - /* pointer to calloc (ANSI C) or */ - /* mxCalloc (for MATLAB mexFunction) */ + /* pointer to calloc (ANSI C) or */ + /* mxCalloc (for MATLAB mexFunction) */ void (*release) (void *) - /* pointer to free (ANSI C) or */ - /* mxFree (for MATLAB mexFunction) */ + /* pointer to free (ANSI C) or */ + /* mxFree (for MATLAB mexFunction) */ ) ; void colamd_report ( - int stats [COLAMD_STATS] + int32_t stats [COLAMD_STATS] ) ; void colamd_l_report ( - SuiteSparse_long stats [COLAMD_STATS] + int64_t stats [COLAMD_STATS] ) ; void symamd_report ( - int stats [COLAMD_STATS] + int32_t stats [COLAMD_STATS] ) ; void symamd_l_report ( - SuiteSparse_long stats [COLAMD_STATS] + int64_t stats [COLAMD_STATS] ) ; #ifdef __cplusplus diff --git a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Lib/Makefile b/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Lib/Makefile deleted file mode 100644 index 2d0c5aa6a..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Lib/Makefile +++ /dev/null @@ -1,73 +0,0 @@ -#------------------------------------------------------------------------------- -# COLAMD Lib/Makefile -#------------------------------------------------------------------------------- - -LIBRARY = libcolamd -VERSION = 2.9.6 -SO_VERSION = 2 - -default: library - -include ../../SuiteSparse_config/SuiteSparse_config.mk - -# COLAMD depends on SuiteSparse_config -LDLIBS += -lsuitesparseconfig - -# compile and install in SuiteSparse/lib -library: - $(MAKE) install INSTALL=$(SUITESPARSE) - -I = -I../Include -I../../SuiteSparse_config - -INC = ../Include/colamd.h ../../SuiteSparse_config/SuiteSparse_config.h - -SRC = ../Source/colamd.c - -OBJ = colamd.o colamd_l.o - -colamd.o: $(SRC) $(INC) - $(CC) $(CF) $(I) -c ../Source/colamd.c - -colamd_l.o: $(SRC) $(INC) - $(CC) $(CF) $(I) -c ../Source/colamd.c -DDLONG -o colamd_l.o - -# creates libcolamd.a, a C-callable COLAMD library -static: $(AR_TARGET) - -$(AR_TARGET): $(OBJ) - $(ARCHIVE) $@ $^ - - $(RANLIB) $@ - -ccode: library - -clean: - - $(RM) -r $(CLEAN) - -purge: distclean - -distclean: clean - - $(RM) -r $(PURGE) - -# install COLAMD -install: $(AR_TARGET) $(INSTALL_LIB)/$(SO_TARGET) - -$(INSTALL_LIB)/$(SO_TARGET): $(OBJ) - @mkdir -p $(INSTALL_LIB) - @mkdir -p $(INSTALL_INCLUDE) - @mkdir -p $(INSTALL_DOC) - $(CC) $(SO_OPTS) $^ -o $@ $(LDLIBS) - ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_PLAIN) ) - ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_MAIN) ) - $(CP) ../Include/colamd.h $(INSTALL_INCLUDE) - $(CP) ../README.txt $(INSTALL_DOC)/COLAMD_README.txt - chmod 755 $(INSTALL_LIB)/$(SO_TARGET) - chmod 644 $(INSTALL_INCLUDE)/colamd.h - chmod 644 $(INSTALL_DOC)/COLAMD_README.txt - -uninstall: - $(RM) $(INSTALL_LIB)/$(SO_TARGET) - $(RM) $(INSTALL_LIB)/$(SO_PLAIN) - $(RM) $(INSTALL_LIB)/$(SO_MAIN) - $(RM) $(INSTALL_INCLUDE)/colamd.h - $(RM) $(INSTALL_DOC)/COLAMD_README.txt - diff --git a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/MATLAB/Contents.m b/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/MATLAB/Contents.m deleted file mode 100644 index 960fd1472..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/MATLAB/Contents.m +++ /dev/null @@ -1,19 +0,0 @@ -% COLAMD, column approximate minimum degree ordering -% -% Primary: -% colamd2 - Column approximate minimum degree permutation. -% symamd2 - SYMAMD Symmetric approximate minimum degree permutation. -% -% helper and test functions: -% colamd_demo - demo for colamd, column approx minimum degree ordering algorithm -% colamd_make - compiles COLAMD2 and SYMAMD2 for MATLAB -% colamd_make - compiles and installs COLAMD2 and SYMAMD2 for MATLAB -% colamd_test - test colamd2 and symamd2 -% luflops - compute the flop count for sparse LU factorization -% -% Example: -% p = colamd2 (A) -% - -% Copyright 1998-2007, Timothy A. Davis, and Stefan Larimore -% Developed in collaboration with J. Gilbert and E. Ng. diff --git a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/MATLAB/colamd2.m b/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/MATLAB/colamd2.m deleted file mode 100644 index 9916cc7f9..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/MATLAB/colamd2.m +++ /dev/null @@ -1,87 +0,0 @@ -function [p,stats] = colamd2 (S, knobs) -%COLAMD2 Column approximate minimum degree permutation. -% P = COLAMD2(S) returns the column approximate minimum degree permutation -% vector for the sparse matrix S. For a non-symmetric matrix S, S(:,P) -% tends to have sparser LU factors than S. The Cholesky factorization of -% S(:,P)'*S(:,P) also tends to be sparser than that of S'*S. The ordering -% is followed by a column elimination tree post-ordering. -% -% Note that this function is the source code for the built-in MATLAB colamd -% function. It has been renamed here to colamd2 to avoid a filename clash. -% colamd and colamd2 are identical. -% -% See also COLAMD, AMD, SYMAMD, SYMAMD2. -% -% Example: -% P = colamd2 (S) -% [P, stats] = colamd2 (S, knobs) -% -% knobs is an optional one- to three-element input vector. If S is m-by-n, -% then rows with more than max(16,knobs(1)*sqrt(n)) entries are ignored. -% Columns with more than max(16,knobs(2)*sqrt(min(m,n))) entries are -% removed prior to ordering, and ordered last in the output permutation P. -% Only completely dense rows or columns are removed if knobs(1) and knobs(2) -% are < 0, respectively. If knobs(3) is nonzero, stats and knobs are -% printed. The default is knobs = [10 10 0]. Note that knobs differs from -% earlier versions of colamd. - -% Copyright 1998-2007, Timothy A. Davis, and Stefan Larimore -% Developed in collaboration with J. Gilbert and E. Ng. -% -% Acknowledgements: This work was supported by the National Science -% Foundation, under grants DMS-9504974 and DMS-9803599. - -%------------------------------------------------------------------------------- -% Perform the colamd ordering: -%------------------------------------------------------------------------------- - -if (nargout <= 1 & nargin == 1) %#ok - p = colamd2mex (S) ; -elseif (nargout <= 1 & nargin == 2) %#ok - p = colamd2mex (S, knobs) ; -elseif (nargout == 2 & nargin == 1) %#ok - [p, stats] = colamd2mex (S) ; -elseif (nargout == 2 & nargin == 2) %#ok - [p, stats] = colamd2mex (S, knobs) ; -else - error ('colamd: incorrect number of input and/or output arguments') ; -end - -%------------------------------------------------------------------------------- -% column elimination tree post-ordering: -%------------------------------------------------------------------------------- - -[ignore, q] = etree (S (:,p), 'col') ; -p = p (q) ; - -% stats is an optional 20-element output vector that provides data about the -% ordering and the validity of the input matrix S. Ordering statistics are -% in stats (1:3). stats (1) and stats (2) are the number of dense or empty -% rows and columns ignored by COLAMD and stats (3) is the number of -% garbage collections performed on the internal data structure used by -% COLAMD (roughly of size 2.2*nnz(S) + 4*m + 7*n integers). -% -% MATLAB built-in functions are intended to generate valid sparse matrices, -% with no duplicate entries, with ascending row indices of the nonzeros -% in each column, with a non-negative number of entries in each column (!) -% and so on. If a matrix is invalid, then COLAMD may or may not be able -% to continue. If there are duplicate entries (a row index appears two or -% more times in the same column) or if the row indices in a column are out -% of order, then COLAMD can correct these errors by ignoring the duplicate -% entries and sorting each column of its internal copy of the matrix S (the -% input matrix S is not repaired, however). If a matrix is invalid in other -% ways then COLAMD cannot continue, an error message is printed, and no -% output arguments (P or stats) are returned. COLAMD is thus a simple way -% to check a sparse matrix to see if it's valid. -% -% stats (4:7) provide information if COLAMD was able to continue. The -% matrix is OK if stats (4) is zero, or 1 if invalid. stats (5) is the -% rightmost column index that is unsorted or contains duplicate entries, -% or zero if no such column exists. stats (6) is the last seen duplicate -% or out-of-order row index in the column index given by stats (5), or zero -% if no such row index exists. stats (7) is the number of duplicate or -% out-of-order row indices. -% -% stats (8:20) is always zero in the current version of COLAMD (reserved -% for future use). - diff --git a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/MATLAB/colamd_demo.m b/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/MATLAB/colamd_demo.m deleted file mode 100644 index 47176f4ad..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/MATLAB/colamd_demo.m +++ /dev/null @@ -1,178 +0,0 @@ -%COLAMD_DEMO demo for colamd, column approx minimum degree ordering algorithm -% -% Example: -% colamd_demo -% -% The following m-files and mexFunctions provide alternative sparse matrix -% ordering methods for MATLAB. They are typically faster (sometimes much -% faster) and typically provide better orderings than their MATLAB counterparts: -% -% colamd a replacement for colmmd. -% -% Typical usage: p = colamd (A) ; -% -% symamd a replacement for symmmd. Based on colamd. -% -% Typical usage: p = symamd (A) ; -% -% For a description of the methods used, see the colamd.c file. -% http://www.suitesparse.com -% -% See also colamd, symamd - -% Minor changes: in MATLAB 7, symmmd and colmmd are flagged as "obsolete". -% This demo checks if they exist, so it should still work when they are removed. - -% Copyright 1998-2007, Timothy A. Davis, and Stefan Larimore -% Developed in collaboration with J. Gilbert and E. Ng. - -%------------------------------------------------------------------------------- -% Print the introduction, the help info, and compile the mexFunctions -%------------------------------------------------------------------------------- - -fprintf (1, '\n-----------------------------------------------------------\n') ; -fprintf (1, 'Colamd2/symamd2 demo.') ; -fprintf (1, '\n-----------------------------------------------------------\n') ; -help colamd_demo ; - -fprintf (1, '\n-----------------------------------------------------------\n') ; -fprintf (1, 'Colamd help information:') ; -fprintf (1, '\n-----------------------------------------------------------\n') ; -help colamd2 ; - -fprintf (1, '\n-----------------------------------------------------------\n') ; -fprintf (1, 'Symamd help information:') ; -fprintf (1, '\n-----------------------------------------------------------\n') ; -help symamd2 ; - -%------------------------------------------------------------------------------- -% Solving Ax=b -%------------------------------------------------------------------------------- - -n = 100 ; -fprintf (1, '\n-----------------------------------------------------------\n') ; -fprintf (1, 'Solving Ax=b for a small %d-by-%d random matrix:', n, n) ; -fprintf (1, '\n-----------------------------------------------------------\n') ; -fprintf (1, '\nNote: Random sparse matrices are AWFUL test cases.\n') ; -fprintf (1, 'They''re just easy to generate in a demo.\n') ; - -% set up the system - -rand ('state', 0) ; -randn ('state', 0) ; -spparms ('default') ; -A = sprandn (n, n, 5/n) + speye (n) ; -b = (1:n)' ; - -fprintf (1, '\n\nSolving via lu (PAQ = LU), where Q is from colamd2:\n') ; -q = colamd2 (A) ; -I = speye (n) ; -Q = I (:, q) ; -[L,U,P] = lu (A*Q) ; -fl = luflops (L, U) ; -x = Q * (U \ (L \ (P * b))) ; -fprintf (1, '\nFlop count for [L,U,P] = lu (A*Q): %d\n', fl) ; -fprintf (1, 'residual: %e\n', norm (A*x-b)); - -try - fprintf (1, '\n\nSolving via lu (PAQ = LU), where Q is from colmmd:\n') ; - q = colmmd (A) ; - I = speye (n) ; - Q = I (:, q) ; - [L,U,P] = lu (A*Q) ; - fl = luflops (L, U) ; - x = Q * (U \ (L \ (P * b))) ; - fprintf (1, '\nFlop count for [L,U,P] = lu (A*Q): %d\n', fl) ; - fprintf (1, 'residual: %e\n', ... - norm (A*x-b)) ; -catch - fprintf (1, 'colmmd is obsolete; test skipped\n') ; -end - -fprintf (1, '\n\nSolving via lu (PA = LU), without regard for sparsity:\n') ; -[L,U,P] = lu (A) ; -fl = luflops (L, U) ; -x = U \ (L \ (P * b)) ; -fprintf (1, '\nFlop count for [L,U,P] = lu (A*Q): %d\n', fl) ; -fprintf (1, 'residual: %e\n', norm (A*x-b)); - -%------------------------------------------------------------------------------- -% Large demo for colamd2 -%------------------------------------------------------------------------------- - -fprintf (1, '\n-----------------------------------------------------------\n') ; -fprintf (1, 'Large demo for colamd2 (symbolic analysis only):') ; -fprintf (1, '\n-----------------------------------------------------------\n') ; - -rand ('state', 0) ; -randn ('state', 0) ; -spparms ('default') ; -n = 1000 ; -fprintf (1, 'Generating a random %d-by-%d sparse matrix.\n', n, n) ; -A = sprandn (n, n, 5/n) + speye (n) ; - -fprintf (1, '\n\nUnordered matrix:\n') ; -lnz = symbfact (A, 'col') ; -fprintf (1, 'nz in Cholesky factors of A''A: %d\n', sum (lnz)) ; -fprintf (1, 'flop count for Cholesky of A''A: %d\n', sum (lnz.^2)) ; - -tic ; -p = colamd2 (A) ; -t = toc ; -lnz = symbfact (A (:,p), 'col') ; -fprintf (1, '\n\nColamd run time: %f\n', t) ; -fprintf (1, 'colamd2 ordering quality: \n') ; -fprintf (1, 'nz in Cholesky factors of A(:,p)''A(:,p): %d\n', sum (lnz)) ; -fprintf (1, 'flop count for Cholesky of A(:,p)''A(:,p): %d\n', sum (lnz.^2)) ; - -try - tic ; - p = colmmd (A) ; - t = toc ; - lnz = symbfact (A (:,p), 'col') ; - fprintf (1, '\n\nColmmd run time: %f\n', t) ; - fprintf (1, 'colmmd ordering quality: \n') ; - fprintf (1, 'nz in Cholesky factors of A(:,p)''A(:,p): %d\n', sum (lnz)) ; - fprintf (1, 'flop count for Cholesky of A(:,p)''A(:,p): %d\n', ... - sum (lnz.^2)) ; -catch - fprintf (1, 'colmmd is obsolete; test skipped\n') ; -end - -%------------------------------------------------------------------------------- -% Large demo for symamd2 -%------------------------------------------------------------------------------- - -fprintf (1, '\n-----------------------------------------------------------\n') ; -fprintf (1, 'Large demo for symamd2 (symbolic analysis only):') ; -fprintf (1, '\n-----------------------------------------------------------\n') ; - -fprintf (1, 'Generating a random symmetric %d-by-%d sparse matrix.\n', n, n) ; -A = A+A' ; - -fprintf (1, '\n\nUnordered matrix:\n') ; -lnz = symbfact (A, 'sym') ; -fprintf (1, 'nz in Cholesky factors of A: %d\n', sum (lnz)) ; -fprintf (1, 'flop count for Cholesky of A: %d\n', sum (lnz.^2)) ; - -tic ; -p = symamd2 (A) ; -t = toc ; -lnz = symbfact (A (p,p), 'sym') ; -fprintf (1, '\n\nSymamd run time: %f\n', t) ; -fprintf (1, 'symamd2 ordering quality: \n') ; -fprintf (1, 'nz in Cholesky factors of A(p,p): %d\n', sum (lnz)) ; -fprintf (1, 'flop count for Cholesky of A(p,p): %d\n', sum (lnz.^2)) ; - -try - tic ; - p = symmmd (A) ; - t = toc ; - lnz = symbfact (A (p,p), 'sym') ; - fprintf (1, '\n\nSymmmd run time: %f\n', t) ; - fprintf (1, 'symmmd ordering quality: \n') ; - fprintf (1, 'nz in Cholesky factors of A(p,p): %d\n', sum (lnz)) ; - fprintf (1, 'flop count for Cholesky of A(p,p): %d\n', sum (lnz.^2)) ; -catch - fprintf (1, 'symmmd is obsolete\n') ; -end diff --git a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/MATLAB/colamd_install.m b/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/MATLAB/colamd_install.m deleted file mode 100644 index 7ac0110b9..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/MATLAB/colamd_install.m +++ /dev/null @@ -1,18 +0,0 @@ -function colamd_install -%COLAMD_MAKE to compile and install the colamd2 and symamd2 mexFunction. -% Your current directory must be COLAMD/MATLAB for this function to work. -% -% Example: -% colamd_install -% -% See also colamd2, symamd2. - -% Copyright 1998-2007, Timothy A. Davis, and Stefan Larimore -% Developed in collaboration with J. Gilbert and E. Ng. - -colamd_make -addpath (pwd) -fprintf ('\nThe following path has been added. You may wish to add it\n') ; -fprintf ('permanently, using the MATLAB pathtool command.\n') ; -fprintf ('%s\n\n', pwd) ; -colamd_demo diff --git a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/MATLAB/colamd_make.m b/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/MATLAB/colamd_make.m deleted file mode 100644 index 1491cf191..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/MATLAB/colamd_make.m +++ /dev/null @@ -1,48 +0,0 @@ -function colamd_make -%COLAMD_MAKE compiles COLAMD2 and SYMAMD2 for MATLAB -% -% Example: -% colamd_make -% -% See also colamd, symamd - -% Copyright 1998-2007, Timothy A. Davis, and Stefan Larimore -% Developed in collaboration with J. Gilbert and E. Ng. - -details = 0 ; % 1 if details of each command are to be printed -d = '' ; -if (~isempty (strfind (computer, '64'))) - d = '-largeArrayDims' ; -end - -% MATLAB 8.3.0 now has a -silent option to keep 'mex' from burbling too much -if (~verLessThan ('matlab', '8.3.0')) - d = ['-silent ' d] ; -end - -src = '../Source/colamd.c ../../SuiteSparse_config/SuiteSparse_config.c' ; -cmd = sprintf ( ... - 'mex -DDLONG -O %s -I../../SuiteSparse_config -I../Include -output ', d) ; -s = [cmd 'colamd2mex colamdmex.c ' src] ; - -if (~(ispc || ismac)) - % for POSIX timing routine - s = [s ' -lrt'] ; -end - -if (details) - fprintf ('%s\n', s) ; -end -eval (s) ; -s = [cmd 'symamd2mex symamdmex.c ' src] ; - -if (~(ispc || ismac)) - % for POSIX timing routine - s = [s ' -lrt'] ; -end - -if (details) - fprintf ('%s\n', s) ; -end -eval (s) ; -fprintf ('COLAMD2 and SYMAMD2 successfully compiled.\n') ; diff --git a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/MATLAB/colamd_test.m b/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/MATLAB/colamd_test.m deleted file mode 100644 index 6c6dac51c..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/MATLAB/colamd_test.m +++ /dev/null @@ -1,510 +0,0 @@ -function colamd_test -%COLAMD_TEST test colamd2 and symamd2 -% Example: -% colamd_test -% -% COLAMD and SYMAMD testing function. Here we try to give colamd2 and symamd2 -% every possible type of matrix and erroneous input that they may encounter. -% We want either a valid permutation returned or we want them to fail -% gracefully. -% -% You are prompted as to whether or not the colamd2 and symand routines and -% the test mexFunctions are to be compiled. -% -% See also colamd2, symamd2 - -% Copyright 1998-2007, Timothy A. Davis, and Stefan Larimore -% Developed in collaboration with J. Gilbert and E. Ng. - - -help colamd_test - - - fprintf ('Compiling colamd2, symamd2, and test mexFunctions.\n') ; - colamd_make ; - - d = '' ; - if (~isempty (strfind (computer, '64'))) - d = '-largeArrayDims' ; - end - cmd = sprintf (... - 'mex -DDLONG -O %s -I../../SuiteSparse_config -I../Include ', d) ; - src = '../Source/colamd.c ../../SuiteSparse_config/SuiteSparse_config.c' ; - if (~(ispc || ismac)) - % for POSIX timing routine - src = [src ' -lrt'] ; - end - eval ([cmd 'colamdtestmex.c ' src]) ; - eval ([cmd 'symamdtestmex.c ' src]) ; - fprintf ('Done compiling.\n') ; - - -fprintf ('\nThe following codes will be tested:\n') ; -which colamd2 -which symamd2 -which colamd2mex -which symamd2mex -which colamdtestmex -which symamdtestmex - -fprintf ('\nStarting the tests. Please be patient.\n') ; - -h = waitbar (0, 'COLAMD test') ; - -rand ('state', 0) ; -randn ('state', 0) ; - -A = sprandn (500,500,0.4) ; - -p = colamd2 (A, [10 10 1]) ; check_perm (p, A) ; -p = colamd2 (A, [2 7 1]) ; check_perm (p, A) ; -p = symamd2 (A, [10 1]) ; check_perm (p, A) ; -p = symamd2 (A, [7 1]) ; check_perm (p, A) ; -p = symamd2 (A, [4 1]) ; check_perm (p, A) ; - - -fprintf ('Null matrices') ; -A = zeros (0,0) ; -A = sparse (A) ; - -[p, stats] = colamd2 (A, [10 10 0]) ; %#ok -check_perm (p, A) ; - -[p, stats] = symamd2 (A, [10 0]) ; %#ok -check_perm (p, A) ; - -A = zeros (0, 100) ; -A = sparse (A) ; -[p, stats] = colamd2 (A, [10 10 0]) ; %#ok -check_perm (p, A) ; - -A = zeros (100, 0) ; -A = sparse (A) ; -[p, stats] = colamd2 (A, [10 10 0]) ; -check_perm (p, A) ; -fprintf (' OK\n') ; - - -fprintf ('Matrices with a few dense row/cols\n') ; - -for trial = 1:20 - - waitbar (trial/20, h, 'COLAMD: with dense rows/cols') ; - - % random square unsymmetric matrix - A = rand_matrix (1000, 1000, 1, 10, 20) ; - - for tol = [0:.1:2 3:20 1e6] - - [p, stats] = colamd2 (A, [tol tol 0]) ; %#ok - check_perm (p, A) ; - - B = A + A' ; - [p, stats] = symamd2 (B, [tol 0]) ; %#ok - check_perm (p, A) ; - - [p, stats] = colamd2 (A, [tol 1 0]) ; %#ok - check_perm (p, A) ; - - [p, stats] = colamd2 (A, [1 tol 0]) ; %#ok - check_perm (p, A) ; - end -end -fprintf (' OK\n') ; - -fprintf ('General matrices\n') ; -for trial = 1:400 - - waitbar (trial/400, h, 'COLAMD: general') ; - - % matrix of random mtype - mtype = irand (3) ; - A = rand_matrix (2000, 2000, mtype, 0, 0) ; - p = colamd2 (A) ; - check_perm (p, A) ; - if (mtype == 3) - p = symamd2 (A) ; - check_perm (p, A) ; - end - -end -fprintf (' OK\n') ; - -fprintf ('Test error handling with invalid inputs\n') ; - -% Check different erroneous input. -for trial = 1:30 - - waitbar (trial/30, h, 'COLAMD: error handling') ; - - A = rand_matrix (1000, 1000, 2, 0, 0) ; - [m n] = size (A) ; - - for err = 1:13 - - p = Tcolamd (A, [n n 0 0 err]) ; - if (p ~= -1) %#ok - check_perm (p, A) ; - end - - if (err == 1) - % check different (valid) input args to colamd2 - p = Acolamd (A) ; - - p2 = Acolamd (A, [10 10 0 0 0]) ; - if (any (p ~= p2)) - error ('colamd2: mismatch 1!') ; - end - [p2 stats] = Acolamd (A) ; %#ok - if (any (p ~= p2)) - error ('colamd2: mismatch 2!') ; - end - [p2 stats] = Acolamd (A, [10 10 0 0 0]) ; - if (any (p ~= p2)) - error ('colamd2: mismatch 3!') ; - end - end - - B = A'*A ; - p = Tsymamd (B, [n 0 err]) ; - if (p ~= -1) %#ok - check_perm (p, A) ; - end - - if (err == 1) - - % check different (valid) input args to symamd2 - p = Asymamd (B) ; - check_perm (p, A) ; - p2 = Asymamd (B, [10 0 0]) ; - if (any (p ~= p2)) - error ('symamd2: mismatch 1!') ; - end - [p2 stats] = Asymamd (B) ; %#ok - if (any (p ~= p2)) - error ('symamd2: mismatch 2!') ; - end - [p2 stats] = Asymamd (B, [10 0 0]) ; %#ok - if (any (p ~= p2)) - error ('symamd2: mismatch 3!') ; - end - end - - end - -end -fprintf (' OK\n') ; - -fprintf ('Matrices with a few empty columns\n') ; - -for trial = 1:400 - - % some are square, some are rectangular - n = 0 ; - while (n < 5) - A = rand_matrix (1000, 1000, irand (2), 0, 0) ; - [m n] = size (A) ; - end - - % Add 5 null columns at random locations. - null_col = randperm (n) ; - null_col = sort (null_col (1:5)) ; - A (:, null_col) = 0 ; - - % Order the matrix and make sure that the null columns are ordered last. - [p, stats] = colamd2 (A, [1e6 1e6 0]) ; - check_perm (p, A) ; - -% if (stats (2) ~= 5) -% stats (2) -% error ('colamd2: wrong number of null columns') ; -% end - - % find all null columns in A - null_col = find (sum (spones (A), 1) == 0) ; - nnull = length (null_col) ; %#ok - if (any (null_col ~= p ((n-4):n))) - error ('colamd2: Null cols are not ordered last in natural order') ; - end - - -end -fprintf (' OK\n') ; - -fprintf ('Matrices with a few empty rows and columns\n') ; - -for trial = 1:400 - - waitbar (trial/400, h, 'COLAMD: with empty rows/cols') ; - - % symmetric matrices - n = 0 ; - while (n < 5) - A = rand_matrix (1000, 1000, 3, 0, 0) ; - [m n] = size (A) ; - end - - % Add 5 null columns and rows at random locations. - null_col = randperm (n) ; - null_col = sort (null_col (1:5)) ; - A (:, null_col) = 0 ; - A (null_col, :) = 0 ; - - % Order the matrix and make sure that the null rows/cols are ordered last. - [p,stats] = symamd2 (A, [10 0]) ; - check_perm (p, A) ; - - % find actual number of null rows and columns - Alo = tril (A, -1) ; - nnull = length (find (sum (Alo') == 0 & sum (Alo) == 0)) ; %#ok - - if (stats (2) ~= nnull | nnull < 5) %#ok - error ('symamd2: wrong number of null columns') ; - end - if (any (null_col ~= p ((n-4):n))) - error ('symamd2: Null cols are not ordered last in natural order') ; - end - -end -fprintf (' OK\n') ; - -fprintf ('Matrices with a few empty rows\n') ; - -% Test matrices with null rows inserted. - -for trial = 1:400 - - waitbar (trial/400, h, 'COLAMD: with null rows') ; - - m = 0 ; - while (m < 5) - A = rand_matrix (1000, 1000, 2, 0, 0) ; - [m n] = size (A) ; %#ok - end - - % Add 5 null rows at random locations. - null_row = randperm (m) ; - null_row = sort (null_row (1:5)) ; - A (null_row, :) = 0 ; - - p = colamd2 (A, [10 10 0]) ; - check_perm (p, A) ; - if (stats (1) ~= 5) - error ('colamd2: wrong number of null rows') ; - end - -end -fprintf (' OK\n') ; - - -fprintf ('\ncolamd2 and symamd2: all tests passed\n\n') ; -close (h) ; - -%------------------------------------------------------------------------------- - -function [p,stats] = Acolamd (S, knobs) -% Acolamd: compare colamd2 and Tcolamd results - -if (nargin < 3) - if (nargout == 1) - [p] = colamd2 (S) ; - [p1] = Tcolamd (S, [10 10 0 0 0]) ; - else - [p, stats] = colamd2 (S) ; - [p1, stats1] = Tcolamd (S, [10 10 0 0 0]) ; %#ok - end -else - if (nargout == 1) - [p] = colamd2 (S, knobs (1:3)) ; - [p1] = Tcolamd (S, knobs) ; - else - [p, stats] = colamd2 (S, knobs (1:3)) ; - [p1, stats1] = Tcolamd (S, knobs) ; %#ok - end -end - -check_perm (p, S) ; -check_perm (p1, S) ; - -if (any (p1 ~= p)) - error ('Acolamd mismatch!') ; -end - - -%------------------------------------------------------------------------------- - -function [p,stats] = Asymamd (S, knobs) -% Asymamd: compare symamd2 and Tsymamd results - -if (nargin < 3) - if (nargout == 1) - [p] = symamd2 (S) ; - [p1] = Tsymamd (S, [10 0 0]) ; - else - [p, stats] = symamd2 (S) ; - [p1, stats1] = Tsymamd (S, [10 0 0]) ; %#ok - end -else - if (nargout == 1) - [p] = symamd2 (S, knobs (1:2)) ; - [p1] = Tsymamd (S, knobs) ; - else - [p, stats] = symamd2 (S, knobs (1:2)) ; - [p1, stats1] = Tsymamd (S, knobs) ; %#ok - end -end - -if (any (p1 ~= p)) - error ('Asymamd mismatch!') ; -end - - -%------------------------------------------------------------------------------- - -function check_perm (p, A) -% check_perm: check for a valid permutation vector - -if (isempty (A) & isempty (p)) %#ok - % empty permutation vectors of empty matrices are OK - return -end - -if (isempty (p)) - error ('bad permutation: cannot be empty') ; -end - -[m n] = size (A) ; -[pm pn] = size (p) ; -if (pn == 1) - % force p to be a row vector - p = p' ; - [pm pn] = size (p) ; -end - -if (n ~= pn) - error ('bad permutation: wrong size') ; -end - -if (pm ~= 1) ; - % p must be a vector - error ('bad permutation: not a vector') ; -else - if (any (sort (p) - (1:pn))) - error ('bad permutation') ; - end -end - -%------------------------------------------------------------------------------- - -function i = irand (n) -% irand: return a random integer between 1 and n -i = min (n, 1 + floor (rand * n)) ; - -%------------------------------------------------------------------------------- - -function A = rand_matrix (nmax, mmax, mtype, drows, dcols) -% rand_matrix: return a random sparse matrix -% -% A = rand_matrix (nmax, mmax, mtype, drows, dcols) -% -% A binary matrix of random size, at most nmax-by-mmax, with drows dense rows -% and dcols dense columns. -% -% mtype 1: square unsymmetric (mmax is ignored) -% mtype 2: rectangular -% mtype 3: symmetric (mmax is ignored) - -n = irand (nmax) ; -if (mtype ~= 2) - % square - m = n ; -else - m = irand (mmax) ; -end - -A = sprand (m, n, 10 / max (m,n)) ; - -if (drows > 0) - % add dense rows - for k = 1:drows - i = irand (m) ; - nz = irand (n) ; - p = randperm (n) ; - p = p (1:nz) ; - A (i,p) = 1 ; - end -end - -if (dcols > 0) - % add dense cols - for k = 1:dcols - j = irand (n) ; - nz = irand (m) ; - p = randperm (m) ; - p = p (1:nz) ; - A (p,j) = 1 ; - end -end - -A = spones (A) ; - -% ensure that there are no empty columns -d = find (full (sum (A)) == 0) ; %#ok -A (m,d) = 1 ; %#ok - -% ensure that there are no empty rows -d = find (full (sum (A,2)) == 0) ; %#ok -A (d,n) = 1 ; %#ok - -if (mtype == 3) - % symmetric - A = A + A' + speye (n) ; -end - -A = spones (A) ; - -%------------------------------------------------------------------------------- - -function [p,stats] = Tcolamd (S, knobs) -% Tcolamd: run colamd2 in a testing mode - -if (nargout <= 1 & nargin == 1) %#ok - p = colamdtestmex (S) ; -elseif (nargout <= 1 & nargin == 2) %#ok - p = colamdtestmex (S, knobs) ; -elseif (nargout == 2 & nargin == 1) %#ok - [p, stats] = colamdtestmex (S) ; -elseif (nargout == 2 & nargin == 2) %#ok - [p, stats] = colamdtestmex (S, knobs) ; -else - error ('colamd2: incorrect number of input and/or output arguments') ; -end - -if (p (1) ~= -1) - [ignore, q] = etree (S (:,p), 'col') ; - p = p (q) ; - check_perm (p, S) ; -end - -%------------------------------------------------------------------------------- - -function [p, stats] = Tsymamd (S, knobs) -% Tsymamd: run symamd2 in a testing mode - -if (nargout <= 1 & nargin == 1) %#ok - p = symamdtestmex (S) ; -elseif (nargout <= 1 & nargin == 2) %#ok - p = symamdtestmex (S, knobs) ; -elseif (nargout == 2 & nargin == 1) %#ok - [p, stats] = symamdtestmex (S) ; -elseif (nargout == 2 & nargin == 2) %#ok - [p, stats] = symamdtestmex (S, knobs) ; -else - error ('symamd2: incorrect number of input and/or output arguments') ; -end - -if (p (1) ~= -1) - [ignore, q] = etree (S (p,p)) ; - p = p (q) ; - check_perm (p, S) ; -end diff --git a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/MATLAB/colamdmex.c b/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/MATLAB/colamdmex.c deleted file mode 100644 index f6be92f23..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/MATLAB/colamdmex.c +++ /dev/null @@ -1,210 +0,0 @@ -/* ========================================================================== */ -/* === colamd mexFunction =================================================== */ -/* ========================================================================== */ - -/* Usage: - - P = colamd2 (A) ; - [ P, stats ] = colamd2 (A, knobs) ; - - see colamd.m for a description. - - Authors: - - The authors of the code itself are Stefan I. Larimore and Timothy A. - Davis (DrTimothyAldenDavis@gmail.com). The algorithm was - developed in collaboration with John Gilbert, Xerox PARC, and Esmond - Ng, Oak Ridge National Laboratory. - - Acknowledgements: - - This work was supported by the National Science Foundation, under - grants DMS-9504974 and DMS-9803599. - - Notice: - - Copyright (c) 1998-2007, Timothy A. Davis, All Rights Reserved. - - Availability: - - The colamd/symamd library is available at http://www.suitesparse.com - -*/ - -/* ========================================================================== */ -/* === Include files ======================================================== */ -/* ========================================================================== */ - -#include "colamd.h" -#include "mex.h" -#include "matrix.h" -#include -#include -#define Long SuiteSparse_long - -/* ========================================================================== */ -/* === colamd mexFunction =================================================== */ -/* ========================================================================== */ - -void mexFunction -( - /* === Parameters ======================================================= */ - - int nlhs, /* number of left-hand sides */ - mxArray *plhs [], /* left-hand side matrices */ - int nrhs, /* number of right--hand sides */ - const mxArray *prhs [] /* right-hand side matrices */ -) -{ - /* === Local variables ================================================== */ - - Long *A ; /* colamd's copy of the matrix, and workspace */ - Long *p ; /* colamd's copy of the column pointers */ - Long Alen ; /* size of A */ - Long n_col ; /* number of columns of A */ - Long n_row ; /* number of rows of A */ - Long nnz ; /* number of entries in A */ - Long full ; /* TRUE if input matrix full, FALSE if sparse */ - double knobs [COLAMD_KNOBS] ; /* colamd user-controllable parameters */ - double *out_perm ; /* output permutation vector */ - double *out_stats ; /* output stats vector */ - double *in_knobs ; /* input knobs vector */ - Long i ; /* loop counter */ - mxArray *Ainput ; /* input matrix handle */ - Long spumoni ; /* verbosity variable */ - Long stats [COLAMD_STATS] ; /* stats for colamd */ - - /* === Check inputs ===================================================== */ - - if (nrhs < 1 || nrhs > 2 || nlhs < 0 || nlhs > 2) - { - mexErrMsgTxt ( - "colamd: incorrect number of input and/or output arguments") ; - } - - /* === Get knobs ======================================================== */ - - colamd_l_set_defaults (knobs) ; - spumoni = 0 ; - - /* check for user-passed knobs */ - if (nrhs == 2) - { - in_knobs = mxGetPr (prhs [1]) ; - i = mxGetNumberOfElements (prhs [1]) ; - if (i > 0) knobs [COLAMD_DENSE_ROW] = in_knobs [0] ; - if (i > 1) knobs [COLAMD_DENSE_COL] = in_knobs [1] ; - if (i > 2) spumoni = (Long) (in_knobs [2] != 0) ; - } - - /* print knob settings if spumoni is set */ - if (spumoni) - { - mexPrintf ("\ncolamd version %d.%d, %s:\n", - COLAMD_MAIN_VERSION, COLAMD_SUB_VERSION, COLAMD_DATE) ; - if (knobs [COLAMD_DENSE_ROW] >= 0) - { - mexPrintf ("knobs(1): %g, rows with > max(16,%g*sqrt(size(A,2)))" - " entries removed\n", in_knobs [0], knobs [COLAMD_DENSE_ROW]) ; - } - else - { - mexPrintf ("knobs(1): %g, only completely dense rows removed\n", - in_knobs [0]) ; - } - if (knobs [COLAMD_DENSE_COL] >= 0) - { - mexPrintf ("knobs(2): %g, cols with > max(16,%g*sqrt(min(size(A)))" - " entries removed\n", in_knobs [1], knobs [COLAMD_DENSE_COL]) ; - } - else - { - mexPrintf ("knobs(2): %g, only completely dense columns removed\n", - in_knobs [1]) ; - } - mexPrintf ("knobs(3): %g, statistics and knobs printed\n", - in_knobs [2]) ; - } - - /* === If A is full, convert to a sparse matrix ========================= */ - - Ainput = (mxArray *) prhs [0] ; - if (mxGetNumberOfDimensions (Ainput) != 2) - { - mexErrMsgTxt ("colamd: input matrix must be 2-dimensional") ; - } - full = !mxIsSparse (Ainput) ; - if (full) - { - mexCallMATLAB (1, &Ainput, 1, (mxArray **) prhs, "sparse") ; - } - - /* === Allocate workspace for colamd ==================================== */ - - /* get size of matrix */ - n_row = mxGetM (Ainput) ; - n_col = mxGetN (Ainput) ; - - /* get column pointer vector so we can find nnz */ - p = (Long *) mxCalloc (n_col+1, sizeof (Long)) ; - (void) memcpy (p, mxGetJc (Ainput), (n_col+1)*sizeof (Long)) ; - nnz = p [n_col] ; - Alen = (Long) colamd_l_recommended (nnz, n_row, n_col) ; - if (Alen == 0) - { - mexErrMsgTxt ("colamd: problem too large") ; - } - - /* === Copy input matrix into workspace ================================= */ - - A = (Long *) mxCalloc (Alen, sizeof (Long)) ; - (void) memcpy (A, mxGetIr (Ainput), nnz*sizeof (Long)) ; - - if (full) - { - mxDestroyArray (Ainput) ; - } - - /* === Order the columns (destroys A) =================================== */ - - if (!colamd_l (n_row, n_col, Alen, A, p, knobs, stats)) - { - colamd_l_report (stats) ; - mexErrMsgTxt ("colamd error!") ; - } - mxFree (A) ; - - /* === Return the permutation vector ==================================== */ - - plhs [0] = mxCreateDoubleMatrix (1, n_col, mxREAL) ; - out_perm = mxGetPr (plhs [0]) ; - for (i = 0 ; i < n_col ; i++) - { - /* colamd is 0-based, but MATLAB expects this to be 1-based */ - out_perm [i] = p [i] + 1 ; - } - mxFree (p) ; - - /* === Return the stats vector ========================================== */ - - /* print stats if spumoni is set */ - if (spumoni) - { - colamd_l_report (stats) ; - } - - if (nlhs == 2) - { - plhs [1] = mxCreateDoubleMatrix (1, COLAMD_STATS, mxREAL) ; - out_stats = mxGetPr (plhs [1]) ; - for (i = 0 ; i < COLAMD_STATS ; i++) - { - out_stats [i] = stats [i] ; - } - - /* fix stats (5) and (6), for 1-based information on jumbled matrix. */ - /* note that this correction doesn't occur if symamd returns FALSE */ - out_stats [COLAMD_INFO1] ++ ; - out_stats [COLAMD_INFO2] ++ ; - } -} diff --git a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/MATLAB/colamdtestmex.c b/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/MATLAB/colamdtestmex.c deleted file mode 100644 index 64ef2cb9b..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/MATLAB/colamdtestmex.c +++ /dev/null @@ -1,567 +0,0 @@ -/* ========================================================================== */ -/* === colamdtest mexFunction =============================================== */ -/* ========================================================================== */ - -/* COLAMD test function - - This MATLAB mexFunction is for testing only. It is not meant for - production use. See colamdmex.c instead. - - Usage: - - [ P, stats ] = colamdtest (A, knobs) ; - - See colamd.m for a description. knobs is required. - - knobs (1) dense row control - knobs (2) dense column control - knobs (3) spumoni - knobs (4) for testing only. Controls the workspace used by - colamd. - - knobs (5) for testing only. Controls how the input matrix is - jumbled prior to calling colamd, to test its error - handling capability. - - Authors: - - The authors of the code itself are Stefan I. Larimore and Timothy A. - Davis (DrTimothyAldenDavis@gmail.com). The algorithm was - developed in collaboration with John Gilbert, Xerox PARC, and Esmond - Ng, Oak Ridge National Laboratory. - - Acknowledgements: - - This work was supported by the National Science Foundation, under - grants DMS-9504974 and DMS-9803599. - - Notice: - - Copyright (c) 1998-2007, Timothy A. Davis, All Rights Reserved. - See COLAMD/Doc/License.txt for the License. - - Availability: - - The colamd/symamd library is available at http://www.suitesparse.com - -*/ - -/* ========================================================================== */ -/* === Include files ======================================================== */ -/* ========================================================================== */ - -#include "colamd.h" -#include "mex.h" -#include "matrix.h" -#include -#include -#define Long SuiteSparse_long - -static void dump_matrix -( - Long A [ ], - Long p [ ], - Long n_row, - Long n_col, - Long Alen, - Long limit -) ; - -/* ========================================================================== */ -/* === colamd mexFunction =================================================== */ -/* ========================================================================== */ - -void mexFunction -( - /* === Parameters ======================================================= */ - - int nlhs, /* number of left-hand sides */ - mxArray *plhs [], /* left-hand side matrices */ - int nrhs, /* number of right--hand sides */ - const mxArray *prhs [] /* right-hand side matrices */ -) -{ - /* === Local variables ================================================== */ - - Long *A ; /* colamd's copy of the matrix, and workspace */ - Long *p ; /* colamd's copy of the column pointers */ - Long Alen ; /* size of A */ - Long n_col ; /* number of columns of A */ - Long n_row ; /* number of rows of A */ - Long nnz ; /* number of entries in A */ - Long full ; /* TRUE if input matrix full, FALSE if sparse */ - double knobs [COLAMD_KNOBS] ; /* colamd user-controllable parameters */ - double *out_perm ; /* output permutation vector */ - double *out_stats ; /* output stats vector */ - double *in_knobs ; /* input knobs vector */ - Long i ; /* loop counter */ - mxArray *Ainput ; /* input matrix handle */ - Long spumoni ; /* verbosity variable */ - Long stats2 [COLAMD_STATS] ;/* stats for colamd */ - - Long *cp, *cp_end, result, col, length ; - Long *stats ; - stats = stats2 ; - - /* === Check inputs ===================================================== */ - - if (nrhs < 1 || nrhs > 2 || nlhs < 0 || nlhs > 2) - { - mexErrMsgTxt ( - "colamd: incorrect number of input and/or output arguments") ; - } - - if (nrhs != 2) - { - mexErrMsgTxt ("colamdtest: knobs are required") ; - } - /* for testing we require all 5 knobs */ - if (mxGetNumberOfElements (prhs [1]) != 5) - { - mexErrMsgTxt ("colamd: must have all 5 knobs for testing") ; - } - - /* === Get knobs ======================================================== */ - - colamd_l_set_defaults (knobs) ; - spumoni = 0 ; - - /* check for user-passed knobs */ - if (nrhs == 2) - { - in_knobs = mxGetPr (prhs [1]) ; - i = mxGetNumberOfElements (prhs [1]) ; - if (i > 0) knobs [COLAMD_DENSE_ROW] = in_knobs [0] ; - if (i > 1) knobs [COLAMD_DENSE_COL] = in_knobs [1] ; - if (i > 2) spumoni = (Long) in_knobs [2] ; - } - - /* print knob settings if spumoni is set */ - if (spumoni) - { - mexPrintf ("\ncolamd version %d.%d, %s:\n", - COLAMD_MAIN_VERSION, COLAMD_SUB_VERSION, COLAMD_DATE) ; - if (knobs [COLAMD_DENSE_ROW] >= 0) - { - mexPrintf ("knobs(1): %g, rows with > max(16,%g*sqrt(size(A,2)))" - " entries removed\n", in_knobs [0], knobs [COLAMD_DENSE_ROW]) ; - } - else - { - mexPrintf ("knobs(1): %g, only completely dense rows removed\n", - in_knobs [0]) ; - } - if (knobs [COLAMD_DENSE_COL] >= 0) - { - mexPrintf ("knobs(2): %g, cols with > max(16,%g*sqrt(min(size(A)))" - " entries removed\n", in_knobs [1], knobs [COLAMD_DENSE_COL]) ; - } - else - { - mexPrintf ("knobs(2): %g, only completely dense columns removed\n", - in_knobs [1]) ; - } - mexPrintf ("knobs(3): %g, statistics and knobs printed\n", - in_knobs [2]) ; - } - - /* === If A is full, convert to a sparse matrix ========================= */ - - Ainput = (mxArray *) prhs [0] ; - if (mxGetNumberOfDimensions (Ainput) != 2) - { - mexErrMsgTxt ("colamd: input matrix must be 2-dimensional") ; - } - full = !mxIsSparse (Ainput) ; - if (full) - { - mexCallMATLAB (1, &Ainput, 1, (mxArray **) prhs, "sparse") ; - } - - /* === Allocate workspace for colamd ==================================== */ - - /* get size of matrix */ - n_row = mxGetM (Ainput) ; - n_col = mxGetN (Ainput) ; - - /* get column pointer vector so we can find nnz */ - p = (Long *) mxCalloc (n_col+1, sizeof (Long)) ; - (void) memcpy (p, mxGetJc (Ainput), (n_col+1)*sizeof (Long)) ; - nnz = p [n_col] ; - Alen = (Long) colamd_l_recommended (nnz, n_row, n_col) ; - if (Alen == 0) - { - mexErrMsgTxt ("colamd: problem too large") ; - } - - -/* === Modify size of Alen if testing ======================================= */ - -/* - knobs [3] amount of workspace given to colamd. - < 0 : TIGHT memory - > 0 : MIN + knob [3] - 1 - == 0 : RECOMMENDED memory -*/ - -/* Here only for testing */ -/* size of the Col and Row structures */ -#define COLAMD_C(n_col) (((n_col) + 1) * 24 / sizeof (Long)) -#define COLAMD_R(n_row) (((n_row) + 1) * 16 / sizeof (Long)) -#ifdef MIN -#undef MIN -#endif -#define MIN(a,b) (((a) < (b)) ? (a) : (b)) -#define COLAMD_MIN_MEMORY(nnz,n_row,n_col) \ - (2 * (nnz) + COLAMD_C (n_col) + COLAMD_R (n_row)) - - /* get knob [3], if negative */ - if (in_knobs [3] < 0) - { - Alen = COLAMD_MIN_MEMORY (nnz, n_row, n_col) + n_col ; - } - else if (in_knobs [3] > 0) - { - Alen = COLAMD_MIN_MEMORY (nnz, n_row, n_col) + in_knobs [3] - 1 ; - } - - /* otherwise, we use the recommended amount set above */ - - /* === Copy input matrix into workspace ================================= */ - - A = (Long *) mxCalloc (Alen, sizeof (Long)) ; - (void) memcpy (A, mxGetIr (Ainput), nnz*sizeof (Long)) ; - - if (full) - { - mxDestroyArray (Ainput) ; - } - - -/* === Jumble matrix ======================================================== */ - -/* - knobs [4] FOR TESTING ONLY: Specifies how to jumble matrix - 0 : No jumbling - 1 : Make n_row less than zero - 2 : Make first pointer non-zero - 3 : Make column pointers not non-decreasing - 4 : Make a column pointer greater or equal to Alen - 5 : Make row indices not strictly increasing - 6 : Make a row index greater or equal to n_row - 7 : Set A = NULL - 8 : Set p = NULL - 9 : Repeat row index - 10: make row indices not sorted - 11: jumble columns massively (note this changes - the pattern of the matrix A.) - 12: Set stats = NULL - 13: Make n_col less than zero -*/ - - /* jumble appropriately */ - switch ((Long) in_knobs [4]) - { - - case 0 : - if (spumoni > 0) - { - mexPrintf ("colamdtest: no errors expected\n") ; - } - result = 1 ; /* no errors */ - break ; - - case 1 : - if (spumoni > 0) - { - mexPrintf ("colamdtest: nrow out of range\n") ; - } - result = 0 ; /* nrow out of range */ - n_row = -1 ; - break ; - - case 2 : - if (spumoni > 0) - { - mexPrintf ("colamdtest: p [0] nonzero\n") ; - } - result = 0 ; /* p [0] must be zero */ - p [0] = 1 ; - break ; - - case 3 : - if (spumoni > 0) - { - mexPrintf ("colamdtest: negative length last column\n") ; - } - result = (n_col == 0) ; /* p must be monotonically inc. */ - p [n_col] = p [0] ; - break ; - - case 4 : - if (spumoni > 0) - { - mexPrintf ("colamdtest: Alen too small\n") ; - } - result = 0 ; /* out of memory */ - p [n_col] = Alen ; - break ; - - case 5 : - if (spumoni > 0) - { - mexPrintf ("colamdtest: row index out of range (-1)\n") ; - } - if (nnz > 0) /* row index out of range */ - { - result = 0 ; - A [nnz-1] = -1 ; - } - else - { - if (spumoni > 0) - { - mexPrintf ("Note: no row indices to put out of range\n") ; - } - result = 1 ; - } - break ; - - case 6 : - if (spumoni > 0) - { - mexPrintf ("colamdtest: row index out of range (n_row)\n") ; - } - if (nnz > 0) /* row index out of range */ - { - if (spumoni > 0) - { - mexPrintf ("Changing A[nnz-1] from %d to %d\n", - A [nnz-1], n_row) ; - } - result = 0 ; - A [nnz-1] = n_row ; - } - else - { - if (spumoni > 0) - { - mexPrintf ("Note: no row indices to put out of range\n") ; - } - result = 1 ; - } - break ; - - case 7 : - if (spumoni > 0) - { - mexPrintf ("colamdtest: A not present\n") ; - } - result = 0 ; /* A not present */ - A = (Long *) NULL ; - break ; - - case 8 : - if (spumoni > 0) - { - mexPrintf ("colamdtest: p not present\n") ; - } - result = 0 ; /* p not present */ - p = (Long *) NULL ; - break ; - - case 9 : - if (spumoni > 0) - { - mexPrintf ("colamdtest: duplicate row index\n") ; - } - result = 1 ; /* duplicate row index */ - - for (col = 0 ; col < n_col ; col++) - { - length = p [col+1] - p [col] ; - if (length > 1) - { - A [p [col]] = A [p [col] + 1] ; - if (spumoni > 0) - { - mexPrintf ("Made duplicate row %d in col %d\n", - A [p [col] + 1], col) ; - } - break ; - } - } - - if (spumoni > 1) - { - dump_matrix (A, p, n_row, n_col, Alen, col+2) ; - } - break ; - - case 10 : - if (spumoni > 0) - { - mexPrintf ("colamdtest: unsorted column\n") ; - } - result = 1 ; /* jumbled columns */ - - for (col = 0 ; col < n_col ; col++) - { - length = p [col+1] - p [col] ; - if (length > 1) - { - i = A[p [col]] ; - A [p [col]] = A[p [col] + 1] ; - A [p [col] + 1] = i ; - if (spumoni > 0) - { - mexPrintf ("Unsorted column %d \n", col) ; - } - break ; - } - } - - if (spumoni > 1) - { - dump_matrix (A, p, n_row, n_col, Alen, col+2) ; - } - break ; - - case 11 : - if (spumoni > 0) - { - mexPrintf ("colamdtest: massive jumbling\n") ; - } - result = 1 ; /* massive jumbling, but no errors */ - srand (1) ; - for (i = 0 ; i < n_col ; i++) - { - cp = &A [p [i]] ; - cp_end = &A [p [i+1]] ; - while (cp < cp_end) - { - *cp++ = rand() % n_row ; - } - } - if (spumoni > 1) - { - dump_matrix (A, p, n_row, n_col, Alen, n_col) ; - } - break ; - - case 12 : - if (spumoni > 0) - { - mexPrintf ("colamdtest: stats not present\n") ; - } - result = 0 ; /* stats not present */ - stats = (Long *) NULL ; - break ; - - case 13 : - if (spumoni > 0) - { - mexPrintf ("colamdtest: ncol out of range\n") ; - } - result = 0 ; /* ncol out of range */ - n_col = -1 ; - break ; - - } - - - /* === Order the columns (destroys A) =================================== */ - - if (!colamd_l (n_row, n_col, Alen, A, p, knobs, stats)) - { - - /* return p = -1 if colamd failed */ - plhs [0] = mxCreateDoubleMatrix (1, 1, mxREAL) ; - out_perm = mxGetPr (plhs [0]) ; - out_perm [0] = -1 ; - mxFree (p) ; - mxFree (A) ; - - if (spumoni > 0 || result) - { - colamd_l_report (stats) ; - } - - if (result) - { - mexErrMsgTxt ("colamd should have returned TRUE\n") ; - } - - return ; - /* mexErrMsgTxt ("colamd error!") ; */ - } - - if (!result) - { - colamd_l_report (stats) ; - mexErrMsgTxt ("colamd should have returned FALSE\n") ; - } - mxFree (A) ; - - /* === Return the permutation vector ==================================== */ - - plhs [0] = mxCreateDoubleMatrix (1, n_col, mxREAL) ; - out_perm = mxGetPr (plhs [0]) ; - for (i = 0 ; i < n_col ; i++) - { - /* colamd is 0-based, but MATLAB expects this to be 1-based */ - out_perm [i] = p [i] + 1 ; - } - mxFree (p) ; - - /* === Return the stats vector ========================================== */ - - /* print stats if spumoni > 0 */ - if (spumoni > 0) - { - colamd_l_report (stats) ; - } - - if (nlhs == 2) - { - plhs [1] = mxCreateDoubleMatrix (1, COLAMD_STATS, mxREAL) ; - out_stats = mxGetPr (plhs [1]) ; - for (i = 0 ; i < COLAMD_STATS ; i++) - { - out_stats [i] = stats [i] ; - } - - /* fix stats (5) and (6), for 1-based information on jumbled matrix. */ - /* note that this correction doesn't occur if symamd returns FALSE */ - out_stats [COLAMD_INFO1] ++ ; - out_stats [COLAMD_INFO2] ++ ; - } -} - - -static void dump_matrix -( - Long A [ ], - Long p [ ], - Long n_row, - Long n_col, - Long Alen, - Long limit -) -{ - Long col, k, row ; - - mexPrintf ("dump matrix: nrow %d ncol %d Alen %d\n", n_row, n_col, Alen) ; - - for (col = 0 ; col < MIN (n_col, limit) ; col++) - { - mexPrintf ("column %d, p[col] %d, p [col+1] %d, length %d\n", - col, p [col], p [col+1], p [col+1] - p [col]) ; - for (k = p [col] ; k < p [col+1] ; k++) - { - row = A [k] ; - mexPrintf (" %d", row) ; - } - mexPrintf ("\n") ; - } -} diff --git a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/MATLAB/luflops.m b/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/MATLAB/luflops.m deleted file mode 100644 index f3c9e453b..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/MATLAB/luflops.m +++ /dev/null @@ -1,34 +0,0 @@ -function fl = luflops (L, U) -%LUFLOPS compute the flop count for sparse LU factorization -% -% Example: -% fl = luflops (L,U) -% -% Given a sparse LU factorization (L and U), return the flop count required -% by a conventional LU factorization algorithm to compute it. L and U can -% be either sparse or full matrices. L must be lower triangular and U must -% be upper triangular. Do not attempt to use this on the permuted L from -% [L,U] = lu (A). Instead, use [L,U,P] = lu (A) or [L,U,P,Q] = lu (A). -% -% Note that there is a subtle undercount in this estimate. Suppose A is -% completely dense, but during LU factorization exact cancellation occurs, -% causing some of the entries in L and U to become identically zero. The -% flop count returned by this routine is an undercount. There is a simple -% way to fix this (L = spones (L) + spones (tril (A))), but the fix is partial. -% It can also occur that some entry in L is a "symbolic" fill-in (zero in -% A, but a fill-in entry and thus must be computed), but numerically -% zero. The only way to get a reliable LU factorization would be to do a -% purely symbolic factorization of A. This cannot be done with -% symbfact (A, 'col'). -% -% See NA Digest, Vol 00, #50, Tuesday, Dec. 5, 2000 -% -% See also symbfact - -% Copyright 1998-2007, Timothy A. Davis - - -Lnz = full (sum (spones (L))) - 1 ; % off diagonal nz in cols of L -Unz = full (sum (spones (U')))' - 1 ; % off diagonal nz in rows of U -fl = 2*Lnz*Unz + sum (Lnz) ; - diff --git a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/MATLAB/symamd2.m b/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/MATLAB/symamd2.m deleted file mode 100644 index ecae450be..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/MATLAB/symamd2.m +++ /dev/null @@ -1,86 +0,0 @@ -function [p, stats] = symamd2 (S, knobs) -%SYMAMD Symmetric approximate minimum degree permutation. -% P = SYMAMD2(S) for a symmetric positive definite matrix S, returns the -% permutation vector p such that S(p,p) tends to have a sparser Cholesky -% factor than S. Sometimes SYMAMD works well for symmetric indefinite -% matrices too. The matrix S is assumed to be symmetric; only the -% strictly lower triangular part is referenced. S must be square. -% Note that p = amd(S) is much faster and generates comparable orderings. -% The ordering is followed by an elimination tree post-ordering. -% -% Note that this function is source code for the built-in MATLAB symamd -% function. It has been renamed here to symamd2 to avoid a filename clash. -% symamd and symamd2 are identical. -% -% See also SYMAMD, AMD, COLAMD, COLAMD2. -% -% Example: -% P = symamd2 (S) -% [P, stats] = symamd2 (S, knobs) -% -% knobs is an optional one- to two-element input vector. If S is n-by-n, -% then rows and columns with more than max(16,knobs(1)*sqrt(n)) entries are -% removed prior to ordering, and ordered last in the output permutation P. -% No rows/columns are removed if knobs(1)<0. If knobs(2) is nonzero, stats -% and knobs are printed. The default is knobs = [10 0]. Note that knobs -% differs from earlier versions of symamd. - -% Copyright 1998-2007, Timothy A. Davis, and Stefan Larimore -% Developed in collaboration with J. Gilbert and E. Ng. -% Acknowledgements: This work was supported by the National Science -% Foundation, under grants DMS-9504974 and DMS-9803599. - -%------------------------------------------------------------------------------- -% perform the symamd ordering: -%------------------------------------------------------------------------------- - -if (nargout <= 1 & nargin == 1) %#ok - p = symamd2mex (S) ; -elseif (nargout <= 1 & nargin == 2) %#ok - p = symamd2mex (S, knobs) ; -elseif (nargout == 2 & nargin == 1) %#ok - [p, stats] = symamd2mex (S) ; -elseif (nargout == 2 & nargin == 2) %#ok - [p, stats] = symamd2mex (S, knobs) ; -else - error('symamd: incorrect number of input and/or output arguments.') ; -end - -%------------------------------------------------------------------------------- -% symmetric elimination tree post-ordering: -%------------------------------------------------------------------------------- - -[ignore, q] = etree (S (p,p)) ; -p = p (q) ; - - -% stats is an optional 20-element output vector that provides data about the -% ordering and the validity of the input matrix S. Ordering statistics are -% in stats (1:3). stats (1) = stats (2) is the number of dense or empty -% rows and columns ignored by SYMAMD and stats (3) is the number of -% garbage collections performed on the internal data structure used by -% SYMAMD (roughly of size 8.4*nnz(tril(S,-1)) + 9*n integers). -% -% MATLAB built-in functions are intended to generate valid sparse matrices, -% with no duplicate entries, with ascending row indices of the nonzeros -% in each column, with a non-negative number of entries in each column (!) -% and so on. If a matrix is invalid, then SYMAMD may or may not be able -% to continue. If there are duplicate entries (a row index appears two or -% more times in the same column) or if the row indices in a column are out -% of order, then SYMAMD can correct these errors by ignoring the duplicate -% entries and sorting each column of its internal copy of the matrix S (the -% input matrix S is not repaired, however). If a matrix is invalid in other -% ways then SYMAMD cannot continue, an error message is printed, and no -% output arguments (P or stats) are returned. SYMAMD is thus a simple way -% to check a sparse matrix to see if it's valid. -% -% stats (4:7) provide information if SYMAMD was able to continue. The -% matrix is OK if stats (4) is zero, or 1 if invalid. stats (5) is the -% rightmost column index that is unsorted or contains duplicate entries, -% or zero if no such column exists. stats (6) is the last seen duplicate -% or out-of-order row index in the column index given by stats (5), or zero -% if no such row index exists. stats (7) is the number of duplicate or -% out-of-order row indices. -% -% stats (8:20) is always zero in the current version of SYMAMD (reserved -% for future use). diff --git a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/MATLAB/symamdmex.c b/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/MATLAB/symamdmex.c deleted file mode 100644 index af79e269d..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/MATLAB/symamdmex.c +++ /dev/null @@ -1,192 +0,0 @@ -/* ========================================================================== */ -/* === symamd mexFunction =================================================== */ -/* ========================================================================== */ - -/* SYMAMD mexFunction - - Usage: - - P = symamd2 (A) ; - [ P, stats ] = symamd2 (A, knobs) ; - - See symamd.m for a description. - - Authors: - - The authors of the code itself are Stefan I. Larimore and Timothy A. - Davis (DrTimothyAldenDavis@gmail.com). The algorithm was - developed in collaboration with John Gilbert, Xerox PARC, and Esmond - Ng, Oak Ridge National Laboratory. - - Acknowledgements: - - This work was supported by the National Science Foundation, under - grants DMS-9504974 and DMS-9803599. - - Notice: - - Copyright (c) 1998-2007, Timothy A. Davis. All Rights Reserved. - See COLAMD/Doc/License.txt for the License. - - Availability: - - The colamd/symamd library is available at http://www.suitesparse.com - -*/ - -/* ========================================================================== */ -/* === Include files ======================================================== */ -/* ========================================================================== */ - -#include "colamd.h" -#include "mex.h" -#include "matrix.h" -#include -#define Long SuiteSparse_long - -/* ========================================================================== */ -/* === symamd mexFunction =================================================== */ -/* ========================================================================== */ - -void mexFunction -( - /* === Parameters ======================================================= */ - - int nlhs, /* number of left-hand sides */ - mxArray *plhs [], /* left-hand side matrices */ - int nrhs, /* number of right--hand sides */ - const mxArray *prhs [] /* right-hand side matrices */ -) -{ - /* === Local variables ================================================== */ - - Long *perm ; /* column ordering of M and ordering of A */ - Long *A ; /* row indices of input matrix A */ - Long *p ; /* column pointers of input matrix A */ - Long n_col ; /* number of columns of A */ - Long n_row ; /* number of rows of A */ - Long full ; /* TRUE if input matrix full, FALSE if sparse */ - double knobs [COLAMD_KNOBS] ; /* colamd user-controllable parameters */ - double *out_perm ; /* output permutation vector */ - double *out_stats ; /* output stats vector */ - double *in_knobs ; /* input knobs vector */ - Long i ; /* loop counter */ - mxArray *Ainput ; /* input matrix handle */ - Long spumoni ; /* verbosity variable */ - Long stats [COLAMD_STATS] ; /* stats for symamd */ - - /* === Check inputs ===================================================== */ - - if (nrhs < 1 || nrhs > 2 || nlhs < 0 || nlhs > 2) - { - mexErrMsgTxt ( - "symamd: incorrect number of input and/or output arguments.") ; - } - - /* === Get knobs ======================================================== */ - - colamd_l_set_defaults (knobs) ; - spumoni = 0 ; - - /* check for user-passed knobs */ - if (nrhs == 2) - { - in_knobs = mxGetPr (prhs [1]) ; - i = mxGetNumberOfElements (prhs [1]) ; - if (i > 0) knobs [COLAMD_DENSE_ROW] = in_knobs [0] ; - if (i > 1) spumoni = (Long) (in_knobs [1] != 0) ; - } - - /* print knob settings if spumoni is set */ - if (spumoni) - { - mexPrintf ("\nsymamd version %d.%d, %s:\n", - COLAMD_MAIN_VERSION, COLAMD_SUB_VERSION, COLAMD_DATE) ; - if (knobs [COLAMD_DENSE_ROW] >= 0) - { - mexPrintf ("knobs(1): %g, rows/cols with > " - "max(16,%g*sqrt(size(A,2))) entries removed\n", - in_knobs [0], knobs [COLAMD_DENSE_ROW]) ; - } - else - { - mexPrintf ("knobs(1): %g, no dense rows removed\n", in_knobs [0]) ; - } - mexPrintf ("knobs(2): %g, statistics and knobs printed\n", - in_knobs [1]) ; - } - - /* === If A is full, convert to a sparse matrix ========================= */ - - Ainput = (mxArray *) prhs [0] ; - if (mxGetNumberOfDimensions (Ainput) != 2) - { - mexErrMsgTxt ("symamd: input matrix must be 2-dimensional.") ; - } - full = !mxIsSparse (Ainput) ; - if (full) - { - mexCallMATLAB (1, &Ainput, 1, (mxArray **) prhs, "sparse") ; - } - - /* === Allocate workspace for symamd ==================================== */ - - /* get size of matrix */ - n_row = mxGetM (Ainput) ; - n_col = mxGetN (Ainput) ; - if (n_col != n_row) - { - mexErrMsgTxt ("symamd: matrix must be square.") ; - } - - A = (Long *) mxGetIr (Ainput) ; - p = (Long *) mxGetJc (Ainput) ; - perm = (Long *) mxCalloc (n_col+1, sizeof (Long)) ; - - /* === Order the rows and columns of A (does not destroy A) ============= */ - - if (!symamd_l (n_col, A, p, perm, knobs, stats, &mxCalloc, &mxFree)) - { - symamd_l_report (stats) ; - mexErrMsgTxt ("symamd error!") ; - } - - if (full) - { - mxDestroyArray (Ainput) ; - } - - /* === Return the permutation vector ==================================== */ - - plhs [0] = mxCreateDoubleMatrix (1, n_col, mxREAL) ; - out_perm = mxGetPr (plhs [0]) ; - for (i = 0 ; i < n_col ; i++) - { - /* symamd is 0-based, but MATLAB expects this to be 1-based */ - out_perm [i] = perm [i] + 1 ; - } - mxFree (perm) ; - - /* === Return the stats vector ========================================== */ - - /* print stats if spumoni is set */ - if (spumoni) - { - symamd_l_report (stats) ; - } - - if (nlhs == 2) - { - plhs [1] = mxCreateDoubleMatrix (1, COLAMD_STATS, mxREAL) ; - out_stats = mxGetPr (plhs [1]) ; - for (i = 0 ; i < COLAMD_STATS ; i++) - { - out_stats [i] = stats [i] ; - } - - /* fix stats (5) and (6), for 1-based information on jumbled matrix. */ - /* note that this correction doesn't occur if symamd returns FALSE */ - out_stats [COLAMD_INFO1] ++ ; - out_stats [COLAMD_INFO2] ++ ; - } -} diff --git a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/MATLAB/symamdtestmex.c b/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/MATLAB/symamdtestmex.c deleted file mode 100644 index 57cf05ca4..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/MATLAB/symamdtestmex.c +++ /dev/null @@ -1,533 +0,0 @@ -/* ========================================================================== */ -/* === symamdtest mexFunction =============================================== */ -/* ========================================================================== */ - -/* SYMAMD test function - - This MATLAB mexFunction is for testing only. It is not meant for - production use. See symamdmex.c instead. - - Usage: - - [ P, stats ] = symamdtest (A, knobs) ; - - See symamd.m for a description. knobs is required. - - knobs (1) dense row control - knobs (2) spumoni - knobs (3) for testing only. Controls how the input matrix is - jumbled prior to calling symamd, to test its error - handling capability. - - Authors: - - The authors of the code itself are Stefan I. Larimore and Timothy A. - Davis (DrTimothyAldenDavis@gmail.com). The algorithm was - developed in collaboration with John Gilbert, Xerox PARC, and Esmond - Ng, Oak Ridge National Laboratory. - - Acknowledgements: - - This work was supported by the National Science Foundation, under - grants DMS-9504974 and DMS-9803599. - - Notice: - - Copyright (c) 1998-2007, Timothy A. Davis. All Rights Reserved. - See COLAMD/Doc/License.txt for the License. - - Availability: - - The colamd/symamd library is available at http://www.suitesparse.com - -*/ - -/* ========================================================================== */ -/* === Include files ======================================================== */ -/* ========================================================================== */ - -#include "colamd.h" -#include "mex.h" -#include "matrix.h" -#include -#include -#define Long SuiteSparse_long - -static void dump_matrix -( - Long A [ ], - Long p [ ], - Long n_row, - Long n_col, - Long Alen, - Long limit -) ; - -/* ========================================================================== */ -/* === symamd mexFunction =================================================== */ -/* ========================================================================== */ - -void mexFunction -( - /* === Parameters ======================================================= */ - - int nlhs, /* number of left-hand sides */ - mxArray *plhs [], /* left-hand side matrices */ - int nrhs, /* number of right--hand sides */ - const mxArray *prhs [] /* right-hand side matrices */ -) -{ - /* === Local variables ================================================== */ - - Long *perm ; /* column ordering of M and ordering of A */ - Long *A ; /* row indices of input matrix A */ - Long *p ; /* column pointers of input matrix A */ - Long n_col ; /* number of columns of A */ - Long n_row ; /* number of rows of A */ - Long full ; /* TRUE if input matrix full, FALSE if sparse */ - double knobs [COLAMD_KNOBS] ; /* colamd user-controllable parameters */ - double *out_perm ; /* output permutation vector */ - double *out_stats ; /* output stats vector */ - double *in_knobs ; /* input knobs vector */ - Long i ; /* loop counter */ - mxArray *Ainput ; /* input matrix handle */ - Long spumoni ; /* verbosity variable */ - Long stats2 [COLAMD_STATS] ;/* stats for symamd */ - - Long *cp, *cp_end, result, nnz, col, length ; - Long *stats ; - stats = stats2 ; - - /* === Check inputs ===================================================== */ - - if (nrhs < 1 || nrhs > 2 || nlhs < 0 || nlhs > 2) - { - mexErrMsgTxt ( - "symamd: incorrect number of input and/or output arguments.") ; - } - - if (nrhs != 2) - { - mexErrMsgTxt ("symamdtest: knobs are required") ; - } - /* for testing we require all 3 knobs */ - if (mxGetNumberOfElements (prhs [1]) != 3) - { - mexErrMsgTxt ("symamdtest: must have all 3 knobs for testing") ; - } - - /* === Get knobs ======================================================== */ - - colamd_l_set_defaults (knobs) ; - spumoni = 0 ; - - /* check for user-passed knobs */ - if (nrhs == 2) - { - in_knobs = mxGetPr (prhs [1]) ; - i = mxGetNumberOfElements (prhs [1]) ; - if (i > 0) knobs [COLAMD_DENSE_ROW] = in_knobs [0] ; - if (i > 1) spumoni = (Long) in_knobs [1] ; - } - - /* print knob settings if spumoni is set */ - if (spumoni) - { - mexPrintf ("\nsymamd version %d.%d, %s:\n", - COLAMD_MAIN_VERSION, COLAMD_SUB_VERSION, COLAMD_DATE) ; - if (knobs [COLAMD_DENSE_ROW] >= 0) - { - mexPrintf ("knobs(1): %g, rows/cols with > " - "max(16,%g*sqrt(size(A,2))) entries removed\n", - in_knobs [0], knobs [COLAMD_DENSE_ROW]) ; - } - else - { - mexPrintf ("knobs(1): %g, no dense rows removed\n", in_knobs [0]) ; - } - mexPrintf ("knobs(2): %g, statistics and knobs printed\n", - in_knobs [1]) ; - mexPrintf ("Testing %d\n", in_knobs [2]) ; - } - - /* === If A is full, convert to a sparse matrix ========================= */ - - Ainput = (mxArray *) prhs [0] ; - if (mxGetNumberOfDimensions (Ainput) != 2) - { - mexErrMsgTxt ("symamd: input matrix must be 2-dimensional.") ; - } - full = !mxIsSparse (Ainput) ; - if (full) - { - mexCallMATLAB (1, &Ainput, 1, (mxArray **) prhs, "sparse") ; - } - - /* === Allocate workspace for symamd ==================================== */ - - /* get size of matrix */ - n_row = mxGetM (Ainput) ; - n_col = mxGetN (Ainput) ; - if (n_col != n_row) - { - mexErrMsgTxt ("symamd: matrix must be square.") ; - } - - /* p = mxGetJc (Ainput) ; */ - p = (Long *) mxCalloc (n_col+1, sizeof (Long)) ; - (void) memcpy (p, mxGetJc (Ainput), (n_col+1)*sizeof (Long)) ; - - nnz = p [n_col] ; - if (spumoni > 0) - { - mexPrintf ("symamdtest: nnz %d\n", nnz) ; - } - - /* A = mxGetIr (Ainput) ; */ - A = (Long *) mxCalloc (nnz+1, sizeof (Long)) ; - (void) memcpy (A, mxGetIr (Ainput), nnz*sizeof (Long)) ; - - perm = (Long *) mxCalloc (n_col+1, sizeof (Long)) ; - -/* === Jumble matrix ======================================================== */ - - -/* - knobs [2] FOR TESTING ONLY: Specifies how to jumble matrix - 0 : No jumbling - 1 : (no errors) - 2 : Make first pointer non-zero - 3 : Make column pointers not non-decreasing - 4 : (no errors) - 5 : Make row indices not strictly increasing - 6 : Make a row index greater or equal to n_row - 7 : Set A = NULL - 8 : Set p = NULL - 9 : Repeat row index - 10: make row indices not sorted - 11: jumble columns massively (note this changes - the pattern of the matrix A.) - 12: Set stats = NULL - 13: Make n_col less than zero -*/ - - /* jumble appropriately */ - switch ((Long) in_knobs [2]) - { - - case 0 : - if (spumoni > 0) - { - mexPrintf ("symamdtest: no errors expected\n") ; - } - result = 1 ; /* no errors */ - break ; - - case 1 : - if (spumoni > 0) - { - mexPrintf ("symamdtest: no errors expected (1)\n") ; - } - result = 1 ; - break ; - - case 2 : - if (spumoni > 0) - { - mexPrintf ("symamdtest: p [0] nonzero\n") ; - } - result = 0 ; /* p [0] must be zero */ - p [0] = 1 ; - break ; - - case 3 : - if (spumoni > 0) - { - mexPrintf ("symamdtest: negative length last column\n") ; - } - result = (n_col == 0) ; /* p must be monotonically inc. */ - p [n_col] = p [0] ; - break ; - - case 4 : - if (spumoni > 0) - { - mexPrintf ("symamdtest: no errors expected (4)\n") ; - } - result = 1 ; - break ; - - case 5 : - if (spumoni > 0) - { - mexPrintf ("symamdtest: row index out of range (-1)\n") ; - } - if (nnz > 0) /* row index out of range */ - { - result = 0 ; - A [nnz-1] = -1 ; - } - else - { - if (spumoni > 0) - { - mexPrintf ("Note: no row indices to put out of range\n") ; - } - result = 1 ; - } - break ; - - case 6 : - if (spumoni > 0) - { - mexPrintf ("symamdtest: row index out of range (ncol)\n") ; - } - if (nnz > 0) /* row index out of range */ - { - result = 0 ; - A [nnz-1] = n_col ; - } - else - { - if (spumoni > 0) - { - mexPrintf ("Note: no row indices to put out of range\n") ; - } - result = 1 ; - } - break ; - - case 7 : - if (spumoni > 0) - { - mexPrintf ("symamdtest: A not present\n") ; - } - result = 0 ; /* A not present */ - A = (Long *) NULL ; - break ; - - case 8 : - if (spumoni > 0) - { - mexPrintf ("symamdtest: p not present\n") ; - } - result = 0 ; /* p not present */ - p = (Long *) NULL ; - break ; - - case 9 : - if (spumoni > 0) - { - mexPrintf ("symamdtest: duplicate row index\n") ; - } - result = 1 ; /* duplicate row index */ - - for (col = 0 ; col < n_col ; col++) - { - length = p [col+1] - p [col] ; - if (length > 1) - { - A [p [col+1]-2] = A [p [col+1] - 1] ; - if (spumoni > 0) - { - mexPrintf ("Made duplicate row %d in col %d\n", - A [p [col+1] - 1], col) ; - } - break ; - } - } - - if (spumoni > 1) - { - dump_matrix (A, p, n_row, n_col, nnz, col+2) ; - } - break ; - - case 10 : - if (spumoni > 0) - { - mexPrintf ("symamdtest: unsorted column\n") ; - } - result = 1 ; /* jumbled columns */ - - for (col = 0 ; col < n_col ; col++) - { - length = p [col+1] - p [col] ; - if (length > 1) - { - i = A[p [col]] ; - A [p [col]] = A[p [col] + 1] ; - A [p [col] + 1] = i ; - if (spumoni > 0) - { - mexPrintf ("Unsorted column %d \n", col) ; - } - break ; - } - } - - if (spumoni > 1) - { - dump_matrix (A, p, n_row, n_col, nnz, col+2) ; - } - break ; - - case 11 : - if (spumoni > 0) - { - mexPrintf ("symamdtest: massive jumbling\n") ; - } - result = 1 ; /* massive jumbling, but no errors */ - srand (1) ; - for (i = 0 ; i < n_col ; i++) - { - cp = &A [p [i]] ; - cp_end = &A [p [i+1]] ; - while (cp < cp_end) - { - *cp++ = rand() % n_row ; - } - } - if (spumoni > 1) - { - dump_matrix (A, p, n_row, n_col, nnz, n_col) ; - } - break ; - - case 12 : - if (spumoni > 0) - { - mexPrintf ("symamdtest: stats not present\n") ; - } - result = 0 ; /* stats not present */ - stats = (Long *) NULL ; - break ; - - case 13 : - if (spumoni > 0) - { - mexPrintf ("symamdtest: ncol out of range\n") ; - } - result = 0 ; /* ncol out of range */ - n_col = -1 ; - break ; - - } - - /* === Order the rows and columns of A (does not destroy A) ============= */ - - if (!symamd_l (n_col, A, p, perm, knobs, stats, &mxCalloc, &mxFree)) - { - - /* return p = -1 if colamd failed */ - plhs [0] = mxCreateDoubleMatrix (1, 1, mxREAL) ; - out_perm = mxGetPr (plhs [0]) ; - out_perm [0] = -1 ; - mxFree (p) ; - mxFree (A) ; - - if (spumoni > 0 || result) - { - symamd_l_report (stats) ; - } - - if (result) - { - mexErrMsgTxt ("symamd should have returned TRUE\n") ; - } - - return ; - /* mexErrMsgTxt ("symamd error!") ; */ - } - - if (!result) - { - symamd_l_report (stats) ; - mexErrMsgTxt ("symamd should have returned FALSE\n") ; - } - - if (full) - { - mxDestroyArray (Ainput) ; - } - - /* === Return the permutation vector ==================================== */ - - plhs [0] = mxCreateDoubleMatrix (1, n_col, mxREAL) ; - out_perm = mxGetPr (plhs [0]) ; - for (i = 0 ; i < n_col ; i++) - { - /* symamd is 0-based, but MATLAB expects this to be 1-based */ - out_perm [i] = perm [i] + 1 ; - } - mxFree (perm) ; - - /* === Return the stats vector ========================================== */ - - /* print stats if spumoni > 0 */ - if (spumoni > 0) - { - symamd_l_report (stats) ; - } - - if (nlhs == 2) - { - plhs [1] = mxCreateDoubleMatrix (1, COLAMD_STATS, mxREAL) ; - out_stats = mxGetPr (plhs [1]) ; - for (i = 0 ; i < COLAMD_STATS ; i++) - { - out_stats [i] = stats [i] ; - } - - /* fix stats (5) and (6), for 1-based information on jumbled matrix. */ - /* note that this correction doesn't occur if symamd returns FALSE */ - out_stats [COLAMD_INFO1] ++ ; - out_stats [COLAMD_INFO2] ++ ; - } -} - - -#ifdef MIN -#undef MIN -#endif -#define MIN(a,b) (((a) < (b)) ? (a) : (b)) - - -static void dump_matrix -( - Long A [ ], - Long p [ ], - Long n_row, - Long n_col, - Long Alen, - Long limit -) -{ - Long col, k, row ; - - mexPrintf ("dump matrix: nrow %d ncol %d Alen %d\n", n_row, n_col, Alen) ; - - if (!A) - { - mexPrintf ("A not present\n") ; - return ; - } - - if (!p) - { - mexPrintf ("p not present\n") ; - return ; - } - - for (col = 0 ; col < MIN (n_col, limit) ; col++) - { - mexPrintf ("column %d, p[col] %d, p [col+1] %d, length %d\n", - col, p [col], p [col+1], p [col+1] - p [col]) ; - for (k = p [col] ; k < p [col+1] ; k++) - { - row = A [k] ; - mexPrintf (" %d", row) ; - } - mexPrintf ("\n") ; - } -} diff --git a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Makefile b/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Makefile index 26eddc755..2c7de9ee4 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Makefile +++ b/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Makefile @@ -1,55 +1,83 @@ -#------------------------------------------------------------------------------ -# COLAMD Makefile -#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------- +# SuiteSparse/COLAMD/Makefile +#------------------------------------------------------------------------------- -SUITESPARSE ?= $(realpath $(CURDIR)/..) -export SUITESPARSE +# COLAMD: Copyright (c) 1998-2022, Timothy A. Davis and Stefan Larimore, +# All Rights Reserved. +# SPDX-License-Identifier: BSD-3-clause -default: all +#------------------------------------------------------------------------------- -include ../SuiteSparse_config/SuiteSparse_config.mk +# A simple Makefile for COLAMD, which relies on cmake to do the +# actual build. All the work is done in cmake so this Makefile is just for +# convenience. -demos: all +# To compile with an alternate compiler: +# +# make CC=gcc CXX=g++ +# +# To compile/install for system-wide usage: +# +# make +# sudo make install +# +# To compile/install for local usage (SuiteSparse/lib and SuiteSparse/include): +# +# make local +# make install +# +# To clean up the files: +# +# make clean -# Compile all C code -all: - ( cd Lib ; $(MAKE) ) - ( cd Demo ; $(MAKE) ) +JOBS ?= 8 -# compile just the C-callable libraries (not Demos) +default: library + +# default is to install only in /usr/local library: - ( cd Lib ; $(MAKE) ) + ( cd build && cmake $(CMAKE_OPTIONS) .. && cmake --build . -j${JOBS} ) -# compile the static libraries only -static: - ( cd Lib ; $(MAKE) static ) +# install only in SuiteSparse/lib and SuiteSparse/include +local: + ( cd build && cmake $(CMAKE_OPTIONS) -DLOCAL_INSTALL=1 .. && cmake --build . -j${JOBS} ) -# remove object files, but keep the compiled programs and library archives -clean: - ( cd Lib ; $(MAKE) clean ) - ( cd Demo ; $(MAKE) clean ) - ( cd MATLAB ; $(RM) $(CLEAN) ) +# install only in /usr/local (default) +global: + ( cd build && cmake $(CMAKE_OPTIONS) -DLOCAL_INSTALL=0 .. && cmake --build . -j${JOBS} ) -# clean, and then remove compiled programs and library archives -purge: - ( cd Lib ; $(MAKE) purge ) - ( cd Demo ; $(MAKE) purge ) - ( cd MATLAB ; $(RM) $(CLEAN) ; $(RM) *.mex* ) +debug: + ( cd build && cmake $(CMAKE_OPTIONS) -DCMAKE_BUILD_TYPE=Debug .. && cmake --build . -j${JOBS} ) -distclean: purge +all: library -# get ready for distribution -dist: purge - ( cd Demo ; $(MAKE) dist ) +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 -ccode: library +# just compile after running cmake; do not run cmake again +remake: + ( cd build && cmake --build . -j${JOBS} ) -lib: library +# just run cmake to set things up +setup: + ( cd build ; cmake $(CMAKE_OPTIONS) .. ) -# install COLAMD install: - ( cd Lib ; $(MAKE) install ) + ( cd build && cmake --install . ) -# uninstall COLAMD +# remove any installed libraries and #include files uninstall: - ( cd Lib ; $(MAKE) uninstall ) + - xargs rm < build/install_manifest.txt + +# remove all files not in the distribution +clean: + - $(RM) -rf build/* Config/*.tmp MATLAB/*.o MATLAB/*.mex* timelog.m + +purge: clean + +distclean: clean + diff --git a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/README.txt b/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/README.txt index 7c79343fa..e6e43b097 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/README.txt +++ b/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/README.txt @@ -1,6 +1,8 @@ -COLAMD, Copyright 1998-2016, Timothy A. Davis. http://www.suitesparse.com +COLAMD, Copyright 1998-2022, Timothy A. Davis. http://www.suitesparse.com ------------------------------------------------------------------------------- +SPDX-License-Identifier: BSD-3-clause + The COLAMD column approximate minimum degree ordering algorithm computes a permutation vector P such that the LU factorization of A (:,P) tends to be sparser than that of A. The Cholesky factorization of @@ -11,18 +13,9 @@ that M'*M has the same pattern as A, and then uses COLAMD to compute a column ordering of M. Colamd and symamd tend to be faster and generate better orderings than their MATLAB counterparts, colmmd and symmmd. -To compile and test the colamd m-files and mexFunctions, just unpack the -COLAMD/ directory from the COLAMD.tar.gz file, and run MATLAB from -within that directory. Next, type colamd_test to compile and test colamd -and symamd. This will work on any computer with MATLAB (Unix, PC, or Mac). -Alternatively, type "make" (in Unix) to compile and run a simple example C -code, without using MATLAB. - To compile and install the colamd m-files and mexFunctions, just cd to COLAMD/MATLAB and type colamd_install in the MATLAB command window. A short demo will run. Optionally, type colamd_test to run an extensive tests. -Type "make" in Unix in the COLAMD directory to compile the C-callable -library and to run a short demo. Colamd is a built-in routine in MATLAB, available from The Mathworks, Inc. Under most cases, the compiled COLAMD from Versions 2.0 to the @@ -33,16 +26,26 @@ mexFunction. v2.5 adds additional checks for integer overflow, so that the "int" version can be safely used with 64-bit pointers. Refer to the ChangeLog for more details. -Other "make" targets: +COLAMD includes a simple top-level Makefile, which is optional. All the work +is done via cmake. Windows users can simply import the CMakeLists.txt into MS +Visual Studio. + +"make" targets: + make compiles the COLAMD library; + "make install" will install in /usr/local/lib, + /usr/local/include, SuiteSparse/lib, and + SuiteSparse/include + make demos compiles and runs a few demos make library compiles a C-callable library containing colamd make clean removes all files not in the distribution, but keeps the compiled libraries. make distclean removes all files not in the distribution - make install installs the library in /usr/local/lib and - /usr/local/include - make uninstall uninstalls the library from /usr/local/lib and - /usr/local/include + make local compiles the COLAMD library; + "make install" will install only in + SuiteSparse/lib and SuiteSparse/include + make install installs the library + make uninstall uninstalls the library To use colamd and symamd within an application written in C, all you need are colamd.c, and colamd.h, which are the C-callable @@ -86,8 +89,9 @@ COLAMD files: Demo simple demo Doc additional documentation (see colamd.c for more) Include include file - Lib compiled C-callable library - Makefile primary Unix Makefile + Config source for colamd.h + Makefile optional Makefile + CMakeLists.txt for using cmake to build COLAMD MATLAB MATLAB functions README.txt this file Source C source code @@ -97,7 +101,6 @@ COLAMD files: colamd_example.out output of colamd_example.c colamd_l_example.c simple example, long integers colamd_l_example.out output of colamd_l_example.c - Makefile Makefile for C demos ./Doc: ChangeLog change log @@ -106,9 +109,6 @@ COLAMD files: ./Include: colamd.h include file - ./Lib: - Makefile Makefile for C-callable library - ./MATLAB: colamd2.m MATLAB interface for colamd2 colamd_demo.m simple demo @@ -126,3 +126,8 @@ COLAMD files: ./Source: colamd.c primary source code + colamd_l.c primary source code for int64_t version + + ./build: where COLAMD is built + .gitignore + diff --git a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Source/colamd.c b/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Source/colamd.c index 8d56c389b..af5b27f7a 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Source/colamd.c +++ b/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Source/colamd.c @@ -1,6 +1,12 @@ -/* ========================================================================== */ -/* === colamd/symamd - a sparse matrix column ordering algorithm ============ */ -/* ========================================================================== */ +//------------------------------------------------------------------------------ +// COLAMD/Source/colamd.c: column approximate minimum degree ordering +//------------------------------------------------------------------------------ + +// COLAMD, Copyright (c) 1998-2022, Timothy A. Davis and Stefan Larimore, +// All Rights Reserved. +// SPDX-License-Identifier: BSD-3-clause + +//------------------------------------------------------------------------------ /* COLAMD / SYMAMD @@ -46,7 +52,7 @@ Copyright and License: - Copyright (c) 1998-2007, Timothy A. Davis, All Rights Reserved. + Copyright (c) 1998-2022, Timothy A. Davis, All Rights Reserved. COLAMD is also available under alternate licenses, contact T. Davis for details. @@ -76,10 +82,9 @@ /* === Description of user-callable routines ================================ */ /* ========================================================================== */ -/* COLAMD includes both int and SuiteSparse_long versions of all its routines. - The description below is for the int version. For SuiteSparse_long, all - int arguments become SuiteSparse_long. SuiteSparse_long is normally - defined as long, except for WIN64. +/* COLAMD includes both int32_t and int64_t versions of all its routines. + The description below is for the int32_t version. For int64_t, all + int32_t arguments become int64_t. ---------------------------------------------------------------------------- colamd_recommended: @@ -88,9 +93,9 @@ C syntax: #include "colamd.h" - size_t colamd_recommended (int nnz, int n_row, int n_col) ; - size_t colamd_l_recommended (SuiteSparse_long nnz, - SuiteSparse_long n_row, SuiteSparse_long n_col) ; + size_t colamd_recommended (int32_t nnz, int32_t n_row, int32_t n_col) ; + size_t colamd_l_recommended (int64_t nnz, + int64_t n_row, int64_t n_col) ; Purpose: @@ -99,19 +104,16 @@ is optional. Not needed for symamd, which dynamically allocates its own memory. - Note that in v2.4 and earlier, these routines returned int or long. - They now return a value of type size_t. - Arguments (all input arguments): - int nnz ; Number of nonzeros in the matrix A. This must + int32_t nnz ; Number of nonzeros in the matrix A. This must be the same value as p [n_col] in the call to colamd - otherwise you will get a wrong value of the recommended memory to use. - int n_row ; Number of rows in the matrix A. + int32_t n_row ; Number of rows in the matrix A. - int n_col ; Number of columns in the matrix A. + int32_t n_col ; Number of columns in the matrix A. ---------------------------------------------------------------------------- colamd_set_defaults: @@ -171,12 +173,12 @@ C syntax: #include "colamd.h" - int colamd (int n_row, int n_col, int Alen, int *A, int *p, - double knobs [COLAMD_KNOBS], int stats [COLAMD_STATS]) ; - SuiteSparse_long colamd_l (SuiteSparse_long n_row, - SuiteSparse_long n_col, SuiteSparse_long Alen, - SuiteSparse_long *A, SuiteSparse_long *p, double knobs - [COLAMD_KNOBS], SuiteSparse_long stats [COLAMD_STATS]) ; + int colamd (int32_t n_row, int32_t n_col, int32_t Alen, int32_t *A, int32_t *p, + double knobs [COLAMD_KNOBS], int32_t stats [COLAMD_STATS]) ; + int colamd_l (int64_t n_row, + int64_t n_col, int64_t Alen, + int64_t *A, int64_t *p, double knobs + [COLAMD_KNOBS], int64_t stats [COLAMD_STATS]) ; Purpose: @@ -191,19 +193,19 @@ Arguments: - int n_row ; Input argument. + int32_t n_row ; Input argument. Number of rows in the matrix A. Restriction: n_row >= 0. Colamd returns FALSE if n_row is negative. - int n_col ; Input argument. + int32_t n_col ; Input argument. Number of columns in the matrix A. Restriction: n_col >= 0. Colamd returns FALSE if n_col is negative. - int Alen ; Input argument. + int32_t Alen ; Input argument. Restriction (see note): Alen >= 2*nnz + 6*(n_col+1) + 4*(n_row+1) + n_col @@ -219,7 +221,7 @@ for integer overflow, and thus is not recommended. Use the colamd_recommended routine instead. - int A [Alen] ; Input argument, undefined on output. + int32_t A [Alen] ; Input argument, undefined on output. A is an integer array of size Alen. Alen must be at least as large as the bare minimum value given above, but this is very @@ -243,7 +245,7 @@ The contents of A are modified during ordering, and are undefined on output. - int p [n_col+1] ; Both input and output argument. + int32_t p [n_col+1] ; Both input and output argument. p is an integer array of size n_col+1. On input, it holds the "pointers" for the column form of the matrix A. Column c of @@ -267,7 +269,7 @@ See colamd_set_defaults for a description. - int stats [COLAMD_STATS] ; Output argument. + int32_t stats [COLAMD_STATS] ; Output argument. Statistics on the ordering, and error status. See colamd.h for related definitions. @@ -367,9 +369,9 @@ #include "colamd.h" #define ALEN 100 - int A [ALEN] = {0, 1, 4, 2, 4, 0, 1, 2, 3, 1, 3} ; - int p [ ] = {0, 3, 5, 9, 11} ; - int stats [COLAMD_STATS] ; + int32_t A [ALEN] = {0, 1, 4, 2, 4, 0, 1, 2, 3, 1, 3} ; + int32_t p [ ] = {0, 3, 5, 9, 11} ; + int32_t stats [COLAMD_STATS] ; colamd (5, 4, ALEN, A, p, (double *) NULL, stats) ; The permutation is returned in the array p, and A is destroyed. @@ -381,12 +383,12 @@ C syntax: #include "colamd.h" - int symamd (int n, int *A, int *p, int *perm, - double knobs [COLAMD_KNOBS], int stats [COLAMD_STATS], + int symamd (int32_t n, int32_t *A, int32_t *p, int32_t *perm, + double knobs [COLAMD_KNOBS], int32_t stats [COLAMD_STATS], void (*allocate) (size_t, size_t), void (*release) (void *)) ; - SuiteSparse_long symamd_l (SuiteSparse_long n, SuiteSparse_long *A, - SuiteSparse_long *p, SuiteSparse_long *perm, double knobs - [COLAMD_KNOBS], SuiteSparse_long stats [COLAMD_STATS], void + int symamd_l (int64_t n, int64_t *A, + int64_t *p, int64_t *perm, double knobs + [COLAMD_KNOBS], int64_t stats [COLAMD_STATS], void (*allocate) (size_t, size_t), void (*release) (void *)) ; Purpose: @@ -406,13 +408,13 @@ Arguments: - int n ; Input argument. + int32_t n ; Input argument. Number of rows and columns in the symmetrix matrix A. Restriction: n >= 0. Symamd returns FALSE if n is negative. - int A [nnz] ; Input argument. + int32_t A [nnz] ; Input argument. A is an integer array of size nnz, where nnz = p [n]. @@ -428,7 +430,7 @@ The contents of A are not modified. - int p [n+1] ; Input argument. + int32_t p [n+1] ; Input argument. p is an integer array of size n+1. On input, it holds the "pointers" for the column form of the matrix A. Column c of @@ -440,7 +442,7 @@ The contents of p are not modified. - int perm [n+1] ; Output argument. + int32_t perm [n+1] ; Output argument. On output, if symamd returns TRUE, the array perm holds the permutation P, where perm [0] is the first index in the new @@ -455,7 +457,7 @@ See colamd_set_defaults for a description. - int stats [COLAMD_STATS] ; Output argument. + int32_t stats [COLAMD_STATS] ; Output argument. Statistics on the ordering, and error status. See colamd.h for related definitions. @@ -553,8 +555,8 @@ C syntax: #include "colamd.h" - colamd_report (int stats [COLAMD_STATS]) ; - colamd_l_report (SuiteSparse_long stats [COLAMD_STATS]) ; + colamd_report (int32_t stats [COLAMD_STATS]) ; + colamd_l_report (int64_t stats [COLAMD_STATS]) ; Purpose: @@ -564,7 +566,7 @@ Arguments: - int stats [COLAMD_STATS] ; Input only. Statistics from colamd. + int32_t stats [COLAMD_STATS] ; Input only. Statistics from colamd. ---------------------------------------------------------------------------- @@ -574,8 +576,8 @@ C syntax: #include "colamd.h" - symamd_report (int stats [COLAMD_STATS]) ; - symamd_l_report (SuiteSparse_long stats [COLAMD_STATS]) ; + symamd_report (int32_t stats [COLAMD_STATS]) ; + symamd_l_report (int64_t stats [COLAMD_STATS]) ; Purpose: @@ -585,7 +587,7 @@ Arguments: - int stats [COLAMD_STATS] ; Input only. Statistics from symamd. + int32_t stats [COLAMD_STATS] ; Input only. Statistics from symamd. */ @@ -637,31 +639,21 @@ /* ========================================================================== */ #include "colamd.h" -#include -#include - -#ifdef MATLAB_MEX_FILE -#include "mex.h" -#include "matrix.h" -#endif /* MATLAB_MEX_FILE */ - -#if !defined (NPRINT) || !defined (NDEBUG) -#include -#endif #ifndef NULL #define NULL ((void *) 0) #endif /* ========================================================================== */ -/* === int or SuiteSparse_long ============================================== */ +/* === int32_t or int64_t ============================================== */ /* ========================================================================== */ #ifdef DLONG -#define Int SuiteSparse_long -#define ID SuiteSparse_long_id -#define Int_MAX SuiteSparse_long_max +#define Int int64_t +#define UInt uint64_t +#define ID "%" PRId64 +#define Int_MAX INT64_MAX #define COLAMD_recommended colamd_l_recommended #define COLAMD_set_defaults colamd_l_set_defaults @@ -672,9 +664,10 @@ #else -#define Int int +#define Int int32_t +#define UInt uint32_t #define ID "%d" -#define Int_MAX INT_MAX +#define Int_MAX INT32_MAX #define COLAMD_recommended colamd_recommended #define COLAMD_set_defaults colamd_set_defaults @@ -746,9 +739,8 @@ typedef struct Colamd_Row_struct /* === Definitions ========================================================== */ /* ========================================================================== */ -/* Routines are either PUBLIC (user-callable) or PRIVATE (not user-callable) */ -#define PUBLIC -#define PRIVATE static +/* Routines are either user-callable or PRIVATE (not user-callable) */ +#define PRIVATE static #define DENSE_DEGREE(alpha,n) \ ((Int) MAX (16.0, (alpha) * sqrt ((double) (n)))) @@ -1029,7 +1021,7 @@ static size_t t_mult (size_t a, size_t k, int *ok) ((t_mult (t_add (n_row, 1, ok), sizeof (Colamd_Row), ok) / sizeof (Int))) -PUBLIC size_t COLAMD_recommended /* returns recommended value of Alen. */ +size_t COLAMD_recommended /* returns recommended value of Alen. */ ( /* === Parameters ======================================================= */ @@ -1051,7 +1043,6 @@ PUBLIC size_t COLAMD_recommended /* returns recommended value of Alen. */ s = t_add (s, r, &ok) ; s = t_add (s, n_col, &ok) ; /* elbow room */ s = t_add (s, nnz/5, &ok) ; /* elbow room */ - ok = ok && (s < Int_MAX) ; return (ok ? s : 0) ; } @@ -1083,7 +1074,7 @@ PUBLIC size_t COLAMD_recommended /* returns recommended value of Alen. */ */ -PUBLIC void COLAMD_set_defaults +void COLAMD_set_defaults ( /* === Parameters ======================================================= */ @@ -1112,7 +1103,7 @@ PUBLIC void COLAMD_set_defaults /* === symamd =============================================================== */ /* ========================================================================== */ -PUBLIC Int SYMAMD_MAIN /* return TRUE if OK, FALSE otherwise */ +int SYMAMD_MAIN /* return TRUE if OK, FALSE otherwise */ ( /* === Parameters ======================================================= */ @@ -1432,7 +1423,7 @@ PUBLIC Int SYMAMD_MAIN /* return TRUE if OK, FALSE otherwise */ (AQ)'(AQ) = LL' remains sparse. */ -PUBLIC Int COLAMD_MAIN /* returns TRUE if successful, FALSE otherwise*/ +int COLAMD_MAIN /* returns TRUE if successful, FALSE otherwise*/ ( /* === Parameters ======================================================= */ @@ -1550,7 +1541,7 @@ PUBLIC Int COLAMD_MAIN /* returns TRUE if successful, FALSE otherwise*/ need = t_add (need, Col_size, &ok) ; need = t_add (need, Row_size, &ok) ; - if (!ok || need > (size_t) Alen || need > Int_MAX) + if (!ok || need > (size_t) Alen) { /* not enough space in array A to perform the ordering */ stats [COLAMD_STATUS] = COLAMD_ERROR_A_too_small ; @@ -1601,7 +1592,7 @@ PUBLIC Int COLAMD_MAIN /* returns TRUE if successful, FALSE otherwise*/ /* === colamd_report ======================================================== */ /* ========================================================================== */ -PUBLIC void COLAMD_report +void COLAMD_report ( Int stats [COLAMD_STATS] ) @@ -1614,7 +1605,7 @@ PUBLIC void COLAMD_report /* === symamd_report ======================================================== */ /* ========================================================================== */ -PUBLIC void SYMAMD_report +void SYMAMD_report ( Int stats [COLAMD_STATS] ) @@ -2193,7 +2184,7 @@ PRIVATE Int find_ordering /* return the number of garbage collections */ Int col ; /* a column index */ Int max_score ; /* maximum possible score */ Int cur_score ; /* score of current column */ - unsigned Int hash ; /* hash value for supernode detection */ + UInt hash ; /* hash value for supernode detection */ Int head_column ; /* head of hash bucket */ Int first_col ; /* first column in hash bucket */ Int tag_mark ; /* marker value for mark array */ @@ -3155,8 +3146,9 @@ PRIVATE void print_report Int i1, i2, i3 ; - SUITESPARSE_PRINTF (("\n%s version %d.%d, %s: ", method, - COLAMD_MAIN_VERSION, COLAMD_SUB_VERSION, COLAMD_DATE)) ; + SUITESPARSE_PRINTF (("\n%s version %d.%d.%d, %s: ", method, + COLAMD_MAIN_VERSION, COLAMD_SUB_VERSION, COLAMD_SUBSUB_VERSION, + COLAMD_DATE)) ; if (!stats) { diff --git a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Source/colamd_l.c b/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Source/colamd_l.c new file mode 100644 index 000000000..ddc06451d --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Source/colamd_l.c @@ -0,0 +1,13 @@ +//------------------------------------------------------------------------------ +// COLAMD/Source/colamd_l.c: int64_t version of colamd +//------------------------------------------------------------------------------ + +// COLAMD, Copyright (c) 1998-2022, Timothy A. Davis and Stefan Larimore, +// All Rights Reserved. +// SPDX-License-Identifier: BSD-3-clause + +//------------------------------------------------------------------------------ + +#define DLONG +#include "colamd.c" + diff --git a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/build/.gitignore b/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/build/.gitignore new file mode 100644 index 000000000..52e15321b --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/build/.gitignore @@ -0,0 +1,4 @@ +# Ignore all files except this file. +* +*/ +!.gitignore diff --git a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/cmake_modules/FindCOLAMD.cmake b/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/cmake_modules/FindCOLAMD.cmake new file mode 100644 index 000000000..00e8a9ac0 --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/cmake_modules/FindCOLAMD.cmake @@ -0,0 +1,129 @@ +#------------------------------------------------------------------------------- +# 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/CONTRIBUTING.md b/deps/AMICI/ThirdParty/SuiteSparse/CONTRIBUTING.md new file mode 100644 index 000000000..a81e104cf --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/CONTRIBUTING.md @@ -0,0 +1,18 @@ +# Contributing to SuiteSparse + +To add an issue for a bug report (gasp!) or a feature request, +you can use the issue tracker on github.com, at +[`https://github.com/DrTimothyAldenDavis/SuiteSparse/issues`] +(https://github.com/DrTimothyAldenDavis/SuiteSparse/issues). + +To contribute code, you can submit a pull request. To do so, +you must first agree to the Contributor License Agreement +[`CONTRIBUTOR-LICENSE.txt`](CONTRIBUTOR-LICENSE.txt). +Print a copy of the txt file (as a PDF), sign and date it, +and email it to me at DrTimothyAldenDavis@gmail.com. Pull +requests will only be included into SuiteSparse after I receive +your email with the signed PDF. + +Do not submit a pull request to the default branch. +Instead, use the dev or dev2 branches. + diff --git a/deps/AMICI/ThirdParty/SuiteSparse/CONTRIBUTOR-LICENSE.txt b/deps/AMICI/ThirdParty/SuiteSparse/CONTRIBUTOR-LICENSE.txt index cc0fe876c..7cd19eec6 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/CONTRIBUTOR-LICENSE.txt +++ b/deps/AMICI/ThirdParty/SuiteSparse/CONTRIBUTOR-LICENSE.txt @@ -164,3 +164,14 @@ Us Timothy A. Davis, and all SuiteSparse co-authors (varies according to the SuiteSparse package) + +You: + + Your Name (printed): + + + Your Signature: + + + Date: + diff --git a/deps/AMICI/ThirdParty/SuiteSparse/CSparse_to_CXSparse b/deps/AMICI/ThirdParty/SuiteSparse/CSparse_to_CXSparse deleted file mode 100755 index 21be29b21..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/CSparse_to_CXSparse +++ /dev/null @@ -1,342 +0,0 @@ -#! /usr/bin/perl -# Constructs the CXSparse package from CSparse, adding four different sets of -# functions (int/cs_long_t, and double/complex). Backward compatible -# with CSparse. No MATLAB interface is provided for CXSparse, however. -# -# To create CXSparse from CSparse, the ./CXSparse directory should not (yet) -# exist. Use the following commands, where CSparse is the CSparse directory: -# -# ./CSparse_to_CXSparse CSparse CXSparse CXSparse_newfiles.tar.gz -# cd CXSparse/Demo -# make > cs_demo.out -# -# Alternatively, use "make cx" in the SuiteSparse directory. -# -# Created by David Bateman, Feb. 2006, David dot Bateman atsign motorola dot -# com, and released by him to Tim Davis' copyright. Modified by Tim Davis, -# 2006-2012, http://www.suitesparse.com. - -use strict; -use Cwd; -use File::Find; -use File::Basename; -use Text::Wrap; -use FileHandle; -use IPC::Open3; - -my $in_dir = @ARGV[0]; -my $out_dir = @ARGV[1]; -my $tar_file = @ARGV[2]; - -#------------------------------------------------------------------------------- -# copy all files from CSparse to CXSparse -#------------------------------------------------------------------------------- - -system ("cp -pr $in_dir $out_dir") ; - -#------------------------------------------------------------------------------- -# Add the new files from the tar file given by the third argument -#------------------------------------------------------------------------------- - -my $old_pwd = cwd(); -chdir($out_dir); -system ("tar xpBvzf $old_pwd/$tar_file"); -chdir($old_pwd); - -#------------------------------------------------------------------------------- -# Convert Demo/* files -#------------------------------------------------------------------------------- - -# convert demo *.[ch] files into the four different versions (di, dl, ci, cl) -my @demo_files = ('demo1.c', 'demo2.c', 'demo3.c', 'demo.c', 'demo.h') ; - -foreach my $fff ( @demo_files ) -{ - my $infile = sprintf ("%s/Demo/cs_%s", $in_dir, $fff) ; - - # create the plain version - my $outfile = sprintf ("%s/Demo/cs_%s", $out_dir, $fff) ; - printf ("%s to %s\n", $infile, $outfile) ; - if (open (OUT, ">$outfile")) - { - if (open (IN, $infile)) - { - while () - { - # change csi to int - s/\bcsi\b/int/g; - print OUT $_; - } - close (IN); - } - close (OUT); - } - - # create di version - my $outfile = sprintf ("%s/Demo/cs_di_%s", $out_dir, $fff) ; - printf ("%s to %s\n", $infile, $outfile) ; - if (open (OUT, ">$outfile")) - { - if (open (IN, $infile)) - { - while () - { - # change csi to int - s/\bcsi\b/int/g; - # change all "cs*" names to "cs_di*", except #include "cs.h" - s/\bcs/cs_di/g ; - s/cs_di\.h/cs.h/ ; - print OUT $_; - } - close (IN); - } - close (OUT); - } - - # create dl version - my $outfile = sprintf ("%s/Demo/cs_dl_%s", $out_dir, $fff) ; - printf ("%s to %s\n", $infile, $outfile) ; - if (open (OUT, ">$outfile")) - { - if (open (IN, $infile)) - { - while () - { - # change csi to cs_long_t - s/\bcsi\b/cs_long_t/g; - # change all "cs*" names to "cs_dl*", except #include "cs.h" - s/\bcs/cs_dl/g ; - s/cs_dl_long_t/cs_long_t/g; - s/cs_dl\.h/cs.h/ ; - print OUT $_; - } - close (IN); - } - close (OUT); - } - - # create ci version - my $outfile = sprintf ("%s/Demo/cs_ci_%s", $out_dir, $fff) ; - printf ("%s to %s\n", $infile, $outfile) ; - if (open (OUT, ">$outfile")) - { - if (open (IN, $infile)) - { - while () - { - # change csi to int - s/\bcsi\b/int/g; - # change all "cs*" names to "cs_ci*", except #include "cs.h" - s/\bcs/cs_ci/g ; - s/cs_ci_long_t/cs_long_t/g; - s/cs_ci\.h/cs.h/ ; - # fabs becomes cabs - s/fabs/cabs/g; - # change double to cs_complex_t - s/\bdouble\b/cs_complex_t/g; - # (double) typecasts stay double - s/\(cs_complex_t\) /(double) /g; - # tic, toc, tol, and norm are double, not cs_complex_t - s/cs_complex_t norm/double norm/; - s/cs_complex_t tic/double tic/; - s/cs_complex_t toc \(cs_complex_t/double toc (double/; - s/cs_complex_t s = tic/double s = tic/; - s/cs_complex_t tol/double tol/; - # cumsum, S->lnz, S->unz are double - s/cs_complex_t lnz/double lnz/; - s/cs_complex_t unz/double unz/; - s/cs_complex_t cs_cumsum/double cs_cumsum/; - # local variable declarations that stay double - s/, / ;\n double / ; - print OUT $_; - } - close (IN); - } - close (OUT); - } - - # create cl version - my $outfile = sprintf ("%s/Demo/cs_cl_%s", $out_dir, $fff) ; - printf ("%s to %s\n", $infile, $outfile) ; - if (open (OUT, ">$outfile")) - { - if (open (IN, $infile)) - { - while () - { - # change csi to cs_long_t - s/\bcsi\b/cs_long_t/g; - # change all "cs*" names to "cs_cl*", except #include "cs.h" - s/\bcs/cs_cl/g ; - s/cs_cl_long_t/cs_long_t/g; - s/cs_cl\.h/cs.h/ ; - # fabs becomes cabs - s/fabs/cabs/g; - # change double to cs_complex_t - s/\bdouble\b/cs_complex_t/g; - # (double) typecasts stay double - s/\(cs_complex_t\) /(double) /g; - # tic, toc, tol, and norm are double, not cs_complex_t - s/cs_complex_t norm/double norm/; - s/cs_complex_t tic/double tic/; - s/cs_complex_t toc \(cs_complex_t/double toc (double/; - s/cs_complex_t s = tic/double s = tic/; - s/cs_complex_t tol/double tol/; - # cumsum, S->lnz, S->unz are double - s/cs_complex_t lnz/double lnz/; - s/cs_complex_t unz/double unz/; - s/cs_complex_t cs_cumsum/double cs_cumsum/; - # local variable declarations that stay double - s/, / ;\n double / ; - print OUT $_; - } - close (IN); - } - close (OUT); - } -} - -#------------------------------------------------------------------------------- -# Convert Source/*.c files (except cs_house.c, cs_print.c, and cs_load.c) -#------------------------------------------------------------------------------- - -# note that cs.h, cs_house.c, cs_updown.c, ... -# are not included in this list -my @src_files = ('Source/cs_add.c', 'Source/cs_amd.c', 'Source/cs_chol.c', - 'Source/cs_cholsol.c', 'Source/cs_counts.c', 'Source/cs_cumsum.c', - 'Source/cs_dfs.c', 'Source/cs_dmperm.c', 'Source/cs_droptol.c', - 'Source/cs_dropzeros.c', 'Source/cs_dupl.c', 'Source/cs_entry.c', - 'Source/cs_etree.c', 'Source/cs_fkeep.c', 'Source/cs_gaxpy.c', - 'Source/cs_happly.c', 'Source/cs_ipvec.c', - 'Source/cs_lsolve.c', 'Source/cs_ltsolve.c', 'Source/cs_lu.c', - 'Source/cs_lusol.c', 'Source/cs_malloc.c', 'Source/cs_maxtrans.c', - 'Source/cs_multiply.c', 'Source/cs_norm.c', 'Source/cs_permute.c', - 'Source/cs_pinv.c', 'Source/cs_post.c', - 'Source/cs_pvec.c', 'Source/cs_qr.c', 'Source/cs_qrsol.c', - 'Source/cs_scatter.c', 'Source/cs_scc.c', 'Source/cs_schol.c', - 'Source/cs_sqr.c', 'Source/cs_symperm.c', 'Source/cs_tdfs.c', - 'Source/cs_transpose.c', 'Source/cs_compress.c', - 'Source/cs_usolve.c', 'Source/cs_util.c', 'Source/cs_utsolve.c', - 'Source/cs_reach.c', 'Source/cs_spsolve.c', 'Source/cs_leaf.c', - 'Source/cs_ereach.c', 'Source/cs_randperm.c' ) ; - -foreach my $file ( @src_files ) -{ - my $infile = sprintf ("%s/%s", $in_dir, $file) ; - my $outfile = sprintf ("%s/%s", $out_dir, $file) ; - my $fbase = basename($file,('.c')); - - if (open(OUT,">$outfile")) - { - if (open(IN,$infile)) - { - # my $qrsol_beta_seen = 0; - while () - { - - # change the name of the package (for cs_print.c) - s/CSparse/CXSparse/g; - - # fabs becomes CS_ABS - s/fabs/CS_ABS/g; - - # change csi to CS_INT - s/\bcsi\b/CS_INT/g; - - # change double to CS_ENTRY - s/\bdouble\b/CS_ENTRY/g; - - # (double) and (double *) typecasts stay double, - # tol and vnz for cs_vcount stays double - s/\(CS_ENTRY\) /(double) /g; - s/\(CS_ENTRY \*\) /(double \*) /; - s/CS_ENTRY tol/double tol/; - s/CS_ENTRY \*vnz/double \*vnz/; - - # local variable declarations that stay double - s/, / ;\n double / ; - - #--------------------------------------------------------------- - # Special cases. Some undo changes made above. - #--------------------------------------------------------------- - - # cs_mex.c - if ($fbase =~ /cs_mex/) - { - s/matrix must be CS_ENTRY/matrix must be double/; - s/A->p =/A->p = (CS_INT *)/; - s/A->i =/A->i = (CS_INT *)/; - s/, A->p/, (mwIndex *) A->p/; - s/, A->i/, (mwIndex *) A->i/; - } - - # fix comments in cs_add_mex.c and cs_permute_mex.c - if ($fbase =~ /cs_add_mex/ || $fbase =~ /cs_permute_mex/) - { - s/via CS_ENTRY transpose/via double transpose/; - } - - # cs_chol - if ($fbase =~ /cs_chol/) - { - s/\(d <= 0\)/(CS_REAL (d) <= 0 || CS_IMAG (d) != 0)\n\t /; - s/lki \* lki/lki * CS_CONJ (lki)/; - s/ = lki/ = CS_CONJ (lki)/; - } - - # cs_norm - if ($fbase =~ /cs_norm/) - { - s/^CS_ENTRY cs_norm/double cs_norm/; - } - - # cs_cumsum - if ($fbase =~ /cs_cumsum/) - { - s/CS_ENTRY/double/; - } - - # cs_transpose - if ($fbase =~ /cs_transpose/) - { - s/Ax \[p\]/(values > 0) ? CS_CONJ (Ax [p]) : Ax [p]/; - } - - # cs_symperm - if ($fbase =~ /cs_symperm/) - { - s/Ax \[p\]/(i2 <= j2) ? Ax [p] : CS_CONJ (Ax [p])/; - } - - # cs_qr - if ($fbase =~ /cs_qr/) - { - s/n, sizeof \(CS_ENTRY\)/n, sizeof (double)/; - } - - # cs_happly - if ($fbase =~ /cs_happly/) - { - s/^(.*tau.*)(Vx\s*\[p\])/$1CS_CONJ ($2)/; - s/CS_ENTRY beta/double beta/; - } - - # cs_ltsolve - if ($fbase =~ /cs_ltsolve/) - { - s/(Lx \[.*?\])(\s+[\*;])/CS_CONJ ($1)$2/; - } - - # cs_utsolve - if ($fbase =~ /cs_utsolve/) - { - s/(Ux \[.*?\])(\s+[\*;])/CS_CONJ ($1)$2/; - } - - print OUT $_; - } - close (IN); - } - close (OUT); - } -} diff --git a/deps/AMICI/ThirdParty/SuiteSparse/ChangeLog b/deps/AMICI/ThirdParty/SuiteSparse/ChangeLog index 854681298..06fe8950b 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/ChangeLog +++ b/deps/AMICI/ThirdParty/SuiteSparse/ChangeLog @@ -1,3 +1,201 @@ +Jan 20, 2023: version 7.0.1 + + * GraphBLAS v7.4.3: debug was left on in GrB_Matrix_removeElement + +Jan 17, 2023: version 7.0.0 + + * SuiteSparse_config: now v7.0.0 + * SuiteSparse_config struct: removed from external visibility to simplify + the Windows build, so that no global data is externally visible. + This requires a major version number increase from v6.x to v7.x for the + SuiteSparse meta-package (which has the same version number as + SuiteSparse_config). Added get/set methods to SuiteSparse_config to + access the contents of the struct. + * NFORTRAN: option added to ignore any Fortran methods, even if a Fortran + compiler is available. + * port of new cmake-based build system to Windows + * UMFPACK 6.1.0: copy/serialize/deserialize methods: added new methods to + copy, serialize, and deserialize the Numeric and Symbolic objects. By + Will Kimmerer, revised by T. Davis. + +Dec 29, 2022: SuiteSparse 6.0.4 + + * NFORTRAN: option added to disable Fortran entirely + * GraphBLAS v7.4.1: global free pool disabled, and GrB_mxm heuristics + revised. + +Dec 23, 2022: SuiteSparse 6.0.3 + + * GraphBLAS v7.4.0: added non-va_arg get/set methods. + * Mongoose v3.0.3: change in build for test coverage + +Dec 9, 2022: SuiteSparse 6.0.2 + + * minor change to build system for nearly all packages: (except CSparse, + ssget, and MATLAB_Tools): allows static linkage of all libraries. + Fortran no longer required. + * AMD 3.0.2: Fortran no longer required (amd.f and amdbar.f skipped); + minor change to build system + * BTF 2.0.2: minor change to build system + * CAMD 3.0,2: minor change to build system + * CCOLAMD 3.0.2: minor change to build system + * CHOLMOD 4.0.2: Fortran no longer required; minor change to build system + * CXSparse 4.0.2: minor change to build system + * GPUQREngine 2.0.2: minor change to build system + * GraphBLAS 7.3.3: -latomic added if needed, using ANSI C11 atomic functions + for gcc (atomic_compare_exachange_weak instead of __atomic_* variants), + chunk factor revised for GrB_mxm (generic saxpy3 method); + minor change to build system + * KLU 2.0.2: Fortran no longer required; minor change to build system + * LDL 3.0.2: minor change to build system + * Mongoose 3.0.2: fixed matrix download in python test scripts (no change + to the compiled library itself, other than the version/date); + minor change to build system + * RBio 3.0.2: minor change to build system + * SPEX 2.0.2: minor change to build system + * SPQR 3.0.2: Fortran no longer required; minor change to build system + * SuiteSparse_GPURuntime 2.0.2: minor change to build system + * SuiteSparse_config 6.0.2: override C-to-Fortran interface handling if + no Fortran compiler found; minor change to build system + * UMFPACK 6.0.2: Fortran no longer required; minor change to build system + * Example: simple package that illustrates how to use SuiteSparse + Find*.cmake modules in cmake. + * not changed from SuiteSparse v6.0.1: ssget, CSparse, MATLAB_Tools + +Nov 12, 2022: SuiteSparse 6.0.1 + + * BLAS: C prototypes for the Fortan BLAS were unintentionally exposed to + the user application. Removed. If you want to use them, see the + instructions in SuiteSparse_config.h. + +Nov 12, 2022: SuiteSparse 6.0.0 + + * major update: using CMake build system for all packages + * CMake Find*: all packages now have a Find*.cmake. See + SuiteSparse_config/cmake_modules. + * integers: int (32-bit) and SuiteSparse_long (nominally 64-bit) replaced + with int32_t and int64_t. The SuiteSparse_long #define has been + deprecated and removed. Replace its use with int64_t in any code that + uses SuiteSparse v6.0.0 or later. This is unlikely to change any + use of any SuiteSparse package, but since it's possible that + SuiteSparse_long was 32-bits on some platforms, the SO_VERSIION of + all packages has been increased by one. + * UMFPACK: new options to support ParU. Single umfpack.h include file. + * CHOLMOD: Single cholmod.h include file. + * SuiteSparse/metis-5.1.0: now embedded into CHOLMOD, in a different + name space. No longer an independent library. + * SPDX License Identifier: added to each file. No change in license. + * BLAS/LAPACK interface: now supports any Fortran BLAS/LAPACK, with + either 32-bit (default) or 64-bit integers, via FindBLAS.cmake. + * SPEX: replaces SLIP_LU + +Aug 25, 2022, SuiteSparse 5.13.0 + + * GraphBLAS v7.2.0: see GraphBLAS/Doc/ChangeLog for details. + * performance: more compact serialization (ZSTD added, now the + default compression method). + * MATLAB interface: faster linear indexing, reshape, bandwidth, + istril, istriu, isbanded, isdiag. C(I,J)=A can now grow the + size of C. + * features: reshape methods, cube root operator, isStoredElement + * bugs: a minor bug; user-defined types were incorrectly limited to + 128 bytes in size in v7.0.3. + +Apr 10, 2022, SuiteSparse 5.12.0 + + * GraphBLAS v7.0.3: see GraphBLAS/Doc/ChangeLog for details. + * performance: GrB_mxm, GrB_assign, and transpose + * bug fix: vector iterator for bitmap + * revised ACM TOMS submission: Doc/toms_parallel_grb2.pdf + * spec bug: GrB_Matrix_diag was implemented incorrectly, + thus requiring a version v7.x + +Mar 14, 2022, SuiteSparse 5.11.0 + + * GraphBLAS v6.2.5: see GraphBLAS/Doc/ChangeLog for changes. + Primary ones highlighted here: + * v2.0 API: v6.x complies with the v2.0 C API of the GraphBLAS Spec. + Note that GrB_wait, GrB_Info are now different than in the v1.3 + C API Specification (and in v5.x of SuiteSparse:GraphBLAS). + * GxB_Iterator: to iterate over rows/cols/entries of a matrix or vector + * GxB_eWiseUnion: like eWiseAdd but with the op always applied + * GxB_Matrix/Vector_sort: to sort the vectors of a matrix + * better performance: for sparse-times-dense and dense-times-sparse, + in particular; also other cases for GrB_mxm, up to 10x faster. + * Apple Silicon: port @GrB to Octave 7 (thanks to Gabor Szarnyas) + * added cpu_features: by Google + * added LZ4/LZ4HC: compression library, http://www.lz4.org (BSD 2), + 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 + 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. + +May 17, 2021, SuiteSparse 5.10.1 + + * CUDA: remove sm_30 from SuiteSparse_config.mk + * GraphBLAS v5.0.5: minor bug fix + * minor changes to Makefiles + +May 16, 2021, SuiteSparse 5.10.0 + + * GraphBLAS v5.0.4: many new features, much faster performance + +Mar 3, 2021, SuiteSparse 5.9.0 + + * GraphBLAS v4.0.3: many new features, much faster performance + +July 14, 2020, SuiteSparse 5.8.1 + + * SLIP_LU v1.0.2: resolved issue #51 + * GraphBLAS v3.3.3: bug fix (GraphBLAS issue #13) + +July 2, 2020, SuiteSparse 5.8.0 + + * SLIP_LU v1.0.1 added: for solving Ax=b exactly. Requires + the GNU GMP and MPRF libraries. + * GraphBLAS v3.3.1: see the GraphBLAS/Doc/Changlog + * replaced UFget with ssget: affects nearly all packages: + UMFPACK, KLU, CHOLMOD, CXSparse/CSparse, etc, + but their version numbers are left unchanged since it affects + the MATLAB tests only, not the compiled libraries. + * ssget v2.2.0: better URL redirects + * updates to SuiteSparse build system + +Apr 8, 2020, SuiteSparse 5.7.2 + + * GraphBLAS v3.2.2: port to Microsoft Windows (with MS Visual Studio) + +Feb 20, 2020, SuiteSparse 5.7.1 + + * SuiteSparse_config: update version number + * Makefile: fixed install issue with README.txt + +Feb 20, 2020, SuiteSparse 5.7.0 + + * GraphBLAS 3.2.0: better performance, new ANY and PAIR operators, + structural mask, GrB_DESC_* from 1.3 C API Specification. + * CHOLMOD 3.0.14: minor update to cholmod_check to print a matrix + * added: CONTRIBUTIING.md, CODE_OF_CONDUCT.md, README.md. + +Oct 21, 2019, SuiteSparse 5.6.0 + + * GraphBLAS 3.1.1: OpenMP parallelism and MATLAB interface + +Oct 20, 2019, SuiteSparse 5.5.0 + + * GraphBLAS 2.3.5: Collected Algorithm of the ACM + * UMFPACK 5.7.9: fix for compiling in MATLAB R2018b; BLAS library + * SPQR, CHOLMOD: fix to *_make.m for compiling in MATLAB; same version + * KLU: fix to Tcov/Makefile; no change to version number + * CXSparse 3.2.0: version was incorrect in CXSparse/Include/cs.h; + the corresponding CSparse v3.2.0 had the correct version information + in its cs.h include file. + * ssget and MATLAB_Tools/SuiteSparseCollection: update to sparse.tamu.edu + * Mongoose 2.0.4: update to sparse.tamu.edu + Dec 28, 2018: SuiteSparse 5.4.0 * GraphBLAS 2.2.2: many upgrades and new features, a few bug fixes diff --git a/deps/AMICI/ThirdParty/SuiteSparse/Contents.m b/deps/AMICI/ThirdParty/SuiteSparse/Contents.m deleted file mode 100644 index af9e7978a..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/Contents.m +++ /dev/null @@ -1,151 +0,0 @@ -% Welcome to SuiteSparse : a Suite of Sparse matrix packages, containing a -% collection of sparse matrix packages authored or co-authored by Tim Davis. -% Only the primary MATLAB functions are listed below. -% -% Example: -% SuiteSparse_install -% compiles and installs all of SuiteSparse, and runs several demos and tests. -% -%------------------------------------------------------------------------------- -% Ordering methods and graph partitioners: -%------------------------------------------------------------------------------- -% -% amd2 - approximate minimum degree ordering. -% colamd2 - column approximate minimum degree ordering. -% symamd2 - symmetrix approximate min degree ordering based on colamd. -% camd - constrained amd. -% ccolamd - constrained colamd. -% csymamd - constrained symamd. -% edgecut - Mongoose graph partitioner -% -%------------------------------------------------------------------------------- -% CHOLMOD: a sparse supernodal Cholesky update/downdate package: -%------------------------------------------------------------------------------- -% -% cholmod2 - computes x=A\b when A is symmetric and positive definite. -% chol2 - same as MATLAB chol(sparse(A)), just faster. -% lchol - computes an LL' factorization. -% ldlchol - computes an LDL' factorization. -% ldlupdate - updates an LDL' factorization. -% resymbol - recomputes symbolic LL or LDL' factorization. -% ldlsolve - solves Ax=b using an LDL' factorization. -% ldlsplit - splits LD into L and D. -% metis - interface to METIS node-nested-dissection. -% nesdis - interface to CHOLMOD's nested-dissection (based on METIS). -% septree - prune a separator tree. -% bisect - interface to METIS' node bisector. -% analyze - order and analyze using CHOLMOD. -% etree2 - same as MATLAB "etree", just faster and more reliable. -% sparse2 - same as MATLAB "sparse", just faster. -% symbfact2 - same as MATLAB "symbfact", just faster and more reliable. -% sdmult - same as MATLAB S*F or S'*F (S sparse, F full), just faster. -% ldl_normest - compute error in LDL' factorization. -% lu_normest - compute error in LU factorization. -% mread - read a sparse matrix in Matrix Market format -% mwrite - write a sparse matrix in Matrix Market format -% spsym - determine the symmetry of a sparse matrix -% -%------------------------------------------------------------------------------- -% CSPARSE / CXSPARSE: a Concise Sparse matrix package: -%------------------------------------------------------------------------------- -% -% Matrices used in CSparse must in general be either sparse and real, or -% dense vectors. Ordering methods can accept any sparse matrix. CXSparse -% supports complex matrices and 64-bit MATLAB; it is installed by default. -% -% cs_add - sparse matrix addition. -% cs_amd - approximate minimum degree ordering. -% cs_chol - sparse Cholesky factorization. -% cs_cholsol - solve A*x=b using a sparse Cholesky factorization. -% cs_counts - column counts for sparse Cholesky factor L. -% cs_dmperm - maximum matching or Dulmage-Mendelsohn permutation. -% cs_dmsol - x=A\b using the coarse Dulmage-Mendelsohn decomposition. -% cs_dmspy - plot the Dulmage-Mendelsohn decomposition of a matrix. -% cs_droptol - remove small entries from a sparse matrix. -% cs_esep - find an edge separator of a symmetric matrix A -% cs_etree - elimination tree of A or A'*A. -% cs_gaxpy - sparse matrix times vector. -% cs_lsolve - solve a sparse lower triangular system L*x=b. -% cs_ltsolve - solve a sparse upper triangular system L'*x=b. -% cs_lu - sparse LU factorization, with fill-reducing ordering. -% cs_lusol - solve Ax=b using LU factorization. -% cs_make - compiles CSparse for use in MATLAB. -% cs_multiply - sparse matrix multiply. -% cs_nd - generalized nested dissection ordering. -% cs_nsep - find a node separator of a symmetric matrix A. -% cs_permute - permute a sparse matrix. -% cs_print - print the contents of a sparse matrix. -% cs_qr - sparse QR factorization. -% cs_qleft - apply Householder vectors on the left. -% cs_qright - apply Householder vectors on the right. -% cs_qrsol - solve a sparse least-squares problem. -% cs_randperm - random permutation. -% cs_sep - convert an edge separator into a node separator. -% cs_scc - strongly-connected components of a square sparse matrix. -% cs_scc2 - cs_scc, or connected components of a bipartite graph. -% cs_sparse - convert a triplet form into a sparse matrix. -% cs_sqr - symbolic sparse QR factorization. -% cs_symperm - symmetric permutation of a symmetric matrix. -% cs_transpose - transpose a sparse matrix. -% cs_updown - rank-1 update/downdate of a sparse Cholesky factorization. -% cs_usolve - solve a sparse upper triangular system U*x=b. -% cs_utsolve - solve a sparse lower triangular system U'*x=b. -% cspy - plot a sparse matrix in color. -% ccspy - plot the connected components of a matrix. -% -%------------------------------------------------------------------------------- -% LDL: Sparse LDL factorization: -%------------------------------------------------------------------------------- -% -% ldlsparse - LDL' factorization of a real, sparse, symmetric matrix. -% ldlrow - an m-file description of the algorithm used by LDL. -% -%------------------------------------------------------------------------------- -% UMFPACK: the Unsymmetric MultiFrontal Package: -%------------------------------------------------------------------------------- -% -% umfpack2 - computes x=A\b, x=A/b, or lu (A) for a sparse matrix A -% umfpack_details - details on all the options for using umfpack in MATLAB -% umfpack_report - prints optional control settings and statistics -% umfpack_btf - factorize A using a block triangular form -% umfpack_solve - x = A\b or x = b/A -% lu_normest - estimates norm (L*U-A,1) without forming L*U-A -% (duplicate of CHOLMOD/lu_normest, for completeness) -% luflop - given L and U, computes # of flops required -% -%------------------------------------------------------------------------------- -% SuiteSparseQR: multifrontal rank-revealing sparse QR -%------------------------------------------------------------------------------- -% -% spqr - sparse QR -% spqr_solve - x=A\b using SuiteSparseQR -% spqr_qmult - y=Q*x, Q'*x, x*Q, or x*Q' using Q in Householder form -% -%------------------------------------------------------------------------------- -% Other packages: -%------------------------------------------------------------------------------- -% -% MATLAB_Tools various MATLAB tools, most in M, some as C mexFunctions: -% -% ssmult sparse matrix times sparse matrix -% meshnd nested dissection of regular 2D and 3D meshes -% linfactor solve Ax=b using LU or CHOL -% dimacs10 MATLAB interface for the DIMACS10 collection -% factorize object-oriented system solver -% sparseinv sparse inverse subset -% spqr_rank toolbox for sparse rank-deficient matrices -% -% SuiteSparseCollection for managing the SuiteSparse Matrix Collection -% RBio for reading/writing Rutherford/Boeing sparse matrices -% ssget MATLAB interface to the SuiteSparse Matrix Collection -% GraphBLAS graph algorithms via sparse linear algebra (graphblas.org), -% does not yet have a MATLAB interface -% -%------------------------------------------------------------------------------- -% -% For help on compiling SuiteSparse or the demos, testing functions, etc., -% please see the help for each individual package. -% -% Copyright 2018, Timothy A. Davis, http://www.suitesparse.com. - -help SuiteSparse diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/CMakeLists.txt b/deps/AMICI/ThirdParty/SuiteSparse/KLU/CMakeLists.txt new file mode 100644 index 000000000..f2e88576e --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/KLU/CMakeLists.txt @@ -0,0 +1,309 @@ +#------------------------------------------------------------------------------- +# SuiteSparse/KLU/CMakeLists.txt: cmake for KLU +#------------------------------------------------------------------------------- + +# KLU, Copyright (c) 2004-2022, University of Florida. All Rights Reserved. +# Authors: Timothy A. Davis and Ekanathan Palamadai. +# SPDX-License-Identifier: LGPL-2.1+ + +#------------------------------------------------------------------------------- +# get the version +#------------------------------------------------------------------------------- + +cmake_minimum_required ( VERSION 3.19 ) + +set ( KLU_DATE "Jan 17, 2023" ) +set ( KLU_VERSION_MAJOR 2 ) +set ( KLU_VERSION_MINOR 0 ) +set ( KLU_VERSION_SUB 3 ) + +message ( STATUS "Building KLU version: v" + ${KLU_VERSION_MAJOR}. + ${KLU_VERSION_MINOR}. + ${KLU_VERSION_SUB} " (" ${KLU_DATE} ")" ) + +#------------------------------------------------------------------------------- +# 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 ) + +include ( SuiteSparsePolicy ) + +#------------------------------------------------------------------------------- +# define the project +#------------------------------------------------------------------------------- + +project ( klu + VERSION "${KLU_VERSION_MAJOR}.${KLU_VERSION_MINOR}.${KLU_VERSION_SUB}" + LANGUAGES C ) + +#------------------------------------------------------------------------------- +# find library dependencies +#------------------------------------------------------------------------------- + +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 ) + endif ( ) +endif ( ) + +if ( NCHOLMOD ) + # tell KLU that CHOLMOD is not available + message ( STATUS "CHOLMOD not found or not requested" ) + add_compile_definitions ( NCHOLMOD ) +else ( ) + message ( STATUS "Using CHOLMOD for addtional pre-ordering options" ) +endif ( ) + +#------------------------------------------------------------------------------- +# configure files +#------------------------------------------------------------------------------- + +configure_file ( "Config/klu.h.in" + "${PROJECT_SOURCE_DIR}/Include/klu.h" + NEWLINE_STYLE LF ) +configure_file ( "Config/klu_version.tex.in" + "${PROJECT_SOURCE_DIR}/Doc/klu_version.tex" + NEWLINE_STYLE LF ) + +#------------------------------------------------------------------------------- +# include directories +#------------------------------------------------------------------------------- + +include_directories ( Source Include User ${SUITESPARSE_CONFIG_INCLUDE_DIR} + ${AMD_INCLUDE_DIR} ${COLAMD_INCLUDE_DIR} ${BTF_INCLUDE_DIR} ) + +#------------------------------------------------------------------------------- +# dynamic klu library properties +#------------------------------------------------------------------------------- + +file ( GLOB KLU_SOURCES "Source/*.c" ) + +add_library ( klu SHARED ${KLU_SOURCES} ) + +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 ) + +#------------------------------------------------------------------------------- +# static klu library properties +#------------------------------------------------------------------------------- + +if ( NOT NSTATIC ) + 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 + OUTPUT_NAME klu + SOVERSION ${KLU_VERSION_MAJOR} ) + + if ( MSVC ) + set_target_properties ( klu_static PROPERTIES + OUTPUT_NAME klu_static ) + endif ( ) +endif ( ) + +#------------------------------------------------------------------------------- +# klu_cholmod library properties +#------------------------------------------------------------------------------- + +if ( NOT NCHOLMOD ) + + file ( GLOB KLU_CHOLMOD_SOURCES "User/*.c" ) + + add_library ( klu_cholmod SHARED ${KLU_CHOLMOD_SOURCES} ) + include_directories ( ${CHOLMOD_INCLUDE_DIR} ) + + 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" ) + + if ( NOT NSTATIC ) + add_library ( klu_cholmod_static STATIC ${KLU_CHOLMOD_SOURCES} ) + + set_target_properties ( klu_cholmod_static PROPERTIES + VERSION ${KLU_VERSION_MAJOR}.${KLU_VERSION_MINOR}.${KLU_VERSION_SUB} + C_STANDARD_REQUIRED 11 + OUTPUT_NAME klu_cholmod + SOVERSION ${KLU_VERSION_MAJOR} ) + + if ( MSVC ) + set_target_properties ( klu_cholmod_static PROPERTIES + OUTPUT_NAME klu_cholmod_static ) + endif ( ) + endif ( ) + +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} ) +endif ( ) + +# libm: +if ( NOT WIN32 ) + target_link_libraries ( klu PUBLIC m ) + if ( NOT NSTATIC ) + target_link_libraries ( klu_static PUBLIC m ) + endif ( ) +endif ( ) + +# amd: +target_link_libraries ( klu PUBLIC ${AMD_LIBRARIES} ) +if ( NOT NSTATIC ) + target_link_libraries ( klu_static PUBLIC ${AMD_STATIC} ) +endif ( ) + +# colamd: +target_link_libraries ( klu PUBLIC ${COLAMD_LIBRARIES} ) +if ( NOT NSTATIC ) + target_link_libraries ( klu_static PUBLIC ${COLAMD_STATIC} ) +endif ( ) + +# btf: +target_link_libraries ( klu PUBLIC ${BTF_LIBRARIES} ) +if ( NOT NSTATIC ) + target_link_libraries ( klu_static PUBLIC ${BTF_STATIC} ) +endif ( ) + +if ( NOT NCHOLMOD ) + + # 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} ) + 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} ) + 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 ( ) + +if ( NOT NCHOLMOD ) + install ( TARGETS klu_cholmod + 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 ( ) +endif ( ) + +#------------------------------------------------------------------------------- +# Demo library and programs +#------------------------------------------------------------------------------- + +option ( DEMO "ON: Build the demo programs. OFF (default): do not build the demo programs." off ) +if ( DEMO ) + + #--------------------------------------------------------------------------- + # demo library + #--------------------------------------------------------------------------- + + message ( STATUS "Also compiling the demos in KLU/Demo" ) + + #--------------------------------------------------------------------------- + # Demo programs + #--------------------------------------------------------------------------- + + add_executable ( klu_simple "Demo/klu_simple.c" ) + if ( NOT NCHOLMOD ) + 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 ) + endif ( ) + +else ( ) + + message ( STATUS "Skipping the demos in KLU/Demo" ) + +endif ( ) + +#------------------------------------------------------------------------------- +# report status +#------------------------------------------------------------------------------- + +include ( SuiteSparseReport ) + diff --git a/deps/AMICI/ThirdParty/SuiteSparse/include/klu.h b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Config/klu.h.in similarity index 71% rename from deps/AMICI/ThirdParty/SuiteSparse/include/klu.h rename to deps/AMICI/ThirdParty/SuiteSparse/KLU/Config/klu.h.in index 07611f72d..769a82aa6 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/include/klu.h +++ b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Config/klu.h.in @@ -1,6 +1,12 @@ -/* ========================================================================== */ -/* === klu include file ===================================================== */ -/* ========================================================================== */ +//------------------------------------------------------------------------------ +// KLU/Source/klu.h: include file for KLU +//------------------------------------------------------------------------------ + +// KLU, Copyright (c) 2004-2022, University of Florida. All Rights Reserved. +// Authors: Timothy A. Davis and Ekanathan Palamadai. +// SPDX-License-Identifier: LGPL-2.1+ + +//------------------------------------------------------------------------------ /* Include file for user programs that call klu_* routines */ @@ -34,7 +40,7 @@ typedef struct double *Lnz ; /* size n, but only Lnz [0..nblocks-1] is used */ /* computed for all orderings: */ - int + int32_t n, /* input matrix A is n-by-n */ nz, /* # entries in input matrix */ *P, /* size n */ @@ -43,11 +49,11 @@ typedef struct nzoff, /* nz in off-diagonal blocks */ nblocks, /* number of blocks */ maxblock, /* size of largest block */ - ordering, /* ordering used (AMD, COLAMD, or GIVEN) */ + ordering, /* ordering used (0:AMD, 1:COLAMD, 2:given, ... */ do_btf ; /* whether or not BTF preordering was requested */ /* only computed if BTF preordering requested */ - int structural_rank ; /* 0 to n-1 if the matrix is structurally rank + int32_t structural_rank ; /* 0 to n-1 if the matrix is structurally rank * deficient. -1 if not computed. n if the matrix has * full structural rank */ @@ -57,7 +63,7 @@ typedef struct /* 64-bit version (otherwise same as above) */ { double symmetry, est_flops, lnz, unz ; double *Lnz ; - SuiteSparse_long n, nz, *P, *Q, *R, nzoff, nblocks, maxblock, ordering, + int64_t n, nz, *P, *Q, *R, nzoff, nblocks, maxblock, ordering, do_btf, structural_rank ; } klu_l_symbolic ; @@ -71,20 +77,20 @@ typedef struct /* LU factors of each block, the pivot row permutation, and the * entries in the off-diagonal blocks */ - int n ; /* A is n-by-n */ - int nblocks ; /* number of diagonal blocks */ - int lnz ; /* actual nz in L, including diagonal */ - int unz ; /* actual nz in U, including diagonal */ - int max_lnz_block ; /* max actual nz in L in any one block, incl. diag */ - int max_unz_block ; /* max actual nz in U in any one block, incl. diag */ - int *Pnum ; /* size n. final pivot permutation */ - int *Pinv ; /* size n. inverse of final pivot permutation */ + int32_t n ; /* A is n-by-n */ + int32_t nblocks ; /* number of diagonal blocks */ + int32_t lnz ; /* actual nz in L, including diagonal */ + int32_t unz ; /* actual nz in U, including diagonal */ + int32_t max_lnz_block ; /* max actual nz in L in any one block, incl. diag */ + int32_t max_unz_block ; /* max actual nz in U in any one block, incl. diag */ + int32_t *Pnum ; /* size n. final pivot permutation */ + int32_t *Pinv ; /* size n. inverse of final pivot permutation */ /* LU factors of each block */ - int *Lip ; /* size n. pointers into LUbx[block] for L */ - int *Uip ; /* size n. pointers into LUbx[block] for U */ - int *Llen ; /* size n. Llen [k] = # of entries in kth column of L */ - int *Ulen ; /* size n. Ulen [k] = # of entries in kth column of U */ + int32_t *Lip ; /* size n. pointers into LUbx[block] for L */ + int32_t *Uip ; /* size n. pointers into LUbx[block] for U */ + int32_t *Llen ; /* size n. Llen [k] = # of entries in kth column of L */ + int32_t *Ulen ; /* size n. Ulen [k] = # of entries in kth column of U */ void **LUbx ; /* L and U indices and entries (excl. diagonal of U) */ size_t *LUsize ; /* size of each LUbx [block], in sizeof (Unit) */ void *Udiag ; /* diagonal of U */ @@ -96,19 +102,19 @@ typedef struct size_t worksize ; /* size (in bytes) of Work */ void *Work ; /* workspace */ void *Xwork ; /* alias into Numeric->Work */ - int *Iwork ; /* alias into Numeric->Work */ + int32_t *Iwork ; /* alias into Numeric->Work */ /* off-diagonal entries in a conventional compressed-column sparse matrix */ - int *Offp ; /* size n+1, column pointers */ - int *Offi ; /* size nzoff, row indices */ + int32_t *Offp ; /* size n+1, column pointers */ + int32_t *Offi ; /* size nzoff, row indices */ void *Offx ; /* size nzoff, numerical values */ - int nzoff ; + int32_t nzoff ; } klu_numeric ; typedef struct /* 64-bit version (otherwise same as above) */ { - SuiteSparse_long n, nblocks, lnz, unz, max_lnz_block, max_unz_block, *Pnum, + int64_t n, nblocks, lnz, unz, max_lnz_block, max_unz_block, *Pnum, *Pinv, *Lip, *Uip, *Llen, *Ulen ; void **LUbx ; size_t *LUsize ; @@ -116,10 +122,10 @@ typedef struct /* 64-bit version (otherwise same as above) */ double *Rs ; size_t worksize ; void *Work, *Xwork ; - SuiteSparse_long *Iwork ; - SuiteSparse_long *Offp, *Offi ; + int64_t *Iwork ; + int64_t *Offp, *Offi ; void *Offx ; - SuiteSparse_long nzoff ; + int64_t nzoff ; } klu_l_numeric ; @@ -154,7 +160,8 @@ typedef struct klu_common_struct * 0: none, 1: sum, 2: max */ /* pointer to user ordering function */ - int (*user_order) (int, int *, int *, int *, struct klu_common_struct *) ; + int32_t (*user_order) (int32_t, int32_t *, int32_t *, int32_t *, + struct klu_common_struct *) ; /* pointer to user data, passed unchanged as the last parameter to the * user ordering function (optional, the user function need not use this @@ -177,23 +184,23 @@ typedef struct klu_common_struct int status ; /* KLU_OK if OK, < 0 if error */ int nrealloc ; /* # of reallocations of L and U */ - int structural_rank ; /* 0 to n-1 if the matrix is structurally rank + int32_t structural_rank ; /* 0 to n-1 if the matrix is structurally rank * deficient (as determined by maxtrans). -1 if not computed. n if the * matrix has full structural rank. This is computed by klu_analyze * if a BTF preordering is requested. */ - int numerical_rank ; /* First k for which a zero U(k,k) was found, + int32_t numerical_rank ; /* First k for which a zero U(k,k) was found, * if the matrix was singular (in the range 0 to n-1). n if the matrix * has full rank. This is not a true rank-estimation. It just reports * where the first zero pivot was found. -1 if not computed. * Computed by klu_factor and klu_refactor. */ - int singular_col ; /* n if the matrix is not singular. If in the + int32_t singular_col ; /* n if the matrix is not singular. If in the * range 0 to n-1, this is the column index of the original matrix A that * corresponds to the column of U that contains a zero diagonal entry. * -1 if not computed. Computed by klu_factor and klu_refactor. */ - int noffdiag ; /* # of off-diagonal pivots, -1 if not computed */ + int32_t noffdiag ; /* # of off-diagonal pivots, -1 if not computed */ double flops ; /* actual factorization flop count, from klu_flops */ double rcond ; /* crude reciprocal condition est., from klu_rcond */ @@ -210,14 +217,12 @@ typedef struct klu_l_common_struct /* 64-bit version (otherwise same as above)*/ { double tol, memgrow, initmem_amd, initmem, maxwork ; - SuiteSparse_long btf, ordering, scale ; - SuiteSparse_long (*user_order) (SuiteSparse_long, SuiteSparse_long *, - SuiteSparse_long *, SuiteSparse_long *, + int btf, ordering, scale ; + int64_t (*user_order) (int64_t, int64_t *, int64_t *, int64_t *, struct klu_l_common_struct *) ; void *user_data ; - SuiteSparse_long halt_if_singular ; - SuiteSparse_long status, nrealloc, structural_rank, numerical_rank, - singular_col, noffdiag ; + int halt_if_singular, status, nrealloc ; + int64_t structural_rank, numerical_rank, singular_col, noffdiag ; double flops, rcond, condest, rgrowth, work ; size_t memusage, mempeak ; @@ -232,7 +237,7 @@ int klu_defaults klu_common *Common ) ; -SuiteSparse_long klu_l_defaults (klu_l_common *Common) ; +int klu_l_defaults (klu_l_common *Common) ; /* -------------------------------------------------------------------------- */ /* klu_analyze: orders and analyzes a matrix */ @@ -244,14 +249,14 @@ SuiteSparse_long klu_l_defaults (klu_l_common *Common) ; klu_symbolic *klu_analyze ( /* inputs, not modified */ - int n, /* A is n-by-n */ - int Ap [ ], /* size n+1, column pointers */ - int Ai [ ], /* size nz, row indices */ + int32_t n, /* A is n-by-n */ + int32_t Ap [ ], /* size n+1, column pointers */ + int32_t Ai [ ], /* size nz, row indices */ klu_common *Common ) ; -klu_l_symbolic *klu_l_analyze (SuiteSparse_long, SuiteSparse_long *, - SuiteSparse_long *, klu_l_common *Common) ; +klu_l_symbolic *klu_l_analyze (int64_t, int64_t *, int64_t *, + klu_l_common *Common) ; /* -------------------------------------------------------------------------- */ @@ -265,17 +270,16 @@ klu_l_symbolic *klu_l_analyze (SuiteSparse_long, SuiteSparse_long *, klu_symbolic *klu_analyze_given ( /* inputs, not modified */ - int n, /* A is n-by-n */ - int Ap [ ], /* size n+1, column pointers */ - int Ai [ ], /* size nz, row indices */ - int P [ ], /* size n, user's row permutation (may be NULL) */ - int Q [ ], /* size n, user's column permutation (may be NULL) */ + int32_t n, /* A is n-by-n */ + int32_t Ap [ ], /* size n+1, column pointers */ + int32_t Ai [ ], /* size nz, row indices */ + int32_t P [ ], /* size n, user's row permutation (may be NULL) */ + int32_t Q [ ], /* size n, user's column permutation (may be NULL) */ klu_common *Common ) ; -klu_l_symbolic *klu_l_analyze_given (SuiteSparse_long, SuiteSparse_long *, - SuiteSparse_long *, SuiteSparse_long *, SuiteSparse_long *, - klu_l_common *) ; +klu_l_symbolic *klu_l_analyze_given (int64_t, int64_t *, int64_t *, int64_t *, + int64_t *, klu_l_common *) ; /* -------------------------------------------------------------------------- */ @@ -285,8 +289,8 @@ klu_l_symbolic *klu_l_analyze_given (SuiteSparse_long, SuiteSparse_long *, klu_numeric *klu_factor /* returns KLU_OK if OK, < 0 if error */ ( /* inputs, not modified */ - int Ap [ ], /* size n+1, column pointers */ - int Ai [ ], /* size nz, row indices */ + int32_t Ap [ ], /* size n+1, column pointers */ + int32_t Ai [ ], /* size nz, row indices */ double Ax [ ], /* size nz, numerical values */ klu_symbolic *Symbolic, klu_common *Common @@ -295,19 +299,19 @@ klu_numeric *klu_factor /* returns KLU_OK if OK, < 0 if error */ klu_numeric *klu_z_factor /* returns KLU_OK if OK, < 0 if error */ ( /* inputs, not modified */ - int Ap [ ], /* size n+1, column pointers */ - int Ai [ ], /* size nz, row indices */ + int32_t Ap [ ], /* size n+1, column pointers */ + int32_t Ai [ ], /* size nz, row indices */ double Ax [ ], /* size 2*nz, numerical values (real,imag pairs) */ klu_symbolic *Symbolic, klu_common *Common ) ; -/* long / real version */ -klu_l_numeric *klu_l_factor (SuiteSparse_long *, SuiteSparse_long *, double *, +/* int64_t / real version */ +klu_l_numeric *klu_l_factor (int64_t *, int64_t *, double *, klu_l_symbolic *, klu_l_common *) ; -/* long / complex version */ -klu_l_numeric *klu_zl_factor (SuiteSparse_long *, SuiteSparse_long *, double *, +/* int64_t / complex version */ +klu_l_numeric *klu_zl_factor (int64_t *, int64_t *, double *, klu_l_symbolic *, klu_l_common *) ; @@ -320,8 +324,8 @@ int klu_solve /* inputs, not modified */ klu_symbolic *Symbolic, klu_numeric *Numeric, - int ldim, /* leading dimension of B */ - int nrhs, /* number of right-hand-sides */ + int32_t ldim, /* leading dimension of B */ + int32_t nrhs, /* number of right-hand-sides */ /* right-hand-side on input, overwritten with solution to Ax=b on output */ double B [ ], /* size ldim*nrhs */ @@ -333,19 +337,19 @@ int klu_z_solve /* inputs, not modified */ klu_symbolic *Symbolic, klu_numeric *Numeric, - int ldim, /* leading dimension of B */ - int nrhs, /* number of right-hand-sides */ + int32_t ldim, /* leading dimension of B */ + int32_t nrhs, /* number of right-hand-sides */ /* right-hand-side on input, overwritten with solution to Ax=b on output */ double B [ ], /* size 2*ldim*nrhs */ klu_common *Common ) ; -SuiteSparse_long klu_l_solve (klu_l_symbolic *, klu_l_numeric *, - SuiteSparse_long, SuiteSparse_long, double *, klu_l_common *) ; +int klu_l_solve (klu_l_symbolic *, klu_l_numeric *, + int64_t, int64_t, double *, klu_l_common *) ; -SuiteSparse_long klu_zl_solve (klu_l_symbolic *, klu_l_numeric *, - SuiteSparse_long, SuiteSparse_long, double *, klu_l_common *) ; +int klu_zl_solve (klu_l_symbolic *, klu_l_numeric *, + int64_t, int64_t, double *, klu_l_common *) ; /* -------------------------------------------------------------------------- */ @@ -357,8 +361,8 @@ int klu_tsolve /* inputs, not modified */ klu_symbolic *Symbolic, klu_numeric *Numeric, - int ldim, /* leading dimension of B */ - int nrhs, /* number of right-hand-sides */ + int32_t ldim, /* leading dimension of B */ + int32_t nrhs, /* number of right-hand-sides */ /* right-hand-side on input, overwritten with solution to Ax=b on output */ double B [ ], /* size ldim*nrhs */ @@ -370,8 +374,8 @@ int klu_z_tsolve /* inputs, not modified */ klu_symbolic *Symbolic, klu_numeric *Numeric, - int ldim, /* leading dimension of B */ - int nrhs, /* number of right-hand-sides */ + int32_t ldim, /* leading dimension of B */ + int32_t nrhs, /* number of right-hand-sides */ /* right-hand-side on input, overwritten with solution to Ax=b on output */ double B [ ], /* size 2*ldim*nrhs */ @@ -380,12 +384,11 @@ int klu_z_tsolve ) ; -SuiteSparse_long klu_l_tsolve (klu_l_symbolic *, klu_l_numeric *, - SuiteSparse_long, SuiteSparse_long, double *, klu_l_common *) ; +int klu_l_tsolve (klu_l_symbolic *, klu_l_numeric *, + int64_t, int64_t, double *, klu_l_common *) ; -SuiteSparse_long klu_zl_tsolve (klu_l_symbolic *, klu_l_numeric *, - SuiteSparse_long, SuiteSparse_long, double *, SuiteSparse_long, - klu_l_common * ) ; +int klu_zl_tsolve (klu_l_symbolic *, klu_l_numeric *, + int64_t, int64_t, double *, int, klu_l_common * ) ; /* -------------------------------------------------------------------------- */ @@ -395,8 +398,8 @@ SuiteSparse_long klu_zl_tsolve (klu_l_symbolic *, klu_l_numeric *, int klu_refactor /* return TRUE if successful, FALSE otherwise */ ( /* inputs, not modified */ - int Ap [ ], /* size n+1, column pointers */ - int Ai [ ], /* size nz, row indices */ + int32_t Ap [ ], /* size n+1, column pointers */ + int32_t Ai [ ], /* size nz, row indices */ double Ax [ ], /* size nz, numerical values */ klu_symbolic *Symbolic, /* input, and numerical values modified on output */ @@ -407,8 +410,8 @@ int klu_refactor /* return TRUE if successful, FALSE otherwise */ int klu_z_refactor /* return TRUE if successful, FALSE otherwise */ ( /* inputs, not modified */ - int Ap [ ], /* size n+1, column pointers */ - int Ai [ ], /* size nz, row indices */ + int32_t Ap [ ], /* size n+1, column pointers */ + int32_t Ai [ ], /* size nz, row indices */ double Ax [ ], /* size 2*nz, numerical values */ klu_symbolic *Symbolic, /* input, and numerical values modified on output */ @@ -416,10 +419,10 @@ int klu_z_refactor /* return TRUE if successful, FALSE otherwise */ klu_common *Common ) ; -SuiteSparse_long klu_l_refactor (SuiteSparse_long *, SuiteSparse_long *, +int klu_l_refactor (int64_t *, int64_t *, double *, klu_l_symbolic *, klu_l_numeric *, klu_l_common *) ; -SuiteSparse_long klu_zl_refactor (SuiteSparse_long *, SuiteSparse_long *, +int klu_zl_refactor (int64_t *, int64_t *, double *, klu_l_symbolic *, klu_l_numeric *, klu_l_common *) ; @@ -433,7 +436,7 @@ int klu_free_symbolic klu_common *Common ) ; -SuiteSparse_long klu_l_free_symbolic (klu_l_symbolic **, klu_l_common *) ; +int klu_l_free_symbolic (klu_l_symbolic **, klu_l_common *) ; /* -------------------------------------------------------------------------- */ @@ -455,8 +458,8 @@ int klu_z_free_numeric klu_common *Common ) ; -SuiteSparse_long klu_l_free_numeric (klu_l_numeric **, klu_l_common *) ; -SuiteSparse_long klu_zl_free_numeric (klu_l_numeric **, klu_l_common *) ; +int klu_l_free_numeric (klu_l_numeric **, klu_l_common *) ; +int klu_zl_free_numeric (klu_l_numeric **, klu_l_common *) ; /* -------------------------------------------------------------------------- */ @@ -483,10 +486,8 @@ int klu_z_sort klu_common *Common ) ; -SuiteSparse_long klu_l_sort (klu_l_symbolic *, klu_l_numeric *, - klu_l_common *) ; -SuiteSparse_long klu_zl_sort (klu_l_symbolic *, klu_l_numeric *, - klu_l_common *) ; +int klu_l_sort (klu_l_symbolic *, klu_l_numeric *, klu_l_common *) ; +int klu_zl_sort (klu_l_symbolic *, klu_l_numeric *, klu_l_common *) ; /* -------------------------------------------------------------------------- */ @@ -511,10 +512,8 @@ int klu_z_flops klu_common *Common ) ; -SuiteSparse_long klu_l_flops (klu_l_symbolic *, klu_l_numeric *, - klu_l_common *) ; -SuiteSparse_long klu_zl_flops (klu_l_symbolic *, klu_l_numeric *, - klu_l_common *) ; +int klu_l_flops (klu_l_symbolic *, klu_l_numeric *, klu_l_common *) ; +int klu_zl_flops (klu_l_symbolic *, klu_l_numeric *, klu_l_common *) ; /* -------------------------------------------------------------------------- */ @@ -536,8 +535,8 @@ SuiteSparse_long klu_zl_flops (klu_l_symbolic *, klu_l_numeric *, int klu_rgrowth ( - int Ap [ ], - int Ai [ ], + int32_t Ap [ ], + int32_t Ai [ ], double Ax [ ], klu_symbolic *Symbolic, klu_numeric *Numeric, @@ -546,18 +545,18 @@ int klu_rgrowth int klu_z_rgrowth ( - int Ap [ ], - int Ai [ ], + int32_t Ap [ ], + int32_t Ai [ ], double Ax [ ], klu_symbolic *Symbolic, klu_numeric *Numeric, klu_common *Common /* Common->rgrowth = reciprocal pivot growth */ ) ; -SuiteSparse_long klu_l_rgrowth (SuiteSparse_long *, SuiteSparse_long *, +int klu_l_rgrowth (int64_t *, int64_t *, double *, klu_l_symbolic *, klu_l_numeric *, klu_l_common *) ; -SuiteSparse_long klu_zl_rgrowth (SuiteSparse_long *, SuiteSparse_long *, +int klu_zl_rgrowth (int64_t *, int64_t *, double *, klu_l_symbolic *, klu_l_numeric *, klu_l_common *) ; @@ -571,7 +570,7 @@ SuiteSparse_long klu_zl_rgrowth (SuiteSparse_long *, SuiteSparse_long *, int klu_condest ( - int Ap [ ], /* size n+1, column pointers, not modified */ + int32_t Ap [ ], /* size n+1, column pointers, not modified */ double Ax [ ], /* size nz = Ap[n], numerical values, not modified*/ klu_symbolic *Symbolic, /* symbolic analysis, not modified */ klu_numeric *Numeric, /* numeric factorization, not modified */ @@ -580,17 +579,17 @@ int klu_condest int klu_z_condest ( - int Ap [ ], + int32_t Ap [ ], double Ax [ ], /* size 2*nz */ klu_symbolic *Symbolic, klu_numeric *Numeric, klu_common *Common /* result returned in Common->condest */ ) ; -SuiteSparse_long klu_l_condest (SuiteSparse_long *, double *, klu_l_symbolic *, +int klu_l_condest (int64_t *, double *, klu_l_symbolic *, klu_l_numeric *, klu_l_common *) ; -SuiteSparse_long klu_zl_condest (SuiteSparse_long *, double *, klu_l_symbolic *, +int klu_zl_condest (int64_t *, double *, klu_l_symbolic *, klu_l_numeric *, klu_l_common *) ; @@ -612,12 +611,8 @@ int klu_z_rcond klu_common *Common /* result in Common->rcond */ ) ; -SuiteSparse_long klu_l_rcond (klu_l_symbolic *, klu_l_numeric *, - klu_l_common *) ; - -SuiteSparse_long klu_zl_rcond (klu_l_symbolic *, klu_l_numeric *, - klu_l_common *) ; - +int klu_l_rcond (klu_l_symbolic *, klu_l_numeric *, klu_l_common *) ; +int klu_zl_rcond (klu_l_symbolic *, klu_l_numeric *, klu_l_common *) ; /* -------------------------------------------------------------------------- */ /* klu_scale */ @@ -627,14 +622,14 @@ int klu_scale /* return TRUE if successful, FALSE otherwise */ ( /* inputs, not modified */ int scale, /* <0: none, no error check; 0: none, 1: sum, 2: max */ - int n, - int Ap [ ], /* size n+1, column pointers */ - int Ai [ ], /* size nz, row indices */ + int32_t n, + int32_t Ap [ ], /* size n+1, column pointers */ + int32_t Ai [ ], /* size nz, row indices */ double Ax [ ], /* outputs, not defined on input */ double Rs [ ], /* workspace, not defined on input or output */ - int W [ ], /* size n, can be NULL */ + int32_t W [ ], /* size n, can be NULL */ klu_common *Common ) ; @@ -642,24 +637,22 @@ int klu_z_scale /* return TRUE if successful, FALSE otherwise */ ( /* inputs, not modified */ int scale, /* <0: none, no error check; 0: none, 1: sum, 2: max */ - int n, - int Ap [ ], /* size n+1, column pointers */ - int Ai [ ], /* size nz, row indices */ + int32_t n, + int32_t Ap [ ], /* size n+1, column pointers */ + int32_t Ai [ ], /* size nz, row indices */ double Ax [ ], /* outputs, not defined on input */ double Rs [ ], /* workspace, not defined on input or output */ - int W [ ], /* size n, can be NULL */ + int32_t W [ ], /* size n, can be NULL */ klu_common *Common ) ; -SuiteSparse_long klu_l_scale (SuiteSparse_long, SuiteSparse_long, - SuiteSparse_long *, SuiteSparse_long *, double *, - double *, SuiteSparse_long *, klu_l_common *) ; +int klu_l_scale (int, int64_t, int64_t *, int64_t *, double *, + double *, int64_t *, klu_l_common *) ; -SuiteSparse_long klu_zl_scale (SuiteSparse_long, SuiteSparse_long, - SuiteSparse_long *, SuiteSparse_long *, double *, - double *, SuiteSparse_long *, klu_l_common *) ; +int klu_zl_scale (int, int64_t, int64_t *, int64_t *, double *, + double *, int64_t *, klu_l_common *) ; /* -------------------------------------------------------------------------- */ @@ -675,31 +668,31 @@ int klu_extract /* returns TRUE if successful, FALSE otherwise */ /* outputs, either allocated on input, or ignored otherwise */ /* L */ - int *Lp, /* size n+1 */ - int *Li, /* size Numeric->lnz */ + int32_t *Lp, /* size n+1 */ + int32_t *Li, /* size Numeric->lnz */ double *Lx, /* size Numeric->lnz */ /* U */ - int *Up, /* size n+1 */ - int *Ui, /* size Numeric->unz */ + int32_t *Up, /* size n+1 */ + int32_t *Ui, /* size Numeric->unz */ double *Ux, /* size Numeric->unz */ /* F */ - int *Fp, /* size n+1 */ - int *Fi, /* size Numeric->nzoff */ + int32_t *Fp, /* size n+1 */ + int32_t *Fi, /* size Numeric->nzoff */ double *Fx, /* size Numeric->nzoff */ /* P, row permutation */ - int *P, /* size n */ + int32_t *P, /* size n */ /* Q, column permutation */ - int *Q, /* size n */ + int32_t *Q, /* size n */ /* Rs, scale factors */ double *Rs, /* size n */ /* R, block boundaries */ - int *R, /* size Symbolic->nblocks+1 (nblocks is at most n) */ + int32_t *R, /* size Symbolic->nblocks+1 (nblocks is at most n) */ klu_common *Common ) ; @@ -714,51 +707,51 @@ int klu_z_extract /* returns TRUE if successful, FALSE otherwise */ /* outputs, all of which must be allocated on input */ /* L */ - int *Lp, /* size n+1 */ - int *Li, /* size nnz(L) */ + int32_t *Lp, /* size n+1 */ + int32_t *Li, /* size nnz(L) */ double *Lx, /* size nnz(L) */ double *Lz, /* size nnz(L) for the complex case, ignored if real */ /* U */ - int *Up, /* size n+1 */ - int *Ui, /* size nnz(U) */ + int32_t *Up, /* size n+1 */ + int32_t *Ui, /* size nnz(U) */ double *Ux, /* size nnz(U) */ double *Uz, /* size nnz(U) for the complex case, ignored if real */ /* F */ - int *Fp, /* size n+1 */ - int *Fi, /* size nnz(F) */ + int32_t *Fp, /* size n+1 */ + int32_t *Fi, /* size nnz(F) */ double *Fx, /* size nnz(F) */ double *Fz, /* size nnz(F) for the complex case, ignored if real */ /* P, row permutation */ - int *P, /* size n */ + int32_t *P, /* size n */ /* Q, column permutation */ - int *Q, /* size n */ + int32_t *Q, /* size n */ /* Rs, scale factors */ double *Rs, /* size n */ /* R, block boundaries */ - int *R, /* size Symbolic->nblocks+1 (nblocks is at most n) */ + int32_t *R, /* size Symbolic->nblocks+1 (nblocks is at most n) */ klu_common *Common ) ; -SuiteSparse_long klu_l_extract (klu_l_numeric *, klu_l_symbolic *, - SuiteSparse_long *, SuiteSparse_long *, double *, - SuiteSparse_long *, SuiteSparse_long *, double *, - SuiteSparse_long *, SuiteSparse_long *, double *, - SuiteSparse_long *, SuiteSparse_long *, double *, - SuiteSparse_long *, klu_l_common *) ; +int klu_l_extract (klu_l_numeric *, klu_l_symbolic *, + int64_t *, int64_t *, double *, + int64_t *, int64_t *, double *, + int64_t *, int64_t *, double *, + int64_t *, int64_t *, double *, + int64_t *, klu_l_common *) ; -SuiteSparse_long klu_zl_extract (klu_l_numeric *, klu_l_symbolic *, - SuiteSparse_long *, SuiteSparse_long *, double *, double *, - SuiteSparse_long *, SuiteSparse_long *, double *, double *, - SuiteSparse_long *, SuiteSparse_long *, double *, double *, - SuiteSparse_long *, SuiteSparse_long *, double *, - SuiteSparse_long *, klu_l_common *) ; +int klu_zl_extract (klu_l_numeric *, klu_l_symbolic *, + int64_t *, int64_t *, double *, double *, + int64_t *, int64_t *, double *, double *, + int64_t *, int64_t *, double *, double *, + int64_t *, int64_t *, double *, + int64_t *, klu_l_common *) ; /* -------------------------------------------------------------------------- */ @@ -797,7 +790,9 @@ void *klu_realloc /* returns pointer to reallocated block */ ) ; void *klu_l_malloc (size_t, size_t, klu_l_common *) ; + 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 *) ; @@ -819,11 +814,12 @@ void *klu_l_realloc (size_t, size_t, size_t, void *, klu_l_common *) ; * #endif */ -#define KLU_DATE "Mar 12, 2018" +#define KLU_DATE "@KLU_DATE@" +#define KLU_MAIN_VERSION @KLU_VERSION_MAJOR@ +#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_MAIN_VERSION 1 -#define KLU_SUB_VERSION 3 -#define KLU_SUBSUB_VERSION 9 #define KLU_VERSION KLU_VERSION_CODE(KLU_MAIN_VERSION,KLU_SUB_VERSION) #ifdef __cplusplus diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Config/klu_version.tex.in b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Config/klu_version.tex.in new file mode 100644 index 000000000..83ad518a7 --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Config/klu_version.tex.in @@ -0,0 +1,2 @@ +% version of SuiteSparse/KLU +\date{VERSION @KLU_VERSION_MAJOR@.@KLU_VERSION_MINOR@.@KLU_VERSION_SUB@, @KLU_DATE@} diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Demo/Makefile b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Demo/Makefile deleted file mode 100644 index 51b0a479e..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Demo/Makefile +++ /dev/null @@ -1,63 +0,0 @@ -# KLU Demo Makefile - -default: all - -include ../../SuiteSparse_config/SuiteSparse_config.mk - -CLIB = $(LDFLAGS) -L../../lib -lklu -lbtf -lamd -lcolamd -lsuitesparseconfig \ - $(LIB_WITH_PARTITION) $(LDLIBS) - -CHOLMOD_LIB = -lcholmod -CHOLMOD = $(CHOLMOD_LIB) $(LAPACK) $(BLAS) - -ifneq ($(GPU_CONFIG),) -CHOLMOD += $(CUDART_LIB) $(CUBLAS_LIB) -endif - -I = -I../../include - -all: library klu_simple kludemo kluldemo - - ./klu_simple - - ./kludemo < ../Matrix/1c.mtx - - ./kludemo < ../Matrix/arrowc.mtx - - ./kludemo < ../Matrix/arrow.mtx - - ./kludemo < ../Matrix/impcol_a.mtx - - ./kludemo < ../Matrix/w156.mtx - - ./kludemo < ../Matrix/ctina.mtx - - ./kluldemo < ../Matrix/1c.mtx - - ./kluldemo < ../Matrix/arrowc.mtx - - ./kluldemo < ../Matrix/arrow.mtx - - ./kluldemo < ../Matrix/impcol_a.mtx - - ./kluldemo < ../Matrix/w156.mtx - - ./kluldemo < ../Matrix/ctina.mtx - -library: - ( cd ../../SuiteSparse_config ; $(MAKE) ) - ( cd ../Lib ; $(MAKE) ) - ( cd ../../BTF ; $(MAKE) library ) - ( cd ../../AMD ; $(MAKE) library ) - ( cd ../../COLAMD ; $(MAKE) library ) - ( cd ../../CHOLMOD ; $(MAKE) library ) - - ( cd ../../CAMD ; $(MAKE) ) - - ( cd ../../CCOLAMD ; $(MAKE) ) - - ( cd ../.. ; $(MAKE) metis ) - -purge: distclean - -distclean: clean - - $(RM) kludemo kluldemo klu_simple - - $(RM) -r $(PURGE) - -clean: - - $(RM) -r $(CLEAN) - -kludemo: kludemo.c Makefile - $(CC) $(CF) $(I) kludemo.c -o kludemo $(CLIB) $(CHOLMOD) - -kluldemo: kludemo.c Makefile - $(CC) $(CF) $(I) kluldemo.c -o kluldemo $(CLIB) $(CHOLMOD) - -klu_simple: klu_simple.c Makefile - $(CC) $(CF) $(I) klu_simple.c -o klu_simple $(CLIB) - - ./klu_simple - diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Demo/klu_simple.c b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Demo/klu_simple.c deleted file mode 100644 index 7530d40fb..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Demo/klu_simple.c +++ /dev/null @@ -1,27 +0,0 @@ -/* klu_simple: a simple KLU demo; solution is x = (1,2,3,4,5) */ - -#include -#include "klu.h" - -int n = 5 ; -int Ap [ ] = {0, 2, 5, 9, 10, 12} ; -int Ai [ ] = { 0, 1, 0, 2, 4, 1, 2, 3, 4, 2, 1, 4} ; -double Ax [ ] = {2., 3., 3., -1., 4., 4., -3., 1., 2., 2., 6., 1.} ; -double b [ ] = {8., 45., -3., 3., 19.} ; - -int main (void) -{ - klu_symbolic *Symbolic ; - klu_numeric *Numeric ; - klu_common Common ; - int i ; - klu_defaults (&Common) ; - Symbolic = klu_analyze (n, Ap, Ai, &Common) ; - Numeric = klu_factor (Ap, Ai, Ax, Symbolic, &Common) ; - klu_solve (Symbolic, Numeric, 5, 1, b, &Common) ; - klu_free_symbolic (&Symbolic, &Common) ; - klu_free_numeric (&Numeric, &Common) ; - for (i = 0 ; i < n ; i++) printf ("x [%d] = %g\n", i, b [i]) ; - return (0) ; -} - diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Demo/klu_simple.out b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Demo/klu_simple.out deleted file mode 100644 index 309065014..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Demo/klu_simple.out +++ /dev/null @@ -1,5 +0,0 @@ -x [0] = 1 -x [1] = 2 -x [2] = 3 -x [3] = 4 -x [4] = 5 diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Demo/kludemo.c b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Demo/kludemo.c deleted file mode 100644 index b6b229501..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Demo/kludemo.c +++ /dev/null @@ -1,326 +0,0 @@ -/* ========================================================================== */ -/* === KLU DEMO ============================================================= */ -/* ========================================================================== */ - -/* Read in a Matrix Market matrix (using CHOLMOD) and solve a linear system. */ - -#include -#include -#include "klu.h" - -/* for handling complex matrices */ -#define REAL(X,i) (X [2*(i)]) -#define IMAG(X,i) (X [2*(i)+1]) -#define CABS(X,i) (sqrt (REAL (X,i) * REAL (X,i) + IMAG (X,i) * IMAG (X,i))) - -#define MAX(a,b) (((a) > (b)) ? (a) : (b)) - -/* ========================================================================== */ -/* === klu_backslash ======================================================== */ -/* ========================================================================== */ - -static int klu_backslash /* return 1 if successful, 0 otherwise */ -( - /* --- input ---- */ - int n, /* A is n-by-n */ - int *Ap, /* size n+1, column pointers */ - int *Ai, /* size nz = Ap [n], row indices */ - double *Ax, /* size nz, numerical values */ - int isreal, /* nonzero if A is real, 0 otherwise */ - double *B, /* size n, right-hand-side */ - - /* --- output ---- */ - double *X, /* size n, solution to Ax=b */ - double *R, /* size n, residual r = b-A*x */ - - /* --- scalar output --- */ - int *lunz, /* nnz (L+U+F) */ - double *rnorm, /* norm (b-A*x,1) / norm (A,1) */ - - /* --- workspace - */ - - klu_common *Common /* default parameters and statistics */ -) -{ - double anorm = 0, asum ; - klu_symbolic *Symbolic ; - klu_numeric *Numeric ; - int i, j, p ; - - if (!Ap || !Ai || !Ax || !B || !X || !B) return (0) ; - - /* ---------------------------------------------------------------------- */ - /* symbolic ordering and analysis */ - /* ---------------------------------------------------------------------- */ - - Symbolic = klu_analyze (n, Ap, Ai, Common) ; - if (!Symbolic) return (0) ; - - if (isreal) - { - - /* ------------------------------------------------------------------ */ - /* factorization */ - /* ------------------------------------------------------------------ */ - - Numeric = klu_factor (Ap, Ai, Ax, Symbolic, Common) ; - if (!Numeric) - { - klu_free_symbolic (&Symbolic, Common) ; - return (0) ; - } - - /* ------------------------------------------------------------------ */ - /* statistics (not required to solve Ax=b) */ - /* ------------------------------------------------------------------ */ - - klu_rgrowth (Ap, Ai, Ax, Symbolic, Numeric, Common) ; - klu_condest (Ap, Ax, Symbolic, Numeric, Common) ; - klu_rcond (Symbolic, Numeric, Common) ; - klu_flops (Symbolic, Numeric, Common) ; - *lunz = Numeric->lnz + Numeric->unz - n + - ((Numeric->Offp) ? (Numeric->Offp [n]) : 0) ; - - /* ------------------------------------------------------------------ */ - /* solve Ax=b */ - /* ------------------------------------------------------------------ */ - - for (i = 0 ; i < n ; i++) - { - X [i] = B [i] ; - } - klu_solve (Symbolic, Numeric, n, 1, X, Common) ; - - /* ------------------------------------------------------------------ */ - /* compute residual, rnorm = norm(b-Ax,1) / norm(A,1) */ - /* ------------------------------------------------------------------ */ - - for (i = 0 ; i < n ; i++) - { - R [i] = B [i] ; - } - for (j = 0 ; j < n ; j++) - { - asum = 0 ; - for (p = Ap [j] ; p < Ap [j+1] ; p++) - { - /* R (i) -= A (i,j) * X (j) */ - R [Ai [p]] -= Ax [p] * X [j] ; - asum += fabs (Ax [p]) ; - } - anorm = MAX (anorm, asum) ; - } - *rnorm = 0 ; - for (i = 0 ; i < n ; i++) - { - *rnorm = MAX (*rnorm, fabs (R [i])) ; - } - - /* ------------------------------------------------------------------ */ - /* free numeric factorization */ - /* ------------------------------------------------------------------ */ - - klu_free_numeric (&Numeric, Common) ; - - } - else - { - - /* ------------------------------------------------------------------ */ - /* statistics (not required to solve Ax=b) */ - /* ------------------------------------------------------------------ */ - - Numeric = klu_z_factor (Ap, Ai, Ax, Symbolic, Common) ; - if (!Numeric) - { - klu_free_symbolic (&Symbolic, Common) ; - return (0) ; - } - - /* ------------------------------------------------------------------ */ - /* statistics */ - /* ------------------------------------------------------------------ */ - - klu_z_rgrowth (Ap, Ai, Ax, Symbolic, Numeric, Common) ; - klu_z_condest (Ap, Ax, Symbolic, Numeric, Common) ; - klu_z_rcond (Symbolic, Numeric, Common) ; - klu_z_flops (Symbolic, Numeric, Common) ; - *lunz = Numeric->lnz + Numeric->unz - n + - ((Numeric->Offp) ? (Numeric->Offp [n]) : 0) ; - - /* ------------------------------------------------------------------ */ - /* solve Ax=b */ - /* ------------------------------------------------------------------ */ - - for (i = 0 ; i < 2*n ; i++) - { - X [i] = B [i] ; - } - klu_z_solve (Symbolic, Numeric, n, 1, X, Common) ; - - /* ------------------------------------------------------------------ */ - /* compute residual, rnorm = norm(b-Ax,1) / norm(A,1) */ - /* ------------------------------------------------------------------ */ - - for (i = 0 ; i < 2*n ; i++) - { - R [i] = B [i] ; - } - for (j = 0 ; j < n ; j++) - { - asum = 0 ; - for (p = Ap [j] ; p < Ap [j+1] ; p++) - { - /* R (i) -= A (i,j) * X (j) */ - i = Ai [p] ; - REAL (R,i) -= REAL(Ax,p) * REAL(X,j) - IMAG(Ax,p) * IMAG(X,j) ; - IMAG (R,i) -= IMAG(Ax,p) * REAL(X,j) + REAL(Ax,p) * IMAG(X,j) ; - asum += CABS (Ax, p) ; - } - anorm = MAX (anorm, asum) ; - } - *rnorm = 0 ; - for (i = 0 ; i < n ; i++) - { - *rnorm = MAX (*rnorm, CABS (R, i)) ; - } - - /* ------------------------------------------------------------------ */ - /* free numeric factorization */ - /* ------------------------------------------------------------------ */ - - klu_z_free_numeric (&Numeric, Common) ; - } - - /* ---------------------------------------------------------------------- */ - /* free symbolic analysis, and residual */ - /* ---------------------------------------------------------------------- */ - - klu_free_symbolic (&Symbolic, Common) ; - return (1) ; -} - - -/* ========================================================================== */ -/* === klu_demo ============================================================= */ -/* ========================================================================== */ - -/* Given a sparse matrix A, set up a right-hand-side and solve X = A\b */ - -static void klu_demo (int n, int *Ap, int *Ai, double *Ax, int isreal) -{ - double rnorm ; - klu_common Common ; - double *B, *X, *R ; - int i, lunz ; - - printf ("KLU: %s, version: %d.%d.%d\n", KLU_DATE, KLU_MAIN_VERSION, - KLU_SUB_VERSION, KLU_SUBSUB_VERSION) ; - - /* ---------------------------------------------------------------------- */ - /* set defaults */ - /* ---------------------------------------------------------------------- */ - - klu_defaults (&Common) ; - - /* ---------------------------------------------------------------------- */ - /* create a right-hand-side */ - /* ---------------------------------------------------------------------- */ - - if (isreal) - { - /* B = 1 + (1:n)/n */ - B = klu_malloc (n, sizeof (double), &Common) ; - X = klu_malloc (n, sizeof (double), &Common) ; - R = klu_malloc (n, sizeof (double), &Common) ; - if (B) - { - for (i = 0 ; i < n ; i++) - { - B [i] = 1 + ((double) i+1) / ((double) n) ; - } - } - } - else - { - /* real (B) = 1 + (1:n)/n, imag(B) = (n:-1:1)/n */ - B = klu_malloc (n, 2 * sizeof (double), &Common) ; - X = klu_malloc (n, 2 * sizeof (double), &Common) ; - R = klu_malloc (n, 2 * sizeof (double), &Common) ; - if (B) - { - for (i = 0 ; i < n ; i++) - { - REAL (B, i) = 1 + ((double) i+1) / ((double) n) ; - IMAG (B, i) = ((double) n-i) / ((double) n) ; - } - } - } - - /* ---------------------------------------------------------------------- */ - /* X = A\b using KLU and print statistics */ - /* ---------------------------------------------------------------------- */ - - if (!klu_backslash (n, Ap, Ai, Ax, isreal, B, X, R, &lunz, &rnorm, &Common)) - { - printf ("KLU failed\n") ; - } - else - { - printf ("n %d nnz(A) %d nnz(L+U+F) %d resid %g\n" - "recip growth %g condest %g rcond %g flops %g\n", - n, Ap [n], lunz, rnorm, Common.rgrowth, Common.condest, - Common.rcond, Common.flops) ; - } - - /* ---------------------------------------------------------------------- */ - /* free the problem */ - /* ---------------------------------------------------------------------- */ - - if (isreal) - { - klu_free (B, n, sizeof (double), &Common) ; - klu_free (X, n, sizeof (double), &Common) ; - klu_free (R, n, sizeof (double), &Common) ; - } - else - { - klu_free (B, 2*n, sizeof (double), &Common) ; - klu_free (X, 2*n, sizeof (double), &Common) ; - klu_free (R, 2*n, sizeof (double), &Common) ; - } - printf ("peak memory usage: %g bytes\n\n", (double) (Common.mempeak)) ; -} - - -/* ========================================================================== */ -/* === main ================================================================= */ -/* ========================================================================== */ - -/* Read in a sparse matrix in Matrix Market format using CHOLMOD, and then - * solve Ax=b with KLU. Note that CHOLMOD is only used to read the matrix. */ - -#include "cholmod.h" - -int main (void) -{ - cholmod_sparse *A ; - cholmod_common ch ; - cholmod_start (&ch) ; - A = cholmod_read_sparse (stdin, &ch) ; - if (A) - { - if (A->nrow != A->ncol || A->stype != 0 - || (!(A->xtype == CHOLMOD_REAL || A->xtype == CHOLMOD_COMPLEX))) - { - printf ("invalid matrix\n") ; - } - else - { - klu_demo (A->nrow, A->p, A->i, A->x, A->xtype == CHOLMOD_REAL) ; - } - cholmod_free_sparse (&A, &ch) ; - } - cholmod_finish (&ch) ; - return (0) ; -} diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Demo/kludemo.out b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Demo/kludemo.out deleted file mode 100644 index d132681f1..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Demo/kludemo.out +++ /dev/null @@ -1,78 +0,0 @@ -./klu_simple -x [0] = 1 -x [1] = 2 -x [2] = 3 -x [3] = 4 -x [4] = 5 -./kludemo < ../Matrix/1c.mtx -KLU: June 3, 2015, version: 1.3.3 -n 1 nnz(A) 1 nnz(L+U+F) 1 resid 0 -recip growth 1 condest 1 rcond 1 flops 0 -peak memory usage: 492 bytes - -./kludemo < ../Matrix/arrowc.mtx -KLU: June 3, 2015, version: 1.3.3 -n 100 nnz(A) 298 nnz(L+U+F) 298 resid 1.68007e-14 -recip growth 0.019999 condest 295.99 rcond 0.019999 flops 297 -peak memory usage: 32244 bytes - -./kludemo < ../Matrix/arrow.mtx -KLU: June 3, 2015, version: 1.3.3 -n 100 nnz(A) 298 nnz(L+U+F) 298 resid 1.77636e-15 -recip growth 0.0204082 condest 303 rcond 0.0204082 flops 297 -peak memory usage: 20412 bytes - -./kludemo < ../Matrix/impcol_a.mtx -KLU: June 3, 2015, version: 1.3.3 -n 207 nnz(A) 572 nnz(L+U+F) 615 resid 6.98492e-10 -recip growth 0.00957447 condest 4.35093e+07 rcond 4.5277e-05 flops 259 -peak memory usage: 34276 bytes - -./kludemo < ../Matrix/w156.mtx -KLU: June 3, 2015, version: 1.3.3 -n 156 nnz(A) 362 nnz(L+U+F) 396 resid 6.23816e-08 -recip growth 0.00889922 condest 1.79787e+09 rcond 0.000124785 flops 175 -peak memory usage: 39516 bytes - -./kludemo < ../Matrix/ctina.mtx -KLU: June 3, 2015, version: 1.3.3 -n 11 nnz(A) 36 nnz(L+U+F) 45 resid 4.44089e-16 -recip growth 1 condest 56 rcond 1 flops 73 -peak memory usage: 4268 bytes - -./kluldemo < ../Matrix/1c.mtx -KLU: June 3, 2015, version: 1.3.3 -n 1 nnz(A) 1 nnz(L+U+F) 1 resid 0 -recip growth 1 condest 1 rcond 1 flops 0 -peak memory usage: 600 bytes - -./kluldemo < ../Matrix/arrowc.mtx -KLU: June 3, 2015, version: 1.3.3 -n 100 nnz(A) 298 nnz(L+U+F) 298 resid 1.68007e-14 -recip growth 0.019999 condest 295.99 rcond 0.019999 flops 297 -peak memory usage: 39000 bytes - -./kluldemo < ../Matrix/arrow.mtx -KLU: June 3, 2015, version: 1.3.3 -n 100 nnz(A) 298 nnz(L+U+F) 298 resid 1.77636e-15 -recip growth 0.0204082 condest 303 rcond 0.0204082 flops 297 -peak memory usage: 29584 bytes - -./kluldemo < ../Matrix/impcol_a.mtx -KLU: June 3, 2015, version: 1.3.3 -n 207 nnz(A) 572 nnz(L+U+F) 615 resid 6.98492e-10 -recip growth 0.00957447 condest 4.35093e+07 rcond 4.5277e-05 flops 259 -peak memory usage: 44800 bytes - -./kluldemo < ../Matrix/w156.mtx -KLU: June 3, 2015, version: 1.3.3 -n 156 nnz(A) 362 nnz(L+U+F) 396 resid 6.23816e-08 -recip growth 0.00889922 condest 1.79787e+09 rcond 0.000124785 flops 175 -peak memory usage: 47480 bytes - -./kluldemo < ../Matrix/ctina.mtx -KLU: June 3, 2015, version: 1.3.3 -n 11 nnz(A) 36 nnz(L+U+F) 45 resid 4.44089e-16 -recip growth 1 condest 56 rcond 1 flops 73 -peak memory usage: 5144 bytes - diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Demo/kluldemo.c b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Demo/kluldemo.c deleted file mode 100644 index ccf064476..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Demo/kluldemo.c +++ /dev/null @@ -1,329 +0,0 @@ -/* ========================================================================== */ -/* === KLU DEMO (long integer version) ====================================== */ -/* ========================================================================== */ - -/* Read in a Matrix Market matrix (using CHOLMOD) and solve a linear system. - * SuiteSparse_long is normally a "long", but it becomes "_int64" on Windows 64. */ - -#include -#include -#include "klu.h" -#define Long SuiteSparse_long - -/* for handling complex matrices */ -#define REAL(X,i) (X [2*(i)]) -#define IMAG(X,i) (X [2*(i)+1]) -#define CABS(X,i) (sqrt (REAL (X,i) * REAL (X,i) + IMAG (X,i) * IMAG (X,i))) - -#define MAX(a,b) (((a) > (b)) ? (a) : (b)) - -/* ========================================================================== */ -/* === klu_l_backslash ====================================================== */ -/* ========================================================================== */ - -static Long klu_l_backslash /* return 1 if successful, 0 otherwise */ -( - /* --- input ---- */ - Long n, /* A is n-by-n */ - Long *Ap, /* size n+1, column pointers */ - Long *Ai, /* size nz = Ap [n], row indices */ - double *Ax, /* size nz, numerical values */ - Long isreal, /* nonzero if A is real, 0 otherwise */ - double *B, /* size n, right-hand-side */ - - /* --- output ---- */ - double *X, /* size n, solution to Ax=b */ - double *R, /* size n, residual r = b-A*x */ - - /* --- scalar output --- */ - Long *lunz, /* nnz (L+U+F) */ - double *rnorm, /* norm (b-A*x,1) / norm (A,1) */ - - /* --- workspace - */ - - klu_l_common *Common /* default parameters and statistics */ -) -{ - double anorm = 0, asum ; - klu_l_symbolic *Symbolic ; - klu_l_numeric *Numeric ; - Long i, j, p ; - - if (!Ap || !Ai || !Ax || !B || !X || !B) return (0) ; - - /* ---------------------------------------------------------------------- */ - /* symbolic ordering and analysis */ - /* ---------------------------------------------------------------------- */ - - Symbolic = klu_l_analyze (n, Ap, Ai, Common) ; - if (!Symbolic) return (0) ; - - if (isreal) - { - - /* ------------------------------------------------------------------ */ - /* factorization */ - /* ------------------------------------------------------------------ */ - - Numeric = klu_l_factor (Ap, Ai, Ax, Symbolic, Common) ; - if (!Numeric) - { - klu_l_free_symbolic (&Symbolic, Common) ; - return (0) ; - } - - /* ------------------------------------------------------------------ */ - /* statistics (not required to solve Ax=b) */ - /* ------------------------------------------------------------------ */ - - klu_l_rgrowth (Ap, Ai, Ax, Symbolic, Numeric, Common) ; - klu_l_condest (Ap, Ax, Symbolic, Numeric, Common) ; - klu_l_rcond (Symbolic, Numeric, Common) ; - klu_l_flops (Symbolic, Numeric, Common) ; - *lunz = Numeric->lnz + Numeric->unz - n + - ((Numeric->Offp) ? (Numeric->Offp [n]) : 0) ; - - /* ------------------------------------------------------------------ */ - /* solve Ax=b */ - /* ------------------------------------------------------------------ */ - - for (i = 0 ; i < n ; i++) - { - X [i] = B [i] ; - } - klu_l_solve (Symbolic, Numeric, n, 1, X, Common) ; - - /* ------------------------------------------------------------------ */ - /* compute residual, rnorm = norm(b-Ax,1) / norm(A,1) */ - /* ------------------------------------------------------------------ */ - - for (i = 0 ; i < n ; i++) - { - R [i] = B [i] ; - } - for (j = 0 ; j < n ; j++) - { - asum = 0 ; - for (p = Ap [j] ; p < Ap [j+1] ; p++) - { - /* R (i) -= A (i,j) * X (j) */ - R [Ai [p]] -= Ax [p] * X [j] ; - asum += fabs (Ax [p]) ; - } - anorm = MAX (anorm, asum) ; - } - *rnorm = 0 ; - for (i = 0 ; i < n ; i++) - { - *rnorm = MAX (*rnorm, fabs (R [i])) ; - } - - /* ------------------------------------------------------------------ */ - /* free numeric factorization */ - /* ------------------------------------------------------------------ */ - - klu_l_free_numeric (&Numeric, Common) ; - - } - else - { - - /* ------------------------------------------------------------------ */ - /* statistics (not required to solve Ax=b) */ - /* ------------------------------------------------------------------ */ - - Numeric = klu_zl_factor (Ap, Ai, Ax, Symbolic, Common) ; - if (!Numeric) - { - klu_l_free_symbolic (&Symbolic, Common) ; - return (0) ; - } - - /* ------------------------------------------------------------------ */ - /* statistics */ - /* ------------------------------------------------------------------ */ - - klu_zl_rgrowth (Ap, Ai, Ax, Symbolic, Numeric, Common) ; - klu_zl_condest (Ap, Ax, Symbolic, Numeric, Common) ; - klu_zl_rcond (Symbolic, Numeric, Common) ; - klu_zl_flops (Symbolic, Numeric, Common) ; - *lunz = Numeric->lnz + Numeric->unz - n + - ((Numeric->Offp) ? (Numeric->Offp [n]) : 0) ; - - /* ------------------------------------------------------------------ */ - /* solve Ax=b */ - /* ------------------------------------------------------------------ */ - - for (i = 0 ; i < 2*n ; i++) - { - X [i] = B [i] ; - } - klu_zl_solve (Symbolic, Numeric, n, 1, X, Common) ; - - /* ------------------------------------------------------------------ */ - /* compute residual, rnorm = norm(b-Ax,1) / norm(A,1) */ - /* ------------------------------------------------------------------ */ - - for (i = 0 ; i < 2*n ; i++) - { - R [i] = B [i] ; - } - for (j = 0 ; j < n ; j++) - { - asum = 0 ; - for (p = Ap [j] ; p < Ap [j+1] ; p++) - { - /* R (i) -= A (i,j) * X (j) */ - i = Ai [p] ; - REAL (R,i) -= REAL(Ax,p) * REAL(X,j) - IMAG(Ax,p) * IMAG(X,j) ; - IMAG (R,i) -= IMAG(Ax,p) * REAL(X,j) + REAL(Ax,p) * IMAG(X,j) ; - asum += CABS (Ax, p) ; - } - anorm = MAX (anorm, asum) ; - } - *rnorm = 0 ; - for (i = 0 ; i < n ; i++) - { - *rnorm = MAX (*rnorm, CABS (R, i)) ; - } - - /* ------------------------------------------------------------------ */ - /* free numeric factorization */ - /* ------------------------------------------------------------------ */ - - klu_zl_free_numeric (&Numeric, Common) ; - } - - /* ---------------------------------------------------------------------- */ - /* free symbolic analysis, and residual */ - /* ---------------------------------------------------------------------- */ - - klu_l_free_symbolic (&Symbolic, Common) ; - return (1) ; -} - - -/* ========================================================================== */ -/* === klu_l_demo =========================================================== */ -/* ========================================================================== */ - -/* Given a sparse matrix A, set up a right-hand-side and solve X = A\b */ - -static void klu_l_demo (Long n, Long *Ap, Long *Ai, double *Ax, Long isreal) -{ - double rnorm ; - klu_l_common Common ; - double *B, *X, *R ; - Long i, lunz ; - - printf ("KLU: %s, version: %d.%d.%d\n", KLU_DATE, KLU_MAIN_VERSION, - KLU_SUB_VERSION, KLU_SUBSUB_VERSION) ; - - /* ---------------------------------------------------------------------- */ - /* set defaults */ - /* ---------------------------------------------------------------------- */ - - klu_l_defaults (&Common) ; - - /* ---------------------------------------------------------------------- */ - /* create a right-hand-side */ - /* ---------------------------------------------------------------------- */ - - if (isreal) - { - /* B = 1 + (1:n)/n */ - B = klu_l_malloc (n, sizeof (double), &Common) ; - X = klu_l_malloc (n, sizeof (double), &Common) ; - R = klu_l_malloc (n, sizeof (double), &Common) ; - if (B) - { - for (i = 0 ; i < n ; i++) - { - B [i] = 1 + ((double) i+1) / ((double) n) ; - } - } - } - else - { - /* real (B) = 1 + (1:n)/n, imag(B) = (n:-1:1)/n */ - B = klu_l_malloc (n, 2 * sizeof (double), &Common) ; - X = klu_l_malloc (n, 2 * sizeof (double), &Common) ; - R = klu_l_malloc (n, 2 * sizeof (double), &Common) ; - if (B) - { - for (i = 0 ; i < n ; i++) - { - REAL (B, i) = 1 + ((double) i+1) / ((double) n) ; - IMAG (B, i) = ((double) n-i) / ((double) n) ; - } - } - } - - /* ---------------------------------------------------------------------- */ - /* X = A\b using KLU and print statistics */ - /* ---------------------------------------------------------------------- */ - - if (!klu_l_backslash (n, Ap, Ai, Ax, isreal, B, X, R, &lunz, &rnorm, - &Common)) - { - printf ("KLU failed\n") ; - } - else - { - printf ("n %ld nnz(A) %ld nnz(L+U+F) %ld resid %g\n" - "recip growth %g condest %g rcond %g flops %g\n", - n, Ap [n], lunz, rnorm, Common.rgrowth, Common.condest, - Common.rcond, Common.flops) ; - } - - /* ---------------------------------------------------------------------- */ - /* free the problem */ - /* ---------------------------------------------------------------------- */ - - if (isreal) - { - klu_l_free (B, n, sizeof (double), &Common) ; - klu_l_free (X, n, sizeof (double), &Common) ; - klu_l_free (R, n, sizeof (double), &Common) ; - } - else - { - klu_l_free (B, 2*n, sizeof (double), &Common) ; - klu_l_free (X, 2*n, sizeof (double), &Common) ; - klu_l_free (R, 2*n, sizeof (double), &Common) ; - } - printf ("peak memory usage: %g bytes\n\n", (double) (Common.mempeak)) ; -} - - -/* ========================================================================== */ -/* === main ================================================================= */ -/* ========================================================================== */ - -/* Read in a sparse matrix in Matrix Market format using CHOLMOD, and then - * solve Ax=b with KLU. Note that CHOLMOD is only used to read the matrix. */ - -#include "cholmod.h" - -int main (void) -{ - cholmod_sparse *A ; - cholmod_common ch ; - cholmod_l_start (&ch) ; - A = cholmod_l_read_sparse (stdin, &ch) ; - if (A) - { - if (A->nrow != A->ncol || A->stype != 0 - || (!(A->xtype == CHOLMOD_REAL || A->xtype == CHOLMOD_COMPLEX))) - { - printf ("invalid matrix\n") ; - } - else - { - klu_l_demo (A->nrow, A->p, A->i, A->x, A->xtype == CHOLMOD_REAL) ; - } - cholmod_l_free_sparse (&A, &ch) ; - } - cholmod_l_finish (&ch) ; - return (0) ; -} diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Doc/ChangeLog b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Doc/ChangeLog index 70a1b4f97..b4a4edabe 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Doc/ChangeLog +++ b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Doc/ChangeLog @@ -1,3 +1,18 @@ +Jan 17, 2023: version 2.0.3 + + * SuiteSparse_config: now v7.0.0 + +Dec 9, 2022: version 2.0.2 + + * minor change to build system + * Fortran: no longer required to build KLU + +Nov 12, 2022: version 2.0.0 + + * using CMake build system + * integers: int (32-bit) and SuiteSparse_long (nominally 64-bit) replaced + with int32_t and int64_t. + Mar 12, 2018: version 1.3.9 * swapped arguments for KLU_malloc; not a bug, just more readable now diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Doc/KLU_UserGuide.pdf b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Doc/KLU_UserGuide.pdf deleted file mode 100644 index d5ae2f2a2..000000000 Binary files a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Doc/KLU_UserGuide.pdf and /dev/null differ diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Doc/KLU_UserGuide.tex b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Doc/KLU_UserGuide.tex index 7ec756ed7..8ed946629 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Doc/KLU_UserGuide.tex +++ b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Doc/KLU_UserGuide.tex @@ -1,4 +1,5 @@ \documentclass[11pt]{article} +\batchmode \newcommand{\m}[1]{{\bf{#1}}} % for matrices and vectors \newcommand{\tr}{^{\sf T}} % transpose @@ -26,7 +27,7 @@ } \and Eka Palamadai Natarajan} -\date{VERSION 1.3.9, Mar 12, 2018} +\input{klu_version.tex} \maketitle %------------------------------------------------------------------------------ @@ -50,7 +51,7 @@ \section{License and Copyright} %------------------------------------------------------------------------------ -KLU, Copyright\copyright 2004-2011 University of Florida. +KLU, Copyright\copyright 2004-2022 University of Florida. All Rights Reserved. KLU is available under alternate licenses; contact T. Davis for details. @@ -308,20 +309,18 @@ \section{Using KLU and BTF in a C program} %------------------------------------------------------------------------------ KLU and BTF include the following C-callable functions. Each function is -available in two or four versions: with {\tt int} or {\tt long} integers, and +available in two or four versions: with \verb'int32_t' or \verb'int64_t' integers, and (for functions that deal with numerical values), with {\tt double} or complex -{\tt double} values. The {\tt long} integer is actually a {\tt SuiteSparse\_long}, -which is typically a {\tt long}, defined with a {\tt \#define} statement. It -becomes an {\tt \_\_int64} on Microsoft Windows 64, however. +{\tt double} values. -The usage of real and complex, and {\tt int} and {\tt SuiteSparse\_long}, must not be +The usage of real and complex, and \verb'int32_t' and \verb'int64_t', must not be mixed, except that some functions can be used for both real and complex cases. %------------------------------------------------------------------------------ \subsection{KLU Common object} %------------------------------------------------------------------------------ -The {\tt klu\_common} object ({\tt klu\_l\_common} for the {\tt SuiteSparse\_long} +The {\tt klu\_common} object ({\tt klu\_l\_common} for the \verb'int64_t' version) contains user-definable parameters and statistics returned from KLU functions. This object appears in every KLU function as the last parameter. Details are given in the {\tt klu.h} include file, which also @@ -366,17 +365,17 @@ \subsection{KLU Common object} \item {\tt user\_order}: a pointer to a function that can be provided by the application that uses KLU, to redefine the fill-reducing ordering used by KLU -for each diagonal block. The {\tt int} and {\tt SuiteSparse\_long} prototypes must be +for each diagonal block. The \verb'int32_t' and \verb'int64_t' prototypes must be as follows: {\footnotesize \begin{verbatim} -int my_ordering_function (int n, int Ap [ ], int Ai [ ], int Perm [ ], klu_common *Common) ; +int32_t my_ordering_function (int32_t n, + int32_t Ap [ ], int32_t Ai [ ], int32_t Perm [ ], klu_common *Common) ; -SuiteSparse_long my_long_ordering_function (SuiteSparse_long n, - SuiteSparse_long Ap [ ], SuiteSparse_long Ai [ ], - SuiteSparse_long Perm [ ], klu_l_common *Common); +int64_t my_long_ordering_function (int64_t n, + int64_t Ap [ ], int64_t Ai [ ], int64_t Perm [ ], klu_l_common *Common) ; \end{verbatim} } @@ -416,7 +415,7 @@ \subsection{KLU Symbolic object} KLU performs its sparse LU factorization in two steps. The first is purely symbolic, and does not depend on the numerical values. This analysis returns a -{\tt klu\_symbolic} object ({\tt klu\_l\_symbolic} in the {\tt SuiteSparse\_long} +{\tt klu\_symbolic} object ({\tt klu\_l\_symbolic} in the \verb'int64_t' version). The {\tt Symbolic} object contains a pre-ordering which combines the block triangular form with the fill-reducing ordering, and an estimate of the number of nonzeros in the factors of each block. Its size is thus modest, only @@ -437,12 +436,6 @@ \subsection{KLU Numeric object} \subsection{A sparse matrix in KLU} %------------------------------------------------------------------------------ -% From here on, only the {\tt int} version is described. In the {\tt SuiteSparse\_long} -% version, the function names change slightly ({\tt klu\_factor} becomes -% {\tt klu\_l\_factor}, and the {\tt int}/complex version {\tt klu\_z\_factor} -% becomes {\tt klu\_zl\_factor}, for example). For more details on the -% {\tt SuiteSparse\_long} version, refer to Section~\ref{klu_include}. - The input matrix provided to KLU is in sparse compressed-column form, which is the same data structure used internally in MATLAB, except that the version used here allows for the row indices to appear in any ordering, and this version @@ -499,9 +492,8 @@ \subsection{{\tt klu\_defaults}: set default parameters} #include "klu.h" - SuiteSparse_long ok ; klu_l_common Common ; - ok = klu_l_defaults (&Common) ; /* real or complex */ + int ok = klu_l_defaults (&Common) ; /* real or complex */ \end{verbatim} } @@ -520,14 +512,14 @@ \subsection{{\tt klu\_analyze}: order and analyze a matrix} {\footnotesize \begin{verbatim} #include "klu.h" - int n, Ap [n+1], Ai [nz] ; + int32_t n, Ap [n+1], Ai [nz] ; klu_symbolic *Symbolic ; klu_common Common ; Symbolic = klu_analyze (n, Ap, Ai, &Common) ; /* real or complex */ #include "klu.h" - SuiteSparse_long n, Ap [n+1], Ai [nz] ; + int64_t n, Ap [n+1], Ai [nz] ; klu_l_symbolic *Symbolic ; klu_l_common Common ; Symbolic = klu_l_analyze (n, Ap, Ai, &Common) ; /* real or complex */ @@ -548,14 +540,14 @@ \subsection{{\tt klu\_analyze\_given}: order and analyze a matrix} {\footnotesize \begin{verbatim} #include "klu.h" - int n, Ap [n+1], Ai [nz], P [n], Q [n] ; + int32_t n, Ap [n+1], Ai [nz], P [n], Q [n] ; klu_symbolic *Symbolic ; klu_common Common ; Symbolic = klu_analyze_given (n, Ap, Ai, P, Q, &Common) ; /* real or complex */ #include "klu.h" - SuiteSparse_long n, Ap [n+1], Ai [nz], P [n], Q [n] ; + int64_t n, Ap [n+1], Ai [nz], P [n], Q [n] ; klu_l_symbolic *Symbolic ; klu_l_common Common ; Symbolic = klu_l_analyze_given (n, Ap, Ai, P, Q, &Common) ; /* real or complex */ @@ -574,7 +566,7 @@ \subsection{{\tt klu\_factor}: numerical factorization} {\footnotesize \begin{verbatim} #include "klu.h" - int Ap [n+1], Ai [nz] ; + int32_t Ap [n+1], Ai [nz] ; double Ax [nz], Az [2*nz] ; klu_symbolic *Symbolic ; klu_numeric *Numeric ; @@ -584,7 +576,7 @@ \subsection{{\tt klu\_factor}: numerical factorization} #include "klu.h" - SuiteSparse_long Ap [n+1], Ai [nz] ; + int64_t Ap [n+1], Ai [nz] ; double Ax [nz], Az [2*nz] ; klu_l_symbolic *Symbolic ; klu_l_numeric *Numeric ; @@ -610,7 +602,7 @@ \subsection{{\tt klu\_solve}: solve a linear system} {\footnotesize \begin{verbatim} #include "klu.h" - int ldim, nrhs, ok ; + int32_t ldim, nrhs ; int ok ; double B [ldim*nrhs], Bz [2*ldim*nrhs] ; klu_symbolic *Symbolic ; klu_numeric *Numeric ; @@ -620,7 +612,7 @@ \subsection{{\tt klu\_solve}: solve a linear system} #include "klu.h" - SuiteSparse_long ldim, nrhs, ok ; + int64_t ldim, nrhs ; int ok ; double B [ldim*nrhs], Bz [2*ldim*nrhs] ; klu_symbolic *Symbolic ; klu_numeric *Numeric ; @@ -642,7 +634,7 @@ \subsection{{\tt klu\_tsolve}: solve a transposed linear system} {\footnotesize \begin{verbatim} #include "klu.h" - int ldim, nrhs, ok ; + int32_t ldim, nrhs ; int ok ; double B [ldim*nrhs], Bz [2*ldim*nrhs] ; klu_symbolic *Symbolic ; klu_numeric *Numeric ; @@ -652,7 +644,7 @@ \subsection{{\tt klu\_tsolve}: solve a transposed linear system} #include "klu.h" - SuiteSparse_long ldim, nrhs, ok ; + int64_t ldim, nrhs ; int ok ; double B [ldim*nrhs], Bz [2*ldim*nrhs] ; klu_symbolic *Symbolic ; klu_numeric *Numeric ; @@ -681,7 +673,7 @@ \subsection{{\tt klu\_refactor}: numerical refactorization} {\footnotesize \begin{verbatim} #include "klu.h" - int ok, Ap [n+1], Ai [nz] ; + int ok ; int32_t Ap [n+1], Ai [nz] ; double Ax [nz], Az [2*nz] ; klu_symbolic *Symbolic ; klu_numeric *Numeric ; @@ -691,7 +683,7 @@ \subsection{{\tt klu\_refactor}: numerical refactorization} #include "klu.h" - SuiteSparse_long ok, Ap [n+1], Ai [nz] ; + int ok ; int64_t Ap [n+1], Ai [nz] ; double Ax [nz], Az [2*nz] ; klu_l_symbolic *Symbolic ; klu_l_numeric *Numeric ; @@ -776,7 +768,7 @@ \subsection{{\tt klu\_sort}: sort the columns of L and U} #include "klu.h" - SuiteSparse_long ok ; + int ok ; klu_l_symbolic *Symbolic ; klu_l_numeric *Numeric ; klu_l_common Common ; @@ -806,7 +798,7 @@ \subsection{{\tt klu\_flops}: determine the flop count} #include "klu.h" - SuiteSparse_long ok ; + int ok ; klu_l_symbolic *Symbolic ; klu_l_numeric *Numeric ; klu_l_common Common ; @@ -836,7 +828,7 @@ \subsection{{\tt klu\_rgrowth}: determine the pivot growth} {\footnotesize \begin{verbatim} #include "klu.h" - int ok, Ap [n+1], Ai [nz] ; + int ok ; int32_t Ap [n+1], Ai [nz] ; double Ax [nz], Az [2*nz] ; klu_symbolic *Symbolic ; klu_numeric *Numeric ; @@ -846,7 +838,7 @@ \subsection{{\tt klu\_rgrowth}: determine the pivot growth} #include "klu.h" - SuiteSparse_long ok, Ap [n+1], Ai [nz] ; + int ok ; int64_t Ap [n+1], Ai [nz] ; double Ax [nz], Az [2*nz] ; klu_l_symbolic *Symbolic ; klu_l_numeric *Numeric ; @@ -870,7 +862,7 @@ \subsection{{\tt klu\_condest}: accurate condition number estimation} {\footnotesize \begin{verbatim} #include "klu.h" - int ok, Ap [n+1] ; + int ok ; int32_t Ap [n+1] ; double Ax [nz], Az [2*nz] ; klu_symbolic *Symbolic ; klu_numeric *Numeric ; @@ -880,7 +872,7 @@ \subsection{{\tt klu\_condest}: accurate condition number estimation} #include "klu.h" - SuiteSparse_long ok, Ap [n+1] ; + int ok ; int64_t Ap [n+1] ; double Ax [nz], Az [2*nz] ; klu_l_symbolic *Symbolic ; klu_l_numeric *Numeric ; @@ -915,7 +907,7 @@ \subsection{{\tt klu\_rcond}: cheap reciprocal condition number estimation} #include "klu.h" - SuiteSparse_long ok ; + int ok ; klu_l_symbolic *Symbolic ; klu_l_numeric *Numeric ; klu_l_common Common ; @@ -952,7 +944,7 @@ \subsection{{\tt klu\_scale}: scale and check a sparse matrix} {\footnotesize \begin{verbatim} #include "klu.h" - int scale, ok, n, Ap [n+1], Ai [nz], W [n] ; + int scale, ok ; int32_t n, Ap [n+1], Ai [nz], W [n] ; double Ax [nz], Az [2*nz], Rs [n] ; klu_common Common ; ok = klu_scale (scale, n, Ap, Ai, Ax, Symbolic, Numeric, &Common) ; /* real */ @@ -960,7 +952,7 @@ \subsection{{\tt klu\_scale}: scale and check a sparse matrix} #include "klu.h" - SuiteSparse_long scale, ok, n, Ap [n+1], Ai [nz], W [n] ; + int scale, ok ; int64_t n, Ap [n+1], Ai [nz], W [n] ; double Ax [nz], Az [2*nz], Rs [n] ; klu_l_common Common ; ok = klu_l_scale (scale, n, Ap, Ai, Ax, Symbolic, Numeric, &Common) ; /* real */ @@ -993,7 +985,8 @@ \subsection{{\tt klu\_extract}: extract the LU factorization} {\footnotesize \begin{verbatim} #include "klu.h" - int ok, Lp [n+1], Li [lnz], Up [n+1], Ui [unz], Fp [n+1], Fi [nzoff], P [n], Q [n], R [n] ; + int ok ; + int32_t Lp [n+1], Li [lnz], Up [n+1], Ui [unz], Fp [n+1], Fi [nzoff], P [n], Q [n], R [n] ; double Lx [lnz], Lz [lnz], Ux [unz], Uz [unz], Fx [nzoff], Fz [nzoff], Rs [n] ; klu_symbolic *Symbolic ; klu_numeric *Numeric ; @@ -1005,7 +998,8 @@ \subsection{{\tt klu\_extract}: extract the LU factorization} #include "klu.h" - SuiteSparse_long ok, Lp [n+1], Li [lnz], Up [n+1], Ui [unz], Fp [n+1], + int ok ; + int64_t Lp [n+1], Li [lnz], Up [n+1], Ui [unz], Fp [n+1], Fi [nzoff], P [n], Q [n], R [n] ; double Lx [lnz], Lz [lnz], Ux [unz], Uz [unz], Fx [nzoff], Fz [nzoff], Rs [n] ; klu_l_symbolic *Symbolic ; @@ -1063,8 +1057,8 @@ \subsection{{\tt klu\_malloc}, {\tt klu\_free}, {\tt klu\_realloc}: \subsection{{\tt btf\_maxtrans}: maximum transversal} %------------------------------------------------------------------------------- -The BTF package includes three user-callable functions (each with {\tt int} -and {\tt SuiteSparse\_long} versions). They do not need to be called directly by an +The BTF package includes three user-callable functions (each with \verb'int32_t' +and \verb'int64_t' versions). They do not need to be called directly by an application that uses KLU. KLU will call these functions to perform the permutation into upper block triangular form. @@ -1090,12 +1084,12 @@ \subsection{{\tt btf\_maxtrans}: maximum transversal} {\footnotesize \begin{verbatim} - int nrow, ncol, Ap [ncol+1], Ai [nz], Match [nrow], Work [5*ncol], nmatch ; + int32_t nrow, ncol, Ap [ncol+1], Ai [nz], Match [nrow], Work [5*ncol], nmatch ; double maxwork, work ; nmatch = btf_maxtrans (nrow, ncol, Ap, Ai, maxwork, &work, Match, Work) ; - SuiteSparse_long nrow, ncol, Ap [ncol+1], Ai [nz], Match [nrow], Work [5*ncol], nmatch ; + int64_t nrow, ncol, Ap [ncol+1], Ai [nz], Match [nrow], Work [5*ncol], nmatch ; double maxwork, work ; nmatch = btf_l_maxtrans (nrow, ncol, Ap, Ai, maxwork, &work, Match, Work) ; \end{verbatim} @@ -1118,11 +1112,11 @@ \subsection{{\tt btf\_strongcomp}: strongly connected components} {\footnotesize \begin{verbatim} - int n, Ap [n+1], Ai [nz], Q [n], P [n], R [n+1], Work [4*n], ncomp ; + int32_t n, Ap [n+1], Ai [nz], Q [n], P [n], R [n+1], Work [4*n], ncomp ; ncomp = btf_strongcomp (n, Ap, Ai, Q, P, R, Work) ; - SuiteSparse_long n, Ap [n+1], Ai [nz], Q [n], P [n], R [n+1], Work [4*n], ncomp ; + int64_t n, Ap [n+1], Ai [nz], Q [n], P [n], R [n+1], Work [4*n], ncomp ; ncomp = btf_l_strongcomp (n, Ap, Ai, Q, P, R, Work) ; \end{verbatim} } @@ -1146,12 +1140,12 @@ \subsection{{\tt btf\_order}: permutation to block triangular form} {\footnotesize \begin{verbatim} - int n, Ap [n+1], Ai [nz], P [n], Q [n], R [n+1], nfound, Work [5*n], ncomp, nfound ; + int32_t n, Ap [n+1], Ai [nz], P [n], Q [n], R [n+1], nfound, Work [5*n], ncomp, nfound ; double maxwork, work ; ncomp = btf_order (n, Ap, Ai, maxwork, &work, P, Q, R, &nfound, Work) ; - SuiteSparse_long n, Ap [n+1], Ai [nz], P [n], Q [n], R [n+1], nfound, Work [5*n], ncomp, nfound ; + int64_t n, Ap [n+1], Ai [nz], P [n], Q [n], R [n+1], nfound, Work [5*n], ncomp, nfound ; double maxwork, work ; ncomp = btf_l_order (n, Ap, Ai, maxwork, &work, P, Q, R, &nfound, Work) ; \end{verbatim} @@ -1212,8 +1206,7 @@ \subsection{Sample C programs that use KLU} matrix). For a more complete program that uses KLU, see {\tt KLU/Demo/kludemo.c} for an -{\tt int} version, and {\tt KLU/Demo/kluldemo.c} for a version that uses {\tt -SuiteSparse\_long} instead. The top-level main routine uses CHOLMOD to read in a +\verb'int32_t' version, and {\tt KLU/Demo/kluldemo.c} for a version that uses \verb'int64_t' instead. The top-level main routine uses CHOLMOD to read in a compressed-column sparse matrix from a Matrix Market file, because KLU does not include such a function. Otherwise, no CHOLMOD functions are used. Unlike {\tt klu\_simple.c}, CHOLMOD is required to run the {\tt kludemo.c} and {\tt @@ -1224,20 +1217,14 @@ \section{Installation} \label{Install} %------------------------------------------------------------------------------ -Installation of the C-callable interface requires the {\tt make} utility, in -Linux/Unix. Alternatively, you can use the Cygwin {\tt make} in Windows. +Installation of the C-callable interface requires the {\tt cmake} utility. The MATLAB installation in any platform, including Windows is simple; just type {\tt klu\_install} to compile and install KLU, BTF, AMD, and COLAMD. -For {\tt make}, system-dependent configurations are in the {\tt -../SuiteSparse\_config/SuiteSparse\_config.mk} file. -You can edit that file to customize the -compilation, but it now automatically detecs your system (Linux, Mac, etc). -So it's not likely that you will need to edit that file. - +An optional \verb'Makefile' is provided to simplify the use of \verb'cmake'. To compile and install the C-callable KLU, BTF, AMD, and COLAMD libraries, go -to the {\tt KLU} directory and type {\tt make}. The KLU and BTF libraries are -placed in {\tt KLU/Lib/libklu.*} and {\tt BTF/Lib/libbtf.*}. +to the {\tt SuiteSparse} directory and type {\tt make}. The KLU and BTF libraries are +placed in {\tt KLU/build/libklu.*} and {\tt BTF/build/libbtf.*}. Two KLU demo programs will be compiled and tested in the {\tt KLU/Demo} directory. You can compare the output of {\tt make} with the results in the KLU distribution, {\tt @@ -1245,21 +1232,21 @@ \section{Installation} Typing {\tt make clean} will remove all but the final compiled libraries and demo programs. Typing {\tt make purge} or {\tt make distclean} removes all -files not in the original distribution. If you compile KLU or BTF and then -later change the {\tt ../SuiteSparse\_config/SuiteSparse\_config.mk} -file then you should type {\tt -make purge} and then {\tt make} to recompile. +files not in the original distribution. When you compile your program that uses the C-callable KLU library, you need to -add the {\tt KLU/Lib/libklu.*}, {\tt BTF/Lib/libbtf.*}, {\tt AMD/Lib/libamd.*}, -and {\tt COLAMD/Lib/libcolamd.*} libraries, and you need to tell your compiler to +add the {\tt KLU/build/libklu.*}, {\tt BTF/build/libbtf.*}, {\tt AMD/build/libamd.*}, +and {\tt COLAMD/build/libcolamd.*} libraries, and you need to tell your compiler to look in the {\tt KLU/Include} and {\tt BTF/Include} directory for include -files. -Alternatively, do {\tt make install}, and KLU will be installed in +files. If using \verb'cmake', each package includes scripts for \verb'find_library'. +Alternatively, do {\tt make install}, and KLU will be installed (on Linux/Mac) in /usr/local/lib and /usr/local/include, and documentation is placed in /usr/local/doc. These installation locations can be changed; see {\tt SuiteSparse/README.txt} for details. +To install in \verb'SuiteSparse/lib' +and \verb'SuiteSparse/include', use \verb'make local ; make install'. + If all you want to use is the KLU mexFunction in MATLAB, you can skip the use of the {\tt make} command entirely. Simply type {\tt klu\_install} in the MATLAB command window while in the {\tt KLU/MATLAB} directory. This works on diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Doc/License.txt b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Doc/License.txt index 82113cabc..e1394b017 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Doc/License.txt +++ b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Doc/License.txt @@ -1,5 +1,7 @@ -KLU, Copyright (C) 2004-2013, University of Florida -by Timothy A. Davis and Ekanathan Palamadai. +KLU, Copyright (c) 2004-2022, University of Florida. All Rights Reserved. +Authors: Timothy A. Davis and Ekanathan Palamadai. +SPDX-License-Identifier: LGPL-2.1+ + KLU is also available under other licenses; contact authors for details. http://www.suitesparse.com diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Doc/Makefile b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Doc/Makefile index a275b4be5..9f1f09fc9 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Doc/Makefile +++ b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Doc/Makefile @@ -2,43 +2,32 @@ # KLU Makefile for creating the user guide #------------------------------------------------------------------------------ -default: dist - -include ../../SuiteSparse_config/SuiteSparse_config.mk - -#------------------------------------------------------------------------------ -# Remove all but the files in the original distribution -#------------------------------------------------------------------------------ - -clean: - - $(RM) -r $(CLEAN) - -purge: distclean - -distclean: clean - - $(RM) -r $(PURGE) - -#------------------------------------------------------------------------------ -# Create the User Guide and Quick Start Guide -#------------------------------------------------------------------------------ +dist: KLU_UserGuide.pdf + - $(RM) *.aux *.bbl *.blg *.log *.toc + - $(RM) klu_simple_c.tex klu_h.tex btf_h.tex KLU_UserGuide.pdf: KLU_UserGuide.tex KLU_UserGuide.bib \ - ../Include/klu.h ../../BTF/Include/btf.h Makefile - echo '\\begin{verbatim}' > klu_h.tex + ../Include/klu.h ../../BTF/Include/btf.h Makefile klu_version.tex + printf '\\begin{verbatim}\n' > klu_h.tex expand -8 ../Include/klu.h >> klu_h.tex - echo '\\end{verbatim}' >> klu_h.tex - echo '\\begin{verbatim}' > btf_h.tex + printf '\\end{verbatim}\n' >> klu_h.tex + printf '\\begin{verbatim}\n' > btf_h.tex expand -8 ../../BTF/Include/btf.h >> btf_h.tex - echo '\\end{verbatim}' >> btf_h.tex - echo '\\begin{verbatim}' > klu_simple_c.tex + printf '\\end{verbatim}\n' >> btf_h.tex + printf '\\begin{verbatim}\n' > klu_simple_c.tex expand -8 ../Demo/klu_simple.c >> klu_simple_c.tex - echo '\\end{verbatim}' >> klu_simple_c.tex + printf '\\end{verbatim}\n' >> klu_simple_c.tex pdflatex KLU_UserGuide bibtex KLU_UserGuide pdflatex KLU_UserGuide pdflatex KLU_UserGuide -dist: KLU_UserGuide.pdf +# Remove all but the files in the original distribution +clean: - $(RM) *.aux *.bbl *.blg *.log *.toc - $(RM) klu_simple_c.tex klu_h.tex btf_h.tex +purge: clean + +distclean: clean + diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Doc/klu_version.tex b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Doc/klu_version.tex new file mode 100644 index 000000000..f15e03053 --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Doc/klu_version.tex @@ -0,0 +1,2 @@ +% version of SuiteSparse/KLU +\date{VERSION 2.0.3, Jan 17, 2023} diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Doc/palamadai_e.pdf b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Doc/palamadai_e.pdf deleted file mode 100644 index 6896e0ed1..000000000 Binary files a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Doc/palamadai_e.pdf and /dev/null differ diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Include/klu.h b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Include/klu.h index 07611f72d..80204ecf7 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Include/klu.h +++ b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Include/klu.h @@ -1,6 +1,12 @@ -/* ========================================================================== */ -/* === klu include file ===================================================== */ -/* ========================================================================== */ +//------------------------------------------------------------------------------ +// KLU/Source/klu.h: include file for KLU +//------------------------------------------------------------------------------ + +// KLU, Copyright (c) 2004-2022, University of Florida. All Rights Reserved. +// Authors: Timothy A. Davis and Ekanathan Palamadai. +// SPDX-License-Identifier: LGPL-2.1+ + +//------------------------------------------------------------------------------ /* Include file for user programs that call klu_* routines */ @@ -34,7 +40,7 @@ typedef struct double *Lnz ; /* size n, but only Lnz [0..nblocks-1] is used */ /* computed for all orderings: */ - int + int32_t n, /* input matrix A is n-by-n */ nz, /* # entries in input matrix */ *P, /* size n */ @@ -43,11 +49,11 @@ typedef struct nzoff, /* nz in off-diagonal blocks */ nblocks, /* number of blocks */ maxblock, /* size of largest block */ - ordering, /* ordering used (AMD, COLAMD, or GIVEN) */ + ordering, /* ordering used (0:AMD, 1:COLAMD, 2:given, ... */ do_btf ; /* whether or not BTF preordering was requested */ /* only computed if BTF preordering requested */ - int structural_rank ; /* 0 to n-1 if the matrix is structurally rank + int32_t structural_rank ; /* 0 to n-1 if the matrix is structurally rank * deficient. -1 if not computed. n if the matrix has * full structural rank */ @@ -57,7 +63,7 @@ typedef struct /* 64-bit version (otherwise same as above) */ { double symmetry, est_flops, lnz, unz ; double *Lnz ; - SuiteSparse_long n, nz, *P, *Q, *R, nzoff, nblocks, maxblock, ordering, + int64_t n, nz, *P, *Q, *R, nzoff, nblocks, maxblock, ordering, do_btf, structural_rank ; } klu_l_symbolic ; @@ -71,20 +77,20 @@ typedef struct /* LU factors of each block, the pivot row permutation, and the * entries in the off-diagonal blocks */ - int n ; /* A is n-by-n */ - int nblocks ; /* number of diagonal blocks */ - int lnz ; /* actual nz in L, including diagonal */ - int unz ; /* actual nz in U, including diagonal */ - int max_lnz_block ; /* max actual nz in L in any one block, incl. diag */ - int max_unz_block ; /* max actual nz in U in any one block, incl. diag */ - int *Pnum ; /* size n. final pivot permutation */ - int *Pinv ; /* size n. inverse of final pivot permutation */ + int32_t n ; /* A is n-by-n */ + int32_t nblocks ; /* number of diagonal blocks */ + int32_t lnz ; /* actual nz in L, including diagonal */ + int32_t unz ; /* actual nz in U, including diagonal */ + int32_t max_lnz_block ; /* max actual nz in L in any one block, incl. diag */ + int32_t max_unz_block ; /* max actual nz in U in any one block, incl. diag */ + int32_t *Pnum ; /* size n. final pivot permutation */ + int32_t *Pinv ; /* size n. inverse of final pivot permutation */ /* LU factors of each block */ - int *Lip ; /* size n. pointers into LUbx[block] for L */ - int *Uip ; /* size n. pointers into LUbx[block] for U */ - int *Llen ; /* size n. Llen [k] = # of entries in kth column of L */ - int *Ulen ; /* size n. Ulen [k] = # of entries in kth column of U */ + int32_t *Lip ; /* size n. pointers into LUbx[block] for L */ + int32_t *Uip ; /* size n. pointers into LUbx[block] for U */ + int32_t *Llen ; /* size n. Llen [k] = # of entries in kth column of L */ + int32_t *Ulen ; /* size n. Ulen [k] = # of entries in kth column of U */ void **LUbx ; /* L and U indices and entries (excl. diagonal of U) */ size_t *LUsize ; /* size of each LUbx [block], in sizeof (Unit) */ void *Udiag ; /* diagonal of U */ @@ -96,19 +102,19 @@ typedef struct size_t worksize ; /* size (in bytes) of Work */ void *Work ; /* workspace */ void *Xwork ; /* alias into Numeric->Work */ - int *Iwork ; /* alias into Numeric->Work */ + int32_t *Iwork ; /* alias into Numeric->Work */ /* off-diagonal entries in a conventional compressed-column sparse matrix */ - int *Offp ; /* size n+1, column pointers */ - int *Offi ; /* size nzoff, row indices */ + int32_t *Offp ; /* size n+1, column pointers */ + int32_t *Offi ; /* size nzoff, row indices */ void *Offx ; /* size nzoff, numerical values */ - int nzoff ; + int32_t nzoff ; } klu_numeric ; typedef struct /* 64-bit version (otherwise same as above) */ { - SuiteSparse_long n, nblocks, lnz, unz, max_lnz_block, max_unz_block, *Pnum, + int64_t n, nblocks, lnz, unz, max_lnz_block, max_unz_block, *Pnum, *Pinv, *Lip, *Uip, *Llen, *Ulen ; void **LUbx ; size_t *LUsize ; @@ -116,10 +122,10 @@ typedef struct /* 64-bit version (otherwise same as above) */ double *Rs ; size_t worksize ; void *Work, *Xwork ; - SuiteSparse_long *Iwork ; - SuiteSparse_long *Offp, *Offi ; + int64_t *Iwork ; + int64_t *Offp, *Offi ; void *Offx ; - SuiteSparse_long nzoff ; + int64_t nzoff ; } klu_l_numeric ; @@ -154,7 +160,8 @@ typedef struct klu_common_struct * 0: none, 1: sum, 2: max */ /* pointer to user ordering function */ - int (*user_order) (int, int *, int *, int *, struct klu_common_struct *) ; + int32_t (*user_order) (int32_t, int32_t *, int32_t *, int32_t *, + struct klu_common_struct *) ; /* pointer to user data, passed unchanged as the last parameter to the * user ordering function (optional, the user function need not use this @@ -177,23 +184,23 @@ typedef struct klu_common_struct int status ; /* KLU_OK if OK, < 0 if error */ int nrealloc ; /* # of reallocations of L and U */ - int structural_rank ; /* 0 to n-1 if the matrix is structurally rank + int32_t structural_rank ; /* 0 to n-1 if the matrix is structurally rank * deficient (as determined by maxtrans). -1 if not computed. n if the * matrix has full structural rank. This is computed by klu_analyze * if a BTF preordering is requested. */ - int numerical_rank ; /* First k for which a zero U(k,k) was found, + int32_t numerical_rank ; /* First k for which a zero U(k,k) was found, * if the matrix was singular (in the range 0 to n-1). n if the matrix * has full rank. This is not a true rank-estimation. It just reports * where the first zero pivot was found. -1 if not computed. * Computed by klu_factor and klu_refactor. */ - int singular_col ; /* n if the matrix is not singular. If in the + int32_t singular_col ; /* n if the matrix is not singular. If in the * range 0 to n-1, this is the column index of the original matrix A that * corresponds to the column of U that contains a zero diagonal entry. * -1 if not computed. Computed by klu_factor and klu_refactor. */ - int noffdiag ; /* # of off-diagonal pivots, -1 if not computed */ + int32_t noffdiag ; /* # of off-diagonal pivots, -1 if not computed */ double flops ; /* actual factorization flop count, from klu_flops */ double rcond ; /* crude reciprocal condition est., from klu_rcond */ @@ -210,14 +217,12 @@ typedef struct klu_l_common_struct /* 64-bit version (otherwise same as above)*/ { double tol, memgrow, initmem_amd, initmem, maxwork ; - SuiteSparse_long btf, ordering, scale ; - SuiteSparse_long (*user_order) (SuiteSparse_long, SuiteSparse_long *, - SuiteSparse_long *, SuiteSparse_long *, + int btf, ordering, scale ; + int64_t (*user_order) (int64_t, int64_t *, int64_t *, int64_t *, struct klu_l_common_struct *) ; void *user_data ; - SuiteSparse_long halt_if_singular ; - SuiteSparse_long status, nrealloc, structural_rank, numerical_rank, - singular_col, noffdiag ; + int halt_if_singular, status, nrealloc ; + int64_t structural_rank, numerical_rank, singular_col, noffdiag ; double flops, rcond, condest, rgrowth, work ; size_t memusage, mempeak ; @@ -232,7 +237,7 @@ int klu_defaults klu_common *Common ) ; -SuiteSparse_long klu_l_defaults (klu_l_common *Common) ; +int klu_l_defaults (klu_l_common *Common) ; /* -------------------------------------------------------------------------- */ /* klu_analyze: orders and analyzes a matrix */ @@ -244,14 +249,14 @@ SuiteSparse_long klu_l_defaults (klu_l_common *Common) ; klu_symbolic *klu_analyze ( /* inputs, not modified */ - int n, /* A is n-by-n */ - int Ap [ ], /* size n+1, column pointers */ - int Ai [ ], /* size nz, row indices */ + int32_t n, /* A is n-by-n */ + int32_t Ap [ ], /* size n+1, column pointers */ + int32_t Ai [ ], /* size nz, row indices */ klu_common *Common ) ; -klu_l_symbolic *klu_l_analyze (SuiteSparse_long, SuiteSparse_long *, - SuiteSparse_long *, klu_l_common *Common) ; +klu_l_symbolic *klu_l_analyze (int64_t, int64_t *, int64_t *, + klu_l_common *Common) ; /* -------------------------------------------------------------------------- */ @@ -265,17 +270,16 @@ klu_l_symbolic *klu_l_analyze (SuiteSparse_long, SuiteSparse_long *, klu_symbolic *klu_analyze_given ( /* inputs, not modified */ - int n, /* A is n-by-n */ - int Ap [ ], /* size n+1, column pointers */ - int Ai [ ], /* size nz, row indices */ - int P [ ], /* size n, user's row permutation (may be NULL) */ - int Q [ ], /* size n, user's column permutation (may be NULL) */ + int32_t n, /* A is n-by-n */ + int32_t Ap [ ], /* size n+1, column pointers */ + int32_t Ai [ ], /* size nz, row indices */ + int32_t P [ ], /* size n, user's row permutation (may be NULL) */ + int32_t Q [ ], /* size n, user's column permutation (may be NULL) */ klu_common *Common ) ; -klu_l_symbolic *klu_l_analyze_given (SuiteSparse_long, SuiteSparse_long *, - SuiteSparse_long *, SuiteSparse_long *, SuiteSparse_long *, - klu_l_common *) ; +klu_l_symbolic *klu_l_analyze_given (int64_t, int64_t *, int64_t *, int64_t *, + int64_t *, klu_l_common *) ; /* -------------------------------------------------------------------------- */ @@ -285,8 +289,8 @@ klu_l_symbolic *klu_l_analyze_given (SuiteSparse_long, SuiteSparse_long *, klu_numeric *klu_factor /* returns KLU_OK if OK, < 0 if error */ ( /* inputs, not modified */ - int Ap [ ], /* size n+1, column pointers */ - int Ai [ ], /* size nz, row indices */ + int32_t Ap [ ], /* size n+1, column pointers */ + int32_t Ai [ ], /* size nz, row indices */ double Ax [ ], /* size nz, numerical values */ klu_symbolic *Symbolic, klu_common *Common @@ -295,19 +299,19 @@ klu_numeric *klu_factor /* returns KLU_OK if OK, < 0 if error */ klu_numeric *klu_z_factor /* returns KLU_OK if OK, < 0 if error */ ( /* inputs, not modified */ - int Ap [ ], /* size n+1, column pointers */ - int Ai [ ], /* size nz, row indices */ + int32_t Ap [ ], /* size n+1, column pointers */ + int32_t Ai [ ], /* size nz, row indices */ double Ax [ ], /* size 2*nz, numerical values (real,imag pairs) */ klu_symbolic *Symbolic, klu_common *Common ) ; -/* long / real version */ -klu_l_numeric *klu_l_factor (SuiteSparse_long *, SuiteSparse_long *, double *, +/* int64_t / real version */ +klu_l_numeric *klu_l_factor (int64_t *, int64_t *, double *, klu_l_symbolic *, klu_l_common *) ; -/* long / complex version */ -klu_l_numeric *klu_zl_factor (SuiteSparse_long *, SuiteSparse_long *, double *, +/* int64_t / complex version */ +klu_l_numeric *klu_zl_factor (int64_t *, int64_t *, double *, klu_l_symbolic *, klu_l_common *) ; @@ -320,8 +324,8 @@ int klu_solve /* inputs, not modified */ klu_symbolic *Symbolic, klu_numeric *Numeric, - int ldim, /* leading dimension of B */ - int nrhs, /* number of right-hand-sides */ + int32_t ldim, /* leading dimension of B */ + int32_t nrhs, /* number of right-hand-sides */ /* right-hand-side on input, overwritten with solution to Ax=b on output */ double B [ ], /* size ldim*nrhs */ @@ -333,19 +337,19 @@ int klu_z_solve /* inputs, not modified */ klu_symbolic *Symbolic, klu_numeric *Numeric, - int ldim, /* leading dimension of B */ - int nrhs, /* number of right-hand-sides */ + int32_t ldim, /* leading dimension of B */ + int32_t nrhs, /* number of right-hand-sides */ /* right-hand-side on input, overwritten with solution to Ax=b on output */ double B [ ], /* size 2*ldim*nrhs */ klu_common *Common ) ; -SuiteSparse_long klu_l_solve (klu_l_symbolic *, klu_l_numeric *, - SuiteSparse_long, SuiteSparse_long, double *, klu_l_common *) ; +int klu_l_solve (klu_l_symbolic *, klu_l_numeric *, + int64_t, int64_t, double *, klu_l_common *) ; -SuiteSparse_long klu_zl_solve (klu_l_symbolic *, klu_l_numeric *, - SuiteSparse_long, SuiteSparse_long, double *, klu_l_common *) ; +int klu_zl_solve (klu_l_symbolic *, klu_l_numeric *, + int64_t, int64_t, double *, klu_l_common *) ; /* -------------------------------------------------------------------------- */ @@ -357,8 +361,8 @@ int klu_tsolve /* inputs, not modified */ klu_symbolic *Symbolic, klu_numeric *Numeric, - int ldim, /* leading dimension of B */ - int nrhs, /* number of right-hand-sides */ + int32_t ldim, /* leading dimension of B */ + int32_t nrhs, /* number of right-hand-sides */ /* right-hand-side on input, overwritten with solution to Ax=b on output */ double B [ ], /* size ldim*nrhs */ @@ -370,8 +374,8 @@ int klu_z_tsolve /* inputs, not modified */ klu_symbolic *Symbolic, klu_numeric *Numeric, - int ldim, /* leading dimension of B */ - int nrhs, /* number of right-hand-sides */ + int32_t ldim, /* leading dimension of B */ + int32_t nrhs, /* number of right-hand-sides */ /* right-hand-side on input, overwritten with solution to Ax=b on output */ double B [ ], /* size 2*ldim*nrhs */ @@ -380,12 +384,11 @@ int klu_z_tsolve ) ; -SuiteSparse_long klu_l_tsolve (klu_l_symbolic *, klu_l_numeric *, - SuiteSparse_long, SuiteSparse_long, double *, klu_l_common *) ; +int klu_l_tsolve (klu_l_symbolic *, klu_l_numeric *, + int64_t, int64_t, double *, klu_l_common *) ; -SuiteSparse_long klu_zl_tsolve (klu_l_symbolic *, klu_l_numeric *, - SuiteSparse_long, SuiteSparse_long, double *, SuiteSparse_long, - klu_l_common * ) ; +int klu_zl_tsolve (klu_l_symbolic *, klu_l_numeric *, + int64_t, int64_t, double *, int, klu_l_common * ) ; /* -------------------------------------------------------------------------- */ @@ -395,8 +398,8 @@ SuiteSparse_long klu_zl_tsolve (klu_l_symbolic *, klu_l_numeric *, int klu_refactor /* return TRUE if successful, FALSE otherwise */ ( /* inputs, not modified */ - int Ap [ ], /* size n+1, column pointers */ - int Ai [ ], /* size nz, row indices */ + int32_t Ap [ ], /* size n+1, column pointers */ + int32_t Ai [ ], /* size nz, row indices */ double Ax [ ], /* size nz, numerical values */ klu_symbolic *Symbolic, /* input, and numerical values modified on output */ @@ -407,8 +410,8 @@ int klu_refactor /* return TRUE if successful, FALSE otherwise */ int klu_z_refactor /* return TRUE if successful, FALSE otherwise */ ( /* inputs, not modified */ - int Ap [ ], /* size n+1, column pointers */ - int Ai [ ], /* size nz, row indices */ + int32_t Ap [ ], /* size n+1, column pointers */ + int32_t Ai [ ], /* size nz, row indices */ double Ax [ ], /* size 2*nz, numerical values */ klu_symbolic *Symbolic, /* input, and numerical values modified on output */ @@ -416,10 +419,10 @@ int klu_z_refactor /* return TRUE if successful, FALSE otherwise */ klu_common *Common ) ; -SuiteSparse_long klu_l_refactor (SuiteSparse_long *, SuiteSparse_long *, +int klu_l_refactor (int64_t *, int64_t *, double *, klu_l_symbolic *, klu_l_numeric *, klu_l_common *) ; -SuiteSparse_long klu_zl_refactor (SuiteSparse_long *, SuiteSparse_long *, +int klu_zl_refactor (int64_t *, int64_t *, double *, klu_l_symbolic *, klu_l_numeric *, klu_l_common *) ; @@ -433,7 +436,7 @@ int klu_free_symbolic klu_common *Common ) ; -SuiteSparse_long klu_l_free_symbolic (klu_l_symbolic **, klu_l_common *) ; +int klu_l_free_symbolic (klu_l_symbolic **, klu_l_common *) ; /* -------------------------------------------------------------------------- */ @@ -455,8 +458,8 @@ int klu_z_free_numeric klu_common *Common ) ; -SuiteSparse_long klu_l_free_numeric (klu_l_numeric **, klu_l_common *) ; -SuiteSparse_long klu_zl_free_numeric (klu_l_numeric **, klu_l_common *) ; +int klu_l_free_numeric (klu_l_numeric **, klu_l_common *) ; +int klu_zl_free_numeric (klu_l_numeric **, klu_l_common *) ; /* -------------------------------------------------------------------------- */ @@ -483,10 +486,8 @@ int klu_z_sort klu_common *Common ) ; -SuiteSparse_long klu_l_sort (klu_l_symbolic *, klu_l_numeric *, - klu_l_common *) ; -SuiteSparse_long klu_zl_sort (klu_l_symbolic *, klu_l_numeric *, - klu_l_common *) ; +int klu_l_sort (klu_l_symbolic *, klu_l_numeric *, klu_l_common *) ; +int klu_zl_sort (klu_l_symbolic *, klu_l_numeric *, klu_l_common *) ; /* -------------------------------------------------------------------------- */ @@ -511,10 +512,8 @@ int klu_z_flops klu_common *Common ) ; -SuiteSparse_long klu_l_flops (klu_l_symbolic *, klu_l_numeric *, - klu_l_common *) ; -SuiteSparse_long klu_zl_flops (klu_l_symbolic *, klu_l_numeric *, - klu_l_common *) ; +int klu_l_flops (klu_l_symbolic *, klu_l_numeric *, klu_l_common *) ; +int klu_zl_flops (klu_l_symbolic *, klu_l_numeric *, klu_l_common *) ; /* -------------------------------------------------------------------------- */ @@ -536,8 +535,8 @@ SuiteSparse_long klu_zl_flops (klu_l_symbolic *, klu_l_numeric *, int klu_rgrowth ( - int Ap [ ], - int Ai [ ], + int32_t Ap [ ], + int32_t Ai [ ], double Ax [ ], klu_symbolic *Symbolic, klu_numeric *Numeric, @@ -546,18 +545,18 @@ int klu_rgrowth int klu_z_rgrowth ( - int Ap [ ], - int Ai [ ], + int32_t Ap [ ], + int32_t Ai [ ], double Ax [ ], klu_symbolic *Symbolic, klu_numeric *Numeric, klu_common *Common /* Common->rgrowth = reciprocal pivot growth */ ) ; -SuiteSparse_long klu_l_rgrowth (SuiteSparse_long *, SuiteSparse_long *, +int klu_l_rgrowth (int64_t *, int64_t *, double *, klu_l_symbolic *, klu_l_numeric *, klu_l_common *) ; -SuiteSparse_long klu_zl_rgrowth (SuiteSparse_long *, SuiteSparse_long *, +int klu_zl_rgrowth (int64_t *, int64_t *, double *, klu_l_symbolic *, klu_l_numeric *, klu_l_common *) ; @@ -571,7 +570,7 @@ SuiteSparse_long klu_zl_rgrowth (SuiteSparse_long *, SuiteSparse_long *, int klu_condest ( - int Ap [ ], /* size n+1, column pointers, not modified */ + int32_t Ap [ ], /* size n+1, column pointers, not modified */ double Ax [ ], /* size nz = Ap[n], numerical values, not modified*/ klu_symbolic *Symbolic, /* symbolic analysis, not modified */ klu_numeric *Numeric, /* numeric factorization, not modified */ @@ -580,17 +579,17 @@ int klu_condest int klu_z_condest ( - int Ap [ ], + int32_t Ap [ ], double Ax [ ], /* size 2*nz */ klu_symbolic *Symbolic, klu_numeric *Numeric, klu_common *Common /* result returned in Common->condest */ ) ; -SuiteSparse_long klu_l_condest (SuiteSparse_long *, double *, klu_l_symbolic *, +int klu_l_condest (int64_t *, double *, klu_l_symbolic *, klu_l_numeric *, klu_l_common *) ; -SuiteSparse_long klu_zl_condest (SuiteSparse_long *, double *, klu_l_symbolic *, +int klu_zl_condest (int64_t *, double *, klu_l_symbolic *, klu_l_numeric *, klu_l_common *) ; @@ -612,12 +611,8 @@ int klu_z_rcond klu_common *Common /* result in Common->rcond */ ) ; -SuiteSparse_long klu_l_rcond (klu_l_symbolic *, klu_l_numeric *, - klu_l_common *) ; - -SuiteSparse_long klu_zl_rcond (klu_l_symbolic *, klu_l_numeric *, - klu_l_common *) ; - +int klu_l_rcond (klu_l_symbolic *, klu_l_numeric *, klu_l_common *) ; +int klu_zl_rcond (klu_l_symbolic *, klu_l_numeric *, klu_l_common *) ; /* -------------------------------------------------------------------------- */ /* klu_scale */ @@ -627,14 +622,14 @@ int klu_scale /* return TRUE if successful, FALSE otherwise */ ( /* inputs, not modified */ int scale, /* <0: none, no error check; 0: none, 1: sum, 2: max */ - int n, - int Ap [ ], /* size n+1, column pointers */ - int Ai [ ], /* size nz, row indices */ + int32_t n, + int32_t Ap [ ], /* size n+1, column pointers */ + int32_t Ai [ ], /* size nz, row indices */ double Ax [ ], /* outputs, not defined on input */ double Rs [ ], /* workspace, not defined on input or output */ - int W [ ], /* size n, can be NULL */ + int32_t W [ ], /* size n, can be NULL */ klu_common *Common ) ; @@ -642,24 +637,22 @@ int klu_z_scale /* return TRUE if successful, FALSE otherwise */ ( /* inputs, not modified */ int scale, /* <0: none, no error check; 0: none, 1: sum, 2: max */ - int n, - int Ap [ ], /* size n+1, column pointers */ - int Ai [ ], /* size nz, row indices */ + int32_t n, + int32_t Ap [ ], /* size n+1, column pointers */ + int32_t Ai [ ], /* size nz, row indices */ double Ax [ ], /* outputs, not defined on input */ double Rs [ ], /* workspace, not defined on input or output */ - int W [ ], /* size n, can be NULL */ + int32_t W [ ], /* size n, can be NULL */ klu_common *Common ) ; -SuiteSparse_long klu_l_scale (SuiteSparse_long, SuiteSparse_long, - SuiteSparse_long *, SuiteSparse_long *, double *, - double *, SuiteSparse_long *, klu_l_common *) ; +int klu_l_scale (int, int64_t, int64_t *, int64_t *, double *, + double *, int64_t *, klu_l_common *) ; -SuiteSparse_long klu_zl_scale (SuiteSparse_long, SuiteSparse_long, - SuiteSparse_long *, SuiteSparse_long *, double *, - double *, SuiteSparse_long *, klu_l_common *) ; +int klu_zl_scale (int, int64_t, int64_t *, int64_t *, double *, + double *, int64_t *, klu_l_common *) ; /* -------------------------------------------------------------------------- */ @@ -675,31 +668,31 @@ int klu_extract /* returns TRUE if successful, FALSE otherwise */ /* outputs, either allocated on input, or ignored otherwise */ /* L */ - int *Lp, /* size n+1 */ - int *Li, /* size Numeric->lnz */ + int32_t *Lp, /* size n+1 */ + int32_t *Li, /* size Numeric->lnz */ double *Lx, /* size Numeric->lnz */ /* U */ - int *Up, /* size n+1 */ - int *Ui, /* size Numeric->unz */ + int32_t *Up, /* size n+1 */ + int32_t *Ui, /* size Numeric->unz */ double *Ux, /* size Numeric->unz */ /* F */ - int *Fp, /* size n+1 */ - int *Fi, /* size Numeric->nzoff */ + int32_t *Fp, /* size n+1 */ + int32_t *Fi, /* size Numeric->nzoff */ double *Fx, /* size Numeric->nzoff */ /* P, row permutation */ - int *P, /* size n */ + int32_t *P, /* size n */ /* Q, column permutation */ - int *Q, /* size n */ + int32_t *Q, /* size n */ /* Rs, scale factors */ double *Rs, /* size n */ /* R, block boundaries */ - int *R, /* size Symbolic->nblocks+1 (nblocks is at most n) */ + int32_t *R, /* size Symbolic->nblocks+1 (nblocks is at most n) */ klu_common *Common ) ; @@ -714,51 +707,51 @@ int klu_z_extract /* returns TRUE if successful, FALSE otherwise */ /* outputs, all of which must be allocated on input */ /* L */ - int *Lp, /* size n+1 */ - int *Li, /* size nnz(L) */ + int32_t *Lp, /* size n+1 */ + int32_t *Li, /* size nnz(L) */ double *Lx, /* size nnz(L) */ double *Lz, /* size nnz(L) for the complex case, ignored if real */ /* U */ - int *Up, /* size n+1 */ - int *Ui, /* size nnz(U) */ + int32_t *Up, /* size n+1 */ + int32_t *Ui, /* size nnz(U) */ double *Ux, /* size nnz(U) */ double *Uz, /* size nnz(U) for the complex case, ignored if real */ /* F */ - int *Fp, /* size n+1 */ - int *Fi, /* size nnz(F) */ + int32_t *Fp, /* size n+1 */ + int32_t *Fi, /* size nnz(F) */ double *Fx, /* size nnz(F) */ double *Fz, /* size nnz(F) for the complex case, ignored if real */ /* P, row permutation */ - int *P, /* size n */ + int32_t *P, /* size n */ /* Q, column permutation */ - int *Q, /* size n */ + int32_t *Q, /* size n */ /* Rs, scale factors */ double *Rs, /* size n */ /* R, block boundaries */ - int *R, /* size Symbolic->nblocks+1 (nblocks is at most n) */ + int32_t *R, /* size Symbolic->nblocks+1 (nblocks is at most n) */ klu_common *Common ) ; -SuiteSparse_long klu_l_extract (klu_l_numeric *, klu_l_symbolic *, - SuiteSparse_long *, SuiteSparse_long *, double *, - SuiteSparse_long *, SuiteSparse_long *, double *, - SuiteSparse_long *, SuiteSparse_long *, double *, - SuiteSparse_long *, SuiteSparse_long *, double *, - SuiteSparse_long *, klu_l_common *) ; +int klu_l_extract (klu_l_numeric *, klu_l_symbolic *, + int64_t *, int64_t *, double *, + int64_t *, int64_t *, double *, + int64_t *, int64_t *, double *, + int64_t *, int64_t *, double *, + int64_t *, klu_l_common *) ; -SuiteSparse_long klu_zl_extract (klu_l_numeric *, klu_l_symbolic *, - SuiteSparse_long *, SuiteSparse_long *, double *, double *, - SuiteSparse_long *, SuiteSparse_long *, double *, double *, - SuiteSparse_long *, SuiteSparse_long *, double *, double *, - SuiteSparse_long *, SuiteSparse_long *, double *, - SuiteSparse_long *, klu_l_common *) ; +int klu_zl_extract (klu_l_numeric *, klu_l_symbolic *, + int64_t *, int64_t *, double *, double *, + int64_t *, int64_t *, double *, double *, + int64_t *, int64_t *, double *, double *, + int64_t *, int64_t *, double *, + int64_t *, klu_l_common *) ; /* -------------------------------------------------------------------------- */ @@ -797,7 +790,9 @@ void *klu_realloc /* returns pointer to reallocated block */ ) ; void *klu_l_malloc (size_t, size_t, klu_l_common *) ; + 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 *) ; @@ -819,11 +814,12 @@ void *klu_l_realloc (size_t, size_t, size_t, void *, klu_l_common *) ; * #endif */ -#define KLU_DATE "Mar 12, 2018" +#define KLU_DATE "Jan 17, 2023" +#define KLU_MAIN_VERSION 2 +#define KLU_SUB_VERSION 0 +#define KLU_SUBSUB_VERSION 3 + #define KLU_VERSION_CODE(main,sub) ((main) * 1000 + (sub)) -#define KLU_MAIN_VERSION 1 -#define KLU_SUB_VERSION 3 -#define KLU_SUBSUB_VERSION 9 #define KLU_VERSION KLU_VERSION_CODE(KLU_MAIN_VERSION,KLU_SUB_VERSION) #ifdef __cplusplus diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Include/klu_internal.h b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Include/klu_internal.h index f3d63c89e..60fa514a3 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Include/klu_internal.h +++ b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Include/klu_internal.h @@ -1,6 +1,12 @@ -/* ========================================================================== */ -/* === KLU/Include/klu_internal.h =========================================== */ -/* ========================================================================== */ +//------------------------------------------------------------------------------ +// KLU/Include/klu_internal.h: internal include file for KLU +//------------------------------------------------------------------------------ + +// KLU, Copyright (c) 2004-2022, University of Florida. All Rights Reserved. +// Authors: Timothy A. Davis and Ekanathan Palamadai. +// SPDX-License-Identifier: LGPL-2.1+ + +//------------------------------------------------------------------------------ /* For internal use in KLU routines only, not for user programs */ @@ -31,11 +37,7 @@ /* ========================================================================== */ -#include #include -#include -#include -#include #undef ASSERT #ifndef NDEBUG diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Include/klu_version.h b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Include/klu_version.h index e762a435c..f2d4915c9 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Include/klu_version.h +++ b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Include/klu_version.h @@ -1,14 +1,24 @@ +//------------------------------------------------------------------------------ +// KLU/Include/klu_version.h: internal include file for KLU +//------------------------------------------------------------------------------ + +// KLU, Copyright (c) 2004-2022, University of Florida. All Rights Reserved. +// Authors: Timothy A. Davis and Ekanathan Palamadai. +// SPDX-License-Identifier: LGPL-2.1+ + +//------------------------------------------------------------------------------ + #ifndef _KLU_VERSION_H #define _KLU_VERSION_H #ifdef DLONG -#define Int SuiteSparse_long -#define Int_id SuiteSparse_long_id -#define Int_MAX SuiteSparse_long_max +#define Int int64_t +#define Int_id "%" PRId64 +#define Int_MAX INT64_MAX #else -#define Int int +#define Int int32_t #define Int_id "%d" -#define Int_MAX INT_MAX +#define Int_MAX INT32_MAX #endif #define NPRINT @@ -41,6 +51,7 @@ #ifdef DLONG +// zl: complex int64_t #define KLU_scale klu_zl_scale #define KLU_solve klu_zl_solve #define KLU_tsolve klu_zl_tsolve @@ -64,6 +75,7 @@ #else +// z: complex int32_t #define KLU_scale klu_z_scale #define KLU_solve klu_z_solve #define KLU_tsolve klu_z_tsolve @@ -91,6 +103,7 @@ #ifdef DLONG +// l: int64_t #define KLU_scale klu_l_scale #define KLU_solve klu_l_solve #define KLU_tsolve klu_l_tsolve @@ -114,6 +127,7 @@ #else +// no prefix: int32_t #define KLU_scale klu_scale #define KLU_solve klu_solve #define KLU_tsolve klu_tsolve @@ -142,6 +156,7 @@ #ifdef DLONG +// l: int64_t #define KLU_analyze klu_l_analyze #define KLU_analyze_given klu_l_analyze_given #define KLU_alloc_symbolic klu_l_alloc_symbolic @@ -166,6 +181,7 @@ #else +// no prefiex: int64_t #define KLU_analyze klu_analyze #define KLU_analyze_given klu_analyze_given #define KLU_alloc_symbolic klu_alloc_symbolic @@ -305,10 +321,10 @@ typedef double Unit ; that possibility. ANSI C *does* guarantee that an array of structs has the same size as n times the size of one struct. - The ANSI C99 version of the C language includes a "double _Complex" type. + The ANSI C11 version of the C language includes a "double complex" type. It should be possible in that case to do the following: - #define Entry double _Complex + #define Entry double complex and remove the Double_Complex struct. The macros, below, could then be replaced with instrinsic operators. Note that the #define Real and @@ -355,7 +371,7 @@ typedef Double_Complex Unit ; #define SPLIT(sz) ((sz) != (double *) NULL) /* c = (s1) + (s2)*i, if s2 is null, then X is in "packed" format (compatible - * with Entry and ANSI C99 double _Complex type). */ + * with Entry and ANSI C11 double complex type). */ /*#define ASSIGN(c,s1,s2,p,split) \ { \ if (split) \ diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Lib/Makefile b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Lib/Makefile deleted file mode 100644 index 2dc62cb4a..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Lib/Makefile +++ /dev/null @@ -1,291 +0,0 @@ -#------------------------------------------------------------------------------- -# KLU Lib/Makefile -#------------------------------------------------------------------------------- - -LIBRARY = libklu -VERSION = 1.3.8 -SO_VERSION = 1 - -default: library - -ccode: all - -include ../../SuiteSparse_config/SuiteSparse_config.mk - -# KLU depends on BTF, AMD, COLAMD, and SuiteSparse_config -LDLIBS += -lamd -lcolamd -lbtf -lsuitesparseconfig - -# compile and install in SuiteSparse/lib -library: - $(MAKE) install INSTALL=$(SUITESPARSE) - -# for testing only: -# TEST = -DTESTING - -C = $(CC) $(CF) - -INC = ../Include/klu.h ../Include/klu_internal.h ../Include/klu_version.h \ - ../../SuiteSparse_config/SuiteSparse_config.h - -I = -I../../AMD/Include -I../../COLAMD/Include -I../../BTF/Include \ - -I../Include -I../../SuiteSparse_config - -all: library - -KLU_D = klu_d.o klu_d_kernel.o klu_d_dump.o \ - klu_d_factor.o klu_d_free_numeric.o klu_d_solve.o \ - klu_d_scale.o klu_d_refactor.o \ - klu_d_tsolve.o klu_d_diagnostics.o klu_d_sort.o klu_d_extract.o - -KLU_Z = klu_z.o klu_z_kernel.o klu_z_dump.o \ - klu_z_factor.o klu_z_free_numeric.o klu_z_solve.o \ - klu_z_scale.o klu_z_refactor.o \ - klu_z_tsolve.o klu_z_diagnostics.o klu_z_sort.o klu_z_extract.o - -KLU_L = klu_l.o klu_l_kernel.o klu_l_dump.o \ - klu_l_factor.o klu_l_free_numeric.o klu_l_solve.o \ - klu_l_scale.o klu_l_refactor.o \ - klu_l_tsolve.o klu_l_diagnostics.o klu_l_sort.o klu_l_extract.o - -KLU_ZL = klu_zl.o klu_zl_kernel.o klu_zl_dump.o \ - klu_zl_factor.o klu_zl_free_numeric.o klu_zl_solve.o \ - klu_zl_scale.o klu_zl_refactor.o \ - klu_zl_tsolve.o klu_zl_diagnostics.o klu_zl_sort.o klu_zl_extract.o - -COMMON = \ - klu_free_symbolic.o klu_defaults.o klu_analyze_given.o \ - klu_analyze.o klu_memory.o \ - klu_l_free_symbolic.o klu_l_defaults.o klu_l_analyze_given.o \ - klu_l_analyze.o klu_l_memory.o - -OBJ = $(COMMON) $(KLU_D) $(KLU_Z) $(KLU_L) $(KLU_ZL) - -static: $(AR_TARGET) - -$(AR_TARGET): $(OBJ) - $(ARCHIVE) $@ $^ - - $(RANLIB) $@ - -$(OBJ): $(INC) - -#------------------------------------------------------------------------------- - -klu_d.o: ../Source/klu.c - $(C) -c $(I) $< -o $@ - -klu_z.o: ../Source/klu.c - $(C) -c -DCOMPLEX $(I) $< -o $@ - -klu_d_kernel.o: ../Source/klu_kernel.c - $(C) -c $(I) $< -o $@ - -klu_z_kernel.o: ../Source/klu_kernel.c - $(C) -c -DCOMPLEX $(I) $< -o $@ - -klu_d_sort.o: ../Source/klu_sort.c - $(C) -c $(I) $< -o $@ - -klu_z_sort.o: ../Source/klu_sort.c - $(C) -c -DCOMPLEX $(I) $< -o $@ - -klu_d_diagnostics.o: ../Source/klu_diagnostics.c - $(C) -c $(I) $< -o $@ - -klu_z_diagnostics.o: ../Source/klu_diagnostics.c - $(C) -c -DCOMPLEX $(I) $< -o $@ - -klu_d_dump.o: ../Source/klu_dump.c - $(C) -c $(I) $< -o $@ - -klu_z_dump.o: ../Source/klu_dump.c - $(C) -c -DCOMPLEX $(I) $< -o $@ - -klu_d_factor.o: ../Source/klu_factor.c - $(C) -c $(I) $< -o $@ - -klu_z_factor.o: ../Source/klu_factor.c - $(C) -c -DCOMPLEX $(I) $< -o $@ - -klu_d_free_numeric.o: ../Source/klu_free_numeric.c - $(C) -c $(I) $< -o $@ - -klu_z_free_numeric.o: ../Source/klu_free_numeric.c - $(C) -c -DCOMPLEX $(I) $< -o $@ - -klu_d_extract.o: ../Source/klu_extract.c - $(C) -c $(I) $< -o $@ - -klu_z_extract.o: ../Source/klu_extract.c - $(C) -c -DCOMPLEX $(I) $< -o $@ - -klu_d_refactor.o: ../Source/klu_refactor.c - $(C) -c $(I) $< -o $@ - -klu_z_refactor.o: ../Source/klu_refactor.c - $(C) -c -DCOMPLEX $(I) $< -o $@ - -klu_d_scale.o: ../Source/klu_scale.c - $(C) -c $(I) $< -o $@ - -klu_z_scale.o: ../Source/klu_scale.c - $(C) -c -DCOMPLEX $(I) $< -o $@ - -klu_d_solve.o: ../Source/klu_solve.c - $(C) -c $(I) $< -o $@ - -klu_z_solve.o: ../Source/klu_solve.c - $(C) -c -DCOMPLEX $(I) $< -o $@ - -klu_d_tsolve.o: ../Source/klu_tsolve.c - $(C) -c $(I) $< -o $@ - -klu_z_tsolve.o: ../Source/klu_tsolve.c - $(C) -c -DCOMPLEX $(I) $< -o $@ - -#------------------------------------------------------------------------------- - -klu_analyze.o: ../Source/klu_analyze.c - $(C) -c $(I) $< -o $@ - -klu_analyze_given.o: ../Source/klu_analyze_given.c - $(C) -c $(I) $< -o $@ - -klu_defaults.o: ../Source/klu_defaults.c - $(C) -c $(I) $< -o $@ - -klu_free_symbolic.o: ../Source/klu_free_symbolic.c - $(C) -c $(I) $< -o $@ - -klu_memory.o: ../Source/klu_memory.c - $(C) -c $(I) $< -o $@ - -#------------------------------------------------------------------------------- - -purge: distclean - -distclean: clean - - $(RM) -r $(PURGE) - -clean: - - $(RM) -r $(CLEAN) - -#------------------------------------------------------------------------------- - -klu_l.o: ../Source/klu.c - $(C) -c -DDLONG $(I) $< -o $@ - -klu_zl.o: ../Source/klu.c - $(C) -c -DCOMPLEX -DDLONG $(I) $< -o $@ - -klu_l_kernel.o: ../Source/klu_kernel.c - $(C) -c -DDLONG $(I) $< -o $@ - -klu_zl_kernel.o: ../Source/klu_kernel.c - $(C) -c -DCOMPLEX -DDLONG $(I) $< -o $@ - -klu_l_sort.o: ../Source/klu_sort.c - $(C) -c -DDLONG $(I) $< -o $@ - -klu_zl_sort.o: ../Source/klu_sort.c - $(C) -c -DCOMPLEX -DDLONG $(I) $< -o $@ - -klu_l_diagnostics.o: ../Source/klu_diagnostics.c - $(C) -c -DDLONG $(I) $< -o $@ - -klu_zl_diagnostics.o: ../Source/klu_diagnostics.c - $(C) -c -DCOMPLEX -DDLONG $(I) $< -o $@ - -klu_l_dump.o: ../Source/klu_dump.c - $(C) -c -DDLONG $(I) $< -o $@ - -klu_zl_dump.o: ../Source/klu_dump.c - $(C) -c -DCOMPLEX -DDLONG $(I) $< -o $@ - -klu_l_factor.o: ../Source/klu_factor.c - $(C) -c -DDLONG $(I) $< -o $@ - -klu_zl_factor.o: ../Source/klu_factor.c - $(C) -c -DCOMPLEX -DDLONG $(I) $< -o $@ - -klu_l_free_numeric.o: ../Source/klu_free_numeric.c - $(C) -c -DDLONG $(I) $< -o $@ - -klu_zl_free_numeric.o: ../Source/klu_free_numeric.c - $(C) -c -DCOMPLEX -DDLONG $(I) $< -o $@ - -klu_l_extract.o: ../Source/klu_extract.c - $(C) -c -DDLONG $(I) $< -o $@ - -klu_zl_extract.o: ../Source/klu_extract.c - $(C) -c -DCOMPLEX -DDLONG $(I) $< -o $@ - -klu_l_refactor.o: ../Source/klu_refactor.c - $(C) -c -DDLONG $(I) $< -o $@ - -klu_zl_refactor.o: ../Source/klu_refactor.c - $(C) -c -DCOMPLEX -DDLONG $(I) $< -o $@ - -klu_l_scale.o: ../Source/klu_scale.c - $(C) -c -DDLONG $(I) $< -o $@ - -klu_zl_scale.o: ../Source/klu_scale.c - $(C) -c -DCOMPLEX -DDLONG $(I) $< -o $@ - -klu_l_solve.o: ../Source/klu_solve.c - $(C) -c -DDLONG $(I) $< -o $@ - -klu_zl_solve.o: ../Source/klu_solve.c - $(C) -c -DCOMPLEX -DDLONG $(I) $< -o $@ - -klu_l_tsolve.o: ../Source/klu_tsolve.c - $(C) -c -DDLONG $(I) $< -o $@ - -klu_zl_tsolve.o: ../Source/klu_tsolve.c - $(C) -c -DCOMPLEX -DDLONG $(I) $< -o $@ - -#------------------------------------------------------------------------------- - -klu_l_analyze.o: ../Source/klu_analyze.c - $(C) -c -DDLONG $(I) $< -o $@ - -klu_l_analyze_given.o: ../Source/klu_analyze_given.c - $(C) -c -DDLONG $(I) $< -o $@ - -klu_l_defaults.o: ../Source/klu_defaults.c - $(C) -c -DDLONG $(I) $< -o $@ - -klu_l_free_symbolic.o: ../Source/klu_free_symbolic.c - $(C) -c -DDLONG $(I) $< -o $@ - -klu_l_memory.o: ../Source/klu_memory.c - $(C) -c -DDLONG $(I) $< -o $@ - -#------------------------------------------------------------------------------- - -# install KLU -install: $(AR_TARGET) $(INSTALL_LIB)/$(SO_TARGET) - -$(INSTALL_LIB)/$(SO_TARGET): $(OBJ) - @mkdir -p $(INSTALL_LIB) - @mkdir -p $(INSTALL_INCLUDE) - @mkdir -p $(INSTALL_DOC) - $(CC) $(SO_OPTS) $^ -o $@ $(LDLIBS) - ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_PLAIN) ) - ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_MAIN) ) - $(CP) ../Include/klu.h $(INSTALL_INCLUDE) - $(CP) ../Doc/KLU_UserGuide.pdf $(INSTALL_DOC) - $(CP) ../README.txt $(INSTALL_DOC)/KLU_README.txt - chmod 755 $(INSTALL_LIB)/$(SO_TARGET) - chmod 644 $(INSTALL_INCLUDE)/klu.h - chmod 644 $(INSTALL_DOC)/KLU_UserGuide.pdf - chmod 644 $(INSTALL_DOC)/KLU_README.txt - -# uninstall KLU -uninstall: - $(RM) $(INSTALL_LIB)/$(SO_TARGET) - $(RM) $(INSTALL_LIB)/$(SO_PLAIN) - $(RM) $(INSTALL_LIB)/$(SO_MAIN) - $(RM) $(INSTALL_INCLUDE)/klu.h - $(RM) $(INSTALL_DOC)/KLU_UserGuide.pdf - $(RM) $(INSTALL_DOC)/KLU_README.txt - diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/MATLAB/Contents.m b/deps/AMICI/ThirdParty/SuiteSparse/KLU/MATLAB/Contents.m deleted file mode 100644 index ee6c1802d..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/MATLAB/Contents.m +++ /dev/null @@ -1,16 +0,0 @@ -% KLU: a "Clark Kent" LU factorization algorithm -% -% klu - sparse left-looking LU factorization, using a block triangular form. -% klu_install - compiles and installs the KLU, BTF, AMD, and COLAMD mexFunctions -% klu_demo - KLU demo -% klu_make - compiles the KLU mexFunctions -% -% Example: -% -% LU = klu (A) ; -% x = klu (A, '\', b) ; -% x = klu (LU, '\', b) ; -% -% Copyright 2004-2009, Univ. of Florida -% KLU Version 1.0. - diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/MATLAB/Test/klu_test.m b/deps/AMICI/ThirdParty/SuiteSparse/KLU/MATLAB/Test/klu_test.m deleted file mode 100644 index f6f0ceb9b..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/MATLAB/Test/klu_test.m +++ /dev/null @@ -1,18 +0,0 @@ -function klu_test (nmat) -%klu_test KLU test -% Example: -% klu_test -% -% See also klu - -% Copyright 2004-2012, University of Florida - -if (nargin < 1) - nmat = 200 ; -end - -test1 (nmat) ; -test2 (nmat) ; -test3 ; -test4 (nmat) ; -test5 ; diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/MATLAB/Test/test1.m b/deps/AMICI/ThirdParty/SuiteSparse/KLU/MATLAB/Test/test1.m deleted file mode 100644 index 8c8e493a2..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/MATLAB/Test/test1.m +++ /dev/null @@ -1,114 +0,0 @@ -function test1 (nmat) -%test1: KLU test -% Example: -% test1 -% -% See also klu - -% Copyright 2004-2012, University of Florida - -clear functions -% rand ('state', 0) ; - -index = UFget ; -f = find (index.nrows == index.ncols & index.isReal) ; -[ignore i] = sort (index.nnz (f)) ; %#ok -f = f (i) ; - -h = waitbar (0, 'KLU test 1 of 5') ; - -if (nargin < 1) - nmat = 500 ; -end -nmat = min (nmat, length (f)) ; - -% just use the first 100 matrices -nmat = min (nmat, 100) ; - -f = f (1:nmat) ; - -% f = 274 -% f = 101 ; % MATLAB condest is poor - -nmat = length (f) ; - -conds_klu = ones (1,nmat) ; -conds_matlab = ones (1,nmat) ; - -clf - -% try - - for k = 1:nmat - - waitbar (k/nmat, h) ; - - i = f (k) ; -% try - c = -1 ; - blocks = 0 ; - rho = 0 ; - c2 = 0 ; - r1 = 0 ; - r2 = 0 ; - err = 0 ; - - Prob = UFget (i,index) ; - A = Prob.A ; - c = condest (A) ; - fprintf ('condest %8.2e :', c) ; - if (c > 1e20) - fprintf ('skipped\n') ; - continue - end - % klu (A) - % [L,U,p,q,R,F,r,info] = klu (A) ; - - [LU, info, c2] = klu (A) ; - - L = LU.L ; - U = LU.U ; - p = LU.p ; - q = LU.q ; - R = LU.R ; - F = LU.F ; - r = LU.r ; - blocks = length (r) - 1 ; - - n = size (A,1) ; - b = rand (n,1) ; - x = klu (LU,'\',b) ; - err = norm (A*x-b,1) / norm (A,1) ; - - % info - rho = lu_normest (R\A(p,q) - F, L, U) ; - r1 = info.rcond ; - r2 = full (min (abs (diag (U))) / max (abs (diag (U)))) ; - - if (r1 ~= r2) - fprintf ('!\n') ; - pause - end - - conds_klu (k) = c2 ; - conds_matlab (k) = c ; - -% catch me -% disp (me.message) ; -% end - - fprintf (... - 'blocks %6d err %8.2e %8.2e rcond %8.2e %8.2e err %8.2e\n', ... - blocks, rho, c2, r1, r2, err) ; - - end - - k = nmat ; - plot (1:k, log10 (conds_klu (1:k) ./ conds_matlab (1:k)), 'o') ; - drawnow - -% catch me -% disp (me.message) ; -% end - -close (h) ; diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/MATLAB/Test/test2.m b/deps/AMICI/ThirdParty/SuiteSparse/KLU/MATLAB/Test/test2.m deleted file mode 100644 index aad51228f..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/MATLAB/Test/test2.m +++ /dev/null @@ -1,173 +0,0 @@ -function test2 (nmat) -%test2: KLU test -% Example: -% test2 -% See also klu - -% Copyright 2004-2012, University of Florida - -clear functions -% rand ('state', 0) ; -% warning ('off', 'MATLAB:singularMatrix') ; -% warning ('off', 'MATLAB:nearlySingularMatrix') ; -% warning ('off', 'MATLAB:divideByZero') ; - -index = UFget ; -f = find (index.nrows == index.ncols) ; -[ignore i] = sort (index.nnz (f)) ; %#ok -f = f (i) ; - -if (nargin < 1) - nmat = 500 ; -end -nmat = min (nmat, length (f)) ; -f = f (1:nmat) ; - -if (~isempty (strfind (computer, '64'))) - is64 = 1 ; -else - is64 = 0 ; -end - -Tklu = 1e-6 * ones (2*nmat,1) ; -Tmatlab = zeros (2*nmat,1) ; -Tcsparse = zeros (2*nmat,1) ; -LUnz = zeros (2*nmat, 1) ; -k = 0 ; - -h = waitbar (0, 'KLU test 2 of 5') ; - -clf - -% try - - for kk = 1:nmat - - Prob = UFget (f (kk), index) ; - - waitbar (kk/nmat, h) ; - - disp (Prob) ; - if (isfield (Prob, 'kind')) - if (~isempty (strfind (Prob.kind, 'subsequent'))) - fprintf ('skip ...\n') ; - continue - end - end - A = Prob.A ; - - for do_complex = 0:1 - - k = k + 1 ; - if (do_complex) - A = sprand (A) + 1i * sprand (A) ; - end - - try - [L,U,p,q] = lu (A, 'vector') ; - catch %#ok - % older version of MATLAB, which doesn't have 'vector' option - [L,U,P,Q] = lu (A) ; - [p ignore1 ignore2] = find (P') ; %#ok - [q ignore1 ignore2] = find (Q) ; %#ok - clear ignore1 ignore2 P Q - end - - LU.L = L ; - LU.U = U ; - if (is64) - LU.p = int64 (p) ; - LU.q = int64 (q) ; - else - LU.p = int32 (p) ; - LU.q = int32 (q) ; - end - LUnz (k) = nnz (L) + nnz (U) ; - - n = size (A,1) ; - - do_klu = (nnz (diag (U)) == n) ; - if (do_klu) - - fprintf ('klu...\n') ; - err = 0 ; - erc = 0 ; - er2 = 0 ; - for nrhs = 10:-1:1 - - b = rand (n,nrhs) ; - - tic ; - x = klu (LU,'\',b) ; - Tklu (k) = max (1e-6, toc) ; - - tic ; - y = U \ (L \ b (p,:)) ; - y (q,:) = y ; - Tmatlab (k) = max (1e-6, toc) ; - - if (nrhs == 1 & isreal (U) & isreal (L) & isreal (b)) %#ok - tic ; - z = cs_usolve (U, cs_lsolve (L, b (p))) ; - z (q) = z ; - Tcsparse (k) = max (1e-6, toc) ; - erc = norm (A*z-b,1) / norm (A,1) ; - end - - err = max (err, norm (A*x-b,1) / norm (A,1)) ; - er2 = max (er2, norm (A*y-b,1) / norm (A,1)) ; - if (err > 100*er2) - fprintf ('error %g %g\n', err, er2) ; - error ('?') ; - end - end - - fprintf ('klu... with randomized scaling for L\n') ; - er3 = 0 ; - er4 = 0 ; - D = spdiags (rand (n,1), 0, n, n) ; - LU.L = D * L ; - A2 = D * A (p,q) ; - if (is64) - LU.p = int64 (1:n) ; - LU.q = int64 (1:n) ; - else - LU.p = int32 (1:n) ; - LU.q = int32 (1:n) ; - end - for nrhs = 1:10 - b = rand (n,nrhs) ; - x = klu (LU,'\',b) ; - y = U \ (LU.L \ b) ; - er3 = max (er3, norm (A2*x-b,1) / norm (A,1)) ; - er4 = max (er4, norm (A2*y-b,1) / norm (A,1)) ; - if (er3 > 1e3*er4) - fprintf ('error %g %g\n', er3, er4) ; - error ('?') ; - end - end - - - else - err = Inf ; - er2 = Inf ; - erc = Inf ; - end - - lumax = max (LUnz (1:k)) ; - loglog (... - LUnz (1:k), Tmatlab (1:k) ./ Tklu (1:k), 'o', ... - LUnz (1:k), Tcsparse (1:k) ./ Tklu (1:k), 'x', ... - [20 lumax], [1 1], 'r-') ; - axis ([20 lumax .1 20]) ; - drawnow - - fprintf ('err %g %g %g\n', err, er2, erc) ; - end - end - -% catch me -% disp (me.message) ; -% end - -close (h) ; diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/MATLAB/Test/test3.m b/deps/AMICI/ThirdParty/SuiteSparse/KLU/MATLAB/Test/test3.m deleted file mode 100644 index f0a48212f..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/MATLAB/Test/test3.m +++ /dev/null @@ -1,61 +0,0 @@ -function test3 -%test3: KLU test -% Example: -% test3 -% See also klu - -% Copyright 2004-2012, University of Florida - -h = waitbar (1/12, 'KLU test 3 of 5') ; - -% rand ('state', 0) ; - -load west0479 -A = west0479 ; -% A = sparse (rand (4)) ; -% A (3:4, 1:2) = 0 ; - -n = size (A,1) ; -b = rand (n,1) ; -spparms ('spumoni',2) -x = A\b ; -spparms ('spumoni',0) -fprintf ('MATLAB resid %g\n', norm (A*x-b,1)) ; - -[LU,info,cond_estimate] = klu (A) ; -fprintf ('\nLU = \n') ; disp (LU) ; -fprintf ('\ninfo = \n') ; disp (info) ; -fprintf ('KLU condest %g\n', cond_estimate) ; -matlab_condest = condest (A) ; -matlab_cond = cond (full (A)) ; -fprintf ('MATLAB condest %g cond %g\n', matlab_condest, matlab_cond) ; - -for nrhs = 1:10 - waitbar (nrhs/12, h) ; - b = rand (n,nrhs) ; - x = klu (LU,'\',b) ; - fprintf ('nrhs: %d resid: %g\n', ... - nrhs, norm (A*x-b,1) / norm (A,1)) ; -end - -[x,info,cond_estimate] = klu (A, '\', b) ; %#ok -fprintf ('\ninfo = \n') ; disp (info) ; -fprintf ('KLU cond_estimate %g\n', cond_estimate) ; - -waitbar (11/12, h) ; - -[x,info] = klu (A, '\', b, struct ('ordering',1)) ; %#ok -fprintf ('\ninfo = \n') ; disp (info) ; -[x,info,cond_estimate] = klu (A, '\', b, struct ('ordering',2)) ; %#ok -fprintf ('\ninfo = \n') ; disp (info) ; -try - [x,info,cond_estimate] = klu (A, '\', b, struct ('ordering',3)) ; %#ok - fprintf ('\ninfo = \n') ; disp (info) ; - [x,info,cond_estimate] = klu (A, '\', b, struct ('ordering',4)) ; %#ok - fprintf ('\ninfo = \n') ; disp (info) ; -catch me - disp (me.message) ; - fprintf ('test with CHOLMOD skipped (CHOLMOD or METIS not installed)\n') ; -end - -close (h) ; diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/MATLAB/Test/test4.m b/deps/AMICI/ThirdParty/SuiteSparse/KLU/MATLAB/Test/test4.m deleted file mode 100644 index 5534de205..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/MATLAB/Test/test4.m +++ /dev/null @@ -1,213 +0,0 @@ -function test4 (nmat) -%test4: KLU test -% Example: -% test4 -% See also klu - -% Copyright 2004-2012, University of Florida - -% rand ('state', 0) ; -% warning ('off', 'MATLAB:singularMatrix') ; -% warning ('off', 'MATLAB:nearlySingularMatrix') ; -% warning ('off', 'KLU:rcond') ; -% warning ('off', 'MATLAB:Axes:NegativeDataInLogAxis') ; - -index = UFget ; -f = find (index.nrows == index.ncols & index.isReal & index.amd_lnz > 0) ; -[ignore i] = sort (index.amd_lnz (f)) ; %#ok -f = f (i) ; -% f = f (1:100) ; - -if (nargin < 1) - nmat = 500 ; -end -nmat = min (nmat, length (f)) ; -f = f (1:nmat) ; - -if (1) - Tlu = -ones (nmat,1) ; - Tklu = -ones (nmat,1) ; - Tklu2 = -ones (nmat,1) ; - LUnz = -ones (nmat,1) ; - k = 0 ; -end - -if (~isempty (strfind (computer, '64'))) - is64 = 1 ; -else - is64 = 0 ; -end - -% 589: Sieber - -h = waitbar (0, 'KLU test 4 of 5') ; - -clf - -% try - - for kk = 1:nmat - - Prob = UFget (f (kk), index) ; - - waitbar (kk/nmat, h) ; - - disp (Prob) ; - if (isfield (Prob, 'kind')) - if (~isempty (strfind (Prob.kind, 'subsequent'))) - fprintf ('skip ...\n') ; - continue - end - if (~isempty (strfind (Prob.kind, 'random'))) - fprintf ('skip ...\n') ; - continue - end - end - k = k + 1 ; - A = Prob.A ; - n = size (A,1) ; - err1 = 0 ; - err2 = 0 ; - err4 = 0 ; - terr1 = 0 ; - terr2 = 0 ; - terr4 = 0 ; - - - for do_imag = 0:1 - if (do_imag) - A = sprand (A) + 1i * sprand (A) ; - end - - % compare with UMFPACK - try - tic - [L,U,p,q,R1] = lu (A, 'vector') ; - t1 = max (1e-6, toc) ; - catch %#ok - % older version of MATLAB, which doesn't have 'vector' option - tic - [L,U,P,Q] = lu (A) ; - t1 = max (1e-6, toc) ; - [p ignore1 ignore2] = find (P') ; %#ok - [q ignore1 ignore2] = find (Q) ; %#ok - clear ignore1 ignore2 P Q - R1 = speye (n) ; - end - - if (Tlu (k) == -1) - Tlu (k) = t1 ; - LUnz (k) = nnz (L) + nnz (U) ; - end - - % note that the scaling R1 and R2 are different with KLU and UMFPACK - % UMFPACK: L*U-P*(R1\A)*Q - % KLU: L*U-R2\(P*A*Q) - % - % R1 and R2 are related, via P, where R2 = P*R*P', or equivalently - % R2 = R1 (p,p). - - rcond = min (abs (diag (U))) / max (abs (diag (U))) ; - if (rcond < 1e-15) - fprintf ('skip...\n') ; - break ; - end - - F.L = L ; - F.U = U ; - if (is64) - F.p = int64(p) ; - F.q = int64(q) ; - else - F.p = int32(p) ; - F.q = int32(q) ; - end - - F.R = R1(p,p) ; - b = rand (n,1) ; - x = klu (F, '\', b) ; - y = klu (b', '/', F) ; - - fprintf ('solve with klu %g\n', ... - norm (A*x-b,1)/norm(A,1)) ; - fprintf ('solve with klu %g transpose\n', ... - norm (y*A-b',1)/norm(A,1)) ; - - - for nrhs = 1:10 - for do_b_imag = 0:1 - b = rand (n, nrhs) ; - if (do_b_imag) - b = b + 1i * rand (n, nrhs) ; - end - - % KLU backslash - tic ; - x = klu (A,'\',b) ; - t2 = max (1e-6, toc) ; - - % KLU slash - xt = klu (b','/',A) ; - - % KLU backslash with precomputed LU - tic - LU = klu (A) ; - z = klu (LU,'\',b) ; - t4 = max (1e-6, toc) ; - - % KLU slash with precomputed LU - zt = klu (b','/',LU) ; - - % UMFPACK - tic - rb = R1 \ b ; - y = U \ (L \ rb (p,:)) ; - y (q,:) = y ; - t3 = max (1e-6, toc) ; - - yt = (L' \ (U' \ b (q,:))) ; - yt (p,:) = yt ; - yt = R1 \ yt ; - yt = yt' ; - - if (Tklu (k) == -1) - Tlu (k) = Tlu (k) + t3 ; - Tklu (k) = t2 ; - Tklu2 (k) = t4 ; - end - - err1 = max (err1, norm (A*x-b,1) / norm (A,1)) ; - err2 = max (err2, norm (A*y-b,1) / norm (A,1)) ; - err4 = max (err4, norm (A*z-b,1) / norm (A,1)) ; - - terr1 = max (terr1, norm (xt*A-b',1) / norm (A,1)) ; - terr2 = max (terr2, norm (yt*A-b',1) / norm (A,1)) ; - terr4 = max (terr4, norm (zt*A-b',1) / norm (A,1)) ; - end - end - end - - fprintf ('err %g %g %g\n', err1, err2, err4) ; - if (err1 > 1e4*err2 | err4 > 1e4*err2) %#ok - fprintf ('warning: KLU inaccurate!\n') - end - - fprintf ('terr %g %g %g\n', terr1, terr2, terr4) ; - if (terr1 > 1e4*terr2 | terr4 > 1e4*terr2) %#ok - fprintf ('warning: KLU T inaccurate!\n') - end - - lunzmax = max (LUnz (1:k)) ; - loglog ( ... - LUnz (1:k), Tklu (1:k) ./ Tlu (1:k), 'o', ... - LUnz (1:k), Tklu2 (1:k) ./ Tlu (1:k), 'x', ... - [10 lunzmax], [1 1], 'r-') ; - drawnow - - end - -% catch me -% disp (me.message) ; -% end - -close (h) ; diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/MATLAB/Test/test5.m b/deps/AMICI/ThirdParty/SuiteSparse/KLU/MATLAB/Test/test5.m deleted file mode 100644 index 9457c4c81..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/MATLAB/Test/test5.m +++ /dev/null @@ -1,174 +0,0 @@ -function test5 -%test5: KLU test -% Example: -% test5 -% -% test circuit matrices in the UF sparse matrix collection. -% -% See also klu - -% Copyright 2004-2012, University of Florida - -do_diary = 0 ; - -if (do_diary) - diary off - s = date ; - t = clock ; - s = sprintf ('diary test5_%s_%d-%d-%d.txt\n', s, t (4), t(5), fix(t(6))); - eval (s) ; -end - -% ATandT frequency-domain circuits, exclude these: -% freq = [ 283 284 285 286 ] ; - -% sorted in order of MATLAB 7.3 x=A\b time on storm -% (AMD Opteron, 64-bit, 8GB mem, 2 cores) -circ = [ -1195 % Rajat/rajat11 n: 135 nz 665 -1198 % Rajat/rajat14 n: 180 nz 1475 -1189 % Rajat/rajat05 n: 301 nz 1250 -1169 % Sandia/oscil_trans_01 n: 430 nz 1614 -1112 % Sandia/oscil_dcop_01 n: 430 nz 1544 -1346 % Rajat/rajat19 n: 1157 nz 3699 -1199 % Hamrle/Hamrle1 n: 32 nz 98 -1106 % Sandia/fpga_trans_01 n: 1220 nz 7382 -1188 % Rajat/rajat04 n: 1041 nz 8725 -1055 % Sandia/fpga_dcop_01 n: 1220 nz 5892 -1108 % Sandia/init_adder1 n: 1813 nz 11156 -1196 % Rajat/rajat12 n: 1879 nz 12818 -1053 % Sandia/adder_trans_01 n: 1814 nz 14579 -539 % Hamm/add20 n: 2395 nz 13151 -371 % Bomhof/circuit_2 n: 4510 nz 21199 -540 % Hamm/add32 n: 4960 nz 19848 -1186 % Rajat/rajat02 n: 1960 nz 11187 -466 % Grund/meg4 n: 5860 nz 25258 -465 % Grund/meg1 n: 2904 nz 58142 -370 % Bomhof/circuit_1 n: 2624 nz 35823 -1200 % Hamrle/Hamrle2 n: 5952 nz 22162 -1197 % Rajat/rajat13 n: 7598 nz 48762 -1187 % Rajat/rajat03 n: 7602 nz 32653 -372 % Bomhof/circuit_3 n: 12127 nz 48137 -1185 % Rajat/rajat01 n: 6833 nz 43250 -1183 % IBM_Austin/coupled n: 11341 nz 97193 -1376 % Rajat/rajat27 n: 20640 nz 97353 -543 % Hamm/memplus n: 17758 nz 99147 -1109 % Sandia/mult_dcop_01 n: 25187 nz 193276 -1371 % Rajat/rajat22 n: 39899 nz 195429 -1375 % Rajat/rajat26 n: 51032 nz 247528 -1414 % IBM_EDA/ckt11752_tr_0 n: 49702 nz 332807 -541 % Hamm/bcircuit n: 68902 nz 375558 -1413 % IBM_EDA/ckt11752_dc_1 n: 49702 nz 333029 -542 % Hamm/hcircuit n: 105676 nz 513072 -1316 % Rajat/rajat15 n: 37261 nz 443573 -1190 % Rajat/rajat06 n: 10922 nz 46983 -1191 % Rajat/rajat07 n: 14842 nz 63913 -1372 % Rajat/rajat23 n: 110355 nz 555441 -373 % Bomhof/circuit_4 n: 80209 nz 307604 -544 % Hamm/scircuit n: 170998 nz 958936 -1412 % AMD/G2_circuit n: 150102 nz 726674 is this solid state device? -1415 % Sandia/ASIC_100k n: 99340 nz 940621 -1416 % Sandia/ASIC_100ks n: 99190 nz 578890 -1420 % Sandia/ASIC_680ks n: 682712 nz 1693767 -1192 % Rajat/rajat08 n: 19362 nz 83443 -1193 % Rajat/rajat09 n: 24482 nz 105573 -1323 % IBM_EDA/trans4 n: 116835 nz 749800 -1320 % IBM_EDA/dc1 n: 116835 nz 766396 -1194 % Rajat/rajat10 n: 30202 nz 130303 -1418 % Sandia/ASIC_320ks n: 321671 nz 1316085 -1417 % Sandia/ASIC_320k n: 321821 nz 1931828 -1343 % Rajat/rajat16 n: 94294 nz 476766 -1345 % Rajat/rajat18 n: 94294 nz 479151 -1344 % Rajat/rajat17 n: 94294 nz 479246 -1377 % Rajat/rajat28 n: 87190 nz 606489 -1369 % Rajat/rajat20 n: 86916 nz 604299 -1374 % Rajat/rajat25 n: 87190 nz 606489 -1370 % Rajat/rajat21 n: 411676 nz 1876011 -1419 % Sandia/ASIC_680k n: 682862 nz 2638997 -% these are large, so are skipped for this test: -% 1396 % Rajat/rajat29 n: 643994 nz 3760246 -% 1201 % Hamrle/Hamrle3 n: 1447360 nz 5514242 -% 1397 % Rajat/rajat30 n: 643994 nz 6175244 -% 1421 % AMD/G3_circuit n: 1585478 nz 7660826 -% 1398 % Rajat/rajat31 n: 4690002 nz 20316253 -% 1373 % Rajat/rajat24 n: 358172 nz 1946979 -]' ; - -fprintf ('Running KLU on %d circuits.\n', length (circ)) ; - -index = UFget ; - -opts_noscale.scale = -1 ; -opts_sum.scale = 1 ; -opts_max.scale = 2 ; % default scaling - -h = waitbar (0, 'KLU test 5 of 5') ; -nmat = length (circ) ; - -try - - for kk = 1:nmat - - k = circ (kk) ; - Prob = UFget (k, index) ; - - waitbar (kk/nmat, h) ; - - A = Prob.A ; - n = size (A,1) ; - b = rand (n,1) ; - fprintf ('\n%d : %s n: %d nz %d\n', k, Prob.name, n, nnz (A)) ; - - try - tic ; - x2 = klu (A, '\', b, opts_noscale) ; - t2 = toc ; - e2 = norm (A*x2-b) ; - catch %#ok - t2 = inf ; - e2 = inf ; - end - fprintf ('KLU no scale: err %8.2e t: %8.4f\n', e2, t2) ; - - try - tic ; - x4 = klu (A, '\', b, opts_max) ; - t4 = toc ; - e4 = norm (A*x4-b) ; - catch %#ok - t4 = inf ; - e4 = inf ; - end - fprintf ('KLU max scale: err %8.2e t: %8.4f\n', e4, t4) ; - - try - tic ; - x3 = klu (A, '\', b, opts_sum) ; - t3 = toc ; - e3 = norm (A*x3-b) ; - catch %#ok - t3 = inf ; - e3 = inf ; - end - fprintf ('KLU sum scale: err %8.2e t: %8.4f\n', e3, t3) ; - - tic - x1 = A\b ; - t1 = toc ; - e1 = norm (A*x1-b) ; - fprintf ('matlab: err %8.2e t: %8.4f\n', e1, t1) ; - - fprintf (' speedup %8.2f\n', t1 / t4) ; - clear Prob - - if (do_diary) - diary off - diary on - end - end - -catch me - disp (me.message) ; -end - -close (h) ; diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/MATLAB/klu.m b/deps/AMICI/ThirdParty/SuiteSparse/KLU/MATLAB/klu.m deleted file mode 100644 index 73794fd80..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/MATLAB/klu.m +++ /dev/null @@ -1,72 +0,0 @@ -function [LU_or_x,info,c] = klu (A,operation,b,opts) %#ok -%KLU sparse left-looking LU factorization, using a block triangular form. -% -% Example: -% LU = klu (A) factorizes R\A(p,q) into L*U+F, returning a struct -% x = klu (A,'\',b) x = A\b, using KLU -% x = klu (b,'/',A) x = b/A, using KLU -% x = klu (LU,'\',b) x = A\b, where LU = klu(A) -% x = klu (b,'/',LU) x = b/A, where LU = klu(A) -% -% KLU(A) factorizes a square sparse matrix, L*U+F = R\A(p,q), where L and U -% are the factors of the diagonal blocks of the block, F are the entries -% above the diagonal blocks. r corresponds to the 3rd output of dmperm; it -% specifies where the block boundaries are. The kth block consists of -% rows/columns r(k) to r(k+1)-1 of A(p,q). -% -% Note that the use of the scale factor R differs between KLU and UMFPACK -% (and the LU function, which is based on UMFPACK). In LU, the factorization -% is L*U = P*(R1\A)*Q; in KLU it is L*U+F = R2\(P*A*Q). R1 and R2 are related -% via R2 = P*R1*P', or equivalently R2 = R1(p,p). -% -% The LU output is a struct containing members L, U, p, q, R, F, and r. -% -% opts is an optional input struct which appears as the last input argument. -% Entries not present are set to their defaults: -% -% default -% opts.tol 0.001 partial pivoting tolerance; valid range 0 to 1. -% opts.btf 1 use block triangular form (BTF) if nonzero -% opts.ordering 0 how each block is ordered: -% 0: AMD, 1: COLAMD, 2: natural, -% 3: CHOLMOD's ordering of (A'*A), -% 4: CHOLMOD's ordering of (A+A') -% opts.scale 2 1: R = diag(sum(abs(A)')), row-sum -% 2: R = diag(max(abs(A)')), max in each row -% otherwise: none (R=I) -% opts.maxwork 0 if > 0, limit work in BTF ordering to -% opts.maxwork*nnz(A); no limit if <= 0. -% -% The CHOLMOD ordering is to try AMD (for A+A') or COLAMD (for A'*A) -% first. If the fill-in with AMD or COLAMD is high, METIS is tried (on -% A+A' or A'*A), and the best ordering found is selected. CHOLMOD, METIS, -% CAMD, and CCOLAMD are required. If not available, only ordering options -% 0, 1, and 2 may be used (AMD and COLAMD are always required by KLU). -% -% Two optional outputs, [LU,info,c] = klu (A) or [x,info,c] = klu (A,'\',b) -% provide statistics about the factorization: -% -% info.noffdiag number of off-diagonal pivots chosen (after preordering) -% info.nrealloc number of memory reallocations of L and U -% info.rcond a very cheap estimate of 1/(condition number) -% info.rgrowth reciprocal pivot growth -% info.flops flop count -% info.nblocks # of blocks in BTF form (1 if not computed) -% info.ordering AMD, COLAMD, natural, cholmod(AA'), cholmod(A+A') -% info.scale scaling (<=0: none, 1: sum, 2: max) -% info.lnz nnz(L), including diagonal -% info.unz nnz(U), including diagonal -% info.offnz nnz(F) -% info.tol pivot tolerance used -% info.memory peak memory usage in bytes -% c the same as MATLAB's condest -% -% info and c are relevant only if the matrix is factorized (LU = klu (A), -% x = klu (A,'/',b), or x = klu (b,'/',A) usages). -% -% See also BTF, LU, DMPERM, CONDEST, CHOLMOD, AMD, COLAMD, CAMD, CCOLAMD. - -% Copyright 2004-2009, Univ. of Florida -% http://www.suitesparse.com - -error ('klu mexFunction not found') ; diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/MATLAB/klu_demo.m b/deps/AMICI/ThirdParty/SuiteSparse/KLU/MATLAB/klu_demo.m deleted file mode 100644 index 9c5c91265..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/MATLAB/klu_demo.m +++ /dev/null @@ -1,79 +0,0 @@ -function klu_demo -% KLU demo -% -% Example: -% klu_demo -% -% See also klu, btf - -% Copyright 2004-2009, Univ. of Florida - -load west0479 -A = west0479 ; - -n = size (A,1) ; -b = rand (n,1) ; - -clf -subplot (2,2,1) ; -spy (A) -title ('west0479') ; - -subplot (2,2,2) ; -[p, q, r] = btf (A) ; -drawbtf (A, p, q, r) ; -title ('BTF form') ; - -[x,info,c] = klu (A, '\', b) ; -matlab_condest = condest (A) ; -matlab_cond = cond (full (A)) ; -fprintf ('MATLAB condest: %g KLU condest: %g cond: %g\n', ... - matlab_condest, c, matlab_cond) ; - -fprintf ('\nKLU with scaling, AMD ordering and condition number estimate:\n') ; -[LU,info] = klu (A, struct ('ordering',0, 'scale', 1)) ; -x = klu (LU, '\', b) ; -resid = norm (A*x-b,1) / norm (A,1) ; -rgrowth = full (min (max (abs ((LU.R \ A (LU.p,LU.q)) - LU.F)) ./ ... - max (abs (LU.U)))) ; -fprintf ('resid: %g KLU condest: %g rgrowth: %g\n', resid, c, rgrowth) ; -disp (info) ; - -subplot (2,2,3) ; -spy (LU.L + LU.U + LU.F) ; -title ('KLU+AMD factors') ; - -fprintf ('\nKLU with COLAMD ordering\n') ; -[LU,info] = klu (A, struct ('ordering',1)) ; -x = klu (LU, '\', b) ; -resid = norm (A*x-b,1) / norm (A,1) ; -fprintf ('resid: %g\n', resid) ; -disp (info) ; - -subplot (2,2,4) ; -spy (LU.L + LU.U + LU.F) ; -title ('KLU+COLAMD factors') ; - -fprintf ('\nKLU with natural ordering (lots of fillin)\n') ; -[x,info] = klu (A, '\', b, struct ('ordering',2)) ; -resid = norm (A*x-b,1) / norm (A,1) ; -fprintf ('resid: %g\n', resid) ; -disp (info) ; - -try - - fprintf ('\nKLU with CHOLMOD(A''*A) ordering\n') ; - [x,info] = klu (A, '\', b, struct ('ordering',3)) ; - resid = norm (A*x-b,1) / norm (A,1) ; - fprintf ('resid: %g\n', resid) ; - disp (info) ; - - fprintf ('\nKLU with CHOLMOD(A+A'') ordering\n') ; - [x,info] = klu (A, '\', b, struct ('ordering',4)) ; - resid = norm (A*x-b,1) / norm (A,1) ; - fprintf ('resid: %g\n', resid) ; - disp (info) ; - -catch - fprintf ('KLU test with CHOLMOD skipped (CHOLMOD not installed)\n') ; -end diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/MATLAB/klu_demo.m.out b/deps/AMICI/ThirdParty/SuiteSparse/KLU/MATLAB/klu_demo.m.out deleted file mode 100644 index 20991b51e..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/MATLAB/klu_demo.m.out +++ /dev/null @@ -1,88 +0,0 @@ -klu_demo -MATLAB condest: 1.42443e+12 KLU condest: 1.42443e+12 cond: 3.25816e+11 - -KLU with scaling, AMD ordering and condition number estimate: -resid: 7.8484e-13 KLU condest: 1.42443e+12 rgrowth: 1.85769e-05 - noffdiag: 12 - nrealloc: 0 - rcond: 7.1253e-09 - rgrowth: 1.8577e-05 - flops: 31592 - nblocks: 166 - ordering: 0 - scale: 1 - lnz: 1923 - unz: 1892 - offnz: 450 - tol: 1.0000e-03 - memory: 136748 - - -KLU with COLAMD ordering -resid: 3.91883e-11 - noffdiag: 14 - nrealloc: 0 - rcond: 9.1888e-10 - rgrowth: 4.3963e-08 - flops: 42513 - nblocks: 166 - ordering: 1 - scale: 2 - lnz: 1956 - unz: 2490 - offnz: 450 - tol: 1.0000e-03 - memory: 397692 - - -KLU with natural ordering (lots of fillin) -resid: 2.42321e-11 - noffdiag: 437 - nrealloc: 0 - rcond: 6.4525e-08 - rgrowth: 1.7654e-04 - flops: 328911 - nblocks: 2 - ordering: 2 - scale: 2 - lnz: 7846 - unz: 7332 - offnz: 40 - tol: 1.0000e-03 - memory: 453556 - - -KLU with CHOLMOD(A'*A) ordering -resid: 2.22827e-13 - noffdiag: 24 - nrealloc: 0 - rcond: 4.1814e-09 - rgrowth: 1.0676e-04 - flops: 36747 - nblocks: 166 - ordering: 3 - scale: 2 - lnz: 2020 - unz: 2227 - offnz: 450 - tol: 1.0000e-03 - memory: 191132 - - -KLU with CHOLMOD(A+A') ordering -resid: 1.45166e-12 - noffdiag: 13 - nrealloc: 0 - rcond: 3.9046e-11 - rgrowth: 2.4378e-06 - flops: 31925 - nblocks: 166 - ordering: 4 - scale: 2 - lnz: 2000 - unz: 1878 - offnz: 450 - tol: 1.0000e-03 - memory: 136044 - -diary off diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/MATLAB/klu_install.m b/deps/AMICI/ThirdParty/SuiteSparse/KLU/MATLAB/klu_install.m deleted file mode 100644 index fff752184..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/MATLAB/klu_install.m +++ /dev/null @@ -1,56 +0,0 @@ -function klu_install (metis_path) -%KLU_INSTALL compiles and installs the KLU, BTF, AMD, and COLAMD mexFunctions -% -% Example: -% klu_install -% -% KLU relies on AMD, COLAMD, and BTF for its ordering options, and can -% optionally use CHOLMOD, CCOLAMD, CAMD, and METIS as well. By default, -% CHOLMOD, CCOLAMD, CAMD, and METIS are compiled and used by KLU. -% -% You must type the klu_install command while in the KLU/MATLAB directory. -% -% See also klu, btf - -% Copyright 2004-2016, Univ. of Florida - -if (nargin < 1) - metis_path = ['../../metis-5.1.0'] ; -end - -% compile KLU and add to the path -klu_make (metis_path) ; -klu_path = pwd ; -addpath (klu_path) - -fprintf ('\nNow compiling the AMD, COLAMD, and BTF mexFunctions:\n') ; - -% compile BTF and add to the path -cd ../../BTF/MATLAB -btf_make -btf_path = pwd ; -addpath (btf_path) - -% compile AMD and add to the path -cd ../../AMD/MATLAB -amd_make -amd_path = pwd ; -addpath (amd_path) - -% compile COLAMD and add to the path -cd ../../COLAMD/MATLAB -colamd_make -colamd_path = pwd ; -addpath (colamd_path) - -cd (klu_path) - -fprintf ('\nThe following paths have been added. You may wish to add them\n') ; -fprintf ('permanently, using the MATLAB pathtool command.\n') ; -fprintf ('%s\n', klu_path) ; -fprintf ('%s\n', amd_path) ; -fprintf ('%s\n', colamd_path) ; -fprintf ('%s\n', btf_path) ; - -fprintf ('\nTo try your new mexFunctions, cut-and-paste this command:\n') ; -fprintf ('klu_demo, btf_demo, amd_demo, colamd_demo\n') ; diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/MATLAB/klu_make.m b/deps/AMICI/ThirdParty/SuiteSparse/KLU/MATLAB/klu_make.m deleted file mode 100644 index 72fe8eeae..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/MATLAB/klu_make.m +++ /dev/null @@ -1,402 +0,0 @@ -function klu_make (metis_path) -%KLU_MAKE compiles the KLU mexFunctions -% -% Example: -% klu_make -% -% KLU relies on AMD, COLAMD, and BTF for its ordering options, and can -% optionally use CHOLMOD, CCOLAMD, CAMD, and METIS as well. -% -% You must type the klu_make command while in the KLU/MATLAB directory. -% -% See also klu. - -% Copyright 2004-2016, Univ. of Florida - -if (nargin < 1) - metis_path = '../../metis-5.1.0' ; -end -with_cholmod = exist (metis_path, 'dir') ; - -details = 0 ; % if 1, print details of each command - -d = '' ; -if (~isempty (strfind (computer, '64'))) - % 64-bit MATLAB - d = '-largeArrayDims' ; -end - -% MATLAB 8.3.0 now has a -silent option to keep 'mex' from burbling too much -if (~verLessThan ('matlab', '8.3.0')) - d = ['-silent ' d] ; -end - -fprintf ('Compiling KLU ') ; -kk = 0 ; - -include = '-I. -I../../AMD/Include -I../../COLAMD/Include -I../Include -I../../SuiteSparse_config -I../../BTF/Include' ; - -if (with_cholmod) - include = [include ' -I../../CCOLAMD/Include -I../../CAMD/Include -I../../CHOLMOD/Include -I../../SuiteSparse_config -I../User'] ; - include = [include ' -I' metis_path '/include'] ; - include = [include ' -I' metis_path '/GKlib'] ; - include = [include ' -I' metis_path '/libmetis'] ; -end - -% do not attempt to compile CHOLMOD with large file support (not needed) -include = [include ' -DNLARGEFILE'] ; - -% fix the METIS 4.0.1 rename.h file -if (with_cholmod) - fprintf ('with CHOLMOD, CAMD, CCOLAMD, and METIS\n') ; - include = ['-DNSUPERNODAL -DNMODIFY -DNMATRIXOPS -DNCHECK ' include] ; -else - fprintf ('without CHOLMOD, CAMD, CCOLAMD, and METIS\n') ; - include = ['-DNCHOLMOD ' include] ; -end - -suitesparse_src = { '../../SuiteSparse_config/SuiteSparse_config' } ; - -amd_src = { ... - '../../AMD/Source/amd_1', ... - '../../AMD/Source/amd_2', ... - '../../AMD/Source/amd_aat', ... - '../../AMD/Source/amd_control', ... - '../../AMD/Source/amd_defaults', ... - '../../AMD/Source/amd_dump', ... - '../../AMD/Source/amd_global', ... - '../../AMD/Source/amd_info', ... - '../../AMD/Source/amd_order', ... - '../../AMD/Source/amd_postorder', ... - '../../AMD/Source/amd_post_tree', ... - '../../AMD/Source/amd_preprocess', ... - '../../AMD/Source/amd_valid' } ; - -colamd_src = { - '../../COLAMD/Source/colamd' } ; - -if (with_cholmod) - - camd_src = { ... - '../../CAMD/Source/camd_1', ... - '../../CAMD/Source/camd_2', ... - '../../CAMD/Source/camd_aat', ... - '../../CAMD/Source/camd_control', ... - '../../CAMD/Source/camd_defaults', ... - '../../CAMD/Source/camd_dump', ... - '../../CAMD/Source/camd_global', ... - '../../CAMD/Source/camd_info', ... - '../../CAMD/Source/camd_order', ... - '../../CAMD/Source/camd_postorder', ... - '../../CAMD/Source/camd_preprocess', ... - '../../CAMD/Source/camd_valid' } ; - - ccolamd_src = { - '../../CCOLAMD/Source/ccolamd' } ; - - metis_src = { - 'GKlib/b64', ... - 'GKlib/blas', ... - 'GKlib/csr', ... - 'GKlib/error', ... - 'GKlib/evaluate', ... - 'GKlib/fkvkselect', ... - 'GKlib/fs', ... - 'GKlib/getopt', ... - 'GKlib/gkregex', ... - 'GKlib/graph', ... - 'GKlib/htable', ... - 'GKlib/io', ... - 'GKlib/itemsets', ... - 'GKlib/mcore', ... - 'GKlib/memory', ... - 'GKlib/omp', ... - 'GKlib/pdb', ... - 'GKlib/pqueue', ... - 'GKlib/random', ... - 'GKlib/rw', ... - 'GKlib/seq', ... - 'GKlib/sort', ... - 'GKlib/string', ... - 'GKlib/timers', ... - 'GKlib/tokenizer', ... - 'GKlib/util', ... - 'libmetis/auxapi', ... - 'libmetis/balance', ... - 'libmetis/bucketsort', ... - 'libmetis/checkgraph', ... - 'libmetis/coarsen', ... - 'libmetis/compress', ... - 'libmetis/contig', ... - 'libmetis/debug', ... - 'libmetis/fm', ... - 'libmetis/fortran', ... - 'libmetis/frename', ... - 'libmetis/gklib', ... - 'libmetis/graph', ... - 'libmetis/initpart', ... - 'libmetis/kmetis', ... - 'libmetis/kwayfm', ... - 'libmetis/kwayrefine', ... - 'libmetis/mcutil', ... - 'libmetis/mesh', ... - 'libmetis/meshpart', ... - 'libmetis/minconn', ... - 'libmetis/mincover', ... - 'libmetis/mmd', ... - 'libmetis/ometis', ... - 'libmetis/options', ... - 'libmetis/parmetis', ... - 'libmetis/pmetis', ... - 'libmetis/refine', ... - 'libmetis/separator', ... - 'libmetis/sfm', ... - 'libmetis/srefine', ... - 'libmetis/stat', ... - 'libmetis/timing', ... - 'libmetis/util', ... - 'libmetis/wspace', ... - } ; - - for i = 1:length (metis_src) - metis_src {i} = [metis_path '/' metis_src{i}] ; - end - - cholmod_src = { - '../../CHOLMOD/Core/cholmod_aat', ... - '../../CHOLMOD/Core/cholmod_add', ... - '../../CHOLMOD/Core/cholmod_band', ... - '../../CHOLMOD/Core/cholmod_change_factor', ... - '../../CHOLMOD/Core/cholmod_common', ... - '../../CHOLMOD/Core/cholmod_complex', ... - '../../CHOLMOD/Core/cholmod_copy', ... - '../../CHOLMOD/Core/cholmod_dense', ... - '../../CHOLMOD/Core/cholmod_error', ... - '../../CHOLMOD/Core/cholmod_factor', ... - '../../CHOLMOD/Core/cholmod_memory', ... - '../../CHOLMOD/Core/cholmod_sparse', ... - '../../CHOLMOD/Core/cholmod_transpose', ... - '../../CHOLMOD/Core/cholmod_triplet', ... - '../../CHOLMOD/Cholesky/cholmod_amd', ... - '../../CHOLMOD/Cholesky/cholmod_analyze', ... - '../../CHOLMOD/Cholesky/cholmod_colamd', ... - '../../CHOLMOD/Cholesky/cholmod_etree', ... - '../../CHOLMOD/Cholesky/cholmod_postorder', ... - '../../CHOLMOD/Cholesky/cholmod_rowcolcounts', ... - '../../CHOLMOD/Partition/cholmod_ccolamd', ... - '../../CHOLMOD/Partition/cholmod_csymamd', ... - '../../CHOLMOD/Partition/cholmod_camd', ... - '../../CHOLMOD/Partition/cholmod_metis', ... - '../../CHOLMOD/Partition/cholmod_nesdis' } ; - -else - camd_src = { } ; - ccolamd_src = { } ; - metis_src = { } ; - cholmod_src = { } ; -end - -btf_src = { - '../../BTF/Source/btf_maxtrans', ... - '../../BTF/Source/btf_order', ... - '../../BTF/Source/btf_strongcomp' } ; - -klu_src = { - '../Source/klu_free_symbolic', ... - '../Source/klu_defaults', ... - '../Source/klu_analyze_given', ... - '../Source/klu_analyze', ... - '../Source/klu_memory' } ; - -if (with_cholmod) - klu_src = [klu_src { '../User/klu_l_cholmod' }] ; %#ok -end - -klu_zlsrc = { - '../Source/klu', ... - '../Source/klu_kernel', ... - '../Source/klu_dump', ... - '../Source/klu_factor', ... - '../Source/klu_free_numeric', ... - '../Source/klu_solve', ... - '../Source/klu_scale', ... - '../Source/klu_refactor', ... - '../Source/klu_tsolve', ... - '../Source/klu_diagnostics', ... - '../Source/klu_sort', ... - '../Source/klu_extract', ... - } ; - -klu_lobj = { - 'klu_l', ... - 'klu_l_kernel', ... - 'klu_l_dump', ... - 'klu_l_factor', ... - 'klu_l_free_numeric', ... - 'klu_l_solve', ... - 'klu_l_scale', ... - 'klu_l_refactor', ... - 'klu_l_tsolve', ... - 'klu_l_diagnostics', ... - 'klu_l_sort', ... - 'klu_l_extract', ... - } ; - -klu_zlobj = { - 'klu_zl', ... - 'klu_zl_kernel', ... - 'klu_zl_dump', ... - 'klu_zl_factor', ... - 'klu_zl_free_numeric', ... - 'klu_zl_solve', ... - 'klu_zl_scale', ... - 'klu_zl_refactor', ... - 'klu_zl_tsolve', ... - 'klu_zl_diagnostics', ... - 'klu_zl_sort', ... - 'klu_zl_extract', ... - } ; - -try - % ispc does not appear in MATLAB 5.3 - pc = ispc ; -catch - % if ispc fails, assume we are on a Windows PC if it's not unix - pc = ~isunix ; -end - -if (pc) - % Windows does not have drand48 and srand48, required by METIS. Use - % drand48 and srand48 in CHOLMOD/MATLAB/Windows/rand48.c instead. - obj_extension = '.obj' ; - cholmod_src = [cholmod_src {'../../CHOLMOD/MATLAB/Windows/rand48'}] ; - include = [include ' -I../../CHOLMOD/MATLAB/Windows'] ; -else - obj_extension = '.o' ; -end - -% compile each library source file -obj = ' ' ; - -source = [suitesparse_src amd_src btf_src klu_src colamd_src] ; -if (with_cholmod) - source = [metis_src ccolamd_src camd_src cholmod_src source] ; -end - -for f = source - ff = f {1} ; - if (isequal (ff, [metis_path '/GKlib/util'])) - % special case, since a file with the same name also exists in libmetis - copyfile ([ff '.c'], 'GKlib_util.c', 'f') ; - ff = 'GKlib_util' ; - o = 'GKlib_util' ; - elseif (isequal (ff, [metis_path '/GKlib/graph'])) - % special case, since a file with the same name also exist in libmetis - copyfile ([ff '.c'], 'GKlib_graph.c', 'f') ; - ff = 'GKlib_graph' ; - o = 'GKlib_graph' ; - else - slash = strfind (ff, '/') ; - if (isempty (slash)) - slash = 1 ; - else - slash = slash (end) + 1 ; - end - o = ff (slash:end) ; - end - % fprintf ('%s\n', o) ; - o = [o obj_extension] ; - obj = [obj ' ' o] ; %#ok - s = sprintf ('mex %s -DDLONG -O %s -c %s.c', d, include, ff) ; - kk = do_cmd (s, kk, details) ; -end - -for k = 1:length(klu_zlsrc) - ff = klu_zlsrc {k} ; - slash = strfind (ff, '/') ; - if (isempty (slash)) - slash = 1 ; - else - slash = slash (end) + 1 ; - end - o = ff (slash:end) ; - s = sprintf ('mex %s -DDLONG -O %s -c %s.c', d, include, ff) ; - kk = do_cmd (s, kk, details) ; - lobj = klu_lobj {k} ; - obj = [obj ' ' lobj obj_extension] ; %#ok - mvfile ([o obj_extension], [lobj obj_extension]) ; - s = sprintf ('mex %s -DDLONG -DCOMPLEX -O %s -c %s.c', d, include, ff) ; - kk = do_cmd (s, kk, details) ; - zlobj = klu_zlobj {k} ; - obj = [obj ' ' zlobj obj_extension] ; %#ok - mvfile ([o obj_extension], [zlobj obj_extension]) ; -end - -% compile the KLU mexFunction -s = sprintf ('mex %s -DDLONG -O %s -output klu klu_mex.c', d, include) ; -s = [s obj] ; %#ok - -if (~(ispc || ismac)) - % for POSIX timing routine - s = [s ' -lrt'] ; -end - -kk = do_cmd (s, kk, details) ; - -% clean up -s = ['delete ' obj] ; -do_cmd (s, kk, details) ; - -rmfile ('GKlib_util.c') ; -rmfile ('GKlib_graph.c') ; - -fprintf ('\nKLU successfully compiled\n') ; - -%------------------------------------------------------------------------------- - -function rmfile (file) -% rmfile: delete a file, but only if it exists -if (length (dir (file)) > 0) %#ok - delete (file) ; -end - -%------------------------------------------------------------------------------- - -function cpfile (src, dst) -% cpfile: copy the src file to the filename dst, overwriting dst if it exists -rmfile (dst) -if (length (dir (src)) == 0) %#ok - fprintf ('File does not exist: %s\n', src) ; - error ('File does not exist') ; -end -try - copyfile (src, dst) ; -catch ME - % ignore errors of the form "cp: preserving permissions: ... - % Operation not supported". rethrow all other errors. - if (isempty (strfind (ME.message, 'Operation not supported'))) - rethrow (ME) ; - end -end - -%------------------------------------------------------------------------------- - -function mvfile (src, dst) -% mvfile: move the src file to the filename dst, overwriting dst if it exists -cpfile (src, dst) ; -rmfile (src) ; - -%------------------------------------------------------------------------------- -function kk = do_cmd (s, kk, details) -%DO_CMD: evaluate a command, and either print it or print a "." -if (details) - fprintf ('%s\n', s) ; -else - if (mod (kk, 60) == 0) - fprintf ('\n') ; - end - kk = kk + 1 ; - fprintf ('.') ; -end -eval (s) ; - diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/MATLAB/klu_mex.c b/deps/AMICI/ThirdParty/SuiteSparse/KLU/MATLAB/klu_mex.c deleted file mode 100644 index f19f8a3ce..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/MATLAB/klu_mex.c +++ /dev/null @@ -1,1974 +0,0 @@ -/* ========================================================================== */ -/* === klu mexFunction ====================================================== */ -/* ========================================================================== */ - -/* KLU: a MATLAB interface to a "Clark Kent" sparse LU factorization algorithm. - - 3 or 4 input arguments: factorize and solve, returning the solution: - - x = klu (A, '\', b) - x = klu (A, '\', b, opts) - x = klu (b, '/', A) - x = klu (b, '/', A, opts) - - A can be the LU struct, instead: - - x = klu (LU, '\', b) - x = klu (LU, '\', b, opts) - x = klu (b, '/', LU) - x = klu (b, '/', LU, opts) - - where LU is a struct containing members: L, U, p, q, R, F, and r. Only L - and U are required. The factorization is L*U+F = R\A(p,q), where r defines - the block boundaries of the BTF form, and F contains the entries in the - upper block triangular part. - - with 1 or 2 input arguments: factorize, returning the LU struct: - - LU = klu (A) - LU = klu (A, opts) - - 2nd optional output: info, which is only meaningful if A was factorized. - - A must be square. b can be a matrix, but it cannot be sparse. - - Obscure options, mainly for testing: - - opts.memgrow 1.2 when L and U need to grow, inc. by this ratio. - valid range: 1 or more. - opts.imemamd 1.2 initial size of L and U with AMD or other - symmetric ordering is 1.2*nnz(L)+n; - valid range 1 or more. - opts.imem 10 initial size of L and U is 10*nnz(A)+n if a - symmetric ordering not used; valid range 1 or - more -*/ - -/* ========================================================================== */ - -#include "klu.h" -#include -#define Long SuiteSparse_long - -#ifndef NCHOLMOD -#include "klu_cholmod.h" -#endif - -#include "mex.h" -#define MAX(a,b) (((a) > (b)) ? (a) : (b)) -#define MIN(a,b) (((a) < (b)) ? (a) : (b)) -#define ABS(x) (((x) < 0) ? -(x) : (x)) -#define STRING_MATCH(s1,s2) (strcmp ((s1), (s2)) == 0) - -/* Complex division. This uses ACM Algo 116, by R. L. Smith, 1962. */ -/* Note that c cannot be the same variable as a or b */ -#define DIV(cx,cz,ax,az,bx,bz) \ -{ \ - double r, den ; \ - if (ABS (bx) >= ABS (bz)) \ - { \ - r = bz / bx ; \ - den = bx + r * bz ; \ - cx = (ax + az * r) / den ; \ - cz = (az - ax * r) / den ; \ - } \ - else \ - { \ - r = bx / bz ; \ - den = r * bx + bz ; \ - cx = (ax * r + az) / den ; \ - cz = (az * r - ax) / den ; \ - } \ -} - -/* complex multiply/subtract, c -= a*b */ -/* Note that c cannot be the same variable as a or b */ -#define MULT_SUB(cx,cz,ax,az,bx,bz) \ -{ \ - cx -= ax * bx - az * bz ; \ - cz -= az * bx + ax * bz ; \ -} - -/* complex multiply/subtract, c -= a*conj(b) */ -/* Note that c cannot be the same variable as a or b */ -#define MULT_SUB_CONJ(cx,cz,ax,az,bx,bz) \ -{ \ - cx -= ax * bx + az * bz ; \ - cz -= az * bx - ax * bz ; \ -} - -/* ========================================================================== */ -/* === klu mexFunction ====================================================== */ -/* ========================================================================== */ - -void mexFunction -( - int nargout, - mxArray *pargout [ ], - int nargin, - const mxArray *pargin [ ] -) -{ - double ukk, lkk, rs, s, lik, uik, x [4], offik, z, ukkz, lkkz, sz, wx, wz ; - double *X, *B, *Xz, *Xx, *Bx, *Bz, *A, *Ax, *Az, *Lx, *Ux, *Rs, *Offx, *Wx, - *Uz, *Lz, *Offz, *Wz, *W, *Xi, *Bi ; - Long *Ap, *Ai, *Lp, *Li, *Up, *Ui, *P, *Q, *R, *Rp, *Ri, *Offp, *Offi ; - char *operator ; - mxArray *L_matlab, *U_matlab, *p_matlab, *q_matlab, *R_matlab, *F_matlab, - *r_matlab, *field ; - const mxArray *A_matlab = NULL, *LU_matlab, *B_matlab = NULL, *opts_matlab ; - klu_l_symbolic *Symbolic ; - klu_l_numeric *Numeric ; - klu_l_common Common ; - Long n = 0, k, nrhs = 0, do_solve, do_factorize, symmetric, - A_complex = 0, B_complex, nz, do_transpose = 0, p, pend, nblocks, - R1 [2], chunk, nr, i, j, block, k1, k2, nk, bn = 0, ordering ; - int mx_int ; - static const char *fnames [ ] = { - "noffdiag", /* # of off-diagonal pivots */ - "nrealloc", /* # of memory reallocations */ - "rcond", /* cheap reciprocal number estimate */ - "rgrowth", /* reciprocal pivot growth */ - "flops", /* flop count */ - "nblocks", /* # of blocks in BTF form (1 if not computed) */ - "ordering", /* AMD, COLAMD, natural, cholmod(AA'), cholmod(A+A') */ - "scale", /* scaling (<=0: none, 1: sum, 2: max */ - "lnz", /* nnz(L), including diagonal */ - "unz", /* nnz(U), including diagonal */ - "offnz", /* nnz(F), including diagonal */ - "tol", /* pivot tolerance used */ - "memory" /* peak memory usage */ - }, - *LUnames [ ] = { "L", "U", "p", "q", "R", "F", "r" } ; - - /* ---------------------------------------------------------------------- */ - /* get inputs */ - /* ---------------------------------------------------------------------- */ - - if (nargin < 1 || nargin > 4 || nargout > 3) - { - mexErrMsgTxt ( - "Usage: x = klu(A,'\',b), x = klu(A,'/',b) or LU = klu(A)") ; - } - - /* return the solution x, or just do LU factorization */ - do_solve = (nargin > 2) ; - - /* determine size of the MATLAB integer */ - if (sizeof (Long) == sizeof (INT32_T)) - { - mx_int = mxINT32_CLASS ; - } - else - { - mx_int = mxINT64_CLASS ; - } - - if (do_solve) - { - - /* ------------------------------------------------------------------ */ - /* slash or backslash */ - /* ------------------------------------------------------------------ */ - - /* usage, where opts is the optional 4th input argument: - x = klu (A, '\', b) - x = klu (LU, '\', b) - x = klu (b, '/', A) - x = klu (b, '/', LU) - */ - - /* determine the operator, slash (/) or backslash (\) */ - if (!mxIsChar (pargin [1])) - { - mexErrMsgTxt ("invalid operator") ; - } - operator = mxArrayToString (pargin [1]) ; - if (STRING_MATCH (operator, "\\")) - { - do_transpose = 0 ; - A_matlab = pargin [0] ; - B_matlab = pargin [2] ; - nrhs = mxGetN (B_matlab) ; - bn = mxGetM (B_matlab) ; - } - else if (STRING_MATCH (operator, "/")) - { - do_transpose = 1 ; - A_matlab = pargin [2] ; - B_matlab = pargin [0] ; - nrhs = mxGetM (B_matlab) ; - bn = mxGetN (B_matlab) ; - } - else - { - mexErrMsgTxt ("invalid operator") ; - } - - if (mxIsSparse (B_matlab)) - { - mexErrMsgTxt ("B cannot be sparse") ; - } - - opts_matlab = (nargin > 3) ? pargin [3] : NULL ; - - /* determine if the factorization needs to be performed */ - do_factorize = !mxIsStruct (A_matlab) ; - if (do_factorize) - { - LU_matlab = NULL ; - } - else - { - LU_matlab = A_matlab ; - A_matlab = NULL ; - } - - } - else - { - - /* ------------------------------------------------------------------ */ - /* factorize A and return LU factorization */ - /* ------------------------------------------------------------------ */ - - /* usage, where opts in the optional 2nd input argument: - LU = klu (A) - */ - - LU_matlab = NULL ; - A_matlab = pargin [0] ; - B_matlab = NULL ; - opts_matlab = (nargin > 1) ? pargin [1] : NULL ; - do_factorize = 1 ; - if (mxIsStruct (A_matlab)) - { - mexErrMsgTxt ("invalid input, A must be a sparse matrix") ; - } - } - - /* ---------------------------------------------------------------------- */ - /* get options and set Common defaults */ - /* ---------------------------------------------------------------------- */ - - klu_l_defaults (&Common) ; - - /* factorization options */ - if (opts_matlab != NULL && mxIsStruct (opts_matlab)) - { - if ((field = mxGetField (opts_matlab, 0, "tol")) != NULL) - { - Common.tol = mxGetScalar (field) ; - } - if ((field = mxGetField (opts_matlab, 0, "memgrow")) != NULL) - { - Common.memgrow = mxGetScalar (field) ; - } - if ((field = mxGetField (opts_matlab, 0, "imemamd")) != NULL) - { - Common.initmem_amd = mxGetScalar (field) ; - } - if ((field = mxGetField (opts_matlab, 0, "imem")) != NULL) - { - Common.initmem = mxGetScalar (field) ; - } - if ((field = mxGetField (opts_matlab, 0, "btf")) != NULL) - { - Common.btf = mxGetScalar (field) ; - } - if ((field = mxGetField (opts_matlab, 0, "ordering")) != NULL) - { - Common.ordering = mxGetScalar (field) ; - } - if ((field = mxGetField (opts_matlab, 0, "scale")) != NULL) - { - Common.scale = mxGetScalar (field) ; - } - if ((field = mxGetField (opts_matlab, 0, "maxwork")) != NULL) - { - Common.maxwork = mxGetScalar (field) ; - } - } - - if (Common.ordering < 0 || Common.ordering > 4) - { - mexErrMsgTxt ("invalid ordering option") ; - } - ordering = Common.ordering ; - -#ifndef NCHOLMOD - /* ordering option 3,4 becomes KLU option 3, with symmetric 0 or 1 */ - symmetric = (Common.ordering == 4) ; - if (symmetric) Common.ordering = 3 ; - Common.user_order = klu_l_cholmod ; - Common.user_data = &symmetric ; -#else - /* CHOLMOD, METIS, CAMD, CCOLAMD, not available */ - if (Common.ordering > 2) - { - mexErrMsgTxt ("invalid ordering option") ; - } -#endif - - if (Common.scale < 1 || Common.scale > 2) - { - Common.scale = -1 ; /* no scaling, and no error checking either */ - } - - /* ---------------------------------------------------------------------- */ - /* factorize, if needed */ - /* ---------------------------------------------------------------------- */ - - if (do_factorize) - { - - /* get input matrix A to factorize */ - n = mxGetN (A_matlab) ; - if (!mxIsSparse (A_matlab) || n != mxGetM (A_matlab) || n == 0) - { - mexErrMsgTxt ("A must be sparse, square, and non-empty") ; - } - - Ap = (Long *) mxGetJc (A_matlab) ; - Ai = (Long *) mxGetIr (A_matlab) ; - Ax = mxGetPr (A_matlab) ; - Az = mxGetPi (A_matlab) ; - nz = Ap [n] ; - A_complex = mxIsComplex (A_matlab) ; - - if (do_solve && (n != bn || nrhs == 0)) - { - mexErrMsgTxt ("B must be non-empty with same number of rows as A") ; - } - - /* ------------------------------------------------------------------ */ - /* analyze */ - /* ------------------------------------------------------------------ */ - - Symbolic = klu_l_analyze (n, Ap, Ai, &Common) ; - if (Symbolic == (klu_l_symbolic *) NULL) - { - mexErrMsgTxt ("klu symbolic analysis failed") ; - } - - /* ------------------------------------------------------------------ */ - /* factorize */ - /* ------------------------------------------------------------------ */ - - if (A_complex) - { - /* A is complex */ - A = mxMalloc (nz * 2 * sizeof (double)) ; - for (k = 0 ; k < nz ; k++) - { - A [2*k ] = Ax [k] ; /* real part */ - A [2*k+1] = Az [k] ; /* imaginary part */ - } - Numeric = klu_zl_factor (Ap, Ai, A, Symbolic, &Common) ; - if (nargout > 1) - { - /* flops and rgrowth, if requested */ - klu_zl_flops (Symbolic, Numeric, &Common) ; - klu_zl_rgrowth (Ap, Ai, A, Symbolic, Numeric, &Common) ; - } - mxFree (A) ; - } - else - { - /* A is real */ - Numeric = klu_l_factor (Ap, Ai, Ax, Symbolic, &Common) ; - if (nargout > 1) - { - /* flops, if requested */ - klu_l_flops (Symbolic, Numeric, &Common) ; - klu_l_rgrowth (Ap, Ai, Ax, Symbolic, Numeric, &Common) ; - } - } - if (Common.status != KLU_OK) - { - mexErrMsgTxt ("klu numeric factorization failed") ; - } - - /* ------------------------------------------------------------------ */ - /* compute cheap condition number estimate */ - /* ------------------------------------------------------------------ */ - - if (A_complex) - { - klu_zl_rcond (Symbolic, Numeric, &Common) ; - } - else - { - klu_l_rcond (Symbolic, Numeric, &Common) ; - } - - /* ------------------------------------------------------------------ */ - /* return info, if requested */ - /* ------------------------------------------------------------------ */ - -#define INFO(i,x) \ - mxSetFieldByNumber (pargout [1], 0, i, mxCreateDoubleScalar (x)) - - if (nargout > 1) - { - pargout [1] = mxCreateStructMatrix (1, 1, 13, fnames) ; - INFO (0, Common.noffdiag) ; - INFO (1, Common.nrealloc) ; - INFO (2, Common.rcond) ; - INFO (3, Common.rgrowth) ; - INFO (4, Common.flops) ; - INFO (5, Symbolic->nblocks) ; - INFO (6, ordering) ; - INFO (7, Common.scale) ; - INFO (8, Numeric->lnz) ; - INFO (9, Numeric->unz) ; - INFO (10, Numeric->nzoff) ; - INFO (11, Common.tol) ; - INFO (12, Common.mempeak) ; - } - if (nargout > 2) - { - /* this is done separately, since it's costly */ - klu_l_condest (Ap, Ax, Symbolic, Numeric, &Common) ; - pargout [2] = mxCreateDoubleMatrix (1, 1, mxREAL) ; - Wx = mxGetPr (pargout [2]) ; - Wx [0] = Common.condest ; - } - - } - else - { - /* create an empty "info" and "condest" output */ - if (nargout > 1) - { - pargout [1] = mxCreateDoubleMatrix (0, 0, mxREAL) ; - } - if (nargout > 2) - { - pargout [2] = mxCreateDoubleMatrix (0, 0, mxREAL) ; - } - } - - /* ---------------------------------------------------------------------- */ - /* solve, or return LU factorization */ - /* ---------------------------------------------------------------------- */ - - if (do_solve) - { - - /* ------------------------------------------------------------------ */ - /* solve, x = klu ( ... ) usage */ - /* ------------------------------------------------------------------ */ - - B_complex = mxIsComplex (B_matlab) ; - - if (do_factorize) - { - - /* -------------------------------------------------------------- */ - /* solve using KLU factors computed above */ - /* -------------------------------------------------------------- */ - - /* klu (A,'\',b) or klu (b,'/',A) usage */ - - /* create X */ - if (do_transpose) - { - pargout [0] = mxCreateDoubleMatrix (nrhs, n, - (A_complex || B_complex) ? mxCOMPLEX : mxREAL) ; - } - else - { - pargout [0] = mxCreateDoubleMatrix (n, nrhs, - (A_complex || B_complex) ? mxCOMPLEX : mxREAL) ; - } - - if (A_complex) - { - - /* ---------------------------------------------------------- */ - /* A is complex, but B might be real */ - /* ---------------------------------------------------------- */ - - X = mxMalloc (n * nrhs * 2 * sizeof (double)) ; - Bx = mxGetPr (B_matlab) ; - Bz = mxGetPi (B_matlab) ; - - if (do_transpose) - { - - /* X = B', merge and transpose B */ - for (j = 0 ; j < nrhs ; j++) - { - for (i = 0 ; i < n ; i++) - { - X [2*(i+j*n) ] = Bx [j+i*nrhs] ; /* real */ - X [2*(i+j*n)+1] = Bz ? (-Bz [j+i*nrhs]) : 0 ; - } - } - - /* solve A'x=b (complex conjugate) */ - klu_zl_tsolve (Symbolic, Numeric, n, nrhs, X, 1, &Common) ; - - /* split and transpose the solution */ - Xx = mxGetPr (pargout [0]) ; - Xz = mxGetPi (pargout [0]) ; - for (j = 0 ; j < nrhs ; j++) - { - for (i = 0 ; i < n ; i++) - { - Xx [j+i*nrhs] = X [2*(i+j*n) ] ; /* real part */ - Xz [j+i*nrhs] = -X [2*(i+j*n)+1] ; /* imag part */ - } - } - - } - else - { - - /* X = B, but create merged X from a split B */ - for (k = 0 ; k < n*nrhs ; k++) - { - X [2*k ] = Bx [k] ; /* real part */ - X [2*k+1] = Bz ? (Bz [k]) : 0 ; /* imaginary part */ - } - - /* solve Ax=b */ - klu_zl_solve (Symbolic, Numeric, n, nrhs, X, &Common) ; - - /* split the solution into real and imaginary parts */ - Xx = mxGetPr (pargout [0]) ; - Xz = mxGetPi (pargout [0]) ; - for (k = 0 ; k < n*nrhs ; k++) - { - Xx [k] = X [2*k ] ; /* real part */ - Xz [k] = X [2*k+1] ; /* imaginary part */ - } - } - - mxFree (X) ; - } - else - { - - if (do_transpose) - { - - /* solve in chunks of 4 columns at a time */ - W = mxMalloc (n * MAX (nrhs,4) * sizeof (double)) ; - X = mxGetPr (pargout [0]) ; - B = mxGetPr (B_matlab) ; - Xi = mxGetPi (pargout [0]) ; - Bi = mxGetPi (B_matlab) ; - - for (chunk = 0 ; chunk < nrhs ; chunk += 4) - { - - /* A is real: real(X) = real(b) / real(A) */ - Long chunksize = MIN (nrhs - chunk, 4) ; - for (j = 0 ; j < chunksize ; j++) - { - for (i = 0 ; i < n ; i++) - { - W [i+j*n] = B [i*nrhs+j] ; - } - } - klu_l_tsolve (Symbolic, Numeric, n, chunksize, W, - &Common) ; - for (j = 0 ; j < chunksize ; j++) - { - for (i = 0 ; i < n ; i++) - { - X [i*nrhs+j] = W [i+j*n] ; - } - } - X += 4 ; - B += 4 ; - - if (B_complex) - { - /* B is complex: imag(X) = imag(B) / real(A) */ - - for (j = 0 ; j < chunksize ; j++) - { - for (i = 0 ; i < n ; i++) - { - W [i+j*n] = Bi [i*nrhs+j] ; - } - } - klu_l_tsolve (Symbolic, Numeric, n, chunksize, W, - &Common) ; - for (j = 0 ; j < chunksize ; j++) - { - for (i = 0 ; i < n ; i++) - { - Xi [i*nrhs+j] = W [i+j*n] ; - } - } - Xi += 4 ; - Bi += 4 ; - } - - } - mxFree (W) ; - - } - else - { - - /* A is real: real(X) = real(A) \ real(b) */ - X = mxGetPr (pargout [0]) ; - B = mxGetPr (B_matlab) ; - for (k = 0 ; k < n*nrhs ; k++) - { - X [k] = B [k] ; - } - klu_l_solve (Symbolic, Numeric, n, nrhs, X, &Common) ; - if (B_complex) - { - /* B is complex: imag(X) = real(A) \ imag(B) */ - X = mxGetPi (pargout [0]) ; - B = mxGetPi (B_matlab) ; - for (k = 0 ; k < n*nrhs ; k++) - { - X [k] = B [k] ; - } - klu_l_solve (Symbolic, Numeric, n, nrhs, X, &Common) ; - } - } - } - - /* -------------------------------------------------------------- */ - /* free Symbolic and Numeric objects */ - /* -------------------------------------------------------------- */ - - klu_l_free_symbolic (&Symbolic, &Common) ; - if (A_complex) - { - klu_zl_free_numeric (&Numeric, &Common) ; - } - else - { - klu_l_free_numeric (&Numeric, &Common) ; - } - - } - else - { - - /* -------------------------------------------------------------- */ - /* solve using LU struct given on input */ - /* -------------------------------------------------------------- */ - - /* the factorization is L*U+F = R\A(p,q), where L*U is block - diagonal, and F contains the entries in the upper block - triangular part */ - - L_matlab = mxGetField (LU_matlab, 0, "L") ; - U_matlab = mxGetField (LU_matlab, 0, "U") ; - p_matlab = mxGetField (LU_matlab, 0, "p") ; - q_matlab = mxGetField (LU_matlab, 0, "q") ; - R_matlab = mxGetField (LU_matlab, 0, "R") ; - F_matlab = mxGetField (LU_matlab, 0, "F") ; - r_matlab = mxGetField (LU_matlab, 0, "r") ; - - if (!L_matlab || !U_matlab || !mxIsSparse (L_matlab) || - !mxIsSparse (U_matlab)) - { - mexErrMsgTxt ("invalid LU struct") ; - } - - n = mxGetM (L_matlab) ; - if (n != mxGetN (L_matlab) || - n != mxGetM (U_matlab) || n != mxGetN (U_matlab) - /* ... */ - ) - { - mexErrMsgTxt ("invalid LU struct") ; - } - - if (n != bn || nrhs == 0) - { - mexErrMsgTxt ( - "B must be non-empty with same number of rows as L and U") ; - } - - /* get L */ - if (!mxIsSparse (L_matlab) || - n != mxGetM (L_matlab) || n != mxGetN (L_matlab)) - { - mexErrMsgTxt ("LU.L must be sparse and same size as A") ; - } - - Lp = (Long *) mxGetJc (L_matlab) ; - Li = (Long *) mxGetIr (L_matlab) ; - Lx = mxGetPr (L_matlab) ; - Lz = mxGetPi (L_matlab) ; - - /* get U */ - if (!mxIsSparse (U_matlab) || - n != mxGetM (U_matlab) || n != mxGetN (U_matlab)) - { - mexErrMsgTxt ("LU.U must be sparse and same size as A") ; - } - Up = (Long *) mxGetJc (U_matlab) ; - Ui = (Long *) mxGetIr (U_matlab) ; - Ux = mxGetPr (U_matlab) ; - Uz = mxGetPi (U_matlab) ; - - /* get p */ - if (p_matlab) - { - if (mxGetNumberOfElements (p_matlab) != n - || mxIsSparse (p_matlab) - || mxGetClassID (p_matlab) != mx_int) - { - mexErrMsgTxt ("P invalid") ; - } - P = (Long *) mxGetData (p_matlab) ; - for (k = 0 ; k < n ; k++) - { - if (P [k] < 1 || P [k] > n) mexErrMsgTxt ("P invalid") ; - } - } - else - { - /* no P, use identity instead */ - P = NULL ; - } - - /* get q */ - if (q_matlab) - { - if (mxGetNumberOfElements (q_matlab) != n - || mxIsSparse (q_matlab) - || mxGetClassID (q_matlab) != mx_int) - { - mexErrMsgTxt ("Q invalid") ; - } - Q = (Long *) mxGetData (q_matlab) ; - for (k = 0 ; k < n ; k++) - { - if (Q [k] < 1 || Q [k] > n) mexErrMsgTxt ("Q invalid.") ; - } - } - else - { - /* no Q, use identity instead */ - Q = NULL ; - } - - /* get r */ - R1 [0] = 1 ; - R1 [1] = n+1 ; - if (r_matlab) - { - nblocks = mxGetNumberOfElements (r_matlab) - 1 ; - if (nblocks < 1 || nblocks > n || mxIsSparse (r_matlab) - || mxGetClassID (r_matlab) != mx_int) - { - mexErrMsgTxt ("r invalid") ; - } - R = (Long *) mxGetData (r_matlab) ; - if (R [0] != 1) mexErrMsgTxt ("r invalid") ; - for (k = 1 ; k <= nblocks ; k++) - { - if (R [k] <= R [k-1] || R [k] > n+1) - { - mexErrMsgTxt ("rinvalid") ; - } - } - if (R [nblocks] != n+1) mexErrMsgTxt ("r invalid") ; - } - else - { - /* no r */ - nblocks = 1 ; - R = R1 ; - } - - /* get R, scale factors */ - if (R_matlab) - { - /* ensure R is sparse, real, and has the right size */ - if (!mxIsSparse (R_matlab) || - n != mxGetM (R_matlab) || n != mxGetN (R_matlab)) - { - mexErrMsgTxt ("LU.R must be sparse and same size as A") ; - } - Rp = (Long *) mxGetJc (R_matlab) ; - Rs = mxGetPr (R_matlab) ; - if (Rp [n] != n) - { - mexErrMsgTxt ("LU.R invalid, must be diagonal") ; - } - } - else - { - /* no scale factors */ - Rs = NULL ; - } - - /* get F, off diagonal entries */ - if (F_matlab) - { - if (!mxIsSparse (F_matlab) || - n != mxGetM (F_matlab) || n != mxGetN (F_matlab)) - { - mexErrMsgTxt ("LU.F must be sparse and same size as A") ; - } - Offp = (Long *) mxGetJc (F_matlab) ; - Offi = (Long *) mxGetIr (F_matlab) ; - Offx = mxGetPr (F_matlab) ; - Offz = mxGetPi (F_matlab) ; - } - else - { - /* no off-diagonal entries */ - Offp = NULL ; - Offi = NULL ; - Offx = NULL ; - Offz = NULL ; - } - - /* -------------------------------------------------------------- */ - /* solve */ - /* -------------------------------------------------------------- */ - - if (mxIsComplex (L_matlab) || mxIsComplex (U_matlab) || - (F_matlab && mxIsComplex (F_matlab)) || B_complex) - { - - /* ========================================================== */ - /* === complex case ========================================= */ - /* ========================================================== */ - - /* create X */ - if (do_transpose) - { - pargout [0] = mxCreateDoubleMatrix (nrhs, n, mxCOMPLEX) ; - } - else - { - pargout [0] = mxCreateDoubleMatrix (n, nrhs, mxCOMPLEX) ; - } - Xx = mxGetPr (pargout [0]) ; - Xz = mxGetPi (pargout [0]) ; - - Bx = mxGetPr (B_matlab) ; - Bz = mxGetPi (B_matlab) ; - - /* get workspace */ - Wx = mxMalloc (n * sizeof (double)) ; - Wz = mxMalloc (n * sizeof (double)) ; - - /* ---------------------------------------------------------- */ - /* do just one row/column of the right-hand-side at a time */ - /* ---------------------------------------------------------- */ - - if (do_transpose) - { - - for (chunk = 0 ; chunk < nrhs ; chunk++) - { - - /* -------------------------------------------------- */ - /* transpose and permute right hand side, W = Q'*B' */ - /* -------------------------------------------------- */ - - for (k = 0 ; k < n ; k++) - { - i = Q ? (Q [k] - 1) : k ; - Wx [k] = Bx [i*nrhs] ; - Wz [k] = Bz ? (-Bz [i*nrhs]) : 0 ; - } - - /* -------------------------------------------------- */ - /* solve W = (L*U + Off)'\W */ - /* -------------------------------------------------- */ - - for (block = 0 ; block < nblocks ; block++) - { - - /* ---------------------------------------------- */ - /* block of size nk, rows/columns k1 to k2-1 */ - /* ---------------------------------------------- */ - - k1 = R [block] - 1 ; /* R is 1-based */ - k2 = R [block+1] - 1 ; - nk = k2 - k1 ; - - /* ---------------------------------------------- */ - /* block back-substitution for off-diagonal-block */ - /* ---------------------------------------------- */ - - if (block > 0 && Offp != NULL) - { - for (k = k1 ; k < k2 ; k++) - { - pend = Offp [k+1] ; - for (p = Offp [k] ; p < pend ; p++) - { - i = Offi [p] ; - /* W [k] -= W [i] * conj(Off [p]) ; */ - z = Offz ? Offz [p] : 0 ; - MULT_SUB_CONJ (Wx [k], Wz [k], - Wx [i], Wz [i], Offx [p], z) ; - } - } - } - - - /* solve the block system */ - if (nk == 1) - { - - /* W [k1] /= conj (L(k1,k1)) ; */ - p = Lp [k1] ; - s = Lx [p] ; - sz = Lz ? (-Lz [p]) : 0 ; - DIV (wx, wz, Wx [k1], Wz [k1], s, sz) ; - Wx [k1] = wx ; - Wz [k1] = wz ; - - /* W [k1] /= conj (U(k1,k1)) ; */ - p = Up [k1] ; - s = Ux [p] ; - sz = Uz ? (-Uz [p]) : 0 ; - DIV (wx, wz, Wx [k1], Wz [k1], s, sz) ; - Wx [k1] = wx ; - Wz [k1] = wz ; - - } - else - { - - /* ------------------------------------------ */ - /* W = U'\W and then W=L'\W */ - /* ------------------------------------------ */ - - /* W = U'\W */ - for (k = k1 ; k < k2 ; k++) - { - pend = Up [k+1] - 1 ; - /* w = W [k] */ - wx = Wx [k] ; - wz = Wz [k] ; - for (p = Up [k] ; p < pend ; p++) - { - i = Ui [p] ; - /* w -= W [i] * conj(U [p]) */ - z = Uz ? Uz [p] : 0 ; - MULT_SUB_CONJ (wx, wz, - Wx [i], Wz [i], Ux [p], z) ; - } - /* W [k] = w / conj(ukk) ; */ - ukk = Ux [pend] ; - ukkz = Uz ? (-Uz [pend]) : 0 ; - DIV (Wx [k], Wz [k], wx, wz, ukk, ukkz) ; - } - - /* W = L'\W */ - for (k = k2-1 ; k >= k1 ; k--) - { - p = Lp [k] ; - pend = Lp [k+1] ; - /* w = W [k] */ - wx = Wx [k] ; - wz = Wz [k] ; - lkk = Lx [p] ; - lkkz = Lz ? (-Lz [p]) : 0 ; - for (p++ ; p < pend ; p++) - { - i = Li [p] ; - /* w -= W [i] * conj (Lx [p]) ; */ - z = Lz ? Lz [p] : 0 ; - MULT_SUB_CONJ (wx, wz, - Wx [i], Wz [i], Lx [p], z) ; - } - /* W [k] = w / conj(lkk) ; */ - DIV (Wx [k], Wz [k], wx, wz, lkk, lkkz) ; - } - } - } - - /* -------------------------------------------------- */ - /* scale, permute, and tranpose: X = (P*(R\W))' */ - /* -------------------------------------------------- */ - - if (Rs == NULL) - { - /* no scaling */ - for (k = 0 ; k < n ; k++) - { - i = P ? (P [k] - 1) : k ; - Xx [i*nrhs] = Wx [k] ; - Xz [i*nrhs] = Wz ? (-Wz [k]) : 0 ; - } - } - else - { - /* with scaling */ - for (k = 0 ; k < n ; k++) - { - i = P ? (P [k] - 1) : k ; - rs = Rs [k] ; - Xx [i*nrhs] = Wx [k] / rs ; - Xz [i*nrhs] = Wz ? (-Wz [k] / rs) : 0 ; - } - } - - /* -------------------------------------------------- */ - /* go to the next row of B and X */ - /* -------------------------------------------------- */ - - Xx++ ; - Xz++ ; - Bx++ ; - if (Bz) Bz++ ; - } - - } - else - { - - for (chunk = 0 ; chunk < nrhs ; chunk++) - { - - /* -------------------------------------------------- */ - /* scale and permute the right hand side, W = P*(R\B) */ - /* -------------------------------------------------- */ - - if (Rs == NULL) - { - /* no scaling */ - for (k = 0 ; k < n ; k++) - { - i = P ? (P [k] - 1) : k ; - Wx [k] = Bx [i] ; - Wz [k] = Bz ? Bz [i] : 0 ; - } - } - else - { - /* with scaling */ - for (k = 0 ; k < n ; k++) - { - i = P ? (P [k] - 1) : k ; - rs = Rs [k] ; - Wx [k] = Bx [i] / rs ; - Wz [k] = Bz ? (Bz [i] / rs) : 0 ; - } - } - - /* -------------------------------------------------- */ - /* solve W = (L*U + Off)\W */ - /* -------------------------------------------------- */ - - for (block = nblocks-1 ; block >= 0 ; block--) - { - - /* ---------------------------------------------- */ - /* block of size nk, rows/columns k1 to k2-1 */ - /* ---------------------------------------------- */ - - k1 = R [block] - 1 ; /* R is 1-based */ - k2 = R [block+1] - 1 ; - nk = k2 - k1 ; - - /* solve the block system */ - if (nk == 1) - { - - /* W [k1] /= L(k1,k1) ; */ - p = Lp [k1] ; - s = Lx [p] ; - sz = Lz ? Lz [p] : 0 ; - DIV (wx, wz, Wx [k1], Wz [k1], s, sz) ; - Wx [k1] = wx ; - Wz [k1] = wz ; - - /* W [k1] /= U(k1,k1) ; */ - p = Up [k1] ; - s = Ux [p] ; - sz = Uz ? Uz [p] : 0 ; - DIV (wx, wz, Wx [k1], Wz [k1], s, sz) ; - Wx [k1] = wx ; - Wz [k1] = wz ; - - } - else - { - - /* ------------------------------------------ */ - /* W = L\W and then W=U\W */ - /* ------------------------------------------ */ - - /* W = L\W */ - for (k = k1 ; k < k2 ; k++) - { - p = Lp [k] ; - pend = Lp [k+1] ; - lkk = Lx [p] ; - lkkz = Lz ? Lz [p] : 0 ; - /* w = W [k] / lkk ; */ - DIV (wx, wz, Wx [k], Wz [k], lkk, lkkz) ; - Wx [k] = wx ; - Wz [k] = wz ; - for (p++ ; p < pend ; p++) - { - i = Li [p] ; - /* W [i] -= Lx [p] * w ; */ - z = Lz ? Lz [p] : 0 ; - MULT_SUB (Wx [i], Wz [i], Lx [p], z, - wx, wz) ; - } - } - - /* W = U\W */ - for (k = k2-1 ; k >= k1 ; k--) - { - pend = Up [k+1] - 1 ; - ukk = Ux [pend] ; - ukkz = Uz ? Uz [pend] : 0 ; - /* w = W [k] / ukk ; */ - DIV (wx, wz, Wx [k], Wz [k], ukk, ukkz) ; - Wx [k] = wx ; - Wz [k] = wz ; - for (p = Up [k] ; p < pend ; p++) - { - i = Ui [p] ; - /* W [i] -= U [p] * w ; */ - z = Uz ? Uz [p] : 0 ; - MULT_SUB (Wx [i], Wz [i], Ux [p], z, - wx, wz) ; - } - } - } - - /* ---------------------------------------------- */ - /* block back-substitution for off-diagonal-block */ - /* ---------------------------------------------- */ - - if (block > 0 && Offp != NULL) - { - for (k = k1 ; k < k2 ; k++) - { - pend = Offp [k+1] ; - wx = Wx [k] ; - wz = Wz [k] ; - for (p = Offp [k] ; p < pend ; p++) - { - i = Offi [p] ; - /* W [Offi [p]] -= Offx [p] * w ; */ - z = Offz ? Offz [p] : 0 ; - MULT_SUB (Wx [i], Wz [i], Offx [p], z, - wx, wz) ; - } - } - } - } - - /* -------------------------------------------------- */ - /* permute the result, X = Q*W */ - /* -------------------------------------------------- */ - - for (k = 0 ; k < n ; k++) - { - i = Q ? (Q [k] - 1) : k ; - Xx [i] = Wx [k] ; - Xz [i] = Wz [k] ; - } - - /* -------------------------------------------------- */ - /* go to the next column of B and X */ - /* -------------------------------------------------- */ - - Xx += n ; - Xz += n ; - Bx += n ; - if (Bz) Bz += n ; - } - } - - /* free workspace */ - mxFree (Wx) ; - mxFree (Wz) ; - - } - else - { - - /* ========================================================== */ - /* === real case ============================================ */ - /* ========================================================== */ - - /* create X */ - if (do_transpose) - { - pargout [0] = mxCreateDoubleMatrix (nrhs, n, mxREAL) ; - } - else - { - pargout [0] = mxCreateDoubleMatrix (n, nrhs, mxREAL) ; - } - - Xx = mxGetPr (pargout [0]) ; - Bx = mxGetPr (B_matlab) ; - - if (do_transpose) - { - - /* ------------------------------------------------------ */ - /* solve in chunks of one row at a time */ - /* ------------------------------------------------------ */ - - /* get workspace */ - Wx = mxMalloc (n * sizeof (double)) ; - - for (chunk = 0 ; chunk < nrhs ; chunk++) - { - - /* -------------------------------------------------- */ - /* transpose and permute right hand side, W = Q'*B' */ - /* -------------------------------------------------- */ - - for (k = 0 ; k < n ; k++) - { - i = Q ? (Q [k] - 1) : k ; - Wx [k] = Bx [i*nrhs] ; - } - - /* -------------------------------------------------- */ - /* solve W = (L*U + Off)'\W */ - /* -------------------------------------------------- */ - - for (block = 0 ; block < nblocks ; block++) - { - - /* ---------------------------------------------- */ - /* block of size nk, rows/columns k1 to k2-1 */ - /* ---------------------------------------------- */ - - k1 = R [block] - 1 ; /* R is 1-based */ - k2 = R [block+1] - 1 ; - nk = k2 - k1 ; - - /* ---------------------------------------------- */ - /* block back-substitution for off-diagonal-block */ - /* ---------------------------------------------- */ - - if (block > 0 && Offp != NULL) - { - for (k = k1 ; k < k2 ; k++) - { - pend = Offp [k+1] ; - for (p = Offp [k] ; p < pend ; p++) - { - Wx [k] -= Wx [Offi [p]] * Offx [p] ; - } - } - } - - /* solve the block system */ - if (nk == 1) - { - Wx [k1] /= Lx [Lp [k1]] ; - Wx [k1] /= Ux [Up [k1]] ; - } - else - { - - /* ------------------------------------------ */ - /* W = U'\W and then W=L'\W */ - /* ------------------------------------------ */ - - /* W = U'\W */ - for (k = k1 ; k < k2 ; k++) - { - pend = Up [k+1] - 1 ; - for (p = Up [k] ; p < pend ; p++) - { - Wx [k] -= Wx [Ui [p]] * Ux [p] ; - } - Wx [k] /= Ux [pend] ; - } - - /* W = L'\W */ - for (k = k2-1 ; k >= k1 ; k--) - { - p = Lp [k] ; - pend = Lp [k+1] ; - lkk = Lx [p] ; - for (p++ ; p < pend ; p++) - { - Wx [k] -= Wx [Li [p]] * Lx [p] ; - } - Wx [k] /= lkk ; - } - } - } - - /* -------------------------------------------------- */ - /* scale, permute, and tranpose: X = (P*(R\W))' */ - /* -------------------------------------------------- */ - - if (Rs == NULL) - { - /* no scaling */ - for (k = 0 ; k < n ; k++) - { - i = P ? (P [k] - 1) : k ; - Xx [i*nrhs] = Wx [k] ; - } - } - else - { - /* with scaling */ - for (k = 0 ; k < n ; k++) - { - i = P ? (P [k] - 1) : k ; - rs = Rs [k] ; - Xx [i*nrhs] = Wx [k] / rs ; - } - } - - /* -------------------------------------------------- */ - /* go to the next row of B and X */ - /* -------------------------------------------------- */ - - Xx++ ; - Bx++ ; - } - - } - else - { - - /* ------------------------------------------------------ */ - /* solve in chunks of 4 columns at a time */ - /* ------------------------------------------------------ */ - - /* get workspace */ - Wx = mxMalloc (n * MAX (4, nrhs) * sizeof (double)) ; - - for (chunk = 0 ; chunk < nrhs ; chunk += 4) - { - /* -------------------------------------------------- */ - /* get the size of the current chunk */ - /* -------------------------------------------------- */ - - nr = MIN (nrhs - chunk, 4) ; - - /* -------------------------------------------------- */ - /* scale and permute the right hand side, W = P*(R\B) */ - /* -------------------------------------------------- */ - - if (Rs == NULL) - { - - /* no scaling */ - switch (nr) - { - - case 1: - - for (k = 0 ; k < n ; k++) - { - i = P ? (P [k] - 1) : k ; - Wx [k] = Bx [i] ; - } - break ; - - case 2: - - for (k = 0 ; k < n ; k++) - { - i = P ? (P [k] - 1) : k ; - Wx [2*k ] = Bx [i ] ; - Wx [2*k + 1] = Bx [i + n ] ; - } - break ; - - case 3: - - for (k = 0 ; k < n ; k++) - { - i = P ? (P [k] - 1) : k ; - Wx [3*k ] = Bx [i ] ; - Wx [3*k + 1] = Bx [i + n ] ; - Wx [3*k + 2] = Bx [i + n*2] ; - } - break ; - - case 4: - - for (k = 0 ; k < n ; k++) - { - i = P ? (P [k] - 1) : k ; - Wx [4*k ] = Bx [i ] ; - Wx [4*k + 1] = Bx [i + n ] ; - Wx [4*k + 2] = Bx [i + n*2] ; - Wx [4*k + 3] = Bx [i + n*3] ; - } - break ; - } - - } - else - { - - switch (nr) - { - - case 1: - - for (k = 0 ; k < n ; k++) - { - i = P ? (P [k] - 1) : k ; - rs = Rs [k] ; - Wx [k] = Bx [i] / rs ; - } - break ; - - case 2: - - for (k = 0 ; k < n ; k++) - { - i = P ? (P [k] - 1) : k ; - rs = Rs [k] ; - Wx [2*k ] = Bx [i ] / rs ; - Wx [2*k + 1] = Bx [i + n ] / rs ; - } - break ; - - case 3: - - for (k = 0 ; k < n ; k++) - { - i = P ? (P [k] - 1) : k ; - rs = Rs [k] ; - Wx [3*k ] = Bx [i ] / rs ; - Wx [3*k + 1] = Bx [i + n ] / rs ; - Wx [3*k + 2] = Bx [i + n*2] / rs ; - } - break ; - - case 4: - - for (k = 0 ; k < n ; k++) - { - i = P ? (P [k] - 1) : k ; - rs = Rs [k] ; - Wx [4*k ] = Bx [i ] / rs ; - Wx [4*k + 1] = Bx [i + n ] / rs ; - Wx [4*k + 2] = Bx [i + n*2] / rs ; - Wx [4*k + 3] = Bx [i + n*3] / rs ; - } - break ; - } - } - - /* -------------------------------------------------- */ - /* solve W = (L*U + Off)\W */ - /* -------------------------------------------------- */ - - for (block = nblocks-1 ; block >= 0 ; block--) - { - - /* ---------------------------------------------- */ - /* block of size nk is rows/columns k1 to k2-1 */ - /* ---------------------------------------------- */ - - k1 = R [block] - 1 ; /* R is 1-based */ - k2 = R [block+1] - 1 ; - nk = k2 - k1 ; - - /* solve the block system */ - if (nk == 1) - { - - /* this is not done if L comes from KLU, since - in that case, L is unit lower triangular */ - s = Lx [Lp [k1]] ; - if (s != 1.0) switch (nr) - { - case 1: - Wx [k1] /= s ; - break ; - case 2: - Wx [2*k1] /= s ; - Wx [2*k1 + 1] /= s ; - break ; - case 3: - Wx [3*k1] /= s ; - Wx [3*k1 + 1] /= s ; - Wx [3*k1 + 2] /= s ; - break ; - case 4: - Wx [4*k1] /= s ; - Wx [4*k1 + 1] /= s ; - Wx [4*k1 + 2] /= s ; - Wx [4*k1 + 3] /= s ; - break ; - } - - s = Ux [Up [k1]] ; - if (s != 1.0) switch (nr) - { - case 1: - Wx [k1] /= s ; - break ; - case 2: - Wx [2*k1] /= s ; - Wx [2*k1 + 1] /= s ; - break ; - case 3: - Wx [3*k1] /= s ; - Wx [3*k1 + 1] /= s ; - Wx [3*k1 + 2] /= s ; - break ; - case 4: - Wx [4*k1] /= s ; - Wx [4*k1 + 1] /= s ; - Wx [4*k1 + 2] /= s ; - Wx [4*k1 + 3] /= s ; - break ; - } - - } - else - { - - /* ------------------------------------------ */ - /* W = L\W and then W=U\W */ - /* ------------------------------------------ */ - - switch (nr) - { - - case 1: - /* W = L\W */ - for (k = k1 ; k < k2 ; k++) - { - p = Lp [k] ; - pend = Lp [k+1] ; - lkk = Lx [p++] ; - x [0] = Wx [k] / lkk ; - Wx [k] = x [0] ; - for ( ; p < pend ; p++) - { - Wx [Li [p]] -= Lx [p] * x [0] ; - } - } - - /* W = U\W */ - for (k = k2-1 ; k >= k1 ; k--) - { - pend = Up [k+1] - 1 ; - ukk = Ux [pend] ; - x [0] = Wx [k] / ukk ; - Wx [k] = x [0] ; - for (p = Up [k] ; p < pend ; p++) - { - Wx [Ui [p]] -= Ux [p] * x [0] ; - } - } - break ; - - case 2: - - /* W = L\W */ - for (k = k1 ; k < k2 ; k++) - { - p = Lp [k] ; - pend = Lp [k+1] ; - lkk = Lx [p++] ; - x [0] = Wx [2*k ] / lkk ; - x [1] = Wx [2*k + 1] / lkk ; - Wx [2*k ] = x [0] ; - Wx [2*k + 1] = x [1] ; - for ( ; p < pend ; p++) - { - i = Li [p] ; - lik = Lx [p] ; - Wx [2*i ] -= lik * x [0] ; - Wx [2*i + 1] -= lik * x [1] ; - } - } - - /* W = U\W */ - for (k = k2-1 ; k >= k1 ; k--) - { - pend = Up [k+1] - 1 ; - ukk = Ux [pend] ; - x [0] = Wx [2*k ] / ukk ; - x [1] = Wx [2*k + 1] / ukk ; - Wx [2*k ] = x [0] ; - Wx [2*k + 1] = x [1] ; - for (p = Up [k] ; p < pend ; p++) - { - i = Ui [p] ; - uik = Ux [p] ; - Wx [2*i ] -= uik * x [0] ; - Wx [2*i + 1] -= uik * x [1] ; - } - } - break ; - - case 3: - - /* W = L\W */ - for (k = k1 ; k < k2 ; k++) - { - p = Lp [k] ; - pend = Lp [k+1] ; - lkk = Lx [p++] ; - x [0] = Wx [3*k ] / lkk ; - x [1] = Wx [3*k + 1] / lkk ; - x [2] = Wx [3*k + 2] / lkk ; - Wx [3*k ] = x [0] ; - Wx [3*k + 1] = x [1] ; - Wx [3*k + 2] = x [2] ; - for ( ; p < pend ; p++) - { - i = Li [p] ; - lik = Lx [p] ; - Wx [3*i ] -= lik * x [0] ; - Wx [3*i + 1] -= lik * x [1] ; - Wx [3*i + 2] -= lik * x [2] ; - } - } - - /* W = U\W */ - for (k = k2-1 ; k >= k1 ; k--) - { - pend = Up [k+1] - 1 ; - ukk = Ux [pend] ; - x [0] = Wx [3*k ] / ukk ; - x [1] = Wx [3*k + 1] / ukk ; - x [2] = Wx [3*k + 2] / ukk ; - Wx [3*k ] = x [0] ; - Wx [3*k + 1] = x [1] ; - Wx [3*k + 2] = x [2] ; - for (p = Up [k] ; p < pend ; p++) - { - i = Ui [p] ; - uik = Ux [p] ; - Wx [3*i ] -= uik * x [0] ; - Wx [3*i + 1] -= uik * x [1] ; - Wx [3*i + 2] -= uik * x [2] ; - } - } - break ; - - case 4: - - /* W = L\W */ - for (k = k1 ; k < k2 ; k++) - { - p = Lp [k] ; - pend = Lp [k+1] ; - lkk = Lx [p++] ; - x [0] = Wx [4*k ] / lkk ; - x [1] = Wx [4*k + 1] / lkk ; - x [2] = Wx [4*k + 2] / lkk ; - x [3] = Wx [4*k + 3] / lkk ; - Wx [4*k ] = x [0] ; - Wx [4*k + 1] = x [1] ; - Wx [4*k + 2] = x [2] ; - Wx [4*k + 3] = x [3] ; - for ( ; p < pend ; p++) - { - i = Li [p] ; - lik = Lx [p] ; - Wx [4*i ] -= lik * x [0] ; - Wx [4*i + 1] -= lik * x [1] ; - Wx [4*i + 2] -= lik * x [2] ; - Wx [4*i + 3] -= lik * x [3] ; - } - } - - /* Wx = U\Wx */ - for (k = k2-1 ; k >= k1 ; k--) - { - pend = Up [k+1] - 1 ; - ukk = Ux [pend] ; - x [0] = Wx [4*k ] / ukk ; - x [1] = Wx [4*k + 1] / ukk ; - x [2] = Wx [4*k + 2] / ukk ; - x [3] = Wx [4*k + 3] / ukk ; - Wx [4*k ] = x [0] ; - Wx [4*k + 1] = x [1] ; - Wx [4*k + 2] = x [2] ; - Wx [4*k + 3] = x [3] ; - for (p = Up [k] ; p < pend ; p++) - { - i = Ui [p] ; - uik = Ux [p] ; - Wx [4*i ] -= uik * x [0] ; - Wx [4*i + 1] -= uik * x [1] ; - Wx [4*i + 2] -= uik * x [2] ; - Wx [4*i + 3] -= uik * x [3] ; - } - } - break ; - } - } - - /* ---------------------------------------------- */ - /* block back-substitution for off-diagonal-block */ - /* ---------------------------------------------- */ - - if (block > 0 && Offp != NULL) - { - switch (nr) - { - - case 1: - - for (k = k1 ; k < k2 ; k++) - { - pend = Offp [k+1] ; - x [0] = Wx [k] ; - for (p = Offp [k] ; p < pend ; p++) - { - Wx [Offi [p]] -= Offx[p] * x[0]; - } - } - break ; - - case 2: - - for (k = k1 ; k < k2 ; k++) - { - pend = Offp [k+1] ; - x [0] = Wx [2*k ] ; - x [1] = Wx [2*k + 1] ; - for (p = Offp [k] ; p < pend ; p++) - { - i = Offi [p] ; - offik = Offx [p] ; - Wx [2*i ] -= offik * x [0] ; - Wx [2*i + 1] -= offik * x [1] ; - } - } - break ; - - case 3: - - for (k = k1 ; k < k2 ; k++) - { - pend = Offp [k+1] ; - x [0] = Wx [3*k ] ; - x [1] = Wx [3*k + 1] ; - x [2] = Wx [3*k + 2] ; - for (p = Offp [k] ; p < pend ; p++) - { - i = Offi [p] ; - offik = Offx [p] ; - Wx [3*i ] -= offik * x [0] ; - Wx [3*i + 1] -= offik * x [1] ; - Wx [3*i + 2] -= offik * x [2] ; - } - } - break ; - - case 4: - - for (k = k1 ; k < k2 ; k++) - { - pend = Offp [k+1] ; - x [0] = Wx [4*k ] ; - x [1] = Wx [4*k + 1] ; - x [2] = Wx [4*k + 2] ; - x [3] = Wx [4*k + 3] ; - for (p = Offp [k] ; p < pend ; p++) - { - i = Offi [p] ; - offik = Offx [p] ; - Wx [4*i ] -= offik * x [0] ; - Wx [4*i + 1] -= offik * x [1] ; - Wx [4*i + 2] -= offik * x [2] ; - Wx [4*i + 3] -= offik * x [3] ; - } - } - break ; - } - } - } - - /* -------------------------------------------------- */ - /* permute the result, X = Q*W */ - /* -------------------------------------------------- */ - - switch (nr) - { - - case 1: - - for (k = 0 ; k < n ; k++) - { - i = Q ? (Q [k] - 1) : k ; - Xx [i] = Wx [k] ; - } - break ; - - case 2: - - for (k = 0 ; k < n ; k++) - { - i = Q ? (Q [k] - 1) : k ; - Xx [i ] = Wx [2*k ] ; - Xx [i + n ] = Wx [2*k + 1] ; - } - break ; - - case 3: - - for (k = 0 ; k < n ; k++) - { - i = Q ? (Q [k] - 1) : k ; - Xx [i ] = Wx [3*k ] ; - Xx [i + n ] = Wx [3*k + 1] ; - Xx [i + n*2] = Wx [3*k + 2] ; - } - break ; - - case 4: - - for (k = 0 ; k < n ; k++) - { - i = Q ? (Q [k] - 1) : k ; - Xx [i ] = Wx [4*k ] ; - Xx [i + n ] = Wx [4*k + 1] ; - Xx [i + n*2] = Wx [4*k + 2] ; - Xx [i + n*3] = Wx [4*k + 3] ; - } - break ; - } - - /* -------------------------------------------------- */ - /* go to the next chunk of B and X */ - /* -------------------------------------------------- */ - - Xx += n*4 ; - Bx += n*4 ; - } - } - - /* free workspace */ - mxFree (Wx) ; - } - - } - - } - else - { - - /* ------------------------------------------------------------------ */ - /* LU = klu (A) usage; extract factorization */ - /* ------------------------------------------------------------------ */ - - /* sort the row indices in each column of L and U */ - if (A_complex) - { - klu_zl_sort (Symbolic, Numeric, &Common) ; - } - else - { - klu_l_sort (Symbolic, Numeric, &Common) ; - } - - /* L */ - L_matlab = mxCreateSparse (n, n, Numeric->lnz, - A_complex ? mxCOMPLEX: mxREAL) ; - Lp = (Long *) mxGetJc (L_matlab) ; - Li = (Long *) mxGetIr (L_matlab) ; - Lx = mxGetPr (L_matlab) ; - Lz = mxGetPi (L_matlab) ; - - /* U */ - U_matlab = mxCreateSparse (n, n, Numeric->unz, - A_complex ? mxCOMPLEX: mxREAL) ; - Up = (Long *) mxGetJc (U_matlab) ; - Ui = (Long *) mxGetIr (U_matlab) ; - Ux = mxGetPr (U_matlab) ; - Uz = mxGetPi (U_matlab) ; - - /* p */ - p_matlab = mxCreateNumericMatrix (1, n, mx_int, mxREAL) ; - P = (Long *) mxGetData (p_matlab) ; - - /* q */ - q_matlab = mxCreateNumericMatrix (1, n, mx_int, mxREAL) ; - Q = (Long *) mxGetData (q_matlab) ; - - /* R, as a sparse diagonal matrix */ - R_matlab = mxCreateSparse (n, n, n+1, mxREAL) ; - Rp = (Long *) mxGetJc (R_matlab) ; - Ri = (Long *) mxGetIr (R_matlab) ; - Rs = mxGetPr (R_matlab) ; - for (k = 0 ; k <= n ; k++) - { - Rp [k] = k ; - Ri [k] = k ; - } - - /* F, off diagonal blocks */ - F_matlab = mxCreateSparse (n, n, Numeric->nzoff, - A_complex ? mxCOMPLEX: mxREAL) ; - Offp = (Long *) mxGetJc (F_matlab) ; - Offi = (Long *) mxGetIr (F_matlab) ; - Offx = mxGetPr (F_matlab) ; - Offz = mxGetPi (F_matlab) ; - - /* r, block boundaries */ - nblocks = Symbolic->nblocks ; - r_matlab = mxCreateNumericMatrix (1, nblocks+1, mx_int, mxREAL) ; - R = (Long *) mxGetData (r_matlab) ; - - /* extract the LU factorization from KLU Numeric and Symbolic objects */ - if (A_complex) - { - klu_zl_extract (Numeric, Symbolic, Lp, Li, Lx, Lz, Up, Ui, Ux, Uz, - Offp, Offi, Offx, Offz, P, Q, Rs, R, &Common) ; - } - else - { - klu_l_extract (Numeric, Symbolic, Lp, Li, Lx, Up, Ui, Ux, - Offp, Offi, Offx, P, Q, Rs, R, &Common) ; - } - - /* fix p and q for 1-based indexing */ - for (k = 0 ; k < n ; k++) - { - P [k]++ ; - Q [k]++ ; - } - - /* fix r for 1-based indexing */ - for (k = 0 ; k <= nblocks ; k++) - { - R [k]++ ; - } - - /* create output LU struct */ - pargout [0] = mxCreateStructMatrix (1, 1, 7, LUnames) ; - mxSetFieldByNumber (pargout [0], 0, 0, L_matlab) ; - mxSetFieldByNumber (pargout [0], 0, 1, U_matlab) ; - mxSetFieldByNumber (pargout [0], 0, 2, p_matlab) ; - mxSetFieldByNumber (pargout [0], 0, 3, q_matlab) ; - mxSetFieldByNumber (pargout [0], 0, 4, R_matlab) ; - mxSetFieldByNumber (pargout [0], 0, 5, F_matlab) ; - mxSetFieldByNumber (pargout [0], 0, 6, r_matlab) ; - - /* ------------------------------------------------------------------ */ - /* free Symbolic and Numeric objects */ - /* ------------------------------------------------------------------ */ - - klu_l_free_symbolic (&Symbolic, &Common) ; - klu_l_free_numeric (&Numeric, &Common) ; - } -} diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Makefile b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Makefile index 5737518b8..896ea4a1b 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Makefile +++ b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Makefile @@ -1,53 +1,99 @@ -#------------------------------------------------------------------------------ -# KLU Makefile -#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------- +# SuiteSparse/KLU/Makefile +#------------------------------------------------------------------------------- -SUITESPARSE ?= $(realpath $(CURDIR)/..) -export SUITESPARSE +# KLU, Copyright (c) 2004-2022, University of Florida. All Rights Reserved. +# Authors: Timothy A. Davis and Ekanathan Palamadai. +# SPDX-License-Identifier: LGPL-2.1+ -default: all +#------------------------------------------------------------------------------- -include ../SuiteSparse_config/SuiteSparse_config.mk +# A simple Makefile for KLU, which relies on cmake to do the +# actual build. All the work is done in cmake so this Makefile is just for +# convenience. -demos: all +# To compile with an alternate compiler: +# +# make CC=gcc CXX=g++ +# +# To compile/install for system-wide usage: +# +# make +# sudo make install +# +# To compile/install for local usage (SuiteSparse/lib and SuiteSparse/include): +# +# make local +# make install +# +# To clean up the files: +# +# make clean -all: - ( cd Lib ; $(MAKE) ) - ( cd Demo ; $(MAKE) ) +JOBS ?= 8 +default: library + +# default is to install only in /usr/local library: - ( cd Lib ; $(MAKE) ) + ( cd build && cmake $(CMAKE_OPTIONS) .. && cmake --build . -j${JOBS} ) -# compile the static libraries only -static: - ( cd Lib ; $(MAKE) static ) +# install only in SuiteSparse/lib and SuiteSparse/include +local: + ( cd build && cmake $(CMAKE_OPTIONS) -DLOCAL_INSTALL=1 .. && cmake --build . -j${JOBS} ) -clean: - ( cd Demo ; $(MAKE) clean ) - ( cd Lib ; $(MAKE) clean ) - ( cd Tcov ; $(MAKE) clean ) - ( cd MATLAB ; $(RM) $(CLEAN) rename.h ) +# install only in /usr/local (default) +global: + ( cd build && cmake $(CMAKE_OPTIONS) -DLOCAL_INSTALL=0 .. && cmake --build . -j${JOBS} ) + +debug: + ( cd build && cmake $(CMAKE_OPTIONS) -DCMAKE_BUILD_TYPE=Debug .. && cmake --build . -j${JOBS} ) -distclean: - ( cd Demo ; $(MAKE) distclean ) - ( cd Lib ; $(MAKE) distclean ) - ( cd Tcov ; $(MAKE) distclean ) - ( cd User ; $(MAKE) distclean ) - ( cd MATLAB ; $(RM) $(CLEAN) rename.h *.mex* ) +all: library -purge: distclean +demos: library + ( cd build && cmake $(CMAKE_OPTIONS) -DDEMO=1 .. && cmake --build . -j${JOBS} ) + - ./build/klu_simple + - ./build/kludemo < ./Matrix/1c.mtx + - ./build/kludemo < ./Matrix/arrowc.mtx + - ./build/kludemo < ./Matrix/arrow.mtx + - ./build/kludemo < ./Matrix/impcol_a.mtx + - ./build/kludemo < ./Matrix/w156.mtx + - ./build/kludemo < ./Matrix/ctina.mtx + - ./build/kluldemo < ./Matrix/1c.mtx + - ./build/kluldemo < ./Matrix/arrowc.mtx + - ./build/kluldemo < ./Matrix/arrow.mtx + - ./build/kluldemo < ./Matrix/impcol_a.mtx + - ./build/kluldemo < ./Matrix/w156.mtx + - ./build/kluldemo < ./Matrix/ctina.mtx cov: - ( cd Tcov ; $(MAKE) ) + ( cd Tcov && $(MAKE) ) -# create PDF documents for the original distribution -docs: - ( cd Doc ; $(MAKE) ) +# just compile after running cmake; do not run cmake again +remake: + ( cd build && cmake --build . -j${JOBS} ) + +# just run cmake to set things up +setup: + ( cd build && cmake $(CMAKE_OPTIONS) .. ) -# install KLU install: - ( cd Lib ; $(MAKE) install ) + ( cd build && cmake --install . ) -# uninstall KLU +# remove any installed libraries and #include files uninstall: - ( cd Lib ; $(MAKE) uninstall ) + - xargs rm < build/install_manifest.txt + +# remove all files not in the distribution +clean: + - $(RM) -rf build/* Config/*.tmp MATLAB/*.o MATLAB/*.mex* + ( cd Tcov && $(MAKE) purge ) + +purge: clean + +distclean: clean + +docs: + ( cd Doc && $(MAKE) ) + diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Matrix/1c.mtx b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Matrix/1c.mtx deleted file mode 100644 index b9da42415..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Matrix/1c.mtx +++ /dev/null @@ -1,3 +0,0 @@ -%%MatrixMarket matrix coordinate complex general -1 1 1 -1 1 1 1 diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Matrix/GD99_cc.mtx b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Matrix/GD99_cc.mtx deleted file mode 100644 index 4f5baec19..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Matrix/GD99_cc.mtx +++ /dev/null @@ -1,152 +0,0 @@ -%%MatrixMarket matrix coordinate complex general -% imaginary part is Pajek/GD99_c, real part is zero -105 105 149 -6 1 0 1 -5 2 0 1 -7 2 0 1 -4 3 0 1 -8 3 0 1 -5 4 0 1 -1 5 0 1 -24 6 0 1 -6 7 0 1 -7 8 0 1 -26 9 0 1 -105 10 0 1 -94 11 0 1 -40 12 0 1 -26 13 0 1 -16 14 0 1 -26 15 0 1 -26 16 0 1 -16 17 0 1 -16 19 0 1 -16 20 0 1 -57 21 0 1 -15 22 0 1 -31 22 0 1 -13 23 0 1 -15 24 0 1 -13 25 0 1 -9 26 0 1 -16 26 0 1 -57 26 0 1 -24 27 0 1 -85 28 0 1 -85 29 0 1 -32 30 0 1 -86 30 0 1 -22 31 0 1 -45 31 0 1 -57 31 0 1 -22 32 0 1 -85 32 0 1 -54 33 0 1 -88 33 0 1 -91 33 0 1 -91 34 0 1 -92 34 0 1 -95 35 0 1 -50 36 0 1 -98 37 0 1 -98 38 0 1 -38 39 0 1 -38 40 0 1 -102 41 0 1 -101 42 0 1 -103 43 0 1 -100 44 0 1 -104 44 0 1 -31 45 0 1 -93 45 0 1 -28 46 0 1 -50 47 0 1 -87 47 0 1 -100 47 0 1 -53 48 0 1 -99 48 0 1 -87 49 0 1 -89 49 0 1 -45 50 0 1 -94 50 0 1 -31 51 0 1 -104 51 0 1 -48 52 0 1 -90 52 0 1 -91 52 0 1 -89 53 0 1 -87 54 0 1 -90 54 0 1 -93 54 0 1 -99 55 0 1 -29 56 0 1 -18 57 0 1 -21 57 0 1 -26 57 0 1 -31 57 0 1 -95 58 0 1 -45 59 0 1 -103 59 0 1 -59 60 0 1 -102 61 0 1 -61 62 0 1 -61 63 0 1 -53 64 0 1 -64 65 0 1 -65 66 0 1 -64 67 0 1 -91 67 0 1 -92 67 0 1 -88 68 0 1 -97 69 0 1 -96 70 0 1 -97 70 0 1 -96 71 0 1 -92 72 0 1 -48 73 0 1 -64 73 0 1 -98 73 0 1 -40 74 0 1 -86 75 0 1 -96 76 0 1 -86 77 0 1 -82 78 0 1 -78 79 0 1 -82 79 0 1 -81 80 0 1 -84 80 0 1 -78 81 0 1 -33 82 0 1 -82 83 0 1 -83 84 0 1 -32 85 0 1 -30 86 0 1 -47 87 0 1 -54 87 0 1 -33 88 0 1 -97 88 0 1 -49 89 0 1 -99 89 0 1 -52 90 0 1 -55 90 0 1 -33 91 0 1 -67 91 0 1 -34 92 0 1 -50 93 0 1 -50 94 0 1 -95 94 0 1 -94 95 0 1 -70 96 0 1 -88 97 0 1 -73 98 0 1 -55 99 0 1 -89 99 0 1 -47 100 0 1 -101 100 0 1 -100 101 0 1 -102 101 0 1 -101 102 0 1 -59 103 0 1 -105 103 0 1 -105 104 0 1 -104 105 0 1 diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Matrix/arrow.mtx b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Matrix/arrow.mtx deleted file mode 100644 index 27419e4a4..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Matrix/arrow.mtx +++ /dev/null @@ -1,300 +0,0 @@ -%%MatrixMarket matrix coordinate integer general -100 100 298 -1 1 2 -2 1 1 -3 1 1 -4 1 1 -5 1 1 -6 1 1 -7 1 1 -8 1 1 -9 1 1 -10 1 1 -11 1 1 -12 1 1 -13 1 1 -14 1 1 -15 1 1 -16 1 1 -17 1 1 -18 1 1 -19 1 1 -20 1 1 -21 1 1 -22 1 1 -23 1 1 -24 1 1 -25 1 1 -26 1 1 -27 1 1 -28 1 1 -29 1 1 -30 1 1 -31 1 1 -32 1 1 -33 1 1 -34 1 1 -35 1 1 -36 1 1 -37 1 1 -38 1 1 -39 1 1 -40 1 1 -41 1 1 -42 1 1 -43 1 1 -44 1 1 -45 1 1 -46 1 1 -47 1 1 -48 1 1 -49 1 1 -50 1 1 -51 1 1 -52 1 1 -53 1 1 -54 1 1 -55 1 1 -56 1 1 -57 1 1 -58 1 1 -59 1 1 -60 1 1 -61 1 1 -62 1 1 -63 1 1 -64 1 1 -65 1 1 -66 1 1 -67 1 1 -68 1 1 -69 1 1 -70 1 1 -71 1 1 -72 1 1 -73 1 1 -74 1 1 -75 1 1 -76 1 1 -77 1 1 -78 1 1 -79 1 1 -80 1 1 -81 1 1 -82 1 1 -83 1 1 -84 1 1 -85 1 1 -86 1 1 -87 1 1 -88 1 1 -89 1 1 -90 1 1 -91 1 1 -92 1 1 -93 1 1 -94 1 1 -95 1 1 -96 1 1 -97 1 1 -98 1 1 -99 1 1 -100 1 1 -1 2 2 -2 2 1 -1 3 1 -3 3 1 -1 4 1 -4 4 1 -1 5 1 -5 5 1 -1 6 1 -6 6 1 -1 7 1 -7 7 1 -1 8 1 -8 8 1 -1 9 1 -9 9 1 -1 10 1 -10 10 1 -1 11 1 -11 11 1 -1 12 1 -12 12 1 -1 13 1 -13 13 1 -1 14 1 -14 14 1 -1 15 1 -15 15 1 -1 16 1 -16 16 1 -1 17 1 -17 17 1 -1 18 1 -18 18 1 -1 19 1 -19 19 1 -1 20 1 -20 20 1 -1 21 1 -21 21 1 -1 22 1 -22 22 1 -1 23 1 -23 23 1 -1 24 1 -24 24 1 -1 25 1 -25 25 1 -1 26 1 -26 26 1 -1 27 1 -27 27 1 -1 28 1 -28 28 1 -1 29 1 -29 29 1 -1 30 1 -30 30 1 -1 31 1 -31 31 1 -1 32 1 -32 32 1 -1 33 1 -33 33 1 -1 34 1 -34 34 1 -1 35 1 -35 35 1 -1 36 1 -36 36 1 -1 37 1 -37 37 1 -1 38 1 -38 38 1 -1 39 1 -39 39 1 -1 40 1 -40 40 1 -1 41 1 -41 41 1 -1 42 1 -42 42 1 -1 43 1 -43 43 1 -1 44 1 -44 44 1 -1 45 1 -45 45 1 -1 46 1 -46 46 1 -1 47 1 -47 47 1 -1 48 1 -48 48 1 -1 49 1 -49 49 1 -1 50 1 -50 50 1 -1 51 1 -51 51 1 -1 52 1 -52 52 1 -1 53 1 -53 53 1 -1 54 1 -54 54 1 -1 55 1 -55 55 1 -1 56 1 -56 56 1 -1 57 1 -57 57 1 -1 58 1 -58 58 1 -1 59 1 -59 59 1 -1 60 1 -60 60 1 -1 61 1 -61 61 1 -1 62 1 -62 62 1 -1 63 1 -63 63 1 -1 64 1 -64 64 1 -1 65 1 -65 65 1 -1 66 1 -66 66 1 -1 67 1 -67 67 1 -1 68 1 -68 68 1 -1 69 1 -69 69 1 -1 70 1 -70 70 1 -1 71 1 -71 71 1 -1 72 1 -72 72 1 -1 73 1 -73 73 1 -1 74 1 -74 74 1 -1 75 1 -75 75 1 -1 76 1 -76 76 1 -1 77 1 -77 77 1 -1 78 1 -78 78 1 -1 79 1 -79 79 1 -1 80 1 -80 80 1 -1 81 1 -81 81 1 -1 82 1 -82 82 1 -1 83 1 -83 83 1 -1 84 1 -84 84 1 -1 85 1 -85 85 1 -1 86 1 -86 86 1 -1 87 1 -87 87 1 -1 88 1 -88 88 1 -1 89 1 -89 89 1 -1 90 1 -90 90 1 -1 91 1 -91 91 1 -1 92 1 -92 92 1 -1 93 1 -93 93 1 -1 94 1 -94 94 1 -1 95 1 -95 95 1 -1 96 1 -96 96 1 -1 97 1 -97 97 1 -1 98 1 -98 98 1 -1 99 1 -99 99 1 -1 100 1 -100 100 1 diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Matrix/arrowc.mtx b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Matrix/arrowc.mtx deleted file mode 100644 index bf4d3c50b..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Matrix/arrowc.mtx +++ /dev/null @@ -1,300 +0,0 @@ -%%MatrixMarket matrix coordinate complex general -100 100 298 -1 1 0 1 -2 1 1 0 -3 1 1 0 -4 1 1 0 -5 1 1 0 -6 1 1 0 -7 1 1 0 -8 1 1 0 -9 1 1 0 -10 1 1 0 -11 1 1 0 -12 1 1 0 -13 1 1 0 -14 1 1 0 -15 1 1 0 -16 1 1 0 -17 1 1 0 -18 1 1 0 -19 1 1 0 -20 1 1 0 -21 1 1 0 -22 1 1 0 -23 1 1 0 -24 1 1 0 -25 1 1 0 -26 1 1 0 -27 1 1 0 -28 1 1 0 -29 1 1 0 -30 1 1 0 -31 1 1 0 -32 1 1 0 -33 1 1 0 -34 1 1 0 -35 1 1 0 -36 1 1 0 -37 1 1 0 -38 1 1 0 -39 1 1 0 -40 1 1 0 -41 1 1 0 -42 1 1 0 -43 1 1 0 -44 1 1 0 -45 1 1 0 -46 1 1 0 -47 1 1 0 -48 1 1 0 -49 1 1 0 -50 1 1 0 -51 1 1 0 -52 1 1 0 -53 1 1 0 -54 1 1 0 -55 1 1 0 -56 1 1 0 -57 1 1 0 -58 1 1 0 -59 1 1 0 -60 1 1 0 -61 1 1 0 -62 1 1 0 -63 1 1 0 -64 1 1 0 -65 1 1 0 -66 1 1 0 -67 1 1 0 -68 1 1 0 -69 1 1 0 -70 1 1 0 -71 1 1 0 -72 1 1 0 -73 1 1 0 -74 1 1 0 -75 1 1 0 -76 1 1 0 -77 1 1 0 -78 1 1 0 -79 1 1 0 -80 1 1 0 -81 1 1 0 -82 1 1 0 -83 1 1 0 -84 1 1 0 -85 1 1 0 -86 1 1 0 -87 1 1 0 -88 1 1 0 -89 1 1 0 -90 1 1 0 -91 1 1 0 -92 1 1 0 -93 1 1 0 -94 1 1 0 -95 1 1 0 -96 1 1 0 -97 1 1 0 -98 1 1 0 -99 1 1 0 -100 1 1 0 -1 2 2 0 -2 2 1 0 -1 3 1 0 -3 3 1 0 -1 4 1 0 -4 4 1 0 -1 5 1 0 -5 5 1 0 -1 6 1 0 -6 6 1 0 -1 7 1 0 -7 7 1 0 -1 8 1 0 -8 8 1 0 -1 9 1 0 -9 9 1 0 -1 10 1 0 -10 10 1 0 -1 11 1 0 -11 11 1 0 -1 12 1 0 -12 12 1 0 -1 13 1 0 -13 13 1 0 -1 14 1 0 -14 14 1 0 -1 15 1 0 -15 15 1 0 -1 16 1 0 -16 16 1 0 -1 17 1 0 -17 17 1 0 -1 18 1 0 -18 18 1 0 -1 19 1 0 -19 19 1 0 -1 20 1 0 -20 20 1 0 -1 21 1 0 -21 21 1 0 -1 22 1 0 -22 22 1 0 -1 23 1 0 -23 23 1 0 -1 24 1 0 -24 24 1 0 -1 25 1 0 -25 25 1 0 -1 26 1 0 -26 26 1 0 -1 27 1 0 -27 27 1 0 -1 28 1 0 -28 28 1 0 -1 29 1 0 -29 29 1 0 -1 30 1 0 -30 30 1 0 -1 31 1 0 -31 31 1 0 -1 32 1 0 -32 32 1 0 -1 33 1 0 -33 33 1 0 -1 34 1 0 -34 34 1 0 -1 35 1 0 -35 35 1 0 -1 36 1 0 -36 36 1 0 -1 37 1 0 -37 37 1 0 -1 38 1 0 -38 38 1 0 -1 39 1 0 -39 39 1 0 -1 40 1 0 -40 40 1 0 -1 41 1 0 -41 41 1 0 -1 42 1 0 -42 42 1 0 -1 43 1 0 -43 43 1 0 -1 44 1 0 -44 44 1 0 -1 45 1 0 -45 45 1 0 -1 46 1 0 -46 46 1 0 -1 47 1 0 -47 47 1 0 -1 48 1 0 -48 48 1 0 -1 49 1 0 -49 49 1 0 -1 50 1 0 -50 50 1 0 -1 51 1 0 -51 51 1 0 -1 52 1 0 -52 52 1 0 -1 53 1 0 -53 53 1 0 -1 54 1 0 -54 54 1 0 -1 55 1 0 -55 55 1 0 -1 56 1 0 -56 56 1 0 -1 57 1 0 -57 57 1 0 -1 58 1 0 -58 58 1 0 -1 59 1 0 -59 59 1 0 -1 60 1 0 -60 60 1 0 -1 61 1 0 -61 61 1 0 -1 62 1 0 -62 62 1 0 -1 63 1 0 -63 63 1 0 -1 64 1 0 -64 64 1 0 -1 65 1 0 -65 65 1 0 -1 66 1 0 -66 66 1 0 -1 67 1 0 -67 67 1 0 -1 68 1 0 -68 68 1 0 -1 69 1 0 -69 69 1 0 -1 70 1 0 -70 70 1 0 -1 71 1 0 -71 71 1 0 -1 72 1 0 -72 72 1 0 -1 73 1 0 -73 73 1 0 -1 74 1 0 -74 74 1 0 -1 75 1 0 -75 75 1 0 -1 76 1 0 -76 76 1 0 -1 77 1 0 -77 77 1 0 -1 78 1 0 -78 78 1 0 -1 79 1 0 -79 79 1 0 -1 80 1 0 -80 80 1 0 -1 81 1 0 -81 81 1 0 -1 82 1 0 -82 82 1 0 -1 83 1 0 -83 83 1 0 -1 84 1 0 -84 84 1 0 -1 85 1 0 -85 85 1 0 -1 86 1 0 -86 86 1 0 -1 87 1 0 -87 87 1 0 -1 88 1 0 -88 88 1 0 -1 89 1 0 -89 89 1 0 -1 90 1 0 -90 90 1 0 -1 91 1 0 -91 91 1 0 -1 92 1 0 -92 92 1 0 -1 93 1 0 -93 93 1 0 -1 94 1 0 -94 94 1 0 -1 95 1 0 -95 95 1 0 -1 96 1 0 -96 96 1 0 -1 97 1 0 -97 97 1 0 -1 98 1 0 -98 98 1 0 -1 99 1 0 -99 99 1 0 -1 100 1 0 -100 100 1 0 diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Matrix/ctina.mtx b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Matrix/ctina.mtx deleted file mode 100644 index 3773ee163..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Matrix/ctina.mtx +++ /dev/null @@ -1,39 +0,0 @@ -%%MatrixMarket matrix coordinate complex general -% complex matrix with same nonzero pattern as Pajek/Tina_AskCog -11 11 36 -3 1 0 1 -6 1 0 1 -10 1 0 1 -1 2 0 1 -3 2 0 1 -5 2 0 1 -6 2 0 1 -8 2 0 1 -9 2 0 1 -10 2 0 1 -1 3 0 1 -6 3 0 1 -10 3 0 1 -2 4 0 1 -6 4 0 1 -7 4 0 1 -8 4 0 1 -11 4 0 1 -6 5 0 1 -10 5 0 1 -5 6 0 1 -11 6 0 1 -4 7 0 1 -6 7 0 1 -9 7 0 1 -2 8 0 1 -4 8 0 1 -5 8 0 1 -6 8 0 1 -7 8 0 1 -9 8 0 1 -11 8 0 1 -7 9 0 1 -3 10 0 1 -5 10 0 1 -8 11 0 1 diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Matrix/impcol_a.mtx b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Matrix/impcol_a.mtx deleted file mode 100644 index 49952a663..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Matrix/impcol_a.mtx +++ /dev/null @@ -1,586 +0,0 @@ -%%MatrixMarket matrix coordinate real general -%------------------------------------------------------------------------------- -% UF Sparse Matrix Collection, Tim Davis -% http://www.cise.ufl.edu/research/sparse/matrices/HB/impcol_a -% name: HB/impcol_a -% [UNSYMMETRIC MATRIX - HEAT EXCHANGER NETWORK (CHEM ENG) ,1982] -% id: 171 -% date: 1982 -% author: D. Bogle -% ed: I. Duff, R. Grimes, J. Lewis -% fields: title A name id date author ed kind -% kind: chemical process simulation problem -%------------------------------------------------------------------------------- -207 207 572 -5 1 -1 -6 1 -1 -8 1 -1 -11 1 .0662129 -12 1 .1634 -1 2 1 -2 2 1 -11 2 -.579712 -12 2 -.422521 -3 3 1 -4 3 1 -10 3 -1 -11 3 17.8775 -12 3 44.1179 -5 4 -1 -65 4 1 -66 4 .5 -1 5 -1 -62 5 -1 -3 6 -1 -64 6 -1 -6 7 680 -7 8 1 -9 9 1 -8 10 1 -14 10 -1 -17 10 .00297937 -18 10 .0247819 -12 11 1 -17 11 -.220568 -18 11 -.808841 -10 12 1 -16 12 -1 -17 12 .804429 -18 12 6.69112 -11 13 .0708967 -12 13 -.0706236 -13 14 -1 -17 14 -.0323714 -18 14 -.00352409 -23 14 -1 -24 14 -1 -17 15 -.779432 -18 15 -.191159 -19 15 1 -20 15 1 -15 16 -1 -17 16 -7.57491 -18 16 -.824638 -21 16 1 -22 16 1 -13 17 1 -31 17 -1 -33 17 -150 -17 18 1 -33 18 -234 -15 19 1 -32 19 -1 -14 20 1 -34 20 -1 -36 20 -126.8 -16 21 1 -17 22 .104857 -18 22 -.0908757 -23 23 -1 -26 23 -1 -29 23 -.0311679 -30 23 -.103571 -19 24 -1 -29 24 -.757389 -30 24 -.625946 -21 25 -1 -28 25 -1 -29 25 -10.7841 -30 25 -35.8356 -24 26 580 -25 27 1 -27 28 1 -26 29 1 -31 29 -1 -33 29 -142.6 -28 30 1 -29 31 -.0979613 -30 31 .0483804 -31 32 1 -33 32 145.6 -33 33 580 -32 34 1 -34 35 -1 -36 35 -161 -38 35 1 -35 36 -1 -40 36 1 -34 37 -1 -36 37 -152.2 -203 37 1 -205 38 1 -34 39 -1 -36 39 -136.3 -197 39 1 -199 40 1 -34 41 -1 -36 41 -108.6 -98 41 1 -100 42 1 -34 43 -1 -36 43 -120.7 -67 43 1 -69 44 1 -34 45 1 -36 45 128 -95 45 1 -96 45 .2 -36 46 680 -92 46 -1 -35 47 1 -94 47 -1 -38 48 -1 -41 48 1.65272 -42 48 2.82163 -47 48 -1 -41 49 -.607865 -42 49 -.263986 -43 49 -1 -40 50 -1 -41 50 36.3599 -42 50 62.0759 -45 50 -1 -37 51 1 -39 52 1 -41 53 .559606 -42 53 -.677581 -47 54 1 -48 54 .7 -53 54 -1 -44 55 -1 -49 55 -1 -46 56 -1 -51 56 -1 -47 57 -1 -48 57 -1 -203 57 -1 -206 57 .478232 -207 57 1.21868 -43 58 1 -44 58 1 -206 58 -.330881 -207 58 -.280695 -45 59 1 -46 59 1 -205 59 -1 -206 59 21.9987 -207 59 56.0594 -48 60 68 -53 61 1 -54 61 .6 -59 61 -1 -50 62 -1 -55 62 -1 -52 63 -1 -57 63 -1 -53 64 -1 -54 64 -1 -197 64 -1 -200 64 .27161 -201 64 .629158 -49 65 1 -50 65 1 -200 65 -.561642 -201 65 -.382811 -51 66 1 -52 66 1 -199 66 -1 -200 66 24.7165 -201 66 57.2534 -54 67 159 -59 68 1 -60 68 .2 -65 68 -1 -56 69 -1 -61 69 -1 -58 70 -1 -63 70 -1 -59 71 -1 -60 71 -1 -98 71 -1 -101 71 .244371 -102 71 1.02444 -55 72 1 -56 72 1 -101 72 -.272351 -102 72 -.432602 -57 73 1 -58 73 1 -100 73 -1 -101 73 11.7298 -102 73 49.1733 -60 74 207 -65 75 -1 -66 75 -1 -67 75 -1 -71 75 .232813 -72 75 .061436 -61 76 1 -62 76 1 -71 76 -.573583 -72 76 -.869891 -63 77 1 -64 77 1 -69 77 -1 -71 77 47.4938 -72 77 12.5329 -66 78 411 -68 79 -1 -71 79 -.341668 -72 79 -.40286 -74 79 1 -70 80 -1 -71 80 -34.1668 -72 80 -40.286 -76 80 1 -68 81 1 -70 82 1 -71 83 -.0459958 -72 83 .0938315 -73 84 -1 -77 84 .179968 -78 84 .0328426 -83 84 -1 -84 84 -1 -77 85 -.53763 -78 85 -.943234 -79 85 1 -80 85 1 -75 86 -1 -77 86 36.7135 -78 86 6.69988 -81 86 1 -82 86 1 -74 87 -1 -77 87 -.311136 -78 87 -.186857 -164 87 1 -76 88 -1 -77 88 -31.1136 -78 88 -18.6857 -166 88 1 -73 89 1 -109 89 -1 -111 89 -201 -75 90 1 -77 91 -.012899 -78 91 .026314 -83 92 1 -84 92 .4 -89 92 -1 -80 93 -1 -85 93 -1 -82 94 -1 -87 94 -1 -83 95 -1 -131 95 -1 -134 95 .0222685 -135 95 .0916503 -79 96 -1 -134 96 -.852598 -135 96 -.686678 -81 97 -1 -133 97 -1 -134 97 6.30198 -135 97 25.937 -84 98 487 -89 99 1 -90 99 .1 -95 99 -1 -86 100 -1 -91 100 -1 -88 101 -1 -93 101 -1 -89 102 -1 -90 102 -1 -104 102 -1 -107 102 .306796 -108 102 1.72865 -85 103 1 -86 103 1 -107 103 -.953179 -108 103 -.478015 -87 104 1 -88 104 1 -106 104 -1 -107 104 14.7262 -108 104 82.9751 -90 105 535 -95 106 -1 -96 106 -1 -122 106 1 -123 106 .6 -91 107 1 -92 107 1 -119 107 -1 -93 108 1 -94 108 1 -121 108 -1 -96 109 680 -97 110 -1 -101 110 -.312836 -102 110 -.082762 -103 110 1 -99 111 -1 -101 111 -31.2836 -102 111 -8.2762 -105 111 1 -97 112 1 -99 113 1 -101 114 .409192 -102 114 -.852483 -104 115 1 -109 115 -1 -111 115 -273.9 -106 116 1 -107 117 .147934 -108 117 -.0810126 -109 118 -1 -111 118 -176.9 -131 118 1 -110 119 -1 -133 119 1 -109 120 -1 -111 120 -209.1 -125 120 1 -127 121 1 -109 122 -1 -111 122 -193 -113 122 1 -111 123 -54 -117 123 1 -115 124 1 -109 125 1 -111 125 197 -161 125 1 -162 125 .6 -111 126 680 -158 126 -1 -110 127 1 -160 127 -1 -113 128 -1 -116 128 .250032 -117 128 .597517 -122 128 -1 -116 129 -.585577 -117 129 -.406865 -118 129 -1 -115 130 -1 -116 130 13.5017 -117 130 32.2659 -120 130 -1 -112 131 1 -202 131 -1 -206 131 -.419796 -207 131 -.122705 -114 132 1 -204 132 -1 -206 132 -41.9796 -207 132 -12.2705 -116 133 .239041 -117 133 -.242126 -122 134 -1 -123 134 -1 -125 134 -1 -128 134 .158767 -129 134 .340658 -118 135 1 -119 135 1 -128 135 -.588216 -129 135 -.353609 -120 136 1 -121 136 1 -127 136 -1 -128 136 14.4478 -129 136 30.9999 -123 137 145 -124 138 -1 -128 138 -.307616 -129 138 -.130446 -190 138 1 -128 139 -.411784 -129 139 -.646391 -194 139 1 -126 140 -1 -128 140 -30.7616 -129 140 -13.0446 -192 140 1 -124 141 1 -196 141 -1 -200 141 -.562373 -201 141 -.225697 -126 142 1 -198 142 -1 -200 142 -56.2373 -201 142 -22.5697 -128 143 .10564 -129 143 -.116088 -130 144 -1 -134 144 -.276727 -135 144 -.171445 -140 144 -1 -141 144 -1 -134 145 -.147402 -135 145 -.313322 -136 145 1 -137 145 1 -132 146 -1 -134 146 -28.7796 -135 146 -17.8303 -138 146 1 -139 146 1 -130 147 1 -154 147 -1 -156 147 -193.6 -132 148 1 -155 148 -1 -134 149 .087773 -135 149 -.0322558 -140 150 -1 -143 150 -1 -146 150 -.0323781 -147 150 -.0683179 -136 151 -1 -146 151 -.60537 -147 151 -.349597 -138 152 -1 -145 152 -1 -146 152 -12.1741 -147 152 -25.6875 -141 153 480 -142 154 1 -144 155 1 -143 156 1 -149 156 -1 -152 156 -.00472003 -153 156 -.0317473 -147 157 1 -152 157 -.934841 -153 157 -.792495 -145 158 1 -151 158 -1 -152 158 -1.77473 -153 158 -11.937 -146 159 -.0203625 -147 159 .0218773 -148 160 1 -150 161 1 -149 162 1 -154 162 -1 -156 162 -198 -153 163 1 -156 163 -376 -151 164 1 -152 165 -.0366465 -153 165 .00813435 -154 166 1 -156 166 197.2 -155 167 1 -161 168 -1 -162 168 -1 -163 168 -1 -167 168 .0165562 -168 168 .00408216 -157 169 1 -158 169 1 -167 169 -.832633 -168 169 -.729721 -159 170 1 -160 170 1 -165 170 -1 -167 170 7.21849 -168 170 1.77982 -161 171 -1 -191 171 -1 -194 171 .00193333 -195 171 .0781847 -157 172 -1 -194 172 -.995803 -195 172 -.705952 -159 173 -1 -193 173 -1 -194 173 .471732 -195 173 19.0771 -162 174 680 -164 175 -1 -167 175 -.0379488 -168 175 -.16853 -170 175 1 -167 176 -.167367 -168 176 -.270279 -174 176 1 -166 177 -1 -167 177 -3.79488 -168 177 -16.853 -172 177 1 -163 178 1 -185 178 -1 -188 178 .0406846 -189 178 .100421 -165 179 1 -187 179 -1 -188 179 17.7385 -189 179 43.7834 -167 180 -.0235888 -168 180 .102847 -169 181 -1 -173 181 .123883 -174 181 .0243372 -191 181 1 -171 182 -1 -173 182 30.2273 -174 182 5.93828 -193 182 1 -170 183 -1 -173 183 -.242234 -174 183 -.211144 -176 183 1 -173 184 -.3775 -174 184 -.0788991 -180 184 1 -172 185 -1 -173 185 -24.2234 -174 185 -21.1144 -178 185 1 -169 186 1 -181 186 -1 -183 186 -281.7 -171 187 1 -182 187 -1 -173 188 -.0173768 -174 188 .0423995 -175 189 -1 -179 189 .0149554 -180 189 .00262203 -181 189 1 -183 189 291 -179 190 -.825997 -180 190 -.904947 -183 190 680 -177 191 -1 -179 191 10.1696 -180 191 1.78298 -182 191 1 -175 192 1 -177 193 1 -179 194 -.00696415 -180 194 .0362189 -181 195 -1 -183 195 -296 -185 195 1 -187 196 1 -184 197 1 -188 198 1 -186 199 1 -188 200 .0382256 -189 200 -.0306734 -194 201 .00264546 -195 201 -.000781169 -196 202 1 -198 203 1 -200 204 .229712 -201 204 -.25243 -202 205 1 -204 206 1 -206 207 .27097 -207 207 -.589066 diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Matrix/one.mtx b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Matrix/one.mtx deleted file mode 100644 index ef0989175..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Matrix/one.mtx +++ /dev/null @@ -1,6 +0,0 @@ -%%MatrixMarket matrix coordinate real general -2 2 4 -1 1 0 -1 2 0 -2 1 0 -2 2 0 diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Matrix/onec.mtx b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Matrix/onec.mtx deleted file mode 100644 index 59c8534ce..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Matrix/onec.mtx +++ /dev/null @@ -1,6 +0,0 @@ -%%MatrixMarket matrix coordinate complex general -2 2 4 -1 1 0 0 -1 2 0 0 -2 1 0 0 -2 2 0 0 diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Matrix/two.mtx b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Matrix/two.mtx deleted file mode 100644 index 7457e818f..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Matrix/two.mtx +++ /dev/null @@ -1,3 +0,0 @@ -%%MatrixMarket matrix coordinate real general -2 2 1 -1 1 3 diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Matrix/w156.mtx b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Matrix/w156.mtx deleted file mode 100644 index 658309cee..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Matrix/w156.mtx +++ /dev/null @@ -1,365 +0,0 @@ -%%MatrixMarket matrix coordinate complex general -% complex matrix with the same real part, and same pattern, as HB/west0156 -156 156 362 -147 1 1 -89.00615831818635 -148 2 1 19.968263502718298 -149 2 -.1632648 -21.370801333956592 -59 3 -1.16206 -56.926762937042156 -134 3 -.8122999 -63.520075069490886 -149 3 1 -84.64919515773938 -52 4 1 -98.51526079522304 -53 5 1 57.75659925548426 -54 5 -.2166122 -96.44247397535688 -54 6 1 75.58829308886945 -59 6 -.1 -29.491646321365707 -118 7 1 44.42795357432983 -119 8 1 93.69016787815801 -120 8 -.2113848 -68.86640328523129 -59 9 1 -67.40889160696356 -105 9 .699017 -37.32052633277537 -120 9 1 -94.12369035075191 -150 10 1.009263 -28.474976610167545 -151 10 11.46533 -94.56192034360119 -150 11 .9908224 58.733403730195796 -152 11 -.5284282 99.84615089171123 -153 11 -.4980997 -77.95229509053108 -156 11 -.06675177 24.520231739121968 -152 12 1 -73.48563095366146 -153 13 1 -37.99404220990422 -155 13 -.8429148 -73.0424624342159 -154 14 1 -55.33473746935735 -156 14 -.0344062 -20.690642338915623 -155 15 1 -72.97130102560114 -156 15 -.00102006 -51.78826211728082 -134 16 5.047402 85.50322996314317 -156 16 1 -21.779825201968972 -138 17 -1.052632 2.25255469346175 -141 17 -.6666667 -81.42077676767339 -141 18 1 -95.66021714041189 -143 18 -.4480102 -68.09303466517918 -143 19 -2.988037 68.90318876644317 -143 20 1 75.83069648519695 -145 20 1 -62.60209482759951 -146 20 -1 98.26098380691137 -2 21 -.0535949 42.4059682333364 -144 21 2 74.27292331881648 -131 22 1 -4.0737630474247055 -133 22 -.9965392 -.7990146886149518 -132 23 1 -42.493627158378786 -133 23 -.003460798 -87.811709810653 -133 24 1 -47.506459158247225 -135 24 1 -62.747796590550934 -134 25 1 83.41795736466501 -135 25 1 -75.34426113632455 -137 26 .07513964 -97.31092337989851 -138 26 1 -26.06185629381749 -144 27 -5.47842 39.72822166458327 -145 27 -3.3405 77.86944264650091 -142 28 1 18.754103827151415 -146 28 -1 -68.66182065372685 -121 29 1.006185 -36.66211626227418 -122 29 10.73614 -53.320902834444595 -121 30 .9938532 -98.31558725078405 -123 30 -.5209677 -20.61924162618064 -124 30 -.4971525 29.973626118500405 -127 30 -.07339495 -82.99987875707477 -123 31 1 53.761900651751795 -124 32 1 93.94034099433995 -126 32 -.8391701 42.95944637524101 -125 33 1 56.39236839209942 -127 33 -.0258101 -52.4869835919564 -126 34 1 -60.85384724334488 -127 34 -.00199955 -47.357216964877104 -105 35 -5.396799 42.757012919227776 -127 35 1 95.51994788712943 -109 36 -.5263158 27.42438816918027 -112 36 -.3333333 9.184831019804385 -112 37 1 69.61144488338648 -114 37 -1.265049 60.41953169602192 -114 38 -1.778183 33.66128201334357 -114 39 1 34.19652679396956 -116 39 1 64.12873645714785 -117 39 -1 94.09447492182123 -2 40 -.0535949 -2.615000891619801 -115 40 2 63.49370710939066 -102 41 1 28.314233569200088 -104 41 -.9965392 -38.73012809722005 -103 42 1 32.186426447893 -104 42 -.003460798 -28.396577322438276 -104 43 1 87.63988602024271 -106 43 1 -2.4666050470267376 -105 44 1 -81.8019284512067 -106 44 1 34.7668233724147 -108 45 .1709599 2.976063797565698 -109 45 1 -55.68413072287828 -115 46 -5.47842 45.001874190444255 -116 46 -3.3405 -86.35067780598133 -113 47 1 92.82485367450755 -117 47 -1 -58.46868232626774 -55 48 -1 -67.77635488972467 -111 48 -1 27.644276518549617 -120 48 -1 -99.9543654216764 -118 49 -.5238095 -32.87341132956595 -120 49 -2.325233 -44.9800357067578 -55 50 -.2113848 -91.09449592159396 -56 50 1 -81.22070048053217 -101 50 -1 -18.000520045594502 -120 50 -.2113848 63.37846389879085 -4 51 -1 74.1034143618639 -17 51 -1 -95.48897590879422 -19 51 3.328735e-12 45.435396739586366 -20 51 1 69.60188961279125 -5 52 -1 45.72003820360473 -23 52 1 91.01972665502697 -24 52 1 31.27022211937156 -15 53 -1 48.46102670181145 -17 53 -.2113848 -31.006797219415848 -18 53 1 76.80438993503029 -61 54 -1 -30.551182700157376 -102 54 1 -88.10466389149548 -103 54 1 43.682918215884015 -110 54 -1 91.64285858148136 -117 54 1 -68.63302698512894 -63 55 -1 -16.72937877072226 -107 55 -1 -81.19302620544275 -66 56 -1 -10.010709210238765 -99 56 -1 73.83048511517521 -102 56 1.009514 -21.676555467798853 -113 56 -1.008042 -49.443269787795316 -67 57 -1 -29.123614686250242 -100 57 -1 48.59559363996606 -103 57 106.1112 30.166404491500764 -113 57 -.1547227 87.9586081050228 -60 58 1 66.5598249196907 -76 58 -1 -6.004426629012382 -139 58 1 25.973121452632107 -62 59 1 -88.36243043738105 -136 59 1 8.437479459481146 -64 60 1 -8.854807545659217 -76 60 -.9795877 72.61737844345258 -128 60 1 71.03939394472891 -65 61 1 -5.548863487187949 -76 61 -3.145565 57.38487793395959 -129 61 1 31.19642765119832 -34 62 -.008064516 -99.99202208021522 -36 63 1.974212 -73.75256764965177 -37 63 -.8416949 -1.0250487994832636 -37 64 1 -92.33336856681214 -38 64 -.008525207 -54.512838099295834 -38 65 1 -34.42340854356044 -39 65 -1 79.89375753843836 -47 65 -1 -37.25390248725466 -39 66 1 -49.66479762290302 -40 66 -1 -13.402173664778449 -40 67 1.895931 68.47644730738057 -41 67 -.9987282 -63.10220167747585 -41 68 1 1.6358424738914135 -42 68 2.087875 -9.552066657022863 -42 69 -1 -34.88311211669629 -46 69 -1 -23.984738606508827 -43 70 5.616771 77.29599272885395 -44 70 7.392325 52.252152063678636 -44 71 1 76.75326698731126 -45 71 7.20301 -8.51874880007002 -45 72 -1 59.84045826963149 -46 72 -.3258084 -73.18457603806863 -2 73 -2.146936 -86.93726233197341 -46 73 1 -24.971068398888395 -1 74 -6.821893 -25.29540601492628 -47 74 1 -3.1955205789929164 -91 75 2.431413 93.89184641063227 -93 75 -1 -31.58778114773145 -91 76 -2.431413 -49.4621487710556 -92 76 1 16.977383351584187 -93 77 1 4.740718882261197 -94 77 -1.26 -67.31619071487106 -2 78 -.8858734 -2.7203702821855025 -94 78 2 -.7878501044749853 -95 79 1 68.63881664718761 -96 79 -1 61.23964308235974 -1 80 -.02308312 71.5571192607996 -96 80 1 21.950827565571494 -82 81 1 13.146073550568627 -84 81 .9638 22.379704330589135 -97 81 1 -79.40469554532865 -84 82 .9905759 -68.33681654633787 -98 82 1 -17.27002806448744 -5 83 1.068219 12.082089707594035 -16 83 -1 -46.26454171200754 -6 84 9.305934e-22 56.85083285313337 -7 84 -.19106 -22.425843053832928 -8 84 -.3460553 -93.8032758232799 -7 85 1 17.10036588328181 -9 85 -.2113848 11.711705644935023 -9 86 1 -59.860885505008746 -10 86 -1 -82.51562297274859 -14 86 -1 86.64597186070104 -10 87 1 -48.124005287383966 -11 87 -1 -59.165721082767206 -12 88 1 -90.15831162239165 -13 88 -5.165179 21.23218795450579 -11 89 2.109794 9.269748032162251 -12 89 -.4423309 -80.83251277071686 -2 90 -1.200652 27.39912937614759 -13 90 1 -11.410334391483856 -1 91 -2.151087 -86.72360822705714 -14 91 1 -25.141483188003487 -7 92 -3.221332 -50.17945514204103 -8 92 -.08256881 84.9750583295307 -16 92 1.030078 25.899858515648553 -68 93 .00729927 75.66175530017894 -69 94 .009933775 28.334886205187427 -70 95 2.109489 59.67812719925334 -71 96 1.923841 -12.994792290221335 -72 97 1 96.2280502631985 -76 97 -.9703559 -80.80844033399066 -73 98 1 5.496479792736464 -76 98 -.02964405 9.129164943297008 -74 99 1 -43.131448638051594 -77 99 -.9703559 -25.83946813902346 -75 100 1 -87.06147957359738 -77 100 -.02964405 8.961812886476107 -76 101 1 67.27512065071366 -77 101 1 -70.93560166316104 -79 101 1 -65.69594863822117 -78 102 .009933775 -86.39056354840274 -79 102 -.3 64.80230680396102 -79 103 -3.333333 -73.2058484284056 -81 103 1 76.95725769618616 -2 104 -.3581659 2.947479893374072 -80 104 .9144476 92.7271192399364 -80 105 -2.355064 -75.90106649225966 -81 105 -2.599699 -90.342023563869 -1 106 1 -23.969599224575187 -3 106 -.5 -17.441756388313255 -2 107 1 -19.721740669438702 -3 107 -.5 -15.800573906372883 -3 108 1 -24.609261720375176 -110 109 1 81.46730529244041 -131 109 -.9431611 34.03242893843479 -132 109 -.5 92.36770491956567 -139 109 -1 -67.40413278123356 -146 109 1 49.72988179123301 -107 110 1 -25.186855192188627 -136 110 -1 -9.152697748159888 -99 111 1 -92.28786082587108 -128 111 -1 12.486401290146553 -131 111 -.9521341 -25.537570672597443 -142 111 -1.008042 58.55674146933576 -100 112 1 59.046194569800136 -129 112 -1 -23.417105544732607 -132 112 -53.05558 -49.44203913337082 -142 112 -.1547227 -31.414428806369965 -4 113 1 93.56089866913022 -9 113 -1.098983 -4.038321887840279 -111 113 1 -26.334487417080844 -127 113 -1.006101 52.91343104779358 -15 114 1 -24.570205949318236 -101 114 1 80.06124313084004 -127 114 -.2126745 -63.313589410507554 -17 115 3.875969e-8 -26.336536744115246 -19 115 -1 83.49140785169018 -21 116 -.9939418 3.183211228849636 -23 117 -1 -81.93854756530253 -17 118 2.727302e-20 47.06229753702564 -18 118 -1 -99.05756753456353 -17 119 1 20.62462338921529 -20 119 -1 91.37333280835817 -25 119 -1 -20.513614148643057 -27 119 .9972523 46.310157066657176 -28 119 1.02473 36.92776530123669 -22 120 -.9939418 95.70060448966404 -29 120 1 -59.24296668628066 -30 120 1 18.66056780239591 -24 121 -1 90.31250335814364 -31 121 1 -47.94551850968427 -32 121 1 2.9355241818408873 -17 122 .2113848 27.26637973052666 -25 122 -.2113848 -19.808178764946284 -26 122 1 -2.677737852379014 -19 123 5453728 50.091622636210076 -20 123 -.2113848 -74.7606532733799 -25 124 .9 -91.38750367809608 -27 124 -1 -25.812131278013382 -140 124 -1 38.66042415449142 -156 124 -1.009619 87.16498856941061 -29 125 -1 -4.484878166369444 -153 125 -.5019003 -74.17998047268341 -156 125 -.2294135 -3.2337575851525746 -31 126 -1 89.11943234297857 -137 126 -1 -26.45117330526361 -151 126 1 -34.30288621503337 -25 127 .1897236 54.575096067325134 -26 127 -1 -40.54519558973049 -130 127 -1 -64.42912801819011 -156 127 -.2128317 38.15983229583313 -25 128 .1 -47.211028939720535 -28 128 -1 -8.452188579330155 -33 128 1 68.73848491242163 -38 128 -1.007626 76.29973210306002 -30 129 -1 40.00399128013372 -36 129 -1 51.13819898638041 -38 129 -.2680827 94.90296027698403 -32 130 -1 -19.569463307138236 -34 130 1 -73.74427235921586 -25 131 .02166122 44.94677740033393 -35 131 1 79.903641471542 -38 131 -.2182642 -65.85973708326853 -27 132 .234872 -91.3942617588595 -28 132 -2.113848 -4.168155014702535 -55 133 .9 -81.21274304896602 -140 133 1 30.009966330192217 -149 133 -.7744864 90.45550778420308 -57 134 1.052135 -8.457460121512772 -137 134 .9248604 7.376129401205689 -55 135 .1897236 -86.70257083968079 -130 135 1 -1.2259832834904283 -149 135 -.1632648 -16.491805524055003 -33 136 -1 -41.548589946331724 -48 136 1 -42.06721249302474 -35 137 -1 50.769199337537785 -51 137 1 -80.6408576103972 -48 138 -1 -84.6165978311754 -54 138 -1 44.18300600646185 -55 138 .1 52.982301945926324 -49 139 -1 31.588984823676935 -52 139 -.5238095 62.08170441145384 -54 139 -2.382735 -25.151381648928016 -50 140 -.991453 -38.7537048036034 -58 140 1.06422 -25.86008018285866 -51 141 -1 41.34942373575503 -54 141 -.2166122 -66.32674228356838 -55 141 .02166122 62.744201350248474 -50 142 -.06837607 -6.7543369226229055 -60 143 -1 44.45713521489259 -85 143 175966.4 98.97378279407572 -86 143 -1 -27.499396494334206 -91 143 -2.431413 46.16562887690614 -95 143 -1 29.933482992494255 -69 144 .9900662 36.26781509331472 -70 144 -1.091241 -98.47761599001427 -76 144 -13 30.829860743864714 -78 144 .9900662 89.04702870503172 -87 144 1 22.65420709853554 -88 144 1 56.585595081575036 -62 145 -1 -99.36930257401184 -89 145 1 59.39156971639217 -90 145 -1 28.363331197466945 -64 146 -1 -64.30427579759511 -82 146 -1 5.880145934392633 -84 146 -.9638 -56.25129719765649 -95 146 -1.009514 9.61048763756911 -97 146 -1 -88.35252745679256 -65 147 -1 17.51741912019611 -85 147 1.8672e7 -16.77928832091964 -92 147 -106.1112 -62.7109528243285 -86 148 .7396663 -87.21785589638561 -87 149 -1 -85.04344448822249 -89 150 -.6169444 -37.992508436142 -82 151 .9905759 88.81698357390519 -83 152 1 96.14532626091892 -85 153 -175966.4 11.023098151583245 -86 153 .2603337 97.70512729352296 -88 154 -1 38.311980523494384 -90 155 1 -51.66782743196781 -84 156 .9905759 61.96273642788614 diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/README.txt b/deps/AMICI/ThirdParty/SuiteSparse/KLU/README.txt index 708d1bab4..7030de97a 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/README.txt +++ b/deps/AMICI/ThirdParty/SuiteSparse/KLU/README.txt @@ -1,11 +1,12 @@ -KLU, Copyright (C) 2004-2013, University of Florida -by Timothy A. Davis and Ekanathan Palamadai. +KLU, Copyright (c) 2004-2022, University of Florida. All Rights Reserved. +Authors: Timothy A. Davis and Ekanathan Palamadai. +SPDX-License-Identifier: LGPL-2.1+ + KLU is also available under other licenses; contact authors for details. http://www.suitesparse.com Requires the AMD, COLAMD, and BTF libraries, in ../AMD, ../COLAMD, and ../BTF, -respectively. Requires the ../SuiteSparse_config/SuiteSparse_config.mk -configuration file. Optionally uses CHOLMOD (KLU/User example ordering). The +respectively. Optionally uses CHOLMOD (KLU/User example ordering). The Tcov tests and the one of the programs in the Demo require CHOLMOD. To compile the libklu.a library, type "make". The compiled library is located @@ -27,7 +28,6 @@ Files in this distribution: Demo example programs that use KLU (requires CHOLMOD) Doc documentation Include include files - Lib compiled library Makefile top-level Makefile MATLAB MATLAB interface Matrix test matrices @@ -35,6 +35,8 @@ Files in this distribution: Source source code Tcov exhaustive test of KLU and BTF User example user ordering function (interface to CHOLMOD) + build where the compiled libraries and demos are placed + Config source file to construct klu.h ./Demo: klu_simple.c a simple demo (does not require CHOLMOD) @@ -42,7 +44,7 @@ Files in this distribution: klu_simple.out output of klu_simple kludemo.c KLU demo (int version) kludemo.out output of "make" in this directory - kluldemo.c KLU demo (SuiteSparse_long version) + kluldemo.c KLU demo (int64_t version) Makefile Makefile for compiling the demo ./Doc: @@ -59,10 +61,6 @@ Files in this distribution: klu_internal.h internal include file, not needed by the user klu_version.h internal include file, not needed by the user -./Lib: - Makefile Makefile for compiling the KLU C-callable library - (with or without CHOLMOD) - ./MATLAB: Contents.m list of MATLAB functions in KLU klu_demo.m MATLAB demo @@ -75,7 +73,7 @@ Files in this distribution: Makefile_no_CHOLMOD Makefile for KLU mexFunction, without CHOLMOD Test MATLAB tests -./MATLAB/Test: KLU tests, requires UFget +./MATLAB/Test: KLU tests, requires ssget test1.m test2.m test3.m @@ -115,17 +113,17 @@ Files in this distribution: ./Tcov: exhaustive test suite; requires Linux/Unix coverage determine statement coverage - klultests KLU SuiteSparse_long tests + klultests KLU int64_t tests klutest.c KLU test program klutests KLU int tests Makefile Makefile for compiling and running the tests README.txt README file for Tcov vklutests KLU int tests, using valgrind - vklultests KLU SuiteSparse_long tests, using valgrind + vklultests KLU int64_t tests, using valgrind ./User: klu_cholmod.c sample KLU user ordering function (int version) klu_cholmod.h include file for klu_cholmod and klu_l_cholmod - klu_l_cholmod.c sample KLU user ordering function (SuiteSparse_long) + klu_l_cholmod.c sample KLU user ordering function (int64_t) Makefile Makefile for compiling the user ordering functions README.txt README for User directory diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu.c b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu.c index 6042fcdfc..7791bf7f0 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu.c +++ b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu.c @@ -1,6 +1,12 @@ -/* ========================================================================== */ -/* === klu ================================================================== */ -/* ========================================================================== */ +//------------------------------------------------------------------------------ +// KLU/Source/klu: primary factorization and forward/backsolve kernels for KLU +//------------------------------------------------------------------------------ + +// KLU, Copyright (c) 2004-2022, University of Florida. All Rights Reserved. +// Authors: Timothy A. Davis and Ekanathan Palamadai. +// SPDX-License-Identifier: LGPL-2.1+ + +//------------------------------------------------------------------------------ /* KLU: factorizes P*A into L*U, using the Gilbert-Peierls algorithm [1], with * optional symmetric pruning by Eisenstat and Liu [2]. The code is by Tim @@ -33,12 +39,6 @@ * Ai [Ap [j] ... Ap [j+1]-1] and the same range of indices in Ax holds the * numerical values. No duplicate entries are allowed. * - * Copyright 2004-2009, Tim Davis. All rights reserved. See the README - * file for details on permitted use. Note that no code from The MathWorks, - * Inc, or from SuperLU, or from any other source appears here. The code is - * written from scratch, from the algorithmic description in Gilbert & Peierls' - * and Eisenstat & Liu's journal papers [1,2]. - * * If an input permutation Q is provided, the factorization L*U = A (P,Q) * is computed, where P is determined by partial pivoting, and Q is the input * ordering. If the pivot tolerance is less than 1, the "diagonal" entry that diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_analyze.c b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_analyze.c index 2fe81c19c..bf9a08ddb 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_analyze.c +++ b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_analyze.c @@ -1,6 +1,12 @@ -/* ========================================================================== */ -/* === klu_analyze ========================================================== */ -/* ========================================================================== */ +//------------------------------------------------------------------------------ +// KLU/Source/klu_analyze: symbolic analysis +//------------------------------------------------------------------------------ + +// KLU, Copyright (c) 2004-2022, University of Florida. All Rights Reserved. +// Authors: Timothy A. Davis and Ekanathan Palamadai. +// SPDX-License-Identifier: LGPL-2.1+ + +//------------------------------------------------------------------------------ /* Order the matrix using BTF (or not), and then AMD, COLAMD, the natural * ordering, or the user-provided-function on the blocks. Does not support diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_analyze_given.c b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_analyze_given.c index 348c011b3..a0c2b46c7 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_analyze_given.c +++ b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_analyze_given.c @@ -1,6 +1,12 @@ -/* ========================================================================== */ -/* === klu_analyze_given ==================================================== */ -/* ========================================================================== */ +//------------------------------------------------------------------------------ +// KLU/Source/klu_analyze_given: symbolic analysis with given permutation +//------------------------------------------------------------------------------ + +// KLU, Copyright (c) 2004-2022, University of Florida. All Rights Reserved. +// Authors: Timothy A. Davis and Ekanathan Palamadai. +// SPDX-License-Identifier: LGPL-2.1+ + +//------------------------------------------------------------------------------ /* Given an input permutation P and Q, create the Symbolic object. BTF can * be done to modify the user's P and Q (does not perform the max transversal; diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_defaults.c b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_defaults.c index ba2b77ee1..52225655f 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_defaults.c +++ b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_defaults.c @@ -1,12 +1,18 @@ -/* ========================================================================== */ -/* === KLU_defaults ========================================================= */ -/* ========================================================================== */ +//------------------------------------------------------------------------------ +// KLU/Source/klu_defaults: default parameters for KLU +//------------------------------------------------------------------------------ + +// KLU, Copyright (c) 2004-2022, University of Florida. All Rights Reserved. +// Authors: Timothy A. Davis and Ekanathan Palamadai. +// SPDX-License-Identifier: LGPL-2.1+ + +//------------------------------------------------------------------------------ /* Sets default parameters for KLU */ #include "klu_internal.h" -Int KLU_defaults +int KLU_defaults ( KLU_common *Common ) diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_diagnostics.c b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_diagnostics.c index dc9d288ec..c8b599fa6 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_diagnostics.c +++ b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_diagnostics.c @@ -1,6 +1,12 @@ -/* ========================================================================== */ -/* === KLU_diagnostics ====================================================== */ -/* ========================================================================== */ +//------------------------------------------------------------------------------ +// KLU/Source/klu_diagnostics: linear algebraic diagnostics +//------------------------------------------------------------------------------ + +// KLU, Copyright (c) 2004-2022, University of Florida. All Rights Reserved. +// Authors: Timothy A. Davis and Ekanathan Palamadai. +// SPDX-License-Identifier: LGPL-2.1+ + +//------------------------------------------------------------------------------ /* Linear algebraic diagnostics: * KLU_rgrowth: reciprocal pivot growth, takes O(|A|+|U|) time @@ -22,7 +28,7 @@ * rgrowth = min (max (abs ((R \ A (p,q)) - F))) ./ max (abs (U))) */ -Int KLU_rgrowth /* return TRUE if successful, FALSE otherwise */ +int KLU_rgrowth /* return TRUE if successful, FALSE otherwise */ ( Int *Ap, Int *Ai, @@ -169,7 +175,7 @@ Int KLU_rgrowth /* return TRUE if successful, FALSE otherwise */ * 1-norm pseudospectra, SIAM J. Matrix Anal. Appl., 21(4):1185-1201, 2000. */ -Int KLU_condest /* return TRUE if successful, FALSE otherwise */ +int KLU_condest /* return TRUE if successful, FALSE otherwise */ ( Int Ap [ ], double Ax [ ], @@ -410,7 +416,7 @@ Int KLU_condest /* return TRUE if successful, FALSE otherwise */ /* Compute the flop count for the LU factorization (in Common->flops) */ -Int KLU_flops /* return TRUE if successful, FALSE otherwise */ +int KLU_flops /* return TRUE if successful, FALSE otherwise */ ( KLU_symbolic *Symbolic, KLU_numeric *Numeric, @@ -494,7 +500,7 @@ Int KLU_flops /* return TRUE if successful, FALSE otherwise */ * pivot, or a NaN pivot, rcond will be zero. Takes O(n) time. */ -Int KLU_rcond /* return TRUE if successful, FALSE otherwise */ +int KLU_rcond /* return TRUE if successful, FALSE otherwise */ ( KLU_symbolic *Symbolic, /* input, not modified */ KLU_numeric *Numeric, /* input, not modified */ diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_dump.c b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_dump.c index 8f5590053..8ce973e8d 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_dump.c +++ b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_dump.c @@ -1,6 +1,12 @@ -/* ========================================================================== */ -/* === KLU_dump ============================================================= */ -/* ========================================================================== */ +//------------------------------------------------------------------------------ +// KLU/Source/klu_dump: debug routines for KLU +//------------------------------------------------------------------------------ + +// KLU, Copyright (c) 2004-2022, University of Florida. All Rights Reserved. +// Authors: Timothy A. Davis and Ekanathan Palamadai. +// SPDX-License-Identifier: LGPL-2.1+ + +//------------------------------------------------------------------------------ /* Debug routines for klu. Only used when NDEBUG is not defined at * compile-time. diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_extract.c b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_extract.c index b009828a7..d393f18cd 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_extract.c +++ b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_extract.c @@ -1,6 +1,12 @@ -/* ========================================================================== */ -/* === KLU_extract ========================================================== */ -/* ========================================================================== */ +//------------------------------------------------------------------------------ +// KLU/Source/klu_extract: extract the KLU factorization +//------------------------------------------------------------------------------ + +// KLU, Copyright (c) 2004-2022, University of Florida. All Rights Reserved. +// Authors: Timothy A. Davis and Ekanathan Palamadai. +// SPDX-License-Identifier: LGPL-2.1+ + +//------------------------------------------------------------------------------ /* Extract KLU factorization into conventional compressed-column matrices. * If any output array is NULL, that part of the LU factorization is not @@ -11,7 +17,7 @@ #include "klu_internal.h" -Int KLU_extract /* returns TRUE if successful, FALSE otherwise */ +int KLU_extract /* returns TRUE if successful, FALSE otherwise */ ( /* inputs: */ KLU_numeric *Numeric, diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_factor.c b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_factor.c index d651f49e1..171b114f4 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_factor.c +++ b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_factor.c @@ -1,6 +1,12 @@ -/* ========================================================================== */ -/* === KLU_factor =========================================================== */ -/* ========================================================================== */ +//------------------------------------------------------------------------------ +// KLU/Source/klu_factor: sparse LU factorization +//------------------------------------------------------------------------------ + +// KLU, Copyright (c) 2004-2022, University of Florida. All Rights Reserved. +// Authors: Timothy A. Davis and Ekanathan Palamadai. +// SPDX-License-Identifier: LGPL-2.1+ + +//------------------------------------------------------------------------------ /* Factor the matrix, after ordering and analyzing it with KLU_analyze * or KLU_analyze_given. @@ -341,14 +347,14 @@ static void factor2 #ifndef NDEBUG { - PRINTF (("\n ############# KLU_BTF_FACTOR done, nblocks %d\n",nblocks)); + PRINTF (("\n ------------ KLU_BTF_FACTOR done, nblocks %d\n",nblocks)); Entry ss, *Udiag = Numeric->Udiag ; for (block = 0 ; block < nblocks && Common->status == KLU_OK ; block++) { k1 = R [block] ; k2 = R [block+1] ; nk = k2 - k1 ; - PRINTF (("\n======================KLU_factor output: k1 %d k2 %d nk %d\n",k1,k2,nk)) ; + PRINTF (("\n== KLU_factor output: k1 %d k2 %d nk %d\n",k1,k2,nk)) ; if (nk == 1) { PRINTF (("singleton ")) ; diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_free_numeric.c b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_free_numeric.c index cd4f3bdcd..359219fdd 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_free_numeric.c +++ b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_free_numeric.c @@ -1,12 +1,18 @@ -/* ========================================================================== */ -/* === KLU_free_numeric ===================================================== */ -/* ========================================================================== */ +//------------------------------------------------------------------------------ +// KLU/Source/klu_free_numeric: free the KLU numeric factorization +//------------------------------------------------------------------------------ + +// KLU, Copyright (c) 2004-2022, University of Florida. All Rights Reserved. +// Authors: Timothy A. Davis and Ekanathan Palamadai. +// SPDX-License-Identifier: LGPL-2.1+ + +//------------------------------------------------------------------------------ /* Free the KLU Numeric object. */ #include "klu_internal.h" -Int KLU_free_numeric +int KLU_free_numeric ( KLU_numeric **NumericHandle, KLU_common *Common diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_free_symbolic.c b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_free_symbolic.c index 20b4000e6..3cf859515 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_free_symbolic.c +++ b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_free_symbolic.c @@ -1,12 +1,18 @@ -/* ========================================================================== */ -/* === KLU_free_symbolic ==================================================== */ -/* ========================================================================== */ +//------------------------------------------------------------------------------ +// KLU/Source/klu_free_symbolic: free the KLU symbolic analysis +//------------------------------------------------------------------------------ + +// KLU, Copyright (c) 2004-2022, University of Florida. All Rights Reserved. +// Authors: Timothy A. Davis and Ekanathan Palamadai. +// SPDX-License-Identifier: LGPL-2.1+ + +//------------------------------------------------------------------------------ /* Free the KLU Symbolic object. */ #include "klu_internal.h" -Int KLU_free_symbolic +int KLU_free_symbolic ( KLU_symbolic **SymbolicHandle, KLU_common *Common diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_kernel.c b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_kernel.c index c3a78b40d..0a425088d 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_kernel.c +++ b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_kernel.c @@ -1,6 +1,12 @@ -/* ========================================================================== */ -/* === KLU_kernel =========================================================== */ -/* ========================================================================== */ +//------------------------------------------------------------------------------ +// KLU/Source/klu_kernel: primary sparse LU factorization kernel +//------------------------------------------------------------------------------ + +// KLU, Copyright (c) 2004-2022, University of Florida. All Rights Reserved. +// Authors: Timothy A. Davis and Ekanathan Palamadai. +// SPDX-License-Identifier: LGPL-2.1+ + +//------------------------------------------------------------------------------ /* Sparse left-looking LU factorization, with partial pivoting. Based on * Gilbert & Peierl's method, with a non-recursive DFS and with Eisenstat & diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_l.c b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_l.c new file mode 100644 index 000000000..d7d556524 --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_l.c @@ -0,0 +1,11 @@ +//------------------------------------------------------------------------------ +// KLU/Source/klu_l.c: int64_t version of klu +//------------------------------------------------------------------------------ + +// KLU, Copyright (C) 2004-2022, University of Florida, All Rights Reserved. +// Authors: Timothy A. Davis and Ekanathan Palamadai. +// SPDX-License-Identifier: LGPL-2.1+ + +#define DLONG +#include "klu.c" + diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_l_analyze.c b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_l_analyze.c new file mode 100644 index 000000000..02eeccf91 --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_l_analyze.c @@ -0,0 +1,11 @@ +//------------------------------------------------------------------------------ +// KLU/Source/klu_l_analyze.c: int64_t version of klu_analyze +//------------------------------------------------------------------------------ + +// KLU, Copyright (C) 2004-2022, University of Florida, All Rights Reserved. +// Authors: Timothy A. Davis and Ekanathan Palamadai. +// SPDX-License-Identifier: LGPL-2.1+ + +#define DLONG +#include "klu_analyze.c" + diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_l_analyze_given.c b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_l_analyze_given.c new file mode 100644 index 000000000..77c757b16 --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_l_analyze_given.c @@ -0,0 +1,11 @@ +//------------------------------------------------------------------------------ +// KLU/Source/klu_l_analyze_given.c: int64_t version of klu_analyze_given +//------------------------------------------------------------------------------ + +// KLU, Copyright (C) 2004-2022, University of Florida, All Rights Reserved. +// Authors: Timothy A. Davis and Ekanathan Palamadai. +// SPDX-License-Identifier: LGPL-2.1+ + +#define DLONG +#include "klu_analyze_given.c" + diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_l_defaults.c b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_l_defaults.c new file mode 100644 index 000000000..c03902543 --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_l_defaults.c @@ -0,0 +1,11 @@ +//------------------------------------------------------------------------------ +// KLU/Source/klu_l_defaults.c: int64_t version of klu_defaults +//------------------------------------------------------------------------------ + +// KLU, Copyright (C) 2004-2022, University of Florida, All Rights Reserved. +// Authors: Timothy A. Davis and Ekanathan Palamadai. +// SPDX-License-Identifier: LGPL-2.1+ + +#define DLONG +#include "klu_defaults.c" + diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_l_diagnostics.c b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_l_diagnostics.c new file mode 100644 index 000000000..d417da1ee --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_l_diagnostics.c @@ -0,0 +1,11 @@ +//------------------------------------------------------------------------------ +// KLU/Source/klu_l_diagnostics.c: int64_t version of klu_diagnostics +//------------------------------------------------------------------------------ + +// KLU, Copyright (C) 2004-2022, University of Florida, All Rights Reserved. +// Authors: Timothy A. Davis and Ekanathan Palamadai. +// SPDX-License-Identifier: LGPL-2.1+ + +#define DLONG +#include "klu_diagnostics.c" + diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_l_dump.c b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_l_dump.c new file mode 100644 index 000000000..e9a9e56ae --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_l_dump.c @@ -0,0 +1,11 @@ +//------------------------------------------------------------------------------ +// KLU/Source/klu_l_dump.c: int64_t version of klu_dump +//------------------------------------------------------------------------------ + +// KLU, Copyright (C) 2004-2022, University of Florida, All Rights Reserved. +// Authors: Timothy A. Davis and Ekanathan Palamadai. +// SPDX-License-Identifier: LGPL-2.1+ + +#define DLONG +#include "klu_dump.c" + diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_l_extract.c b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_l_extract.c new file mode 100644 index 000000000..dee0639c8 --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_l_extract.c @@ -0,0 +1,11 @@ +//------------------------------------------------------------------------------ +// KLU/Source/klu_l_extract.c: int64_t version of klu_extract +//------------------------------------------------------------------------------ + +// KLU, Copyright (C) 2004-2022, University of Florida, All Rights Reserved. +// Authors: Timothy A. Davis and Ekanathan Palamadai. +// SPDX-License-Identifier: LGPL-2.1+ + +#define DLONG +#include "klu_extract.c" + diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_l_factor.c b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_l_factor.c new file mode 100644 index 000000000..1ea0818df --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_l_factor.c @@ -0,0 +1,11 @@ +//------------------------------------------------------------------------------ +// KLU/Source/klu_l_factor.c: int64_t version of klu_factor +//------------------------------------------------------------------------------ + +// KLU, Copyright (C) 2004-2022, University of Florida, All Rights Reserved. +// Authors: Timothy A. Davis and Ekanathan Palamadai. +// SPDX-License-Identifier: LGPL-2.1+ + +#define DLONG +#include "klu_factor.c" + diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_l_free_numeric.c b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_l_free_numeric.c new file mode 100644 index 000000000..401a7557c --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_l_free_numeric.c @@ -0,0 +1,11 @@ +//------------------------------------------------------------------------------ +// KLU/Source/klu_l_free_numeric.c: int64_t version of klu_free_numeric +//------------------------------------------------------------------------------ + +// KLU, Copyright (C) 2004-2022, University of Florida, All Rights Reserved. +// Authors: Timothy A. Davis and Ekanathan Palamadai. +// SPDX-License-Identifier: LGPL-2.1+ + +#define DLONG +#include "klu_free_numeric.c" + diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_l_free_symbolic.c b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_l_free_symbolic.c new file mode 100644 index 000000000..10c949c18 --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_l_free_symbolic.c @@ -0,0 +1,11 @@ +//------------------------------------------------------------------------------ +// KLU/Source/klu_l_free_symbolic.c: int64_t version of klu_free_symbolic +//------------------------------------------------------------------------------ + +// KLU, Copyright (C) 2004-2022, University of Florida, All Rights Reserved. +// Authors: Timothy A. Davis and Ekanathan Palamadai. +// SPDX-License-Identifier: LGPL-2.1+ + +#define DLONG +#include "klu_free_symbolic.c" + diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_l_kernel.c b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_l_kernel.c new file mode 100644 index 000000000..20497c4d3 --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_l_kernel.c @@ -0,0 +1,11 @@ +//------------------------------------------------------------------------------ +// KLU/Source/klu_l_kernel.c: int64_t version of klu_kernel +//------------------------------------------------------------------------------ + +// KLU, Copyright (C) 2004-2022, University of Florida, All Rights Reserved. +// Authors: Timothy A. Davis and Ekanathan Palamadai. +// SPDX-License-Identifier: LGPL-2.1+ + +#define DLONG +#include "klu_kernel.c" + diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_l_memory.c b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_l_memory.c new file mode 100644 index 000000000..12a7367a6 --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_l_memory.c @@ -0,0 +1,11 @@ +//------------------------------------------------------------------------------ +// KLU/Source/klu_l_memory.c: int64_t version of klu_memory +//------------------------------------------------------------------------------ + +// KLU, Copyright (C) 2004-2022, University of Florida, All Rights Reserved. +// Authors: Timothy A. Davis and Ekanathan Palamadai. +// SPDX-License-Identifier: LGPL-2.1+ + +#define DLONG +#include "klu_memory.c" + diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_l_refactor.c b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_l_refactor.c new file mode 100644 index 000000000..cd49e9e6f --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_l_refactor.c @@ -0,0 +1,11 @@ +//------------------------------------------------------------------------------ +// KLU/Source/klu_l_refactor.c: int64_t version of klu_refactor +//------------------------------------------------------------------------------ + +// KLU, Copyright (C) 2004-2022, University of Florida, All Rights Reserved. +// Authors: Timothy A. Davis and Ekanathan Palamadai. +// SPDX-License-Identifier: LGPL-2.1+ + +#define DLONG +#include "klu_refactor.c" + diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_l_scale.c b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_l_scale.c new file mode 100644 index 000000000..9b5b7793e --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_l_scale.c @@ -0,0 +1,11 @@ +//------------------------------------------------------------------------------ +// KLU/Source/klu_l_scale.c: int64_t version of klu_scale +//------------------------------------------------------------------------------ + +// KLU, Copyright (C) 2004-2022, University of Florida, All Rights Reserved. +// Authors: Timothy A. Davis and Ekanathan Palamadai. +// SPDX-License-Identifier: LGPL-2.1+ + +#define DLONG +#include "klu_scale.c" + diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_l_solve.c b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_l_solve.c new file mode 100644 index 000000000..9811ea944 --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_l_solve.c @@ -0,0 +1,11 @@ +//------------------------------------------------------------------------------ +// KLU/Source/klu_l_solve.c: int64_t version of klu_solve +//------------------------------------------------------------------------------ + +// KLU, Copyright (C) 2004-2022, University of Florida, All Rights Reserved. +// Authors: Timothy A. Davis and Ekanathan Palamadai. +// SPDX-License-Identifier: LGPL-2.1+ + +#define DLONG +#include "klu_solve.c" + diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_l_sort.c b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_l_sort.c new file mode 100644 index 000000000..b5eb86d40 --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_l_sort.c @@ -0,0 +1,11 @@ +//------------------------------------------------------------------------------ +// KLU/Source/klu_l_sort.c: int64_t version of klu_sort +//------------------------------------------------------------------------------ + +// KLU, Copyright (C) 2004-2022, University of Florida, All Rights Reserved. +// Authors: Timothy A. Davis and Ekanathan Palamadai. +// SPDX-License-Identifier: LGPL-2.1+ + +#define DLONG +#include "klu_sort.c" + diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_l_tsolve.c b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_l_tsolve.c new file mode 100644 index 000000000..9b21998e2 --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_l_tsolve.c @@ -0,0 +1,11 @@ +//------------------------------------------------------------------------------ +// KLU/Source/klu_l_tsolve.c: int64_t version of klu_tsolve +//------------------------------------------------------------------------------ + +// KLU, Copyright (C) 2004-2022, University of Florida, All Rights Reserved. +// Authors: Timothy A. Davis and Ekanathan Palamadai. +// SPDX-License-Identifier: LGPL-2.1+ + +#define DLONG +#include "klu_tsolve.c" + diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_memory.c b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_memory.c index d391a0836..64fac3c4b 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_memory.c +++ b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_memory.c @@ -1,6 +1,12 @@ -/* ========================================================================== */ -/* === KLU_memory =========================================================== */ -/* ========================================================================== */ +//------------------------------------------------------------------------------ +// KLU/Source/klu_memory: memory management for KLU +//------------------------------------------------------------------------------ + +// KLU, Copyright (c) 2004-2022, University of Florida. All Rights Reserved. +// Authors: Timothy A. Davis and Ekanathan Palamadai. +// SPDX-License-Identifier: LGPL-2.1+ + +//------------------------------------------------------------------------------ /* KLU memory management routines: * @@ -78,7 +84,7 @@ void *KLU_malloc /* returns pointer to the newly malloc'd block */ Common->status = KLU_INVALID ; p = NULL ; } - else if (n >= Int_MAX) + else if (sizeof (size_t) > sizeof (Int) && n >= Int_MAX) { /* object is too big to allocate; p[i] where i is an Int will not * be enough. */ @@ -189,7 +195,7 @@ void *KLU_realloc /* returns pointer to reallocated block */ /* A fresh object is being allocated. */ p = KLU_malloc (nnew, size, Common) ; } - else if (nnew >= Int_MAX) + else if (sizeof (size_t) > sizeof (Int) && nnew >= Int_MAX) { /* failure: nnew is too big. Do not change p */ Common->status = KLU_TOO_LARGE ; diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_refactor.c b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_refactor.c index 02539bebd..b28863571 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_refactor.c +++ b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_refactor.c @@ -1,6 +1,12 @@ -/* ========================================================================== */ -/* === KLU_refactor ========================================================= */ -/* ========================================================================== */ +//------------------------------------------------------------------------------ +// KLU/Source/klu_refactor: factor another matrix (no pivoting) +//------------------------------------------------------------------------------ + +// KLU, Copyright (c) 2004-2022, University of Florida. All Rights Reserved. +// Authors: Timothy A. Davis and Ekanathan Palamadai. +// SPDX-License-Identifier: LGPL-2.1+ + +//------------------------------------------------------------------------------ /* Factor the matrix, after ordering and analyzing it with KLU_analyze, and * factoring it once with KLU_factor. This routine cannot do any numerical @@ -15,7 +21,7 @@ /* === KLU_refactor ========================================================= */ /* ========================================================================== */ -Int KLU_refactor /* returns TRUE if successful, FALSE otherwise */ +int KLU_refactor /* returns TRUE if successful, FALSE otherwise */ ( /* inputs, not modified */ Int Ap [ ], /* size n+1, column pointers */ @@ -440,14 +446,14 @@ Int KLU_refactor /* returns TRUE if successful, FALSE otherwise */ ASSERT (KLU_valid (n, Numeric->Offp, Numeric->Offi, Offx)) ; if (Common->status == KLU_OK) { - PRINTF (("\n ########### KLU_BTF_REFACTOR done, nblocks %d\n",nblocks)); + PRINTF (("\n ----------- KLU_BTF_REFACTOR done, nblocks %d\n",nblocks)); for (block = 0 ; block < nblocks ; block++) { k1 = R [block] ; k2 = R [block+1] ; nk = k2 - k1 ; PRINTF (( - "\n================KLU_refactor output: k1 %d k2 %d nk %d\n", + "\n--------------- KLU_refactor output: k1 %d k2 %d nk %d\n", k1, k2, nk)) ; if (nk == 1) { diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_scale.c b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_scale.c index 479612006..6a61c3a1f 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_scale.c +++ b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_scale.c @@ -1,6 +1,12 @@ -/* ========================================================================== */ -/* === KLU_scale ============================================================ */ -/* ========================================================================== */ +//------------------------------------------------------------------------------ +// KLU/Source/klu_scale: scale a sparse matrix +//------------------------------------------------------------------------------ + +// KLU, Copyright (c) 2004-2022, University of Florida. All Rights Reserved. +// Authors: Timothy A. Davis and Ekanathan Palamadai. +// SPDX-License-Identifier: LGPL-2.1+ + +//------------------------------------------------------------------------------ /* Scale a matrix and check to see if it is valid. Can be called by the user. * This is called by KLU_factor and KLU_refactor. Returns TRUE if the input @@ -16,10 +22,10 @@ #include "klu_internal.h" -Int KLU_scale /* return TRUE if successful, FALSE otherwise */ +int KLU_scale /* return TRUE if successful, FALSE otherwise */ ( /* inputs, not modified */ - Int scale, /* 0: none, 1: sum, 2: max */ + int scale, /* 0: none, 1: sum, 2: max */ Int n, Int Ap [ ], /* size n+1, column pointers */ Int Ai [ ], /* size nz, row indices */ diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_solve.c b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_solve.c index d23a14095..d0904027d 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_solve.c +++ b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_solve.c @@ -1,6 +1,12 @@ -/* ========================================================================== */ -/* === KLU_solve ============================================================ */ -/* ========================================================================== */ +//------------------------------------------------------------------------------ +// KLU/Source/klu_solve: solve x=A\b using the KLU factorization +//------------------------------------------------------------------------------ + +// KLU, Copyright (c) 2004-2022, University of Florida. All Rights Reserved. +// Authors: Timothy A. Davis and Ekanathan Palamadai. +// SPDX-License-Identifier: LGPL-2.1+ + +//------------------------------------------------------------------------------ /* Solve Ax=b using the symbolic and numeric objects from KLU_analyze * (or KLU_analyze_given) and KLU_factor. Note that no iterative refinement is @@ -11,7 +17,7 @@ #include "klu_internal.h" -Int KLU_solve +int KLU_solve ( /* inputs, not modified */ KLU_symbolic *Symbolic, diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_sort.c b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_sort.c index a3ce98f46..3c67c13c6 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_sort.c +++ b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_sort.c @@ -1,6 +1,12 @@ -/* ========================================================================== */ -/* === KLU_sort ============================================================= */ -/* ========================================================================== */ +//------------------------------------------------------------------------------ +// KLU/Source/klu_sort: sorts the L and U factors of KLU +//------------------------------------------------------------------------------ + +// KLU, Copyright (c) 2004-2022, University of Florida. All Rights Reserved. +// Authors: Timothy A. Davis and Ekanathan Palamadai. +// SPDX-License-Identifier: LGPL-2.1+ + +//------------------------------------------------------------------------------ /* sorts the columns of L and U so that the row indices appear in strictly * increasing order. @@ -88,7 +94,7 @@ static void sort (Int n, Int *Xip, Int *Xlen, Unit *LU, Int *Tp, Int *Tj, /* === KLU_sort ============================================================= */ /* ========================================================================== */ -Int KLU_sort +int KLU_sort ( KLU_symbolic *Symbolic, KLU_numeric *Numeric, diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_tsolve.c b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_tsolve.c index c1f10f708..7aefeffa6 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_tsolve.c +++ b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_tsolve.c @@ -1,6 +1,12 @@ -/* ========================================================================== */ -/* === KLU_tsolve =========================================================== */ -/* ========================================================================== */ +//------------------------------------------------------------------------------ +// KLU/Source/klu_tsolve: solve x=A'\b using the KLU factorization +//------------------------------------------------------------------------------ + +// KLU, Copyright (c) 2004-2022, University of Florida. All Rights Reserved. +// Authors: Timothy A. Davis and Ekanathan Palamadai. +// SPDX-License-Identifier: LGPL-2.1+ + +//------------------------------------------------------------------------------ /* Solve A'x=b using the symbolic and numeric objects from KLU_analyze * (or KLU_analyze_given) and KLU_factor. Note that no iterative refinement is @@ -11,7 +17,7 @@ #include "klu_internal.h" -Int KLU_tsolve +int KLU_tsolve ( /* inputs, not modified */ KLU_symbolic *Symbolic, @@ -23,7 +29,7 @@ Int KLU_tsolve double B [ ], /* size n*nrhs, in column-oriented form, with * leading dimension d. */ #ifdef COMPLEX - Int conj_solve, /* TRUE for conjugate transpose solve, FALSE for + int conj_solve, /* TRUE for conjugate transpose solve, FALSE for * array transpose solve. Used for the complex * case only. */ #endif diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Tcov/Makefile b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Tcov/Makefile deleted file mode 100644 index 787c0a062..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Tcov/Makefile +++ /dev/null @@ -1,425 +0,0 @@ -# If the libraries (AMD, COLAMD, CAMD, CCOLAMD, metis, and CHOLMOD) are not -# yet built, use "make libs" first. Then "make" to compile and run all tests. -# -# This test requires BTF, METIS, CHOLMOD, AMD, COLAMD, CAMD, and CCOLAMD. - -TCOV=yes - -default: all - -include ../../SuiteSparse_config/SuiteSparse_config.mk - -BLAS = -lrefblas -LAPACK = -llapack - -# CF = -Wall -W -Wshadow -Wmissing-prototypes -Wstrict-prototypes \ -# -Wredundant-decls -Wnested-externs -Wdisabled-optimization \ -# -pedantic -ansi -O3 -pg - -# for statement coverage, picky tests -CF = -Wall -W -Wshadow -Wmissing-prototypes -Wstrict-prototypes \ - -Wredundant-decls -Wnested-externs -Wdisabled-optimization \ - -ansi -g -ftest-coverage -fprofile-arcs -fexceptions \ - -fopenmp - -C = $(CC) $(CF) - -LDLIBS = -L../../lib -lamd -lcolamd -lcholmod -lcamd -lccolamd \ - -lmetis -lsuitesparseconfig \ - $(LAPACK) $(BLAS) $(CUDART_LIB) $(CUBLAS_LIB) -lm -lrt \ - -Wl,-rpath=$(SUITESPARSE)/lib -lstdc++ - -I = -I../../include -I../Include -I../User -I../../BTF/Include - -all: purge libs klutest klultest - - ./klultests > klultests.out - - ./klutests > klutests.out - - ./coverage - -valgrind: purge klutest klultest - - ./vklutests > klutests.out - - ./vklultests > klultests.out - - ./coverage - -libs: - ( cd ../../SuiteSparse_config && $(MAKE) TCOV=yes) - ( cd ../../AMD && $(MAKE) library TCOV=yes) - ( cd ../../COLAMD && $(MAKE) library TCOV=yes) - ( cd ../../CAMD && $(MAKE) library TCOV=yes) - ( cd ../../CCOLAMD && $(MAKE) library TCOV=yes) - ( cd ../.. && $(MAKE) metis ) - ( cd ../../CHOLMOD && $(MAKE) library TCOV=yes) - -purge: distclean - -distclean: clean - - $(RM) klutest klultest *.out cov_*.c - - $(RM) -r $(PURGE) - -clean: - - $(RM) -r $(CLEAN) - -INC = \ - ../Include/klu.h \ - ../Include/klu_internal.h \ - ../Include/klu_version.h - -BTFOBJ = \ - cov_btf_order.o \ - cov_btf_maxtrans.o \ - cov_btf_strongcomp.o \ - -BTFLOBJ = \ - cov_btf_l_order.o \ - cov_btf_l_maxtrans.o \ - cov_btf_l_strongcomp.o - -KLUOBJ = \ - cov_klu_analyze.o \ - cov_klu_analyze_given.o \ - cov_klu_defaults.o \ - cov_klu_free_symbolic.o \ - cov_klu_memory.o \ - cov_klu_d.o \ - cov_klu_d_diagnostics.o \ - cov_klu_d_dump.o \ - cov_klu_d_factor.o \ - cov_klu_d_free_numeric.o \ - cov_klu_d_kernel.o \ - cov_klu_d_extract.o \ - cov_klu_d_refactor.o \ - cov_klu_d_scale.o \ - cov_klu_d_solve.o \ - cov_klu_d_tsolve.o \ - cov_klu_z.o \ - cov_klu_z_diagnostics.o \ - cov_klu_z_dump.o \ - cov_klu_z_factor.o \ - cov_klu_z_free_numeric.o \ - cov_klu_z_kernel.o \ - cov_klu_z_extract.o \ - cov_klu_z_refactor.o \ - cov_klu_z_scale.o \ - cov_klu_z_solve.o \ - cov_klu_z_tsolve.o - -KLULOBJ = \ - cov_klu_l_analyze.o \ - cov_klu_l_analyze_given.o \ - cov_klu_l_defaults.o \ - cov_klu_l_free_symbolic.o \ - cov_klu_l_memory.o \ - cov_klu_l.o \ - cov_klu_l_diagnostics.o \ - cov_klu_l_dump.o \ - cov_klu_l_factor.o \ - cov_klu_l_free_numeric.o \ - cov_klu_l_kernel.o \ - cov_klu_l_extract.o \ - cov_klu_l_refactor.o \ - cov_klu_l_scale.o \ - cov_klu_l_solve.o \ - cov_klu_l_tsolve.o \ - cov_klu_zl.o \ - cov_klu_zl_diagnostics.o \ - cov_klu_zl_dump.o \ - cov_klu_zl_factor.o \ - cov_klu_zl_free_numeric.o \ - cov_klu_zl_kernel.o \ - cov_klu_zl_extract.o \ - cov_klu_zl_refactor.o \ - cov_klu_zl_scale.o \ - cov_klu_zl_solve.o \ - cov_klu_zl_tsolve.o - -KLUCHOLMODOBJ = cov_klu_cholmod.o - -KLUCHOLMODLOBJ = cov_klu_l_cholmod.o - -OBJ = $(BTFOBJ) $(KLUOBJ) $(KLUCHOLMODOBJ) - -LOBJ = $(BTFLOBJ) $(KLULOBJ) $(KLUCHOLMODLOBJ) - -$(OBJ): $(INC) - -$(LOBJ): $(INC) - -klutest: $(OBJ) klutest.c - $(C) $(I) klutest.c -o klutest $(OBJ) $(LDLIBS) - -klultest: $(LOBJ) klutest.c - $(C) -DDLONG $(I) klutest.c -o klultest $(LOBJ) $(LDLIBS) - -.c.o: - $(C) -c $(I) $*.c - -#------------------------------------------------------------------------------- - -cov_klu_d.o: ../Source/klu.c - $(C) -E $(I) $< | $(PRETTY) > cov_klu_d.c - $(C) -c $(I) cov_klu_d.c - -cov_klu_z.o: ../Source/klu.c - $(C) -E $(I) -DCOMPLEX $< | $(PRETTY) > cov_klu_z.c - $(C) -c $(I) cov_klu_z.c - -cov_klu_d_kernel.o: ../Source/klu_kernel.c - $(C) -E $(I) $< | $(PRETTY) > cov_klu_d_kernel.c - $(C) -c $(I) cov_klu_d_kernel.c - -cov_klu_z_kernel.o: ../Source/klu_kernel.c - $(C) -E $(I) -DCOMPLEX $< | $(PRETTY) > cov_klu_z_kernel.c - $(C) -c $(I) cov_klu_z_kernel.c - -cov_klu_d_diagnostics.o: ../Source/klu_diagnostics.c - $(C) -E $(I) $< | $(PRETTY) > cov_klu_d_diagnostics.c - $(C) -c $(I) cov_klu_d_diagnostics.c - -cov_klu_z_diagnostics.o: ../Source/klu_diagnostics.c - $(C) -E $(I) -DCOMPLEX $< | $(PRETTY) > cov_klu_z_diagnostics.c - $(C) -c $(I) cov_klu_z_diagnostics.c - -cov_klu_d_dump.o: ../Source/klu_dump.c - $(C) -E $(I) $< | $(PRETTY) > cov_klu_d_dump.c - $(C) -c $(I) cov_klu_d_dump.c - -cov_klu_z_dump.o: ../Source/klu_dump.c - $(C) -E $(I) -DCOMPLEX $< | $(PRETTY) > cov_klu_z_dump.c - $(C) -c $(I) cov_klu_z_dump.c - -cov_klu_d_factor.o: ../Source/klu_factor.c - $(C) -E $(I) $< | $(PRETTY) > cov_klu_d_factor.c - $(C) -c $(I) cov_klu_d_factor.c - -cov_klu_z_factor.o: ../Source/klu_factor.c - $(C) -E $(I) -DCOMPLEX $< | $(PRETTY) > cov_klu_z_factor.c - $(C) -c $(I) cov_klu_z_factor.c - -cov_klu_d_free_numeric.o: ../Source/klu_free_numeric.c - $(C) -E $(I) $< | $(PRETTY) > cov_klu_d_free_numeric.c - $(C) -c $(I) cov_klu_d_free_numeric.c - -cov_klu_z_free_numeric.o: ../Source/klu_free_numeric.c - $(C) -E $(I) -DCOMPLEX $< | $(PRETTY) > cov_klu_z_free_numeric.c - $(C) -c $(I) cov_klu_z_free_numeric.c - -cov_klu_d_extract.o: ../Source/klu_extract.c - $(C) -E $(I) $< | $(PRETTY) > cov_klu_d_extract.c - $(C) -c $(I) cov_klu_d_extract.c - -cov_klu_z_extract.o: ../Source/klu_extract.c - $(C) -E $(I) -DCOMPLEX $< | $(PRETTY) > cov_klu_z_extract.c - $(C) -c $(I) cov_klu_z_extract.c - -cov_klu_d_refactor.o: ../Source/klu_refactor.c - $(C) -E $(I) $< | $(PRETTY) > cov_klu_d_refactor.c - $(C) -c $(I) cov_klu_d_refactor.c - -cov_klu_z_refactor.o: ../Source/klu_refactor.c - $(C) -E $(I) -DCOMPLEX $< | $(PRETTY) > cov_klu_z_refactor.c - $(C) -c $(I) cov_klu_z_refactor.c - -cov_klu_d_scale.o: ../Source/klu_scale.c - $(C) -E $(I) $< | $(PRETTY) > cov_klu_d_scale.c - $(C) -c $(I) cov_klu_d_scale.c - -cov_klu_z_scale.o: ../Source/klu_scale.c - $(C) -E $(I) -DCOMPLEX $< | $(PRETTY) > cov_klu_z_scale.c - $(C) -c $(I) cov_klu_z_scale.c - -cov_klu_d_solve.o: ../Source/klu_solve.c - $(C) -E $(I) $< | $(PRETTY) > cov_klu_d_solve.c - $(C) -c $(I) cov_klu_d_solve.c - -cov_klu_z_solve.o: ../Source/klu_solve.c - $(C) -E $(I) -DCOMPLEX $< | $(PRETTY) > cov_klu_z_solve.c - $(C) -c $(I) cov_klu_z_solve.c - -cov_klu_d_tsolve.o: ../Source/klu_tsolve.c - $(C) -E $(I) $< | $(PRETTY) > cov_klu_d_tsolve.c - $(C) -c $(I) cov_klu_d_tsolve.c - -cov_klu_z_tsolve.o: ../Source/klu_tsolve.c - $(C) -E $(I) -DCOMPLEX $< | $(PRETTY) > cov_klu_z_tsolve.c - $(C) -c $(I) cov_klu_z_tsolve.c - -#------------------------------------------------------------------------------- - -cov_klu_analyze.o: ../Source/klu_analyze.c - $(C) -E $(I) $< | $(PRETTY) > cov_klu_analyze.c - $(C) -c $(I) cov_klu_analyze.c - -cov_klu_analyze_given.o: ../Source/klu_analyze_given.c - $(C) -E $(I) $< | $(PRETTY) > cov_klu_analyze_given.c - $(C) -c $(I) cov_klu_analyze_given.c - -cov_klu_defaults.o: ../Source/klu_defaults.c - $(C) -E $(I) $< | $(PRETTY) > cov_klu_defaults.c - $(C) -c $(I) cov_klu_defaults.c - -cov_klu_free_symbolic.o: ../Source/klu_free_symbolic.c - $(C) -E $(I) $< | $(PRETTY) > cov_klu_free_symbolic.c - $(C) -c $(I) cov_klu_free_symbolic.c - -cov_klu_memory.o: ../Source/klu_memory.c - $(C) -E $(I) $< | $(PRETTY) > cov_klu_memory.c - $(C) -c $(I) cov_klu_memory.c - -#------------------------------------------------------------------------------- - -cov_btf_order.o: ../../BTF/Source/btf_order.c - $(C) -E $(I) $< | $(PRETTY) > cov_btf_order.c - $(C) -c $(I) cov_btf_order.c - -cov_btf_maxtrans.o: ../../BTF/Source/btf_maxtrans.c - $(C) -E $(I) $< | $(PRETTY) > cov_btf_maxtrans.c - $(C) -c $(I) cov_btf_maxtrans.c - -cov_btf_strongcomp.o: ../../BTF/Source/btf_strongcomp.c - $(C) -E $(I) $< | $(PRETTY) > cov_btf_strongcomp.c - $(C) -c $(I) cov_btf_strongcomp.c - -#------------------------------------------------------------------------------- - -cov_klu_cholmod.o: ../User/klu_cholmod.c - $(C) -E $(I) $< | $(PRETTY) > cov_klu_cholmod.c - $(C) -c $(I) cov_klu_cholmod.c - - - - - - - -#------------------------------------------------------------------------------- - -cov_klu_l.o: ../Source/klu.c - $(C) -E $(I) -DDLONG $< | $(PRETTY) > cov_klu_l.c - $(C) -c $(I) cov_klu_l.c - -cov_klu_zl.o: ../Source/klu.c - $(C) -E $(I) -DDLONG -DCOMPLEX $< | $(PRETTY) > cov_klu_zl.c - $(C) -c $(I) cov_klu_zl.c - -cov_klu_l_kernel.o: ../Source/klu_kernel.c - $(C) -E $(I) -DDLONG $< | $(PRETTY) > cov_klu_l_kernel.c - $(C) -c $(I) cov_klu_l_kernel.c - -cov_klu_zl_kernel.o: ../Source/klu_kernel.c - $(C) -E $(I) -DDLONG -DCOMPLEX $< | $(PRETTY) > cov_klu_zl_kernel.c - $(C) -c $(I) cov_klu_zl_kernel.c - -cov_klu_l_diagnostics.o: ../Source/klu_diagnostics.c - $(C) -E $(I) -DDLONG $< | $(PRETTY) > cov_klu_l_diagnostics.c - $(C) -c $(I) cov_klu_l_diagnostics.c - -cov_klu_zl_diagnostics.o: ../Source/klu_diagnostics.c - $(C) -E $(I) -DDLONG -DCOMPLEX $< | $(PRETTY) > cov_klu_zl_diagnostics.c - $(C) -c $(I) cov_klu_zl_diagnostics.c - -cov_klu_l_dump.o: ../Source/klu_dump.c - $(C) -E $(I) -DDLONG $< | $(PRETTY) > cov_klu_l_dump.c - $(C) -c $(I) cov_klu_l_dump.c - -cov_klu_zl_dump.o: ../Source/klu_dump.c - $(C) -E $(I) -DDLONG -DCOMPLEX $< | $(PRETTY) > cov_klu_zl_dump.c - $(C) -c $(I) cov_klu_zl_dump.c - -cov_klu_l_factor.o: ../Source/klu_factor.c - $(C) -E $(I) -DDLONG $< | $(PRETTY) > cov_klu_l_factor.c - $(C) -c $(I) cov_klu_l_factor.c - -cov_klu_zl_factor.o: ../Source/klu_factor.c - $(C) -E $(I) -DDLONG -DCOMPLEX $< | $(PRETTY) > cov_klu_zl_factor.c - $(C) -c $(I) cov_klu_zl_factor.c - -cov_klu_l_free_numeric.o: ../Source/klu_free_numeric.c - $(C) -E $(I) -DDLONG $< | $(PRETTY) > cov_klu_l_free_numeric.c - $(C) -c $(I) cov_klu_l_free_numeric.c - -cov_klu_zl_free_numeric.o: ../Source/klu_free_numeric.c - $(C) -E $(I) -DDLONG -DCOMPLEX $< | $(PRETTY) > cov_klu_zl_free_numeric.c - $(C) -c $(I) cov_klu_zl_free_numeric.c - -cov_klu_l_extract.o: ../Source/klu_extract.c - $(C) -E $(I) -DDLONG $< | $(PRETTY) > cov_klu_l_extract.c - $(C) -c $(I) cov_klu_l_extract.c - -cov_klu_zl_extract.o: ../Source/klu_extract.c - $(C) -E $(I) -DDLONG -DCOMPLEX $< | $(PRETTY) > cov_klu_zl_extract.c - $(C) -c $(I) cov_klu_zl_extract.c - -cov_klu_l_refactor.o: ../Source/klu_refactor.c - $(C) -E $(I) -DDLONG $< | $(PRETTY) > cov_klu_l_refactor.c - $(C) -c $(I) cov_klu_l_refactor.c - -cov_klu_zl_refactor.o: ../Source/klu_refactor.c - $(C) -E $(I) -DDLONG -DCOMPLEX $< | $(PRETTY) > cov_klu_zl_refactor.c - $(C) -c $(I) cov_klu_zl_refactor.c - -cov_klu_l_scale.o: ../Source/klu_scale.c - $(C) -E $(I) -DDLONG $< | $(PRETTY) > cov_klu_l_scale.c - $(C) -c $(I) cov_klu_l_scale.c - -cov_klu_zl_scale.o: ../Source/klu_scale.c - $(C) -E $(I) -DDLONG -DCOMPLEX $< | $(PRETTY) > cov_klu_zl_scale.c - $(C) -c $(I) cov_klu_zl_scale.c - -cov_klu_l_solve.o: ../Source/klu_solve.c - $(C) -E $(I) -DDLONG $< | $(PRETTY) > cov_klu_l_solve.c - $(C) -c $(I) cov_klu_l_solve.c - -cov_klu_zl_solve.o: ../Source/klu_solve.c - $(C) -E $(I) -DDLONG -DCOMPLEX $< | $(PRETTY) > cov_klu_zl_solve.c - $(C) -c $(I) cov_klu_zl_solve.c - -cov_klu_l_tsolve.o: ../Source/klu_tsolve.c - $(C) -E $(I) -DDLONG $< | $(PRETTY) > cov_klu_l_tsolve.c - $(C) -c $(I) cov_klu_l_tsolve.c - -cov_klu_zl_tsolve.o: ../Source/klu_tsolve.c - $(C) -E $(I) -DDLONG -DCOMPLEX $< | $(PRETTY) > cov_klu_zl_tsolve.c - $(C) -c $(I) cov_klu_zl_tsolve.c - -#------------------------------------------------------------------------------- - -cov_klu_l_analyze.o: ../Source/klu_analyze.c - $(C) -E $(I) -DDLONG $< | $(PRETTY) > cov_klu_l_analyze.c - $(C) -c $(I) cov_klu_l_analyze.c - -cov_klu_l_analyze_given.o: ../Source/klu_analyze_given.c - $(C) -E $(I) -DDLONG $< | $(PRETTY) > cov_klu_l_analyze_given.c - $(C) -c $(I) cov_klu_l_analyze_given.c - -cov_klu_l_defaults.o: ../Source/klu_defaults.c - $(C) -E $(I) -DDLONG $< | $(PRETTY) > cov_klu_l_defaults.c - $(C) -c $(I) cov_klu_l_defaults.c - -cov_klu_l_free_symbolic.o: ../Source/klu_free_symbolic.c - $(C) -E $(I) -DDLONG $< | $(PRETTY) > cov_klu_l_free_symbolic.c - $(C) -c $(I) cov_klu_l_free_symbolic.c - -cov_klu_l_memory.o: ../Source/klu_memory.c - $(C) -E $(I) -DDLONG $< | $(PRETTY) > cov_klu_l_memory.c - $(C) -c $(I) cov_klu_l_memory.c - -#------------------------------------------------------------------------------- - -cov_btf_l_order.o: ../../BTF/Source/btf_order.c - $(C) -E $(I) -DDLONG $< | $(PRETTY) > cov_btf_l_order.c - $(C) -c $(I) cov_btf_l_order.c - -cov_btf_l_maxtrans.o: ../../BTF/Source/btf_maxtrans.c - $(C) -E $(I) -DDLONG $< | $(PRETTY) > cov_btf_l_maxtrans.c - $(C) -c $(I) cov_btf_l_maxtrans.c - -cov_btf_l_strongcomp.o: ../../BTF/Source/btf_strongcomp.c - $(C) -E $(I) -DDLONG $< | $(PRETTY) > cov_btf_l_strongcomp.c - $(C) -c $(I) cov_btf_l_strongcomp.c - -#------------------------------------------------------------------------------- - -cov_klu_l_cholmod.o: ../User/klu_l_cholmod.c - $(C) -E $(I) -DDLONG $< | $(PRETTY) > cov_klu_l_cholmod.c - $(C) -c $(I) cov_klu_l_cholmod.c - diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Tcov/README.txt b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Tcov/README.txt deleted file mode 100644 index e8c862b91..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Tcov/README.txt +++ /dev/null @@ -1,7 +0,0 @@ -Test suite for KLU - -To compile and run the test suite, first type "make" in the Linux/Unix shell. -The libraries must be compiled first (if they aren't use "make libs"). -"make clean" or "make distclean" will remove all unnecessary files. - -Timothy A. Davis, http://www.suitesparse.com diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Tcov/coverage b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Tcov/coverage deleted file mode 100755 index c08158828..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Tcov/coverage +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/csh -gcov cov*.c >& /dev/null -echo -n 'statements not covered: ' -grep "#####" *.c.gcov | wc -l diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Tcov/klultests b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Tcov/klultests deleted file mode 100755 index 98f2734aa..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Tcov/klultests +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/csh - -# 57 unique statements: -./klultest < ../Matrix/impcol_a.mtx - -# 20: -./klultest < ../Matrix/GD99_cc.mtx - -# 17: -./klultest < ../Matrix/two.mtx - -# 10: -./klultest < ../Matrix/w156.mtx - -# 3, xsize memgrow in klu_kernel -./klultest < ../Matrix/arrow.mtx - -# 3, xsize memgrow in klu_kernel, 1 in klu_z_condest, -./klultest < ../Matrix/arrowc.mtx - -# 2 in klu_z_kernel (if pivot == 0 and halt_if_singular, and in complex divide) -./klultest < ../Matrix/onec.mtx - -# 1 in klu_kernel (if pivot == 0 and halt if singular) -./klultest < ../Matrix/one.mtx - -# 1 in klu_z_condest: -./klultest < ../Matrix/1c.mtx - -# 1 in klu_z_condest: -./klultest < ../Matrix/ctina.mtx diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Tcov/klutest.c b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Tcov/klutest.c deleted file mode 100644 index c09781499..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Tcov/klutest.c +++ /dev/null @@ -1,1384 +0,0 @@ -/* ========================================================================== */ -/* === KLU test ============================================================= */ -/* ========================================================================== */ - -/* Exhaustive test for KLU and BTF (int, long, real, and complex versions) */ - -#include -#include "cholmod.h" -#include "klu_cholmod.h" -#include "klu_internal.h" - -#define ID Int_id - -#define NRHS 6 - -#define HALT { fprintf (stderr, "Test failure: %d\n", __LINE__) ; abort () ; } -#define OK(a) { if (!(a)) HALT ; } -#define FAIL(a) { if (a) HALT ; } - -#define MAX(a,b) (((a) > (b)) ? (a) : (b)) - - -#ifdef DLONG - -#define klu_z_scale klu_zl_scale -#define klu_z_solve klu_zl_solve -#define klu_z_tsolve klu_zl_tsolve -#define klu_z_free_numeric klu_zl_free_numeric -#define klu_z_factor klu_zl_factor -#define klu_z_refactor klu_zl_refactor -#define klu_z_lsolve klu_zl_lsolve -#define klu_z_ltsolve klu_zl_ltsolve -#define klu_z_usolve klu_zl_usolve -#define klu_z_utsolve klu_zl_utsolve -#define klu_z_defaults klu_zl_defaults -#define klu_z_rgrowth klu_zl_rgrowth -#define klu_z_rcond klu_zl_rcond -#define klu_z_extract klu_zl_extract -#define klu_z_condest klu_zl_condest -#define klu_z_flops klu_zl_flops - -#define klu_scale klu_l_scale -#define klu_solve klu_l_solve -#define klu_tsolve klu_l_tsolve -#define klu_free_numeric klu_l_free_numeric -#define klu_factor klu_l_factor -#define klu_refactor klu_l_refactor -#define klu_lsolve klu_l_lsolve -#define klu_ltsolve klu_l_ltsolve -#define klu_usolve klu_l_usolve -#define klu_utsolve klu_l_utsolve -#define klu_defaults klu_l_defaults -#define klu_rgrowth klu_l_rgrowth -#define klu_rcond klu_l_rcond -#define klu_extract klu_l_extract -#define klu_condest klu_l_condest -#define klu_flops klu_l_flops - -#define klu_analyze klu_l_analyze -#define klu_analyze_given klu_l_analyze_given -#define klu_malloc klu_l_malloc -#define klu_free klu_l_free -#define klu_realloc klu_l_realloc -#define klu_free_symbolic klu_l_free_symbolic -#define klu_free_numeric klu_l_free_numeric -#define klu_defaults klu_l_defaults - -#define klu_cholmod klu_l_cholmod - -#endif - - -#ifdef DLONG - -#define CHOLMOD_print_sparse cholmod_l_print_sparse -#define CHOLMOD_print_dense cholmod_l_print_dense -#define CHOLMOD_copy_sparse cholmod_l_copy_sparse -#define CHOLMOD_copy_dense cholmod_l_copy_dense -#define CHOLMOD_transpose cholmod_l_transpose -#define CHOLMOD_sdmult cholmod_l_sdmult -#define CHOLMOD_norm_dense cholmod_l_norm_dense -#define CHOLMOD_norm_sparse cholmod_l_norm_sparse -#define CHOLMOD_free_sparse cholmod_l_free_sparse -#define CHOLMOD_free_dense cholmod_l_free_dense -#define CHOLMOD_start cholmod_l_start -#define CHOLMOD_read_sparse cholmod_l_read_sparse -#define CHOLMOD_allocate_dense cholmod_l_allocate_dense -#define CHOLMOD_finish cholmod_l_finish - -#else - -#define CHOLMOD_print_sparse cholmod_print_sparse -#define CHOLMOD_print_dense cholmod_print_dense -#define CHOLMOD_copy_sparse cholmod_copy_sparse -#define CHOLMOD_copy_dense cholmod_copy_dense -#define CHOLMOD_transpose cholmod_transpose -#define CHOLMOD_sdmult cholmod_sdmult -#define CHOLMOD_norm_dense cholmod_norm_dense -#define CHOLMOD_norm_sparse cholmod_norm_sparse -#define CHOLMOD_free_sparse cholmod_free_sparse -#define CHOLMOD_free_dense cholmod_free_dense -#define CHOLMOD_start cholmod_start -#define CHOLMOD_read_sparse cholmod_read_sparse -#define CHOLMOD_allocate_dense cholmod_allocate_dense -#define CHOLMOD_finish cholmod_finish - -#endif - -/* ========================================================================== */ -/* === random numbers ======================================================= */ -/* ========================================================================== */ - -#define MY_RAND_MAX 32767 - -static unsigned long next = 1 ; - -static Int my_rand (void) -{ - next = next * 1103515245 + 12345 ; - return ((unsigned)(next/65536) % (MY_RAND_MAX+1)) ; -} - -static void my_srand (unsigned seed) -{ - next = seed ; -} - -/* ========================================================================== */ -/* === memory management ==================================================== */ -/* ========================================================================== */ - -void *my_malloc (size_t size) ; -void *my_calloc (size_t n, size_t size) ; -void *my_realloc (void *p, size_t size) ; -void my_free (void *p) ; - -Int my_tries = -1 ; - -void *my_malloc (size_t size) -{ - if (my_tries == 0) return (NULL) ; /* pretend to fail */ - if (my_tries > 0) my_tries-- ; - return (malloc (size)) ; -} - -void *my_calloc (size_t n, size_t size) -{ - if (my_tries == 0) return (NULL) ; /* pretend to fail */ - if (my_tries > 0) my_tries-- ; - return (calloc (n, size)) ; -} - -void *my_realloc (void *p, size_t size) -{ - if (my_tries == 0) return (NULL) ; /* pretend to fail */ - if (my_tries > 0) my_tries-- ; - return (realloc (p, size)) ; -} - -void my_free (void *p) -{ - if (p) free (p) ; -} - -static void normal_memory_handler ( void ) -{ - SuiteSparse_config.malloc_func = malloc ; - SuiteSparse_config.calloc_func = calloc ; - SuiteSparse_config.realloc_func = realloc ; - SuiteSparse_config.free_func = free ; - - my_tries = -1 ; -} - -static void test_memory_handler ( void ) -{ - SuiteSparse_config.malloc_func = my_malloc ; - SuiteSparse_config.calloc_func = my_calloc ; - SuiteSparse_config.realloc_func = my_realloc ; - SuiteSparse_config.free_func = my_free ; - my_tries = -1 ; -} - - -/* ========================================================================== */ -/* === print_sparse ========================================================= */ -/* ========================================================================== */ - -/* print a sparse matrix */ - -static void print_sparse (Int n, Int isreal, Int *Ap, Int *Ai, double *Ax, - double *Az) -{ - double ax, az ; - Int i, j, p ; - for (j = 0 ; j < n ; j++) - { - printf ("column "ID":\n", j) ; - for (p = Ap [j] ; p < Ap [j+1] ; p++) - { - i = Ai [p] ; - if (isreal) - { - ax = Ax [p] ; - az = 0 ; - } - else if (Az) - { - /* split complex */ - ax = Ax [p] ; - az = Az [p] ; - } - else - { - /* merged complex */ - ax = Ax [2*p ] ; - az = Ax [2*p+1] ; - } - printf (" row "ID" : %g", i, ax) ; - if (!isreal) - { - printf (" + (%g)i", az) ; - } - printf ("\n") ; - } - } - fflush (stdout) ; -} - - -/* ========================================================================== */ -/* === print_int ============================================================ */ -/* ========================================================================== */ - -/* print an Int vector */ - -static void print_int (Int n, Int *P) -{ - Int j ; - for (j = 0 ; j < n ; j++) - { - printf (" "ID" : "ID"\n", j, P [j]) ; - } - fflush (stdout) ; -} - - -/* ========================================================================== */ -/* === print_double ========================================================= */ -/* ========================================================================== */ - -/* print a double vector */ - -static void print_double (Int n, double *X) -{ - Int j ; - for (j = 0 ; j < n ; j++) - { - printf (" "ID" : %g\n", j, X [j]) ; - } - fflush (stdout) ; -} - - -/* ========================================================================== */ -/* === ludump =============================================================== */ -/* ========================================================================== */ - -/* extract and print the LU factors */ - -static void ludump (KLU_symbolic *Symbolic, KLU_numeric *Numeric, Int isreal, - cholmod_common *ch, KLU_common *Common) -{ - Int *Lp, *Li, *Up, *Ui, *Fp, *Fi, *P, *Q, *R ; - double *Lx, *Ux, *Fx, *Lz, *Uz, *Fz, *Rs ; - Int n, lnz, unz, fnz, nb, result ; - - if (Symbolic == NULL || Numeric == NULL) - { - return ; - } - - n = Symbolic->n ; - lnz = Numeric->lnz ; - unz = Numeric->unz ; - fnz = Numeric->Offp [n] ; - nb = Symbolic->nblocks ; - - printf ("n "ID" lnz "ID" unz "ID" fnz "ID" nblocks "ID" isreal "ID"\n", - n, lnz, unz, fnz, nb, isreal) ; - fflush (stdout) ; - - Lp = malloc ((n+1) * sizeof (Int)) ; - Li = malloc (lnz * sizeof (Int)) ; - Lx = malloc (lnz * sizeof (double)) ; - Lz = malloc (lnz * sizeof (double)) ; - - Up = malloc ((n+1) * sizeof (Int)) ; - Ui = malloc (unz * sizeof (Int)) ; - Ux = malloc (unz * sizeof (double)) ; - Uz = malloc (unz * sizeof (double)) ; - - Fp = malloc ((n+1) * sizeof (Int)) ; - Fi = malloc (fnz * sizeof (Int)) ; - Fx = malloc (fnz * sizeof (double)) ; - Fz = malloc (fnz * sizeof (double)) ; - - P = malloc (n * sizeof (Int)) ; - Q = malloc (n * sizeof (Int)) ; - Rs = malloc (n * sizeof (double)) ; - R = malloc ((nb+1) * sizeof (double)) ; - - if (isreal) - { - result = klu_extract (Numeric, Symbolic, Lp, Li, Lx, - Up, Ui, Ux, Fp, Fi, Fx, P, Q, Rs, R, Common) ; - } - else - { - result = klu_z_extract (Numeric, Symbolic, Lp, Li, Lx, Lz, - Up, Ui, Ux, Uz, Fp, Fi, Fx, Fz, P, Q, Rs, R, Common) ; - } - - if (my_tries != 0) OK (result) ; - - if (ch->print >= 5) - { - printf ("------ L:\n") ; print_sparse (n, isreal, Lp, Li, Lx, Lz) ; - printf ("------ U:\n") ; print_sparse (n, isreal, Up, Ui, Ux, Uz) ; - printf ("------ F:\n") ; print_sparse (n, isreal, Fp, Fi, Fx, Fz) ; - printf ("------ P:\n") ; print_int (n, P) ; - printf ("------ Q:\n") ; print_int (n, Q) ; - printf ("------ Rs:\n") ; print_double (n, Rs) ; - printf ("------ R:\n") ; print_int (nb+1, R) ; - } - - free (Lp) ; - free (Li) ; - free (Lx) ; - free (Lz) ; - - free (Up) ; - free (Ui) ; - free (Ux) ; - free (Uz) ; - - free (Fp) ; - free (Fi) ; - free (Fx) ; - free (Fz) ; - - free (P) ; - free (Q) ; - free (Rs) ; - free (R) ; -} - - -/* ========================================================================== */ -/* === randperm ============================================================= */ -/* ========================================================================== */ - -/* return a random permutation vector */ - -static Int *randperm (Int n, Int seed) -{ - Int *p, k, j, t ; - p = malloc (n * sizeof (Int)) ; - for (k = 0 ; k < n ; k++) - { - p [k] = k ; - } - my_srand (seed) ; /* get new random number seed */ - for (k = 0 ; k < n ; k++) - { - j = k + (my_rand ( ) % (n-k)) ; /* j = my_rand in range k to n-1 */ - t = p [j] ; /* swap p[k] and p[j] */ - p [j] = p [k] ; - p [k] = t ; - } - return (p) ; -} - - -/* ========================================================================== */ -/* === do_1_solve =========================================================== */ -/* ========================================================================== */ - -static double do_1_solve (cholmod_sparse *A, cholmod_dense *B, - cholmod_dense *Xknown, Int *Puser, Int *Quser, - KLU_common *Common, cholmod_common *ch, Int *isnan) -{ - Int *Ai, *Ap ; - double *Ax, *Xknownx, *Xx, *Ax2, *Axx ; - KLU_symbolic *Symbolic = NULL ; - KLU_numeric *Numeric = NULL ; - cholmod_dense *X = NULL, *R = NULL ; - cholmod_sparse *AT = NULL, *A2 = NULL, *AT2 = NULL ; - double one [2], minusone [2], - rnorm, anorm, xnorm, relresid, relerr, err = 0. ; - Int i, j, nrhs2, isreal, n, nrhs, transpose, step, k, save, tries ; - - printf ("\ndo_1_solve: btf "ID" maxwork %g scale "ID" ordering "ID" user: " - ID" P,Q: %d halt: "ID"\n", - Common->btf, Common->maxwork, Common->scale, Common->ordering, - Common->user_data ? (*((Int *) Common->user_data)) : -1, - (Puser != NULL || Quser != NULL), Common->halt_if_singular) ; - fflush (stdout) ; - fflush (stderr) ; - - CHOLMOD_print_sparse (A, "A", ch) ; - CHOLMOD_print_dense (B, "B", ch) ; - - Ap = A->p ; - Ai = A->i ; - Ax = A->x ; - n = A->nrow ; - isreal = (A->xtype == CHOLMOD_REAL) ; - /* Bx = B->x ; */ - Xknownx = Xknown->x ; - nrhs = B->ncol ; - - one [0] = 1 ; - one [1] = 0 ; - - minusone [0] = -1 ; - minusone [1] = 0 ; - - /* ---------------------------------------------------------------------- */ - /* symbolic analysis */ - /* ---------------------------------------------------------------------- */ - - Symbolic = NULL ; - my_tries = 0 ; - for (tries = 0 ; Symbolic == NULL && my_tries == 0 ; tries++) - { - my_tries = tries ; - if (Puser != NULL || Quser != NULL) - { - Symbolic = klu_analyze_given (n, Ap, Ai, Puser, Quser, Common) ; - } - else - { - Symbolic = klu_analyze (n, Ap, Ai, Common) ; - } - } - printf ("sym try "ID" btf "ID" ordering "ID"\n", - tries, Common->btf, Common->ordering) ; - if (Symbolic == NULL) - { - printf ("Symbolic is null\n") ; - return (998) ; - } - my_tries = -1 ; - - /* create a modified version of A */ - - A2 = CHOLMOD_copy_sparse (A, ch) ; - Ax2 = A2->x ; - my_srand (42) ; - for (k = 0 ; k < Ap [n] * (isreal ? 1:2) ; k++) - { - Ax2 [k] = Ax [k] * - (1 + 1e-4 * ((double) my_rand ( )) / ((double) MY_RAND_MAX)) ; - } - - AT = isreal ? NULL : CHOLMOD_transpose (A, 1, ch) ; - AT2 = isreal ? NULL : CHOLMOD_transpose (A2, 1, ch) ; - - /* ---------------------------------------------------------------------- */ - /* factorize then solve */ - /* ---------------------------------------------------------------------- */ - - for (step = 1 ; step <= 3 ; step++) - { - printf ("step: "ID"\n", step) ; - fflush (stdout) ; - - /* ------------------------------------------------------------------ */ - /* factorization or refactorization */ - /* ------------------------------------------------------------------ */ - - /* step 1: factor - step 2: refactor with same A - step 3: refactor with modified A, and scaling forced on - and solve each time - */ - - if (step == 1) - { - /* numeric factorization */ - - Numeric = NULL ; - my_tries = 0 ; - for (tries = 0 ; Numeric == NULL && my_tries == 0 ; tries++) - { - my_tries = tries ; - if (isreal) - { - Numeric = klu_factor (Ap, Ai, Ax, Symbolic, Common) ; - } - else - { - Numeric = klu_z_factor (Ap, Ai, Ax, Symbolic, Common) ; - } - } - printf ("num try "ID" btf "ID"\n", tries, Common->btf) ; - my_tries = -1 ; - - if (Common->status == KLU_OK || - (Common->status == KLU_SINGULAR && !Common->halt_if_singular)) - { - OK (Numeric) ; - } - else - { - FAIL (Numeric) ; - } - - if (Common->status < KLU_OK) - { - printf ("factor failed: "ID"\n", Common->status) ; - } - - } - else if (step == 2) - { - - /* numeric refactorization with same values, same scaling */ - if (isreal) - { - klu_refactor (Ap, Ai, Ax, Symbolic, Numeric, Common) ; - } - else - { - klu_z_refactor (Ap, Ai, Ax, Symbolic, Numeric, Common) ; - } - - } - else - { - - /* numeric refactorization with different values */ - save = Common->scale ; - if (Common->scale == 0) - { - Common->scale = 1 ; - } - for (tries = 0 ; tries <= 1 ; tries++) - { - my_tries = tries ; - if (isreal) - { - klu_refactor (Ap, Ai, Ax2, Symbolic, Numeric, Common) ; - } - else - { - klu_z_refactor (Ap, Ai, Ax2, Symbolic, Numeric, Common) ; - } - } - my_tries = -1 ; - Common->scale = save ; - } - - if (Common->status == KLU_SINGULAR) - { - printf ("# singular column : "ID"\n", Common->singular_col) ; - } - - /* ------------------------------------------------------------------ */ - /* diagnostics */ - /* ------------------------------------------------------------------ */ - - Axx = (step == 3) ? Ax2 : Ax ; - - if (isreal) - { - klu_rgrowth (Ap, Ai, Axx, Symbolic, Numeric, Common) ; - klu_condest (Ap, Axx, Symbolic, Numeric, Common) ; - klu_rcond (Symbolic, Numeric, Common) ; - klu_flops (Symbolic, Numeric, Common) ; - } - else - { - klu_z_rgrowth (Ap, Ai, Axx, Symbolic, Numeric, Common) ; - klu_z_condest (Ap, Axx, Symbolic, Numeric, Common) ; - klu_z_rcond (Symbolic, Numeric, Common) ; - klu_z_flops (Symbolic, Numeric, Common) ; - } - - printf ("growth %g condest %g rcond %g flops %g\n", - Common->rgrowth, Common->condest, Common->rcond, Common->flops) ; - - ludump (Symbolic, Numeric, isreal, ch, Common) ; - - if (Numeric == NULL || Common->status < KLU_OK) - { - continue ; - } - - /* ------------------------------------------------------------------ */ - /* solve */ - /* ------------------------------------------------------------------ */ - - /* forward/backsolve to solve A*X=B or A'*X=B */ - for (transpose = (isreal ? 0 : -1) ; transpose <= 1 ; transpose++) - { - - for (nrhs2 = 1 ; nrhs2 <= nrhs ; nrhs2++) - { - /* mangle B so that it has only nrhs2 columns */ - B->ncol = nrhs2 ; - - X = CHOLMOD_copy_dense (B, ch) ; - CHOLMOD_print_dense (X, "X before solve", ch) ; - Xx = X->x ; - - if (isreal) - { - if (transpose) - { - /* solve A'x=b */ - klu_tsolve (Symbolic, Numeric, n, nrhs2, Xx, Common) ; - } - else - { - /* solve A*x=b */ - klu_solve (Symbolic, Numeric, n, nrhs2, Xx, Common) ; - } - } - else - { - if (transpose) - { - /* solve A'x=b (if 1) or A.'x=b (if -1) */ - klu_z_tsolve (Symbolic, Numeric, n, nrhs2, Xx, - (transpose == 1), Common) ; - } - else - { - /* solve A*x=b */ - klu_z_solve (Symbolic, Numeric, n, nrhs2, Xx, Common) ; - } - } - - CHOLMOD_print_dense (X, "X", ch) ; - - /* compute the residual, R = B-A*X, B-A'*X, or B-A.'*X */ - R = CHOLMOD_copy_dense (B, ch) ; - if (transpose == -1) - { - /* R = B-A.'*X (use A.' explicitly) */ - CHOLMOD_sdmult ((step == 3) ? AT2 : AT, - 0, minusone, one, X, R, ch) ; - } - else - { - /* R = B-A*X or B-A'*X */ - CHOLMOD_sdmult ((step == 3) ? A2 :A, - transpose, minusone, one, X, R, ch) ; - } - - CHOLMOD_print_dense (R, "R", ch) ; - - /* compute the norms of R, A, X, and B */ - rnorm = CHOLMOD_norm_dense (R, 1, ch) ; - anorm = CHOLMOD_norm_sparse ((step == 3) ? A2 : A, 1, ch) ; - xnorm = CHOLMOD_norm_dense (X, 1, ch) ; - /* bnorm = CHOLMOD_norm_dense (B, 1, ch) ; */ - - CHOLMOD_free_dense (&R, ch) ; - - /* relative residual = norm (r) / (norm (A) * norm (x)) */ - relresid = rnorm ; - if (anorm > 0) - { - relresid /= anorm ; - } - if (xnorm > 0) - { - relresid /= xnorm ; - } - - if (SCALAR_IS_NAN (relresid)) - { - *isnan = TRUE ; - } - else - { - err = MAX (err, relresid) ; - } - - /* relative error = norm (x - xknown) / norm (xknown) */ - /* overwrite X with X - Xknown */ - if (transpose || step == 3) - { - /* not computed */ - relerr = -1 ; - } - else - { - for (j = 0 ; j < nrhs2 ; j++) - { - for (i = 0 ; i < n ; i++) - { - if (isreal) - { - Xx [i+j*n] -= Xknownx [i+j*n] ; - } - else - { - Xx [2*(i+j*n) ] -= Xknownx [2*(i+j*n) ] ; - Xx [2*(i+j*n)+1] -= Xknownx [2*(i+j*n)+1] ; - } - } - } - relerr = CHOLMOD_norm_dense (X, 1, ch) ; - xnorm = CHOLMOD_norm_dense (Xknown, 1, ch) ; - if (xnorm > 0) - { - relerr /= xnorm ; - } - - if (SCALAR_IS_NAN (relerr)) - { - *isnan = TRUE ; - } - else - { - err = MAX (relerr, err) ; - } - - } - - CHOLMOD_free_dense (&X, ch) ; - - printf (ID" "ID" relresid %10.3g relerr %10.3g %g\n", - transpose, nrhs2, relresid, relerr, err) ; - - B->ncol = nrhs ; /* restore B */ - } - } - } - - /* ---------------------------------------------------------------------- */ - /* free factorization and temporary matrices, and return */ - /* ---------------------------------------------------------------------- */ - - klu_free_symbolic (&Symbolic, Common) ; - if (isreal) - { - klu_free_numeric (&Numeric, Common) ; - } - else - { - klu_z_free_numeric (&Numeric, Common) ; - } - CHOLMOD_free_sparse (&A2, ch) ; - CHOLMOD_free_sparse (&AT, ch) ; - CHOLMOD_free_sparse (&AT2, ch) ; - fflush (stdout) ; - fflush (stderr) ; - return (err) ; -} - - -/* ========================================================================== */ -/* === do_solves ============================================================ */ -/* ========================================================================== */ - -/* test KLU with many options */ - -static double do_solves (cholmod_sparse *A, cholmod_dense *B, cholmod_dense *X, - Int *Puser, Int *Quser, KLU_common *Common, cholmod_common *ch, Int *isnan) -{ - double err, maxerr = 0 ; - Int n = A->nrow, sflag ; - *isnan = FALSE ; - - /* ---------------------------------------------------------------------- */ - /* test KLU with the system A*X=B and default options */ - /* ---------------------------------------------------------------------- */ - - maxerr = do_1_solve (A, B, X, NULL, NULL, Common, ch, isnan) ; - - /* ---------------------------------------------------------------------- */ - /* test with non-default options */ - /* ---------------------------------------------------------------------- */ - - Common->user_order = klu_cholmod ; - for (Common->btf = 0 ; Common->btf <= 2 ; Common->btf++) - { - Common->maxwork = (Common->btf == 2) ? 0.001 : 0 ; - - for (Common->halt_if_singular = 0 ; Common->halt_if_singular <= 1 ; - Common->halt_if_singular++) - { - for (Common->scale = 0 ; Common->scale <= 2 ; Common->scale++) - - { - fprintf (stderr, ".") ; - fflush (stderr) ; - - /* orderings: 0: AMD, 1: COLAMD, 2: natural, 3: user function */ - for (Common->ordering = 0 ; Common->ordering <= 3 ; - Common->ordering++) - { - err = do_1_solve (A, B, X, NULL, NULL, Common, ch, isnan) ; - maxerr = MAX (maxerr, err) ; - } - - /* user-ordering, unsymmetric case */ - Common->ordering = 3 ; - Common->user_data = &sflag ; - sflag = 0 ; - err = do_1_solve (A, B, X, NULL, NULL, Common, ch, isnan) ; - maxerr = MAX (maxerr, err) ; - Common->user_data = NULL ; - - /* Puser and Quser, but only for small matrices */ - Common->ordering = 2 ; - if (n < 200) - { - err = do_1_solve (A, B, X, Puser, Quser, Common, ch, isnan); - maxerr = MAX (maxerr, err) ; - } - } - } - } - - /* restore defaults */ - Common->btf = TRUE ; - Common->maxwork = 0 ; - Common->ordering = 0 ; - Common->scale = -1 ; - Common->halt_if_singular = TRUE ; - Common->user_order = NULL ; - - my_tries = -1 ; - return (maxerr) ; -} - - -/* ========================================================================== */ -/* === main ================================================================= */ -/* ========================================================================== */ - -int main (void) -{ - KLU_common Common ; - cholmod_sparse *A, *A2 ; - cholmod_dense *X, *B ; - cholmod_common ch ; - Int *Ap, *Ai, *Puser, *Quser, *Gunk ; - double *Ax, *Xx, *A2x ; - double one [2], zero [2], xsave, maxerr ; - Int n, i, j, nz, save, isreal, k, isnan ; - KLU_symbolic *Symbolic, *Symbolic2 ; - KLU_numeric *Numeric ; - - one [0] = 1 ; - one [1] = 0 ; - zero [0] = 0 ; - zero [1] = 0 ; - - printf ("klu test: -------------------------------------------------\n") ; - OK (klu_defaults (&Common)) ; - CHOLMOD_start (&ch) ; - ch.print = 0 ; - normal_memory_handler ( ) ; - - /* ---------------------------------------------------------------------- */ - /* read in a sparse matrix from stdin */ - /* ---------------------------------------------------------------------- */ - - A = CHOLMOD_read_sparse (stdin, &ch) ; - - if (A->nrow != A->ncol || A->stype != 0) - { - fprintf (stderr, "error: only square unsymmetric matrices handled\n") ; - CHOLMOD_free_sparse (&A, &ch) ; - return (0) ; - } - if (!(A->xtype == CHOLMOD_REAL || A->xtype == CHOLMOD_COMPLEX)) - { - fprintf (stderr, "error: only real or complex matrices hanlded\n") ; - CHOLMOD_free_sparse (&A, &ch) ; - return (0) ; - } - - n = A->nrow ; - Ap = A->p ; - Ai = A->i ; - Ax = A->x ; - nz = Ap [n] ; - isreal = (A->xtype == CHOLMOD_REAL) ; - - /* ---------------------------------------------------------------------- */ - /* construct random permutations */ - /* ---------------------------------------------------------------------- */ - - Puser = randperm (n, n) ; - Quser = randperm (n, n) ; - - /* ---------------------------------------------------------------------- */ - /* select known solution to Ax=b */ - /* ---------------------------------------------------------------------- */ - - X = CHOLMOD_allocate_dense (n, NRHS, n, A->xtype, &ch) ; - Xx = X->x ; - for (j = 0 ; j < NRHS ; j++) - { - for (i = 0 ; i < n ; i++) - { - if (isreal) - { - Xx [i] = 1 + ((double) i) / ((double) n) + j * 100; - } - else - { - Xx [2*i ] = 1 + ((double) i) / ((double) n) + j * 100 ; - Xx [2*i+1] = - ((double) i+1) / ((double) n + j) ; - if (j == NRHS-1) - { - Xx [2*i+1] = 0 ; /* zero imaginary part */ - } - else if (j == NRHS-2) - { - Xx [2*i] = 0 ; /* zero real part */ - } - } - } - Xx += isreal ? n : 2*n ; - } - - /* B = A*X */ - B = CHOLMOD_allocate_dense (n, NRHS, n, A->xtype, &ch) ; - CHOLMOD_sdmult (A, 0, one, zero, X, B, &ch) ; - /* Bx = B->x ; */ - - /* ---------------------------------------------------------------------- */ - /* test KLU */ - /* ---------------------------------------------------------------------- */ - - test_memory_handler ( ) ; - maxerr = do_solves (A, B, X, Puser, Quser, &Common, &ch, &isnan) ; - - /* ---------------------------------------------------------------------- */ - /* basic error checking */ - /* ---------------------------------------------------------------------- */ - - FAIL (klu_defaults (NULL)) ; - - FAIL (klu_extract (NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL)) ; - FAIL (klu_extract (NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, &Common)) ; - - FAIL (klu_z_extract (NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL)) ; - FAIL (klu_z_extract (NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &Common)) ; - - FAIL (klu_analyze (0, NULL, NULL, NULL)) ; - FAIL (klu_analyze (0, NULL, NULL, &Common)) ; - - FAIL (klu_analyze_given (0, NULL, NULL, NULL, NULL, NULL)) ; - FAIL (klu_analyze_given (0, NULL, NULL, NULL, NULL, &Common)) ; - - FAIL (klu_cholmod (0, NULL, NULL, NULL, NULL)) ; - - FAIL (klu_factor (NULL, NULL, NULL, NULL, NULL)) ; - FAIL (klu_factor (NULL, NULL, NULL, NULL, &Common)) ; - - FAIL (klu_z_factor (NULL, NULL, NULL, NULL, NULL)) ; - FAIL (klu_z_factor (NULL, NULL, NULL, NULL, &Common)) ; - - FAIL (klu_refactor (NULL, NULL, NULL, NULL, NULL, NULL)) ; - FAIL (klu_refactor (NULL, NULL, NULL, NULL, NULL, &Common)) ; - - FAIL (klu_z_refactor (NULL, NULL, NULL, NULL, NULL, NULL)) ; - FAIL (klu_z_refactor (NULL, NULL, NULL, NULL, NULL, &Common)) ; - - FAIL (klu_rgrowth (NULL, NULL, NULL, NULL, NULL, NULL)) ; - FAIL (klu_rgrowth (NULL, NULL, NULL, NULL, NULL, &Common)) ; - - FAIL (klu_z_rgrowth (NULL, NULL, NULL, NULL, NULL, NULL)) ; - FAIL (klu_z_rgrowth (NULL, NULL, NULL, NULL, NULL, &Common)) ; - - FAIL (klu_condest (NULL, NULL, NULL, NULL, NULL)) ; - FAIL (klu_condest (NULL, NULL, NULL, NULL, &Common)) ; - - FAIL (klu_z_condest (NULL, NULL, NULL, NULL, NULL)) ; - FAIL (klu_z_condest (NULL, NULL, NULL, NULL, &Common)) ; - - FAIL (klu_flops (NULL, NULL, NULL)) ; - FAIL (klu_flops (NULL, NULL, &Common)) ; - - FAIL (klu_z_flops (NULL, NULL, NULL)) ; - FAIL (klu_z_flops (NULL, NULL, &Common)) ; - - FAIL (klu_rcond (NULL, NULL, NULL)) ; - FAIL (klu_rcond (NULL, NULL, &Common)) ; - - FAIL (klu_z_rcond (NULL, NULL, NULL)) ; - FAIL (klu_z_rcond (NULL, NULL, &Common)) ; - - FAIL (klu_free_symbolic (NULL, NULL)) ; - OK (klu_free_symbolic (NULL, &Common)) ; - - FAIL (klu_free_numeric (NULL, NULL)) ; - OK (klu_free_numeric (NULL, &Common)) ; - - FAIL (klu_z_free_numeric (NULL, NULL)) ; - OK (klu_z_free_numeric (NULL, &Common)) ; - - FAIL (klu_scale (0, 0, NULL, NULL, NULL, NULL, NULL, NULL)) ; - FAIL (klu_scale (0, 0, NULL, NULL, NULL, NULL, NULL, &Common)) ; - OK (klu_scale (-1, 0, NULL, NULL, NULL, NULL, NULL, &Common)) ; - - FAIL (klu_z_scale (0, 0, NULL, NULL, NULL, NULL, NULL, NULL)) ; - FAIL (klu_z_scale (0, 0, NULL, NULL, NULL, NULL, NULL, &Common)) ; - OK (klu_z_scale (-1, 0, NULL, NULL, NULL, NULL, NULL, &Common)) ; - - FAIL (klu_solve (NULL, NULL, 0, 0, NULL, NULL)) ; - FAIL (klu_solve (NULL, NULL, 0, 0, NULL, &Common)) ; - - FAIL (klu_z_solve (NULL, NULL, 0, 0, NULL, NULL)) ; - FAIL (klu_z_solve (NULL, NULL, 0, 0, NULL, &Common)) ; - - FAIL (klu_tsolve (NULL, NULL, 0, 0, NULL, NULL)) ; - FAIL (klu_tsolve (NULL, NULL, 0, 0, NULL, &Common)) ; - - FAIL (klu_z_tsolve (NULL, NULL, 0, 0, NULL, 0, NULL)) ; - FAIL (klu_z_tsolve (NULL, NULL, 0, 0, NULL, 0, &Common)) ; - - FAIL (klu_malloc (0, 0, NULL)) ; - FAIL (klu_malloc (0, 0, &Common)) ; - FAIL (klu_malloc (Int_MAX, 1, &Common)) ; - - FAIL (klu_realloc (0, 0, 0, NULL, NULL)) ; - FAIL (klu_realloc (0, 0, 0, NULL, &Common)) ; - FAIL (klu_realloc (Int_MAX, 1, 0, NULL, &Common)) ; - Gunk = (Int *) klu_realloc (1, 0, sizeof (Int), NULL, &Common) ; - OK (Gunk) ; - OK (klu_realloc (Int_MAX, 1, sizeof (Int), Gunk, &Common)) ; - OK (Common.status == KLU_TOO_LARGE) ; - klu_free (Gunk, 1, sizeof (Int), &Common) ; - - /* ---------------------------------------------------------------------- */ - /* mangle the matrix, and other error checking */ - /* ---------------------------------------------------------------------- */ - - printf ("\nerror handling:\n") ; - Symbolic = klu_analyze (n, Ap, Ai, &Common) ; - OK (Symbolic) ; - - Xx = X->x ; - if (nz > 0) - { - - /* ------------------------------------------------------------------ */ - /* row index out of bounds */ - /* ------------------------------------------------------------------ */ - - save = Ai [0] ; - Ai [0] = -1 ; - FAIL (klu_analyze (n, Ap, Ai, &Common)) ; - if (isreal) - { - FAIL (klu_scale (1, n, Ap, Ai, Ax, Xx, Puser, &Common)) ; - } - else - { - FAIL (klu_z_scale (1, n, Ap, Ai, Ax, Xx, Puser, &Common)) ; - } - Ai [0] = save ; - - /* ------------------------------------------------------------------ */ - /* row index out of bounds */ - /* ------------------------------------------------------------------ */ - - save = Ai [0] ; - Ai [0] = Int_MAX ; - FAIL (klu_analyze (n, Ap, Ai, &Common)) ; - if (isreal) - { - FAIL (klu_scale (1, n, Ap, Ai, Ax, Xx, Puser, &Common)) ; - } - else - { - FAIL (klu_z_scale (1, n, Ap, Ai, Ax, Xx, Puser, &Common)) ; - } - Ai [0] = save ; - - /* ------------------------------------------------------------------ */ - /* column pointers mangled */ - /* ------------------------------------------------------------------ */ - - save = Ap [n] ; - Ap [n] = -1 ; - FAIL (klu_analyze (n, Ap, Ai, &Common)) ; - if (isreal) - { - FAIL (klu_scale (1, n, Ap, Ai, Ax, Xx, Puser, &Common)) ; - } - else - { - FAIL (klu_z_scale (1, n, Ap, Ai, Ax, Xx, Puser, &Common)) ; - } - Ap [n] = save ; - - /* ------------------------------------------------------------------ */ - /* column pointers mangled */ - /* ------------------------------------------------------------------ */ - - save = Ap [n] ; - Ap [n] = Ap [n-1] - 1 ; - FAIL (klu_analyze (n, Ap, Ai, &Common)) ; - if (isreal) - { - FAIL (klu_scale (1, n, Ap, Ai, Ax, Xx, Puser, &Common)) ; - } - else - { - FAIL (klu_z_scale (1, n, Ap, Ai, Ax, Xx, Puser, &Common)) ; - } - Ap [n] = save ; - - /* ------------------------------------------------------------------ */ - /* duplicates */ - /* ------------------------------------------------------------------ */ - - if (n > 1 && Ap [1] - Ap [0] > 1) - { - save = Ai [1] ; - Ai [1] = Ai [0] ; - FAIL (klu_analyze (n, Ap, Ai, &Common)) ; - if (isreal) - { - FAIL (klu_scale (1, n, Ap, Ai, Ax, Xx, Puser, &Common)) ; - } - else - { - FAIL (klu_z_scale (1, n, Ap, Ai, Ax, Xx, Puser, &Common)) ; - } - Ai [1] = save ; - } - - /* ------------------------------------------------------------------ */ - /* invalid ordering */ - /* ------------------------------------------------------------------ */ - - save = Common.ordering ; - Common.ordering = 42 ; - FAIL (klu_analyze (n, Ap, Ai, &Common)) ; - Common.ordering = save ; - - /* ------------------------------------------------------------------ */ - /* invalid ordering (klu_cholmod, with NULL user_ordering) */ - /* ------------------------------------------------------------------ */ - - save = Common.ordering ; - Common.user_order = NULL ; - Common.ordering = 3 ; - FAIL (klu_analyze (n, Ap, Ai, &Common)) ; - Common.ordering = save ; - } - - /* ---------------------------------------------------------------------- */ - /* tests with valid symbolic factorization */ - /* ---------------------------------------------------------------------- */ - - Common.halt_if_singular = FALSE ; - Common.scale = 0 ; - Numeric = NULL ; - - if (nz > 0) - { - - /* ------------------------------------------------------------------ */ - /* Int overflow */ - /* ------------------------------------------------------------------ */ - - if (n == 100) - { - Common.ordering = 2 ; - Symbolic2 = klu_analyze (n, Ap, Ai, &Common) ; - OK (Symbolic2) ; - Common.memgrow = Int_MAX ; - if (isreal) - { - Numeric = klu_factor (Ap, Ai, Ax, Symbolic2, &Common) ; - } - else - { - Numeric = klu_z_factor (Ap, Ai, Ax, Symbolic2, &Common) ; - } - Common.memgrow = 1.2 ; - Common.ordering = 0 ; - klu_free_symbolic (&Symbolic2, &Common) ; - klu_free_numeric (&Numeric, &Common) ; - } - - /* ------------------------------------------------------------------ */ - /* Int overflow again */ - /* ------------------------------------------------------------------ */ - - Common.initmem = Int_MAX ; - Common.initmem_amd = Int_MAX ; - if (isreal) - { - Numeric = klu_factor (Ap, Ai, Ax, Symbolic, &Common) ; - } - else - { - Numeric = klu_z_factor (Ap, Ai, Ax, Symbolic, &Common) ; - } - Common.initmem = 10 ; - Common.initmem_amd = 1.2 ; - klu_free_numeric (&Numeric, &Common) ; - - /* ------------------------------------------------------------------ */ - /* mangle the matrix */ - /* ------------------------------------------------------------------ */ - - save = Ai [0] ; - Ai [0] = -1 ; - - if (isreal) - { - Numeric = klu_factor (Ap, Ai, Ax, Symbolic, &Common) ; - } - else - { - Numeric = klu_z_factor (Ap, Ai, Ax, Symbolic, &Common) ; - } - FAIL (Numeric) ; - Ai [0] = save ; - - /* ------------------------------------------------------------------ */ - /* nan and inf handling */ - /* ------------------------------------------------------------------ */ - - xsave = Ax [0] ; - Ax [0] = one [0] / zero [0] ; - if (isreal) - { - Numeric = klu_factor (Ap, Ai, Ax, Symbolic, &Common) ; - klu_rcond (Symbolic, Numeric, &Common) ; - klu_condest (Ap, Ax, Symbolic, Numeric, &Common) ; - } - else - { - Numeric = klu_z_factor (Ap, Ai, Ax, Symbolic, &Common) ; - klu_z_rcond (Symbolic, Numeric, &Common) ; - klu_z_condest (Ap, Ax, Symbolic, Numeric, &Common) ; - } - printf ("Nan case: rcond %g condest %g\n", - Common.rcond, Common.condest) ; - OK (Numeric) ; - Ax [0] = xsave ; - - /* ------------------------------------------------------------------ */ - /* mangle the matrix again */ - /* ------------------------------------------------------------------ */ - - save = Ai [0] ; - Ai [0] = -1 ; - if (isreal) - { - FAIL (klu_refactor (Ap, Ai, Ax, Symbolic, Numeric, &Common)) ; - } - else - { - FAIL (klu_z_refactor (Ap, Ai, Ax, Symbolic, Numeric, &Common)) ; - } - Ai [0] = save ; - - /* ------------------------------------------------------------------ */ - /* all zero */ - /* ------------------------------------------------------------------ */ - - A2 = CHOLMOD_copy_sparse (A, &ch) ; - A2x = A2->x ; - for (k = 0 ; k < nz * (isreal ? 1:2) ; k++) - { - A2x [k] = 0 ; - } - for (Common.halt_if_singular = 0 ; Common.halt_if_singular <= 1 ; - Common.halt_if_singular++) - { - for (Common.scale = -1 ; Common.scale <= 2 ; Common.scale++) - { - if (isreal) - { - klu_refactor (Ap, Ai, A2x, Symbolic, Numeric, &Common) ; - klu_condest (Ap, A2x, Symbolic, Numeric, &Common) ; - } - else - { - klu_z_refactor (Ap, Ai, A2x, Symbolic, Numeric, &Common) ; - klu_z_condest (Ap, A2x, Symbolic, Numeric, &Common) ; - } - OK (Common.status = KLU_SINGULAR) ; - } - } - CHOLMOD_free_sparse (&A2, &ch) ; - - /* ------------------------------------------------------------------ */ - /* all one, or all 1i for complex case */ - /* ------------------------------------------------------------------ */ - - A2 = CHOLMOD_copy_sparse (A, &ch) ; - A2x = A2->x ; - for (k = 0 ; k < nz ; k++) - { - if (isreal) - { - A2x [k] = 1 ; - } - else - { - A2x [2*k ] = 0 ; - A2x [2*k+1] = 1 ; - } - } - Common.halt_if_singular = 0 ; - Common.scale = 0 ; - if (isreal) - { - klu_refactor (Ap, Ai, A2x, Symbolic, Numeric, &Common) ; - klu_condest (Ap, A2x, Symbolic, Numeric, &Common) ; - } - else - { - klu_z_refactor (Ap, Ai, A2x, Symbolic, Numeric, &Common) ; - klu_z_condest (Ap, A2x, Symbolic, Numeric, &Common) ; - } - OK (Common.status = KLU_SINGULAR) ; - CHOLMOD_free_sparse (&A2, &ch) ; - } - - klu_free_symbolic (&Symbolic, &Common) ; - if (isreal) - { - klu_free_numeric (&Numeric, &Common) ; - } - else - { - klu_z_free_numeric (&Numeric, &Common) ; - } - - /* ---------------------------------------------------------------------- */ - /* free problem and quit */ - /* ---------------------------------------------------------------------- */ - - CHOLMOD_free_dense (&X, &ch) ; - CHOLMOD_free_dense (&B, &ch) ; - CHOLMOD_free_sparse (&A, &ch) ; - free (Puser) ; - free (Quser) ; - CHOLMOD_finish (&ch) ; - fprintf (stderr, " maxerr %10.3e", maxerr) ; - printf (" maxerr %10.3e", maxerr) ; - if (maxerr < 1e-8) - { - fprintf (stderr, " test passed") ; - printf (" test passed") ; - } - else - { - fprintf (stderr, " test FAILED") ; - printf (" test FAILED") ; - } - if (isnan) - { - fprintf (stderr, " *") ; - printf (" *") ; - } - fprintf (stderr, "\n") ; - printf ("\n-----------------------------------------------------------\n") ; - return (0) ; -} diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Tcov/klutests b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Tcov/klutests deleted file mode 100755 index 9b110bd34..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Tcov/klutests +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/csh - -# 57 unique statements: -./klutest < ../Matrix/impcol_a.mtx - -# 20: -./klutest < ../Matrix/GD99_cc.mtx - -# 17: -./klutest < ../Matrix/two.mtx - -# 10: -./klutest < ../Matrix/w156.mtx - -# 3, xsize memgrow in klu_kernel -./klutest < ../Matrix/arrow.mtx - -# 3, xsize memgrow in klu_kernel, 1 in klu_z_condest, -./klutest < ../Matrix/arrowc.mtx - -# 2 in klu_z_kernel (if pivot == 0 and halt_if_singular, and in complex divide) -./klutest < ../Matrix/onec.mtx - -# 1 in klu_kernel (if pivot == 0 and halt if singular) -./klutest < ../Matrix/one.mtx - -# 1 in klu_z_condest: -./klutest < ../Matrix/1c.mtx - -# 1 in klu_z_condest: -./klutest < ../Matrix/ctina.mtx diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Tcov/vklultests b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Tcov/vklultests deleted file mode 100755 index 8aa8dcf54..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Tcov/vklultests +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/csh - -# 57 unique statements: -valgrind ./klultest < ../Matrix/impcol_a.mtx - -# 20: -valgrind ./klultest < ../Matrix/GD99_cc.mtx - -# 17: -valgrind ./klultest < ../Matrix/two.mtx - -# 10: -valgrind ./klultest < ../Matrix/w156.mtx - -# 3, xsize memgrow in klu_kernel -valgrind ./klultest < ../Matrix/arrow.mtx - -# 3, xsize memgrow in klu_kernel, 1 in klu_z_condest, -valgrind ./klultest < ../Matrix/arrowc.mtx - -# 2 in klu_z_kernel (if pivot == 0 and halt_if_singular, and in complex divide) -valgrind ./klultest < ../Matrix/onec.mtx - -# 1 in klu_kernel (if pivot == 0 and halt if singular) -valgrind ./klultest < ../Matrix/one.mtx - -# 1 in klu_z_condest: -valgrind ./klultest < ../Matrix/1c.mtx - -# 1 in klu_z_condest: -valgrind ./klultest < ../Matrix/ctina.mtx diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Tcov/vklutests b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Tcov/vklutests deleted file mode 100755 index 7dfada902..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Tcov/vklutests +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/csh - -# 57 unique statements: -valgrind ./klutest < ../Matrix/impcol_a.mtx - -# 20: -valgrind ./klutest < ../Matrix/GD99_cc.mtx - -# 17: -valgrind ./klutest < ../Matrix/two.mtx - -# 10: -valgrind ./klutest < ../Matrix/w156.mtx - -# 3, xsize memgrow in klu_kernel -valgrind ./klutest < ../Matrix/arrow.mtx - -# 3, xsize memgrow in klu_kernel, 1 in klu_z_condest, -valgrind ./klutest < ../Matrix/arrowc.mtx - -# 2 in klu_z_kernel (if pivot == 0 and halt_if_singular, and in complex divide) -valgrind ./klutest < ../Matrix/onec.mtx - -# 1 in klu_kernel (if pivot == 0 and halt if singular) -valgrind ./klutest < ../Matrix/one.mtx - -# 1 in klu_z_condest: -valgrind ./klutest < ../Matrix/1c.mtx - -# 1 in klu_z_condest: -valgrind ./klutest < ../Matrix/ctina.mtx diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/User/Makefile b/deps/AMICI/ThirdParty/SuiteSparse/KLU/User/Makefile deleted file mode 100644 index b01a389c7..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/User/Makefile +++ /dev/null @@ -1,28 +0,0 @@ -default: all - -include ../../SuiteSparse_config/SuiteSparse_config.mk - -all: libklu_cholmod.a - -I = -I../../CHOLMOD/Include -I../../SuiteSparse_config \ - -I../Include -I../../AMD/Include \ - -I../../BTF/Include -I../../COLAMD/Include - -libklu_cholmod.a: library klu_cholmod.c klu_cholmod.h - $(CC) $(CF) $(I) -c klu_cholmod.c - $(ARCHIVE) libklu_cholmod.a klu_cholmod.o - - $(RANLIB) libklu_cholmod.a - -distclean: purge - -purge: clean - - $(RM) -r $(PURGE) - -clean: - - $(RM) -r $(CLEAN) - -library: - ( cd ../../AMD ; $(MAKE) library ) - ( cd ../../COLAMD ; $(MAKE) library ) - ( cd ../../CHOLMOD/Lib ; $(MAKE) ) - ( cd ../../SuiteSparse_config ; $(MAKE) ) diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/User/README.txt b/deps/AMICI/ThirdParty/SuiteSparse/KLU/User/README.txt deleted file mode 100644 index 04195b8e1..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/User/README.txt +++ /dev/null @@ -1,4 +0,0 @@ -This directory contains a sample user-ordering function, klu_cholmod. -Its use (and the use of CHOLMOD) is optional. - -Timothy A. Davis, http://www.suitesparse.com diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/User/klu_cholmod.c b/deps/AMICI/ThirdParty/SuiteSparse/KLU/User/klu_cholmod.c deleted file mode 100644 index c1c1872e4..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/User/klu_cholmod.c +++ /dev/null @@ -1,108 +0,0 @@ -/* ========================================================================== */ -/* === klu_cholmod ========================================================== */ -/* ========================================================================== */ - -/* klu_cholmod: user-defined ordering function to interface KLU to CHOLMOD. - * - * This routine is an example of a user-provided ordering function for KLU. - * Its return value is klu_cholmod's estimate of max (nnz(L),nnz(U)): - * 0 if error, - * -1 if OK, but estimate of max (nnz(L),nnz(U)) not computed - * > 0 if OK and estimate computed. - * - * This function can be assigned to KLU's Common->user_order function pointer. - */ - -#include "klu_cholmod.h" -#include "cholmod.h" -#define TRUE 1 -#define FALSE 0 - -int klu_cholmod -( - /* inputs */ - int n, /* A is n-by-n */ - int Ap [ ], /* column pointers */ - int Ai [ ], /* row indices */ - /* outputs */ - int Perm [ ], /* fill-reducing permutation */ - /* user-defined */ - klu_common *Common /* user-defined data is in Common->user_data */ -) -{ - double one [2] = {1,0}, zero [2] = {0,0}, lnz = 0 ; - cholmod_sparse Amatrix, *A, *AT, *S ; - cholmod_factor *L ; - cholmod_common cm ; - int *P ; - int k, symmetric ; - - if (Ap == NULL || Ai == NULL || Perm == NULL || n < 0) - { - /* invalid inputs */ - return (0) ; - } - - /* start CHOLMOD */ - cholmod_start (&cm) ; - cm.supernodal = CHOLMOD_SIMPLICIAL ; - cm.print = 0 ; - - /* construct a CHOLMOD version of the input matrix A */ - A = &Amatrix ; - A->nrow = n ; /* A is n-by-n */ - A->ncol = n ; - A->nzmax = Ap [n] ; /* with nzmax entries */ - A->packed = TRUE ; /* there is no A->nz array */ - A->stype = 0 ; /* A is unsymmetric */ - A->itype = CHOLMOD_INT ; - A->xtype = CHOLMOD_PATTERN ; - A->dtype = CHOLMOD_DOUBLE ; - A->nz = NULL ; - A->p = Ap ; /* column pointers */ - A->i = Ai ; /* row indices */ - A->x = NULL ; /* no numerical values */ - A->z = NULL ; - A->sorted = FALSE ; /* columns of A are not sorted */ - - /* get the user_data; default is symmetric if user_data is NULL */ - symmetric = (Common->user_data == NULL) ? TRUE : - (((int *) (Common->user_data)) [0] != 0) ; - - /* AT = pattern of A' */ - AT = cholmod_transpose (A, 0, &cm) ; - if (symmetric) - { - /* S = the symmetric pattern of A+A' */ - S = cholmod_add (A, AT, one, zero, FALSE, FALSE, &cm) ; - cholmod_free_sparse (&AT, &cm) ; - if (S != NULL) - { - S->stype = 1 ; - } - } - else - { - /* S = A'. CHOLMOD will order S*S', which is A'*A */ - S = AT ; - } - - /* order and analyze S or S*S' */ - L = cholmod_analyze (S, &cm) ; - - /* copy the permutation from L to the output */ - if (L != NULL) - { - P = L->Perm ; - for (k = 0 ; k < n ; k++) - { - Perm [k] = P [k] ; - } - lnz = cm.lnz ; - } - - cholmod_free_sparse (&S, &cm) ; - cholmod_free_factor (&L, &cm) ; - cholmod_finish (&cm) ; - return (lnz) ; -} diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/User/klu_cholmod.h b/deps/AMICI/ThirdParty/SuiteSparse/KLU/User/klu_cholmod.h deleted file mode 100644 index 8f532a23f..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/User/klu_cholmod.h +++ /dev/null @@ -1,7 +0,0 @@ -#include "klu.h" - -int klu_cholmod (int n, int Ap [ ], int Ai [ ], int Perm [ ], klu_common *) ; - -SuiteSparse_long klu_l_cholmod (SuiteSparse_long n, SuiteSparse_long Ap [ ], - SuiteSparse_long Ai [ ], SuiteSparse_long Perm [ ], klu_l_common *) ; - diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/User/klu_l_cholmod.c b/deps/AMICI/ThirdParty/SuiteSparse/KLU/User/klu_l_cholmod.c deleted file mode 100644 index 0307546bd..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/User/klu_l_cholmod.c +++ /dev/null @@ -1,108 +0,0 @@ -/* ========================================================================== */ -/* === klu_cholmod ========================================================== */ -/* ========================================================================== */ - -/* klu_l_cholmod: user-defined ordering function to interface KLU to CHOLMOD. - * - * This routine is an example of a user-provided ordering function for KLU. - * Its return value is klu_l_cholmod's estimate of max (nnz(L),nnz(U)): - * 0 if error, - * -1 if OK, but estimate of max (nnz(L),nnz(U)) not computed - * > 0 if OK and estimate computed. - * - * This function can be assigned to KLU's Common->user_order function pointer. - */ - -#include "klu_cholmod.h" -#include "cholmod.h" -#define TRUE 1 -#define FALSE 0 - -SuiteSparse_long klu_l_cholmod -( - /* inputs */ - SuiteSparse_long n, /* A is n-by-n */ - SuiteSparse_long Ap [ ], /* column pointers */ - SuiteSparse_long Ai [ ], /* row indices */ - /* outputs */ - SuiteSparse_long Perm [ ], /* fill-reducing permutation */ - /* user-defined */ - klu_l_common *Common /* user-defined data is in Common->user_data */ -) -{ - double one [2] = {1,0}, zero [2] = {0,0}, lnz = 0 ; - cholmod_sparse Amatrix, *A, *AT, *S ; - cholmod_factor *L ; - cholmod_common cm ; - SuiteSparse_long *P ; - SuiteSparse_long k, symmetric ; - - if (Ap == NULL || Ai == NULL || Perm == NULL || n < 0) - { - /* invalid inputs */ - return (0) ; - } - - /* start CHOLMOD */ - cholmod_l_start (&cm) ; - cm.supernodal = CHOLMOD_SIMPLICIAL ; - cm.print = 0 ; - - /* construct a CHOLMOD version of the input matrix A */ - A = &Amatrix ; - A->nrow = n ; /* A is n-by-n */ - A->ncol = n ; - A->nzmax = Ap [n] ; /* with nzmax entries */ - A->packed = TRUE ; /* there is no A->nz array */ - A->stype = 0 ; /* A is unsymmetric */ - A->itype = CHOLMOD_INT ; - A->xtype = CHOLMOD_PATTERN ; - A->dtype = CHOLMOD_DOUBLE ; - A->nz = NULL ; - A->p = Ap ; /* column pointers */ - A->i = Ai ; /* row indices */ - A->x = NULL ; /* no numerical values */ - A->z = NULL ; - A->sorted = FALSE ; /* columns of A are not sorted */ - - /* get the user_data; default is symmetric if user_data is NULL */ - symmetric = (Common->user_data == NULL) ? TRUE : - (((SuiteSparse_long *) (Common->user_data)) [0] != 0) ; - - /* AT = pattern of A' */ - AT = cholmod_l_transpose (A, 0, &cm) ; - if (symmetric) - { - /* S = the symmetric pattern of A+A' */ - S = cholmod_l_add (A, AT, one, zero, FALSE, FALSE, &cm) ; - cholmod_l_free_sparse (&AT, &cm) ; - if (S != NULL) - { - S->stype = 1 ; - } - } - else - { - /* S = A'. CHOLMOD will order S*S', which is A'*A */ - S = AT ; - } - - /* order and analyze S or S*S' */ - L = cholmod_l_analyze (S, &cm) ; - - /* copy the permutation from L to the output */ - if (L != NULL) - { - P = L->Perm ; - for (k = 0 ; k < n ; k++) - { - Perm [k] = P [k] ; - } - lnz = cm.lnz ; - } - - cholmod_l_free_sparse (&S, &cm) ; - cholmod_l_free_factor (&L, &cm) ; - cholmod_l_finish (&cm) ; - return (lnz) ; -} diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/build/.gitignore b/deps/AMICI/ThirdParty/SuiteSparse/KLU/build/.gitignore new file mode 100644 index 000000000..52e15321b --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/KLU/build/.gitignore @@ -0,0 +1,4 @@ +# Ignore all files except this file. +* +*/ +!.gitignore diff --git a/deps/AMICI/ThirdParty/SuiteSparse/KLU/cmake_modules/FindKLU.cmake b/deps/AMICI/ThirdParty/SuiteSparse/KLU/cmake_modules/FindKLU.cmake new file mode 100644 index 000000000..cd3f4040e --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/KLU/cmake_modules/FindKLU.cmake @@ -0,0 +1,129 @@ +#------------------------------------------------------------------------------- +# 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 new file mode 100644 index 000000000..d3b366322 --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/KLU/cmake_modules/FindKLU_CHOLMOD.cmake @@ -0,0 +1,137 @@ +#------------------------------------------------------------------------------- +# 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 445759392..0e51e9e0c 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/LICENSE.txt +++ b/deps/AMICI/ThirdParty/SuiteSparse/LICENSE.txt @@ -1,11 +1,57 @@ +This file lists all licenses for all packages in SuiteSparse, for your +convenience. Each package has its own separate license, which can be +found in the lists below. + +==> SPEX/License/license.txt <== + + SPEX: a Sparse Left-looking Integer-Preserving LU Factorization + + Copyright (c) 2019-2022, Christopher Lourenco, JinHao Chen, Erick Moreno- + Centeno, and Timothy A. Davis. + + Available at: + + https://github.com/clouren/SPEX + http://suitesparse.com + + Contact Chris Lourenco, chrisjlourenco@gmail.com, or Tim Davis + (timdavis@aldenmath.com or DrTimothyAldenDavis@gmail.com) for a commercial + license. + + -------------------------------------------------------------------------------- + + SPEX is free software; you can redistribute it and/or modify + it under the terms of either: + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at your + option) any later version. + + or + + * 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. + + or both in parallel, as here. + + SPEX 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. + + You should have received copies of the GNU General Public License and the + GNU Lesser General Public License along with this software. If not, + see https://www.gnu.org/licenses/. + ==> AMD/Doc/License.txt <== - AMD, Copyright (c), 1996-2015, Timothy A. Davis, + AMD, Copyright (c), 1996-2022, Timothy A. Davis, Patrick R. Amestoy, and Iain S. Duff. All Rights Reserved. Availability: - http://www.suitesparse.com + http://suitesparse.com ------------------------------------------------------------------------------- AMD License: BSD 3-clause: @@ -36,10 +82,10 @@ DAMAGE. ==> BTF/Doc/License.txt <== - BTF, Copyright (C) 2004-2013, University of Florida + BTF, Copyright (C) 2004-2022, University of Florida by Timothy A. Davis and Ekanathan Palamadai. BTF is also available under other licenses; contact authors for details. - http://www.suitesparse.com + http://suitesparse.com -------------------------------------------------------------------------------- @@ -92,15 +138,15 @@ Availability: - http://www.suitesparse.com + http://suitesparse.com ==> CCOLAMD/Doc/License.txt <== CCOLAMD: constrained column approximate minimum degree ordering - Copyright (C) 2005-2016, Univ. of Florida. Authors: Timothy A. Davis, + Copyright (C) 2005-2022, 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://www.suitesparse.com + http://suitesparse.com -------------------------------------------------------------------------------- @@ -137,9 +183,9 @@ ==> Check/License.txt <== -------------------------------------------------------------------------------- - CHOLMOD/Check Module. Copyright (C) 2005-2006, Timothy A. Davis CHOLMOD is + CHOLMOD/Check Module. Copyright (C) 2005-2022, Timothy A. Davis CHOLMOD is also available under other licenses; contact authors for details. - http://www.suitesparse.com + http://suitesparse.com Note that this license is for the CHOLMOD/Check module only. All CHOLMOD modules are licensed separately. @@ -164,9 +210,9 @@ ==> Cholesky/License.txt <== -------------------------------------------------------------------------------- - CHOLMOD/Cholesky module, Copyright (C) 2005-2006, Timothy A. Davis. + CHOLMOD/Cholesky module, Copyright (C) 2005-2022, Timothy A. Davis. CHOLMOD is also available under other licenses; contact authors for - details. http://www.suitesparse.com + details. http://suitesparse.com Note that this license is for the CHOLMOD/Cholesky module only. All CHOLMOD modules are licensed separately. @@ -192,9 +238,9 @@ ==> Core/License.txt <== -------------------------------------------------------------------------------- - CHOLMOD/Core Module. Copyright (C) 2005-2006, Univ. of Florida. Author: + 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://www.suitesparse.com + authors for details. http://suitesparse.com Note that this license is for the CHOLMOD/Core module only. All CHOLMOD modules are licensed separately. @@ -221,9 +267,9 @@ ==> Demo/License.txt <== -------------------------------------------------------------------------------- - CHOLMOD/Demo Module. Copyright (C) 2005-2006, Timothy A. Davis. CHOLMOD + CHOLMOD/Demo Module. Copyright (C) 2005-2022, Timothy A. Davis. CHOLMOD is also available under other licenses; contact authors for details. - http://www.suitesparse.com + http://suitesparse.com Note that this license is for the CHOLMOD/Demo module only. All CHOLMOD modules are licensed separately. @@ -250,14 +296,13 @@ ==> Include/License.txt <== -------------------------------------------------------------------------------- - CHOLMOD/Include/* files. Copyright (C) 2005-2006, either Univ. of Florida + 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_blas.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 @@ -279,10 +324,10 @@ ==> MATLAB/License.txt <== -------------------------------------------------------------------------------- - CHOLMOD/MATLAB Module. Copyright (C) 2005-2006, Timothy A. Davis. CHOLMOD + CHOLMOD/MATLAB Module. Copyright (C) 2005-2022, 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://www.suitesparse.com + http://suitesparse.com Note that this license is for the CHOLMOD/MATLAB module only. All CHOLMOD modules are licensed separately. @@ -308,9 +353,9 @@ ==> MatrixOps/License.txt <== -------------------------------------------------------------------------------- - CHOLMOD/MatrixOps Module. Copyright (C) 2005-2006, Timothy A. Davis. + CHOLMOD/MatrixOps Module. Copyright (C) 2005-2022, Timothy A. Davis. CHOLMOD is also available under other licenses; contact authors for - details. http://www.suitesparse.com + details. http://suitesparse.com Note that this license is for the CHOLMOD/MatrixOps module only. All CHOLMOD modules are licensed separately. @@ -337,9 +382,9 @@ ==> Modify/License.txt <== -------------------------------------------------------------------------------- - CHOLMOD/Modify Module. Copyright (C) 2005-2006, Timothy A. Davis and + CHOLMOD/Modify Module. Copyright (C) 2005-2022, Timothy A. Davis and William W. Hager. CHOLMOD is also available under other licenses; contact - authors for details. http://www.suitesparse.com + authors for details. http://suitesparse.com Note that this license is for the CHOLMOD/Modify module only. All CHOLMOD modules are licensed separately. @@ -367,9 +412,9 @@ -------------------------------------------------------------------------------- CHOLMOD/Partition Module. - Copyright (C) 2005-2006, Univ. of Florida. Author: Timothy A. Davis + Copyright (C) 2005-2022, Univ. of Florida. Author: Timothy A. Davis CHOLMOD is also available under other licenses; contact authors for details. - http://www.suitesparse.com + http://suitesparse.com Note that this license is for the CHOLMOD/Partition module only. All CHOLMOD modules are licensed separately. @@ -397,9 +442,9 @@ -------------------------------------------------------------------------------- CHOLMOD/Supernodal Module. - Copyright (C) 2005-2006, Timothy A. Davis + Copyright (C) 2005-2022, Timothy A. Davis CHOLMOD is also available under other licenses; contact authors for details. - http://www.suitesparse.com + http://suitesparse.com Note that this license is for the CHOLMOD/Supernodal module only. All CHOLMOD modules are licensed separately. @@ -426,9 +471,9 @@ ==> Tcov/License.txt <== -------------------------------------------------------------------------------- - CHOLMOD/Tcov Module. Copyright (C) 2005-2006, Timothy A. Davis + CHOLMOD/Tcov Module. Copyright (C) 2005-2022, Timothy A. Davis CHOLMOD is also available under other licenses; contact authors for details. - http://www.suitesparse.com + http://suitesparse.com Note that this license is for the CHOLMOD/Tcov module only. All CHOLMOD modules are licensed separately. @@ -455,9 +500,9 @@ ==> Valgrind/License.txt <== -------------------------------------------------------------------------------- - CHOLMOD/Valgrind Module. Copyright (C) 2005-2006, Timothy A. Davis. + CHOLMOD/Valgrind Module. Copyright (C) 2005-2022, Timothy A. Davis. CHOLMOD is also available under other licenses; contact authors for details. - http://www.suitesparse.com + http://suitesparse.com Note that this license is for the CHOLMOD/Valgrind module only. All CHOLMOD modules are licensed separately. @@ -481,8 +526,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. ==> COLAMD/Doc/License.txt <== - COLAMD, Copyright 1998-2016, Timothy A. Davis. http://www.suitesparse.com - http://www.suitesparse.com + COLAMD, Copyright 1998-2022, Timothy A. Davis. http://suitesparse.com + http://suitesparse.com COLAMD License: BSD 3-clause @@ -512,8 +557,8 @@ ==> CSparse/Doc/License.txt <== CSparse: a Concise Sparse matrix package. - Copyright (c) 2006, Timothy A. Davis. - http://www.suitesparse.com + Copyright (c) 2006-2022, Timothy A. Davis. + http://suitesparse.com -------------------------------------------------------------------------------- @@ -533,8 +578,8 @@ ==> CXSparse/Doc/License.txt <== CXSparse: a Concise Sparse matrix package - Extended. - Copyright (c) 2006, Timothy A. Davis. - http://www.suitesparse.com + Copyright (c) 2006-2022, Timothy A. Davis. + http://suitesparse.com -------------------------------------------------------------------------------- @@ -554,8 +599,8 @@ ==> CXSparse_newfiles/Doc/License.txt <== CXSparse: a Concise Sparse matrix package - Extended. - Copyright (c) 2006, Timothy A. Davis. - http://www.suitesparse.com + Copyright (c) 2006-2022, Timothy A. Davis. + http://suitesparse.com -------------------------------------------------------------------------------- @@ -574,9 +619,9 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ==> GPUQREngine/Doc/License.txt <== - GPUQREngine Copyright (c) 2013, Timothy A. Davis, Sencer Nuri Yeralan, + GPUQREngine Copyright (c) 2013-2022, Timothy A. Davis, Sencer Nuri Yeralan, and Sanjay Ranka. - http://www.suitesparse.com + http://suitesparse.com 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 @@ -593,10 +638,10 @@ ==> KLU/Doc/License.txt <== - KLU, Copyright (C) 2004-2013, University of Florida + KLU, Copyright (C) 2004-2022, University of Florida by Timothy A. Davis and Ekanathan Palamadai. KLU is also available under other licenses; contact authors for details. - http://www.suitesparse.com + http://suitesparse.com -------------------------------------------------------------------------------- @@ -615,9 +660,9 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ==> LDL/Doc/License.txt <== - LDL Copyright (c) 2005-2013 by Timothy A. Davis. + LDL Copyright (c) 2005-2022 by Timothy A. Davis. LDL is also available under other licenses; contact the author for details. - http://www.suitesparse.com + http://suitesparse.com -------------------------------------------------------------------------------- @@ -694,8 +739,8 @@ SSMULT License: -------------------------------------------------------------------------------- - SSMULT, Copyright (c) 2007-2011, Timothy A. Davis, - http://www.suitesparse.com. + SSMULT, Copyright (c) 2007-2022, Timothy A. Davis, + http://suitesparse.com. SSMULT 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 @@ -713,9 +758,9 @@ ==> RBio/Doc/License.txt <== - RBio toolbox. Copyright (C) 2006-2009, Timothy A. Davis + RBio toolbox. Copyright (C) 2006-2022, Timothy A. Davis RBio is also available under other licenses; contact authors for details. - http://www.suitesparse.com + http://suitesparse.com -------------------------------------------------------------------------------- @@ -735,7 +780,7 @@ ==> SPQR/Doc/License.txt <== - SPQR, Copyright 2008-2016 by Timothy A. Davis. + SPQR, Copyright 2008-2022 by Timothy A. Davis. All Rights Reserved. SPQR is available under alternate licenses, contact T. Davis for details. @@ -771,12 +816,12 @@ Availability: - http://www.suitesparse.com + http://suitesparse.com ==> SuiteSparse_GPURuntime/Doc/License.txt <== - SuiteSparse_GPURuntime Copyright (c) 2013-2016, Timothy A. Davis, - Sencer Nuri Yeralan, and Sanjay Ranka. http://www.suitesparse.com + SuiteSparse_GPURuntime Copyright (c) 2013-2022, Timothy A. Davis, + Sencer Nuri Yeralan, and Sanjay Ranka. http://suitesparse.com -------------------------------------------------------------------------------- @@ -795,7 +840,7 @@ Street, Fifth Floor, Boston, MA 02110-1301, USA. ==> ssget/Doc/License.txt <== - Copyright (c), 2009-2016, Timothy A. Davis, All Rights Reserved. + Copyright (c), 2009-2022, 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: @@ -823,7 +868,7 @@ ==> UMFPACK/Doc/License.txt <== - UMFPACK, Copyright 1995-2009 by Timothy A. Davis. + UMFPACK, Copyright 1995-2022 by Timothy A. Davis. All Rights Reserved. UMFPACK is available under alternate licenses, contact T. Davis for details. @@ -859,11 +904,11 @@ Availability: - http://www.suitesparse.com + http://suitesparse.com ==> CSparse/MATLAB/ssget/Doc/License.txt <== - Copyright (c), 2009-2016, Timothy A. Davis, All Rights Reserved. + Copyright (c), 2009-2022, 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: @@ -891,7 +936,7 @@ ==> CXSparse/MATLAB/ssget/Doc/License.txt <== - Copyright (c), 2009-2016, Timothy A. Davis, All Rights Reserved. + Copyright (c), 2009-2022, 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: @@ -917,24 +962,77 @@ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -==> GraphBLAS/Doc/License.txt <== - SuiteSparse:GraphBLAS, Copyright 2017, Timothy A. Davis +==> GraphBLAS/LICENSE <== + + SuiteSparse:GraphBLAS, Timothy A. Davis, (c) 2017-2021, All Rights Reserved. + The following Apache-2.0 applies to all of SuiteSparse:GraphBLAS except for the + @GrB MATLAB interface: + + SPDX-License-Identifier: Apache-2.0 + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use SuiteSparse:GraphBLAS except in compliance with the + License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + No files that are compiled and packaged as part of the libgraphblas.so or + libgraphblas.a libraries are licensed under the GNU GPLv3. All of those files + are licensed under the Apache-2.0 license. + + ================================================================================ + + The @GrB MATLAB interface, including its test suite and demos (all files in the + GraphBLAS/GraphBLAS folder) are not under the above Apache-2.0 license, but + under the GNU GPLv3 (or later) license instead. Refer to the file + GraphBLAS/GraphBLAS/@GrB/LICENSE for details. + + All files throughout this package and the @GrB MATLAB interface are tagged + with an SPDX license identifier, file by file. Either: - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use SuiteSparse:GraphBLAS except in compliance with the - License. You may obtain a copy of the License at + SPDX-License-Identifier: Apache-2.0 - http://www.apache.org/licenses/LICENSE-2.0 + or, for the @GrB MATLAB interface, its test suite, and demos: - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. + SPDX-License-Identifier: GPL-3.0-or-later ==> Mongoose License <== - Mongoose, Copyright 2018, Timothy A. Davis, Scott P. Kolodziej, + Mongoose, Copyright 2018-2022, 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. +SPDX-License-Identifier: BSD-3-clause + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * 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. + * Neither the name of the organizations to which the authors are + affiliated, nor the names of its contributors may be used to endorse + or promote products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + DAMAGE. diff --git a/deps/AMICI/ThirdParty/SuiteSparse/Makefile b/deps/AMICI/ThirdParty/SuiteSparse/Makefile deleted file mode 100644 index 7d7d12d66..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/Makefile +++ /dev/null @@ -1,287 +0,0 @@ -#------------------------------------------------------------------------------- -# Makefile for all SuiteSparse packages -#------------------------------------------------------------------------------- - -SUITESPARSE = $(CURDIR) -export SUITESPARSE - -default: go - -include SuiteSparse_config/SuiteSparse_config.mk - -# Compile the default rules for each package -go: metis - ( cd SuiteSparse_config && $(MAKE) ) - ( cd GraphBLAS && $(MAKE) CMAKE_OPTIONS='$(CMAKE_OPTIONS)' ) - ( cd Mongoose && $(MAKE) CMAKE_OPTIONS='$(CMAKE_OPTIONS)' ) - ( cd AMD && $(MAKE) ) - ( cd BTF && $(MAKE) ) - ( cd CAMD && $(MAKE) ) - ( cd CCOLAMD && $(MAKE) ) - ( cd COLAMD && $(MAKE) ) - ( cd CHOLMOD && $(MAKE) ) - ( cd CSparse && $(MAKE) ) - ( cd CXSparse && $(MAKE) ) - ( cd LDL && $(MAKE) ) - ( cd KLU && $(MAKE) ) - ( cd UMFPACK && $(MAKE) ) - ( cd RBio && $(MAKE) ) -ifneq ($(GPU_CONFIG),) - ( cd SuiteSparse_GPURuntime && $(MAKE) ) - ( cd GPUQREngine && $(MAKE) ) -endif - ( cd SPQR && $(MAKE) ) -# ( cd PIRO_BAND && $(MAKE) ) -# ( cd SKYLINE_SVD && $(MAKE) ) - -# install all packages in /usr/local/lib and /usr/local/include -# (note that CSparse is not installed; CXSparse is installed instead) -install: metisinstall - ( cd SuiteSparse_config && $(MAKE) install ) - ( cd GraphBLAS && $(MAKE) CMAKE_OPTIONS='$(CMAKE_OPTIONS)' install ) - ( cd Mongoose && $(MAKE) CMAKE_OPTIONS='$(CMAKE_OPTIONS)' install ) - ( cd AMD && $(MAKE) install ) - ( cd BTF && $(MAKE) install ) - ( cd CAMD && $(MAKE) install ) - ( cd CCOLAMD && $(MAKE) install ) - ( cd COLAMD && $(MAKE) install ) - ( cd CHOLMOD && $(MAKE) install ) - ( cd CXSparse && $(MAKE) install ) - ( cd LDL && $(MAKE) install ) - ( cd KLU && $(MAKE) install ) - ( cd UMFPACK && $(MAKE) install ) - ( cd RBio && $(MAKE) install ) -ifneq (,$(GPU_CONFIG)) - ( cd SuiteSparse_GPURuntime && $(MAKE) install ) - ( cd GPUQREngine && $(MAKE) install ) -endif - ( cd SPQR && $(MAKE) install ) -# ( cd PIRO_BAND && $(MAKE) install ) -# ( cd SKYLINE_SVD && $(MAKE) install ) - $(CP) README.txt $(INSTALL_DOC)/SuiteSparse_README.txt - chmod 644 $(INSTALL_DOC)/SuiteSparse_README.txt - -metisinstall: metis -ifeq (,$(MY_METIS_LIB)) - # install METIS from SuiteSparse/metis-5.1.0 - @mkdir -p $(INSTALL_LIB) - @mkdir -p $(INSTALL_INCLUDE) - @mkdir -p $(INSTALL_DOC) - - $(CP) lib/libmetis.* $(INSTALL_LIB) - - $(CP) metis-5.1.0/manual/manual.pdf $(INSTALL_DOC)/METIS_manual.pdf - - $(CP) metis-5.1.0/README.txt $(INSTALL_DOC)/METIS_README.txt - # the following is needed only on the Mac, so *.dylib is hardcoded: - $(SO_INSTALL_NAME) $(INSTALL_LIB)/libmetis.dylib $(INSTALL_LIB)/libmetis.dylib - - $(CP) include/metis.h $(INSTALL_INCLUDE) - chmod 755 $(INSTALL_LIB)/libmetis.* - chmod 644 $(INSTALL_INCLUDE)/metis.h - chmod 644 $(INSTALL_DOC)/METIS_manual.pdf - chmod 644 $(INSTALL_DOC)/METIS_README.txt -endif - -# uninstall all packages -uninstall: - $(RM) $(INSTALL_DOC)/SuiteSparse_README.txt - ( cd SuiteSparse_config && $(MAKE) uninstall ) - - ( cd metis-5.1.0 && $(MAKE) uninstall ) - - ( cd GraphBLAS && $(MAKE) uninstall ) - - ( cd Mongoose && $(MAKE) uninstall ) - ( cd AMD && $(MAKE) uninstall ) - ( cd CAMD && $(MAKE) uninstall ) - ( cd COLAMD && $(MAKE) uninstall ) - ( cd BTF && $(MAKE) uninstall ) - ( cd KLU && $(MAKE) uninstall ) - ( cd LDL && $(MAKE) uninstall ) - ( cd CCOLAMD && $(MAKE) uninstall ) - ( cd UMFPACK && $(MAKE) uninstall ) - ( cd CHOLMOD && $(MAKE) uninstall ) - ( cd CSparse && $(MAKE) uninstall ) - ( cd CXSparse && $(MAKE) uninstall ) - ( cd RBio && $(MAKE) uninstall ) - ( cd SuiteSparse_GPURuntime && $(MAKE) uninstall ) - ( cd GPUQREngine && $(MAKE) uninstall ) - ( cd SPQR && $(MAKE) uninstall ) -# ( cd PIRO_BAND && $(MAKE) uninstall ) -# ( cd SKYLINE_SVD && $(MAKE) uninstall ) -ifeq (,$(MY_METIS_LIB)) - # uninstall METIS, which came from SuiteSparse/metis-5.1.0 - $(RM) $(INSTALL_LIB)/libmetis.* - $(RM) $(INSTALL_INCLUDE)/metis.h - $(RM) $(INSTALL_DOC)/METIS_manual.pdf - $(RM) $(INSTALL_DOC)/METIS_README.txt -endif - $(RM) -r $(INSTALL_DOC) - -# compile the dynamic libraries. For GraphBLAS and Mongoose, this also builds -# the static library -library: metis - ( cd SuiteSparse_config && $(MAKE) ) - ( cd GraphBLAS && $(MAKE) CMAKE_OPTIONS='$(CMAKE_OPTIONS)' library ) - ( cd Mongoose && $(MAKE) CMAKE_OPTIONS='$(CMAKE_OPTIONS)' library ) - ( cd AMD && $(MAKE) library ) - ( cd BTF && $(MAKE) library ) - ( cd CAMD && $(MAKE) library ) - ( cd CCOLAMD && $(MAKE) library ) - ( cd COLAMD && $(MAKE) library ) - ( cd CHOLMOD && $(MAKE) library ) - ( cd KLU && $(MAKE) library ) - ( cd LDL && $(MAKE) library ) - ( cd UMFPACK && $(MAKE) library ) - ( cd CSparse && $(MAKE) library ) - ( cd CXSparse && $(MAKE) library ) - ( cd RBio && $(MAKE) library ) -ifneq (,$(GPU_CONFIG)) - ( cd SuiteSparse_GPURuntime && $(MAKE) library ) - ( cd GPUQREngine && $(MAKE) library ) -endif - ( cd SPQR && $(MAKE) library ) -# ( cd PIRO_BAND && $(MAKE) library ) -# ( cd SKYLINE_SVD && $(MAKE) library ) - -# compile the static libraries (except for metis, GraphBLAS, and Mongoose). -# metis is only dynamic, and the 'make static' for GraphBLAS and Mongoose makes -# both the dynamic and static libraries. -static: metis - ( cd SuiteSparse_config && $(MAKE) static ) - ( cd GraphBLAS && $(MAKE) CMAKE_OPTIONS='$(CMAKE_OPTIONS)' static ) - ( cd Mongoose && $(MAKE) CMAKE_OPTIONS='$(CMAKE_OPTIONS)' static ) - ( cd AMD && $(MAKE) static ) - ( cd BTF && $(MAKE) static ) - ( cd CAMD && $(MAKE) static ) - ( cd CCOLAMD && $(MAKE) static ) - ( cd COLAMD && $(MAKE) static ) - ( cd CHOLMOD && $(MAKE) static ) - ( cd KLU && $(MAKE) static ) - ( cd LDL && $(MAKE) static ) - ( cd UMFPACK && $(MAKE) static ) - ( cd CSparse && $(MAKE) static ) - ( cd CXSparse && $(MAKE) static ) - ( cd RBio && $(MAKE) static ) -ifneq (,$(GPU_CONFIG)) - ( cd SuiteSparse_GPURuntime && $(MAKE) static ) - ( cd GPUQREngine && $(MAKE) static ) -endif - ( cd SPQR && $(MAKE) static ) -# ( cd PIRO_BAND && $(MAKE) static ) -# ( cd SKYLINE_SVD && $(MAKE) static ) - -# Remove all files not in the original distribution -purge: - - ( cd SuiteSparse_config && $(MAKE) purge ) - - ( cd metis-5.1.0 && $(MAKE) distclean ) - - ( cd AMD && $(MAKE) purge ) - - ( cd GraphBLAS && $(MAKE) purge ) - - ( cd Mongoose && $(MAKE) purge ) - - ( cd CAMD && $(MAKE) purge ) - - ( cd COLAMD && $(MAKE) purge ) - - ( cd BTF && $(MAKE) purge ) - - ( cd KLU && $(MAKE) purge ) - - ( cd LDL && $(MAKE) purge ) - - ( cd CCOLAMD && $(MAKE) purge ) - - ( cd UMFPACK && $(MAKE) purge ) - - ( cd CHOLMOD && $(MAKE) purge ) - - ( cd CSparse && $(MAKE) purge ) - - ( cd CXSparse && $(MAKE) purge ) - - ( cd RBio && $(MAKE) purge ) - - ( cd MATLAB_Tools/SuiteSparseCollection && $(RM) *.mex* ) - - ( cd MATLAB_Tools/SSMULT && $(RM) *.mex* ) - - ( cd SuiteSparse_GPURuntime && $(MAKE) purge ) - - ( cd GPUQREngine && $(MAKE) purge ) - - ( cd SPQR && $(MAKE) purge ) -# - ( cd PIRO_BAND && $(MAKE) purge ) -# - ( cd SKYLINE_SVD && $(MAKE) purge ) - - $(RM) MATLAB_Tools/*/*.mex* MATLAB_Tools/spok/private/*.mex* - - $(RM) -r include/* bin/* lib/* share/* - -# Remove all files not in the original distribution, but keep the libraries -clean: - - ( cd SuiteSparse_config && $(MAKE) clean ) - - ( cd metis-5.1.0 && $(MAKE) clean ) - - ( cd GraphBLAS && $(MAKE) clean ) - - ( cd Mongoose && $(MAKE) clean ) - - ( cd AMD && $(MAKE) clean ) - - ( cd CAMD && $(MAKE) clean ) - - ( cd COLAMD && $(MAKE) clean ) - - ( cd BTF && $(MAKE) clean ) - - ( cd KLU && $(MAKE) clean ) - - ( cd LDL && $(MAKE) clean ) - - ( cd CCOLAMD && $(MAKE) clean ) - - ( cd UMFPACK && $(MAKE) clean ) - - ( cd CHOLMOD && $(MAKE) clean ) - - ( cd CSparse && $(MAKE) clean ) - - ( cd CXSparse && $(MAKE) clean ) - - ( cd RBio && $(MAKE) clean ) - - ( cd SuiteSparse_GPURuntime && $(MAKE) clean ) - - ( cd GPUQREngine && $(MAKE) clean ) - - ( cd SPQR && $(MAKE) clean ) -# - ( cd PIRO_BAND && $(MAKE) clean ) -# - ( cd SKYLINE_SVD && $(MAKE) clean ) - -# Create the PDF documentation -docs: - ( cd GraphBLAS && $(MAKE) docs ) - ( cd Mongoose && $(MAKE) docs ) - ( cd AMD && $(MAKE) docs ) - ( cd CAMD && $(MAKE) docs ) - ( cd KLU && $(MAKE) docs ) - ( cd LDL && $(MAKE) docs ) - ( cd UMFPACK && $(MAKE) docs ) - ( cd CHOLMOD && $(MAKE) docs ) - ( cd SPQR && $(MAKE) docs ) -# ( cd PIRO_BAND && $(MAKE) docs ) -# ( cd SKYLINE_SVD && $(MAKE) docs ) - -distclean: purge - -# Create CXSparse from CSparse -# Note that the CXSparse directory should initially not exist. -cx: - ( cd CSparse ; $(MAKE) purge ) - ( cd SuiteSparse_config && $(MAKE) ) - ( cd CXSparse_newfiles ; tar cfv - * | gzip -9 > ../CXSparse_newfiles.tar.gz ) - ./CSparse_to_CXSparse CSparse CXSparse CXSparse_newfiles.tar.gz - ( cd CXSparse/Demo ; $(MAKE) ) - ( cd CXSparse/Demo ; $(MAKE) > cs_demo.out ) - ( cd CXSparse ; $(MAKE) purge ) - $(RM) -f CXSparse_newfiles.tar.gz - -# statement coverage (Linux only); this requires a lot of time. -# The umfpack tcov requires a lot of disk space in /tmp -cov: purge - ( cd CXSparse && $(MAKE) cov ) - ( cd CSparse && $(MAKE) cov ) - ( cd CHOLMOD && $(MAKE) cov ) - ( cd KLU && $(MAKE) cov ) - ( cd SPQR && $(MAKE) cov ) - ( cd UMFPACK && $(MAKE) cov ) -# ( cd PIRO_BAND && $(MAKE) cov ) -# ( cd SKYLINE_SVD && $(MAKE) cov ) - -# configure and compile METIS, placing the libmetis.* library in -# SuiteSparse/lib and the metis.h include file in SuiteSparse/include. -metis: include/metis.h - -# Install the shared version of METIS in SuiteSparse/lib. -# The SO_INSTALL_NAME commmand is only needed on the Mac, so *.dylib is -# hardcoded below. -include/metis.h: -ifeq (,$(MY_METIS_LIB)) - - ( cd metis-5.1.0 && $(MAKE) config shared=1 prefix=$(SUITESPARSE) cc=$(CC) ) - - ( cd metis-5.1.0 && $(MAKE) ) - - ( cd metis-5.1.0 && $(MAKE) install ) - - $(SO_INSTALL_NAME) $(SUITESPARSE)/lib/libmetis.dylib \ - $(SUITESPARSE)/lib/libmetis.dylib -else - @echo 'Using pre-installed METIS 5.1.0 library at ' '[$(MY_METIS_LIB)]' -endif - -# just compile GraphBLAS -gb: - echo $(CMAKE_OPTIONS) - ( cd GraphBLAS && $(MAKE) CMAKE_OPTIONS='$(CMAKE_OPTIONS)' ) - -# just install GraphBLAS -gbinstall: - echo $(CMAKE_OPTIONS) - ( cd GraphBLAS && $(MAKE) CMAKE_OPTIONS='$(CMAKE_OPTIONS)' install ) - diff --git a/deps/AMICI/ThirdParty/SuiteSparse/README.md b/deps/AMICI/ThirdParty/SuiteSparse/README.md new file mode 100644 index 000000000..6771fa1d8 --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/README.md @@ -0,0 +1,682 @@ +----------------------------------------------------------------------------- +SuiteSparse: A Suite of Sparse matrix packages at http://suitesparse.com +----------------------------------------------------------------------------- + +Jan 20, 2023, SuiteSparse VERSION 7.0.1 + +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. + +METIS is authored by George Karypis. + +Additional algorithm designers: Esmond Ng and John Gilbert. + +Refer to each package for license, copyright, and author information. + +----------------------------------------------------------------------------- +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. + +----------------------------------------------------------------------------- +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 + 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 GraphBLAS, and `C=A*B` 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. 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: + + 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 (SPQR): (also cite AMD, COLAMD): + + 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 SuiteSparseQR/GPU: + + 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 + + * for CHOLMOD: (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 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/Modify Module: (also cite AMD, COLAMD): + + 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 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 + + 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 CHOLMOD/GPU Modules: + + 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 + + * for AMD and CAMD: + + 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 + + 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 COLAMD, SYMAMD, CCOLAMD, and CSYMAMD: + + 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, 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 UMFPACK: (also cite AMD and COLAMD): + + 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, 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 + + 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 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 the FACTORIZE m-file: + + 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 KLU and BTF (also cite AMD and COLAMD): + + 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 LDL: + + 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 + + * for ssget and the SuiteSparse Matrix Collection: + + 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 + + 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 `spqr_rank`: + + 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 Mongoose: + + 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. + +To select your BLAS/LAPACK, see the instructions in SuiteSparseBLAS.cmake in +`SuiteSparse_config/cmake_modules`. If `SuiteSparse_config` finds a BLAS with +64-bit integers (such as the Intel MKL ilp64 BLAS), it configures +`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). + +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`. + +By default, SuiteSparse hunts for a suitable BLAS library. To enforce a +particular BLAS library use either: + + CMAKE_OPTIONS="-DBLA_VENDOR=OpenBLAS" make + 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. + +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. + +------------------ +SuiteSparse/README +------------------ + +Packages in SuiteSparse, and files in this directory: + + 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. + +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. + +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. + +----------------------------------------------------------------------------- +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: + + 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: + + DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH:/Users/me/SuiteSparse/lib + export DYLD_LIBRARY_PATH + +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 +your own SuiteSparse/lib. + +Then in the MATLAB Command Window, cd to the SuiteSparse directory and type +`SuiteSparse_install`. All packages will be compiled, and several demos will be +run. To run a (long!) exhaustive test, do `SuiteSparse_test`. + +Save your MATLAB path for future sessions with the MATLAB pathtool or savepath +commands. If those methods fail because you don't have system-wide permission, +add the new paths to your startup.m file, normally in +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: +----------------------------------------------------------------------------- + +For Linux and Mac: type the following in this directory (requires system +priviledge to do the `sudo make install`): + + make + sudo make install + +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. + +For Windows, import each `*/CMakeLists.txt` file into MS Visual Studio. + +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). + +To compile the libraries and install them only in SuiteSparse/lib (not +/usr/local/lib), do this instead in the top-level of SuiteSparse: + + make local + +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 -lumfpack -lamd -lcholmod -lsuitesparseconfig -lm + +To change the C and C++ compilers, and to compile in parallel use: + + CC=gcc CX=g++ JOBS=32 make + +for example, which changes the compiler to gcc and g++, and runs make with +'make -j32', in parallel with 32 jobs. + +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. + +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. + +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 + +For the Mac, use this instead: + + DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH:/home/me/SuiteSparse/lib + export DYLD_LIBRARY_PATH + +----------------------------------------------------------------------------- +Python interface +----------------------------------------------------------------------------- + +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 + +----------------------------------------------------------------------------- +Compilation options +----------------------------------------------------------------------------- + +You can set specific options for CMake with the command (for example): + + CMAKE_OPTIONS="-DNPARTITION=1 -DNSTATIC=1 -DCMAKE_BUILD_TYPE=Debug" make + +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 + +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. + +----------------------------------------------------------------------------- +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 +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. + +See also the various Acknowledgements within each package. + diff --git a/deps/AMICI/ThirdParty/SuiteSparse/README.txt b/deps/AMICI/ThirdParty/SuiteSparse/README.txt deleted file mode 100644 index 6678053b3..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/README.txt +++ /dev/null @@ -1,412 +0,0 @@ -SuiteSparse: A Suite of Sparse matrix packages at http://www.suitesparse.com - -Dec 20, 2018. SuiteSparse VERSION 5.4.0 - -Now includes GraphBLAS and a new interface to the SuiteSparse Matrix -Collection (ssget), via MATLAB and a Java GUI, to http://sparse.tamu.edu. - -Primary author of SuiteSparse (codes and algorithms, excl. METIS): Tim Davis - -Code co-authors, in alphabetical order (not including METIS): - - Patrick Amestoy, David Bateman, Yanqing Chen, Iain Duff, Les Foster, - William Hager, Scott Kolodziej, Stefan Larimore, Ekanathan Palamadai, - Sivasankaran Rajamanickam, Sanjay Ranka, Wissam Sid-Lakhdar, Nuri Yeralan. - -Additional algorithm designers: Esmond Ng and John Gilbert. - -Refer to each package for license, copyright, and author information. All -codes are authored or co-authored by Timothy A. Davis. - ------------------- -SuiteSparse/README ------------------- - -================================================================================ -Packages in SuiteSparse, and files in this directory: -================================================================================ - - GraphBLAS graph algorithms in the language of linear algebra. - https://graphblas.org - A stand-alone package that uses cmake to compile; see - GraphBLAS/README.txt. The rest of SuiteSparse still uses - 'make'. A cmake setup for all of SuiteSparse is in progress. - author: Tim Davis - - AMD approximate minimum degree ordering. This is the built-in AMD - function in MATLAB. - authors: Tim Davis, Patrick Amestoy, Iain Duff - - bin where the metis-5.1.0 programs are placed when METIS is 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. - It does have a 'make install' but I recommend using - 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 - - CSparse_to_CXSparse - a Perl script to create CXSparse from CSparse and - CXSparse_newfiles - author: David Bateman, Motorola - - 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 - - CXSparse_newfiles - Files unique to CXSparse - author: Tim Davis, David Bateman - - share 'make' places documentation for each package here - - GPUQREngine GPU support package for SPQR (not built into MATLAB, however) - authors: Tim Davis, Nuri Yeralan, Sanjay Ranka, - Wissam Sid-Lakhdar - - include 'make' places user-visible include fomes for each package here - - 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' places shared libraries for each package here - - Makefile to compile all of SuiteSparse (except GraphBLAS) - make compiles SuiteSparse libraries and runs demos - make install compiles SuiteSparse and installs in /usr/local - make uninstall undoes 'make install' - make library compiles SuiteSparse libraries (not demos) - 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. - make config displays parameter settings; does not compile - - Each individual package also has each of the above 'make' - targets. Doing 'make config' in each package */Lib directory - displays the exact shared and static library names. - - Things you don't need to do: - make cx creates CXSparse from CSparse - make docs creates user guides from LaTeX files - make cov runs statement coverage tests (Linux only) - make metis compiles METIS (also done by 'make') - - 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 maitains 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 - - metis-5.1.0 a modified version of METIS. See the README.txt files for - details. - author: George Karypis; not an integral component of - SuiteSparse, however. This is just a copy included with - SuiteSparse via the open-source license provided by - George Karypis - - 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. - author of the CPU code: Tim Davis - author of GPU modules: Tim Davis, Nuri Yeralan, - Wissam Sid-Lakhdar, Sanjay Ranka - - SuiteSparse_config configuration file for all the above packages. The - SuiteSparse_config/SuiteSparse_config.mk is included in the - Makefile's of all packages. CSparse and MATLAB_Tools do not - use SuiteSparse_config. - author: Tim Davis - - SuiteSparse_GPURuntime GPU support package for SPQR - (not builtin to MATLAB, however). - - SuiteSparse_install.m install SuiteSparse for MATLAB - - SuiteSparse_test.m exhaustive test for SuiteSparse in MATLAB - - ssget MATLAB interface to the SuiteSparse Matrix Collection - (formerly called the UF Sparse Matrix Collection). - Includes a UFget function for backward compatibility. - 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 metis-5.1.0 directory. Its use is optional, so you can remove it before -compiling SuiteSparse, if you desire. The use of METIS will improve the -ordering quality. METIS has been slightly modified for use in SuiteSparse; see -the metis-5.1.0/README.txt file for details. SuiteSparse can use the -unmodified METIS 5.1.0, however. To use your own copy of METIS, or a -pre-installed copy of METIS use 'make MY_METIS_LIB=-lmymetis' or -'make MY_METIS_LIB=/my/stuff/metis-5.1.0/whereeveritis/libmetis.so - MY_METIS_INC=/my/stuff/metis-5.1.0/include'. -If you want to use METIS in MATLAB, however, you MUST use the version provided -here, in SuiteSparse/metis-5.1.0. The MATLAB interface to METIS required some -small changes in METIS itself to get it to work. The original METIS 5.1.0 -will segfault MATLAB. - -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 - -Licenses for each package are located in the following files, all in -PACKAGENAME/Doc/License.txt: - - AMD/Doc/License.txt - BTF/Doc/License.txt - CAMD/Doc/License.txt - CCOLAMD/Doc/License.txt - CHOLMOD/Doc/License.txt - COLAMD/Doc/License.txt - CSparse/Doc/License.txt - CXSparse/Doc/License.txt - GPUQREngine/Doc/License.txt - KLU/Doc/License.txt - LDL/Doc/License.txt - MATLAB_Tools/Doc/License.txt - Mongoose/Doc/License.txt - RBio/Doc/License.txt - SPQR/Doc/License.txt - SuiteSparse_GPURuntime/Doc/License.txt - ssget/Doc/License.txt - UMFPACK/Doc/License.txt - GraphBLAS/Doc/License.txt - -These files are also present, but they are simply copies of the above license -files for CXSparse and ssget: - - CXSparse_newfiles/Doc/License.txt - CSparse/MATLAB/ssget/Doc/License.txt - CXSparse/MATLAB/ssget/Doc/License.txt - -METIS 5.0.1 is distributed with SuiteSparse, and is Copyright (c) -by George Karypis. Please refer to that package for its License. - -================================================================================ -QUICK START FOR MATLAB USERS (Linux, Mac, or Windows): uncompress the -SuiteSparse.zip or SuiteSparse.tar.gz archive file (they contain the same -thing), then in the MATLAB Command Window, cd to the SuiteSparse directory and -type SuiteSparse_install. All packages will be compiled, and several demos -will be run. To run a (long!) exhaustive test, do SuiteSparse_test. -================================================================================ - - -================================================================================ -QUICK START FOR THE C/C++ LIBRARIES: - -For just GraphBLAS, do this: - - cd GraphBLAS/build ; cmake .. ; make ; cd ../Demo ; ./demo - cd ../build ; sudo make install - -For all other packages, type 'make' in this directory. All libraries will be -created and copied into SuiteSparse/lib. All include files need by the -applications that use SuiteSparse are copied into SuiteSparse/include. All -user documenation is copied into SuiteSparse/share/doc. - -When compiling the libraries, do NOT use the INSTALL=... options for -installing. Just do: - - make - -or to compile just the libraries without running the demos, do: - - make library - -Any program that uses SuiteSparse can thus use a simpler rule as compared to -earlier versions of SuiteSparse. If you add /home/myself/SuiteSparse/lib to -your library search patch, you can do the following (for example): - - S = /home/myself/SuiteSparse - cc myprogram.c -I$(S)/include -lumfpack -lamd -lcholmod -lsuitesparseconfig -lm - -To change the C and C++ compilers, and to compile in parallel use: - - AUTOCC=no CC=gcc CX=g++ JOBS=32 make - -for example, which changes the compiler to gcc and g++, and runs make with -'make -j32', in parallel with 32 jobs. - -Now you can install the libraries, if you wish, in a location other than -SuiteSparse/lib, SuiteSparse/include, and SuiteSparse/share/doc, using -'make install INSTALL=...' - -Do 'make install' if you want to install the libraries and include files in -SuiteSparse/lib and SuiteSparse/include, and the documentation in -SuiteSparse/doc/suitesparse-VERSION. -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. If this -fails, see the SuiteSparse_config/SuiteSparse_config.mk file. There are many -options that you can either list on the 'make' command line, or you can just -edit that file. For example, to compile with your own BLAS: - - make BLAS=-lmyblaslibraryhere - -To list all configuration options (but not compile anything), do: - - make config - -Any parameter you see in the output of 'make config' with an equal sign -can be modified at the 'make' command line. - -If you do "make install" by itself, then the packages are all installed in -SuiteSparse/lib (libraries), SuiteSparse/include (include *.h files), and -SuiteSparse/doc/suitesparse-VERSION (documentation). If you want to install -elsewhere, do: - - make install INSTALL=/my/path - -which puts the files in /my/path/lib, /my/path/include, and /my/path/doc. -If you want to selectively put the libraries, include files, and doc files -in different locations, do: - - make install INSTALL_LIB=/my/libs INSTALL_INCLUDE=/myotherstuff/include INSTALL_DOC=/mydocs - -for example. Any term not defined will be set to its default, so if you don't -want to install the documentation, but wish to install the libraries and -includes in /usr/local/lib and /usr/local/include, do: - - make install INSTALL_DOC=/tmp/doc - -which copies the documentation to /tmp/doc where you can then remove it later. - -Both the static (*.a) and shared (*.so) libraries are compiled. The *.a -libraries are left in the package Lib folder (AMD/Lib/libamd.a for example). -The main exception to this rule is the SuiteSparse_config library, which is in -SuiteSparse/libsuiteSparseconfig.a. SuiteSparse_config is required by all -packages. The (extremely) optional xerbla library is also an exception, but it -is highly unlikely that you need that library. - -The 'make uninstall' takes the same command-line arguments. - ----------------------------------- -Step-by-step details: ----------------------------------- - -(1) Use the right BLAS and LAPACK libraries - - Determine where your BLAS and LAPACK libraries are. If the default - 'make' does not find them, use - 'make BLAS=-lmyblaslibraryhere LAPACK=-lmylapackgoeshere' - -(2) Install Intel's Threading Building Blocks (TBB) - - This is optionally used by SuiteSparseQR. Refer to the User Guide in - SuiteSparse/SPQR/Doc/spqr_user_guide.pdf for details. - -(3) Determine what other command line options you need for 'make'. All options - can be set at the 'make' command line without the need to edit this file. - Browse that file to see what options you can control. If you choose - different options and wish to recompile, be sure to do 'make distclean' in - this directory first, to remove all files not in the original distribution. - -(4) Type "make" in this directory. All packages will be be compiled. METIS - 5.1.0 will be compiled if you have it (note that METIS require CMake to - build it). Several demos will be run. - - To compile just the libraries, without running any demos, use - "make library". - - The libraries will appear in */Lib/*.so.* (*.dylib for the Mac). Include - files, as needed by user programs that use CHOLMOD, AMD, CAMD, COLAMD, - CCOLAMD, BTF, KLU, UMFPACK, LDL, etc. are in */Include/*.h. The include - files required by user programs are then copied into SuiteSparse/include, - and the compiled libraries are copied into SuiteSparse/lib. Documentation - is copied into SuiteSparse/doc. - - The GraphBLAS libraries are created by cmake and placed in GraphBLAS/build. - - NOTE: on Linux, you may see some errors when you compile METIS - ('make: *** No rule to make target 'w'.). You can safely ignore those - errors. - -(6) To install, type "make install". This will place copies of all - libraries in SuiteSparse/lib, and all include files in SuiteSparse/include, - and all documentation in SuiteSparse/doc/suitesparse-VERSION. You can - change the install location by "make install INSTALL=/my/path" which puts - the libraries in /my/path/lib, the include files in /my/path/include, and - documentation in /my/path/doc. These directories are created if they do - not already exist. - -(7) To uninstall, type "make uninstall", which reverses "make install" - by removing the SuiteSparse libraries, include files, and documentation - from the place they were installed. If you pass INSTALL_***= options - to 'make install', you must pass the same to 'make uninstall'. - diff --git a/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/CMakeLists.txt b/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/CMakeLists.txt new file mode 100644 index 000000000..3c2538236 --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/CMakeLists.txt @@ -0,0 +1,169 @@ +#------------------------------------------------------------------------------- +# SuiteSparse/SuiteSparse_config/CMakeLists.txt: cmake for SuiteSparse_config +#------------------------------------------------------------------------------- + +# SuiteSparse_config, Copyright (c) 2012-2023, Timothy A. Davis. +# All Rights Reserved. +# SPDX-License-Identifier: BSD-3-clause + +#------------------------------------------------------------------------------- +# get the version +#------------------------------------------------------------------------------- + +# cmake 3.22 is required to find the BLAS +cmake_minimum_required ( VERSION 3.22 ) + +# version of both SuiteSparse and SuiteSparse_config +set ( SUITESPARSE_DATE "Jan 20, 2023" ) +set ( SUITESPARSE_VERSION_MAJOR 7 ) +set ( SUITESPARSE_VERSION_MINOR 0 ) +set ( SUITESPARSE_VERSION_SUB 1 ) + +message ( STATUS "Building SuiteSparse_config version: v" + ${SUITESPARSE_VERSION_MAJOR}. + ${SUITESPARSE_VERSION_MINOR}. + ${SUITESPARSE_VERSION_SUB} " (" ${SUITESPARSE_DATE} ")" ) + +#------------------------------------------------------------------------------- +# SuiteSparse policies +#------------------------------------------------------------------------------- + +set ( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} + ${CMAKE_SOURCE_DIR}/cmake_modules ) + +include ( SuiteSparsePolicy ) + +if ( NOT NFORTRAN ) + include ( FortranCInterface ) +else ( ) + # No Fortran compiler available or enabled, configuration is not automatic. + set ( FortranCInterface_GLOBAL_MACRO ${SUITESPARSE_C_TO_FORTRAN} ) + set ( FortranCInterface_GLOBAL__MACRO ${SUITESPARSE_C_TO_FORTRAN} ) +endif ( ) + +#------------------------------------------------------------------------------- +# define the project +#------------------------------------------------------------------------------- + +project ( suitesparseconfig + VERSION "${SUITESPARSE_VERSION_MAJOR}.${SUITESPARSE_VERSION_MINOR}.${SUITESPARSE_VERSION_SUB}" + LANGUAGES C ) + +#------------------------------------------------------------------------------- +# find library dependencies +#------------------------------------------------------------------------------- + +option ( NOPENMP "ON: do not use OpenMP. OFF (default): use OpenMP" off ) +if ( NOPENMP ) + # OpenMP has been disabled + message ( STATUS "OpenMP disabled" ) + set ( OPENMP_FOUND false ) +else ( ) + find_package ( OpenMP ) +endif ( ) + +# AMICI +# include ( SuiteSparseBLAS ) +set(SuiteSparse_BLAS_integer int64_t) + +#------------------------------------------------------------------------------- +# configure files +#------------------------------------------------------------------------------- + +configure_file ( "Config/SuiteSparse_config.h.in" + "${PROJECT_SOURCE_DIR}/SuiteSparse_config.h" + NEWLINE_STYLE LF ) + +configure_file ( "Config/README.md.in" + "${PROJECT_SOURCE_DIR}/../README.md" + NEWLINE_STYLE LF ) + +#------------------------------------------------------------------------------- +# 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 ) + +#------------------------------------------------------------------------------- +# static suitesparseconfig library properties +#------------------------------------------------------------------------------- + +if ( NOT NSTATIC ) + 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 + OUTPUT_NAME suitesparseconfig + SOVERSION ${SUITESPARSE_VERSION_MAJOR} ) + + if ( MSVC ) + set_target_properties ( suitesparseconfig_static PROPERTIES + OUTPUT_NAME suitesparseconfig_static ) + endif ( ) +endif ( ) + +#------------------------------------------------------------------------------- +# add the library dependencies +#------------------------------------------------------------------------------- + +# libm: +if ( NOT WIN32 ) + target_link_libraries ( suitesparseconfig PUBLIC m ) + if ( NOT NSTATIC ) + target_link_libraries ( suitesparseconfig_static PUBLIC m ) + endif ( ) +endif ( ) + +# OpenMP: +if ( OPENMP_FOUND ) + 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} ) + endif ( ) + set ( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS} " ) + include_directories ( ${OpenMP_C_INCLUDE_DIRS} ) +endif ( ) + +# BLAS: +if ( BLAS_FOUND ) + # SuiteSparse_config does not itself require the BLAS. It just needs to + # know which BLAS is going to be used by the rest of SuiteSparse so it + # can configure SuiteSparse_config.h properly. + message ( STATUS "BLAS libraries: ${BLAS_LIBRARIES} ") + message ( STATUS "BLAS linker flags: ${BLAS_LINKER_FLAGS} ") + message ( STATUS "BLAS include: ${BLAS_INCLUDE_DIRS} ") +endif ( ) + +#------------------------------------------------------------------------------- +# suitesparseconfig installation location +#------------------------------------------------------------------------------- + +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} ) +install ( FILES + ${SUITESPARSE_CMAKE_MODULES} + DESTINATION ${SUITESPARSE_LIBDIR}/cmake/SuiteSparse + COMPONENT Development ) +if ( NOT NSTATIC ) + install ( TARGETS suitesparseconfig_static + ARCHIVE DESTINATION ${SUITESPARSE_LIBDIR} ) +endif ( ) + +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 new file mode 100644 index 000000000..e45ae9c08 --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/Config/README.md.in @@ -0,0 +1,682 @@ +----------------------------------------------------------------------------- +SuiteSparse: A Suite of Sparse matrix packages at http://suitesparse.com +----------------------------------------------------------------------------- + +@SUITESPARSE_DATE@, SuiteSparse VERSION @SUITESPARSE_VERSION_MAJOR@.@SUITESPARSE_VERSION_MINOR@.@SUITESPARSE_VERSION_SUB@ + +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. + +METIS is authored by George Karypis. + +Additional algorithm designers: Esmond Ng and John Gilbert. + +Refer to each package for license, copyright, and author information. + +----------------------------------------------------------------------------- +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. + +----------------------------------------------------------------------------- +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 + 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 GraphBLAS, and `C=A*B` 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. 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: + + 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 (SPQR): (also cite AMD, COLAMD): + + 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 SuiteSparseQR/GPU: + + 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 + + * for CHOLMOD: (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 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/Modify Module: (also cite AMD, COLAMD): + + 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 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 + + 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 CHOLMOD/GPU Modules: + + 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 + + * for AMD and CAMD: + + 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 + + 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 COLAMD, SYMAMD, CCOLAMD, and CSYMAMD: + + 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, 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 UMFPACK: (also cite AMD and COLAMD): + + 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, 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 + + 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 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 the FACTORIZE m-file: + + 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 KLU and BTF (also cite AMD and COLAMD): + + 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 LDL: + + 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 + + * for ssget and the SuiteSparse Matrix Collection: + + 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 + + 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 `spqr_rank`: + + 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 Mongoose: + + 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. + +To select your BLAS/LAPACK, see the instructions in SuiteSparseBLAS.cmake in +`SuiteSparse_config/cmake_modules`. If `SuiteSparse_config` finds a BLAS with +64-bit integers (such as the Intel MKL ilp64 BLAS), it configures +`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). + +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`. + +By default, SuiteSparse hunts for a suitable BLAS library. To enforce a +particular BLAS library use either: + + CMAKE_OPTIONS="-DBLA_VENDOR=OpenBLAS" make + 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. + +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. + +------------------ +SuiteSparse/README +------------------ + +Packages in SuiteSparse, and files in this directory: + + 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. + +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. + +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. + +----------------------------------------------------------------------------- +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: + + 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: + + DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH:/Users/me/SuiteSparse/lib + export DYLD_LIBRARY_PATH + +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 +your own SuiteSparse/lib. + +Then in the MATLAB Command Window, cd to the SuiteSparse directory and type +`SuiteSparse_install`. All packages will be compiled, and several demos will be +run. To run a (long!) exhaustive test, do `SuiteSparse_test`. + +Save your MATLAB path for future sessions with the MATLAB pathtool or savepath +commands. If those methods fail because you don't have system-wide permission, +add the new paths to your startup.m file, normally in +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: +----------------------------------------------------------------------------- + +For Linux and Mac: type the following in this directory (requires system +priviledge to do the `sudo make install`): + + make + sudo make install + +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. + +For Windows, import each `*/CMakeLists.txt` file into MS Visual Studio. + +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). + +To compile the libraries and install them only in SuiteSparse/lib (not +/usr/local/lib), do this instead in the top-level of SuiteSparse: + + make local + +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 -lumfpack -lamd -lcholmod -lsuitesparseconfig -lm + +To change the C and C++ compilers, and to compile in parallel use: + + CC=gcc CX=g++ JOBS=32 make + +for example, which changes the compiler to gcc and g++, and runs make with +'make -j32', in parallel with 32 jobs. + +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. + +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. + +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 + +For the Mac, use this instead: + + DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH:/home/me/SuiteSparse/lib + export DYLD_LIBRARY_PATH + +----------------------------------------------------------------------------- +Python interface +----------------------------------------------------------------------------- + +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 + +----------------------------------------------------------------------------- +Compilation options +----------------------------------------------------------------------------- + +You can set specific options for CMake with the command (for example): + + CMAKE_OPTIONS="-DNPARTITION=1 -DNSTATIC=1 -DCMAKE_BUILD_TYPE=Debug" make + +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 + +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. + +----------------------------------------------------------------------------- +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 +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. + +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 new file mode 100644 index 000000000..09d05c922 --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/Config/SuiteSparse_config.h.in @@ -0,0 +1,1408 @@ +//------------------------------------------------------------------------------ +// SuiteSparse_config/SuiteSparse_config.h: common utilites for SuiteSparse +//------------------------------------------------------------------------------ + +// SuiteSparse_config, Copyright (c) 2012-2023, Timothy A. Davis. +// All Rights Reserved. +// SPDX-License-Identifier: BSD-3-clause + +//------------------------------------------------------------------------------ + +// Configuration file for SuiteSparse: a Suite of Sparse matrix packages: AMD, +// COLAMD, CCOLAMD, CAMD, CHOLMOD, UMFPACK, CXSparse, SuiteSparseQR, ParU, ... + +// The SuiteSparse_config.h file is configured by CMake to be specific to the +// C/C++ compiler and BLAS library being used for SuiteSparse. The original +// file is SuiteSparse_config/SuiteSparse_config.h.in. Do not edit the +// SuiteSparse_config.h file directly. + +#ifndef SUITESPARSE_CONFIG_H +#define SUITESPARSE_CONFIG_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +//------------------------------------------------------------------------------ +// SuiteSparse-wide ANSI C11 #include files +//------------------------------------------------------------------------------ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//------------------------------------------------------------------------------ +// SuiteSparse_long is now int64_t in SuiteSparse v6.0.0 and later +//------------------------------------------------------------------------------ + +// The use of SuiteSparse_long is deprecated. User applications should use +// int64_t instead. + +#undef SuiteSparse_long +#undef SuiteSparse_long_max +#undef SuiteSparse_long_idd +#undef SuiteSparse_long_id + +#define SuiteSparse_long int64_t +#define SuiteSparse_long_max INT64_MAX +#define SuiteSparse_long_idd PRId64 +#define SuiteSparse_long_id "%" SuiteSparse_long_idd + +//------------------------------------------------------------------------------ +// OpenMP +//------------------------------------------------------------------------------ + +#if defined ( _OPENMP ) + + #include + #define SUITESPARSE_OPENMP_MAX_THREADS omp_get_max_threads ( ) + #define SUITESPARSE_OPENMP_GET_NUM_THREADS omp_get_num_threads ( ) + #define SUITESPARSE_OPENMP_GET_WTIME omp_get_wtime ( ) + #define SUITESPARSE_OPENMP_GET_THREAD_ID omp_get_thread_num ( ) + +#else + + // OpenMP not available + #define SUITESPARSE_OPENMP_MAX_THREADS (1) + #define SUITESPARSE_OPENMP_GET_NUM_THREADS (1) + #define SUITESPARSE_OPENMP_GET_WTIME (0) + #define SUITESPARSE_OPENMP_GET_THREAD_ID (0) + +#endif + +//------------------------------------------------------------------------------ +// MATLAB/Octave +//------------------------------------------------------------------------------ + +#if defined ( MATLAB_MEX_FILE ) +#include "mex.h" +#include "matrix.h" +#endif + +//------------------------------------------------------------------------------ +// string and token handling macros +//------------------------------------------------------------------------------ + +// SUITESPARSE_STR: convert the content of x into a string "x" +#define SUITESPARSE_XSTR(x) SUITESPARSE_STR(x) +#define SUITESPARSE_STR(x) #x + +// SUITESPARSE_CAT(x,y): concatenate two tokens +#define SUITESPARSE_CAT2(x,y) x ## y +#define SUITESPARSE_CAT(x,y) SUITESPARSE_CAT2(x,y) + +//------------------------------------------------------------------------------ +// determine which compiler is in use +//------------------------------------------------------------------------------ + +#define SUITESPARSE_COMPILER_NVCC 0 +#define SUITESPARSE_COMPILER_ICX 0 +#define SUITESPARSE_COMPILER_ICC 0 +#define SUITESPARSE_COMPILER_CLANG 0 +#define SUITESPARSE_COMPILER_GCC 0 +#define SUITESPARSE_COMPILER_MSC 0 +#define SUITESPARSE_COMPILER_XLC 0 + +#if defined ( __NVCC__ ) + + // NVIDIA nvcc compiler + #undef SUITESPARSE_COMPILER_NVCC + #define SUITESPARSE_COMPILER_NVCC 1 + + #define SUITESPARSE_COMPILER_MAJOR __CUDACC_VER_MAJOR__ + #define SUITESPARSE_COMPILER_MINOR __CUDACC_VER_MINOR__ + #define SUITESPARSE_COMPILER_SUB __CUDACC_VER_BUILD__ + #define SUITESPARSE_COMPILER_NAME "nvcc" + +#elif defined ( __INTEL_CLANG_COMPILER ) + + // Intel icx compiler, 2022.0.0 based on clang/llvm 14.0.0 + #undef SUITESPARSE_COMPILER_ICX + #define SUITESPARSE_COMPILER_ICX 1 + + #define SUITESPARSE_COMPILER_MAJOR __INTEL_CLANG_COMPILER + #define SUITESPARSE_COMPILER_MINOR 0 + #define SUITESPARSE_COMPILER_SUB 0 + #define SUITESPARSE_COMPILER_NAME __VERSION__ + +#elif defined ( __INTEL_COMPILER ) + + // Intel icc compiler: 2021.5.0 uses "gcc 7.5 mode" + #undef SUITESPARSE_COMPILER_ICC + #define SUITESPARSE_COMPILER_ICC 1 + + #define SUITESPARSE_COMPILER_MAJOR __INTEL_COMPILER + #define SUITESPARSE_COMPILER_MINOR __INTEL_COMPILER_UPDATE + #define SUITESPARSE_COMPILER_SUB 0 + #define SUITESPARSE_COMPILER_NAME __VERSION__ + +#elif defined ( __clang__ ) + + // clang + #undef SUITESPARSE_COMPILER_CLANG + #define SUITESPARSE_COMPILER_CLANG 1 + + #define SUITESPARSE_COMPILER_MAJOR __clang_major__ + #define SUITESPARSE_COMPILER_MINOR __clang_minor__ + #define SUITESPARSE_COMPILER_SUB __clang_patchlevel__ + #define SUITESPARSE_COMPILER_NAME "clang " __clang_version__ + +#elif defined ( __xlC__ ) + + // xlc + #undef SUITESPARSE_COMPILER_XLC + #define SUITESPARSE_COMPILER_XLC 1 + + #define SUITESPARSE_COMPILER_MAJOR ( __xlC__ / 256 ) + #define SUITESPARSE_COMPILER_MINOR \ + ( __xlC__ - 256 * SUITESPARSE_COMPILER_MAJOR) + #define SUITESPARSE_COMPILER_SUB 0 + #define SUITESPARSE_COMPILER_NAME "IBM xlc " SUITESPARSE_XSTR (__xlC__) + +#elif defined ( __GNUC__ ) + + // gcc + #undef SUITESPARSE_COMPILER_GCC + #define SUITESPARSE_COMPILER_GCC 1 + + #define SUITESPARSE_COMPILER_MAJOR __GNUC__ + #define SUITESPARSE_COMPILER_MINOR __GNUC_MINOR__ + #define SUITESPARSE_COMPILER_SUB __GNUC_PATCHLEVEL__ + #define SUITESPARSE_COMPILER_NAME "GNU gcc " \ + SUITESPARSE_XSTR (__GNUC__) "." \ + SUITESPARSE_XSTR (__GNUC_MINOR__) "." \ + SUITESPARSE_XSTR (__GNUC_PATCHLEVEL__) + +#elif defined ( _MSC_VER ) + + // Microsoft Visual Studio (cl compiler) + #undef SUITESPARSE_COMPILER_MSC + #define SUITESPARSE_COMPILER_MSC 1 + + #define SUITESPARSE_COMPILER_MAJOR ( _MSC_VER / 100 ) + #define SUITESPARSE_COMPILER_MINOR \ + ( _MSC_VER - 100 * SUITESPARSE_COMPILER_MAJOR) + #define SUITESPARSE_COMPILER_SUB 0 + #define SUITESPARSE_COMPILER_NAME \ + "Microsoft Visual Studio " SUITESPARSE_XSTR (_MSC_VER) + +#else + + // other compiler + #define SUITESPARSE_COMPILER_MAJOR 0 + #define SUITESPARSE_COMPILER_MINOR 0 + #define SUITESPARSE_COMPILER_SUB 0 + #define SUITESPARSE_COMPILER_NAME "other C compiler" + +#endif + +//------------------------------------------------------------------------------ +// malloc.h: required include file for Microsoft Visual Studio +//------------------------------------------------------------------------------ + +#if SUITESPARSE_COMPILER_MSC + #include +#endif + +// this was formerly "extern", or "__declspec ..." for Windows. +#define SUITESPARSE_PUBLIC + +//------------------------------------------------------------------------------ +// determine the ANSI C version +//------------------------------------------------------------------------------ + +#ifdef __STDC_VERSION__ +// ANSI C17: 201710L +// ANSI C11: 201112L +// ANSI C99: 199901L +// ANSI C95: 199409L +#define SUITESPARSE_STDC_VERSION __STDC_VERSION__ +#else +// assume ANSI C90 / C89 +#define SUITESPARSE_STDC_VERSION 199001L +#endif + +//------------------------------------------------------------------------------ +// handle the restrict keyword +//------------------------------------------------------------------------------ + +#if defined ( __cplusplus ) + + // C++ does not have the "restrict" keyword + #define SUITESPARSE_RESTRICT + +#elif SUITESPARSE_COMPILER_MSC + + // MS Visual Studio + #define SUITESPARSE_RESTRICT __restrict + +#elif SUITESPARSE_COMPILER_NVCC + + // NVIDIA nvcc + #define SUITESPARSE_RESTRICT __restrict__ + +#elif SUITESPARSE_STDC_VERSION >= 199901L + + // ANSI C99 or later + #define SUITESPARSE_RESTRICT restrict + +#else + + // ANSI C95 and earlier: no restrict keyword + #define SUITESPARSE_RESTRICT + +#endif + +//============================================================================== +// SuiteSparse_config parameters and functions +//============================================================================== + +// SuiteSparse-wide parameters are placed in a single static struct, defined +// locally in SuiteSparse_config.c. It is not meant to be updated frequently +// by multiple threads. Rather, if an application needs to modify +// SuiteSparse_config, it should do it once at the beginning of the +// application, before multiple threads are launched. + +// The intent of these function pointers is that they not be used in your +// application directly, except to assign them to the desired user-provided +// functions. Rather, you should use the SuiteSparse_malloc/calloc, etc +// wrappers defined below to access them. + +// The SuiteSparse_config_*_get methods return the contents of the struct: +void *(*SuiteSparse_config_malloc_func_get (void)) (size_t); +void *(*SuiteSparse_config_calloc_func_get (void)) (size_t, size_t); +void *(*SuiteSparse_config_realloc_func_get (void)) (void *, size_t); +void (*SuiteSparse_config_free_func_get (void)) (void *); +int (*SuiteSparse_config_printf_func_get (void)) (const char *, ...); +double (*SuiteSparse_config_hypot_func_get (void)) (double, double); +int (*SuiteSparse_config_divcomplex_func_get (void)) (double, double, double, double, double *, double *); + +// The SuiteSparse_config_*_set methods modify the contents of the struct: +void SuiteSparse_config_malloc_func_set (void *(*malloc_func) (size_t)); +void SuiteSparse_config_calloc_func_set (void *(*calloc_func) (size_t, size_t)); +void SuiteSparse_config_realloc_func_set (void *(*realloc_func) (void *, size_t)); +void SuiteSparse_config_free_func_set (void (*free_func) (void *)); +void SuiteSparse_config_printf_func_set (int (*printf_func) (const char *, ...)); +void SuiteSparse_config_hypot_func_set (double (*hypot_func) (double, double)); +void SuiteSparse_config_divcomplex_func_set (int (*divcomplex_func) (double, double, double, double, double *, double *)); + +// The SuiteSparse_config_*_func methods are wrappers that call the function +// pointers in the struct. Note that there is no wrapper for the printf_func. +// See the SUITESPARSE_PRINTF macro instead. +void *SuiteSparse_config_malloc (size_t s) ; +void *SuiteSparse_config_calloc (size_t n, size_t s) ; +void *SuiteSparse_config_realloc (void *, size_t s) ; +void SuiteSparse_config_free (void *) ; +double SuiteSparse_config_hypot (double x, double y) ; +int SuiteSparse_config_divcomplex +( + double xr, double xi, double yr, double yi, double *zr, double *zi +) ; + +void SuiteSparse_start ( void ) ; // called to start SuiteSparse + +void SuiteSparse_finish ( void ) ; // called to finish SuiteSparse + +void *SuiteSparse_malloc // pointer to allocated block of memory +( + size_t nitems, // number of items to malloc (>=1 is enforced) + size_t size_of_item // sizeof each item +) ; + +void *SuiteSparse_calloc // pointer to allocated block of memory +( + size_t nitems, // number of items to calloc (>=1 is enforced) + size_t size_of_item // sizeof each item +) ; + +void *SuiteSparse_realloc // pointer to reallocated block of memory, or + ///to original block if the realloc failed. +( + size_t nitems_new, // new number of items in the object + size_t nitems_old, // old number of items in the object + size_t size_of_item, // sizeof each item + void *p, // old object to reallocate + int *ok // 1 if successful, 0 otherwise +) ; + +void *SuiteSparse_free // always returns NULL +( + void *p // block to free +) ; + +void SuiteSparse_tic // start the timer +( + double tic [2] // output, contents undefined on input +) ; + +double SuiteSparse_toc // return time in seconds since last tic +( + double tic [2] // input: from last call to SuiteSparse_tic +) ; + +double SuiteSparse_time // returns current wall clock time in seconds +( + void +) ; + +// returns sqrt (x^2 + y^2), computed reliably +double SuiteSparse_hypot (double x, double y) ; + +// complex division of c = a/b +int SuiteSparse_divcomplex +( + double ar, double ai, // real and imaginary parts of a + double br, double bi, // real and imaginary parts of b + double *cr, double *ci // real and imaginary parts of c +) ; + +// determine which timer to use, if any +#ifndef NTIMER + #if defined ( _OPENMP ) + #define SUITESPARSE_TIMER_ENABLED + #elif defined ( _POSIX_C_SOURCE ) + #if _POSIX_C_SOURCE >= 199309L + #define SUITESPARSE_TIMER_ENABLED + #endif + #endif +#endif + +// SuiteSparse printf macro +#define SUITESPARSE_PRINTF(params) \ +{ \ + int (*printf_func) (const char *, ...) ; \ + printf_func = SuiteSparse_config_printf_func_get ( ) ; \ + if (printf_func != NULL) \ + { \ + (void) (printf_func) params ; \ + } \ +} + +//============================================================================== +// SuiteSparse version +//============================================================================== + +// SuiteSparse is not a package itself, but a collection of packages, some of +// which must be used together (UMFPACK requires AMD, CHOLMOD requires AMD, +// COLAMD, CAMD, and CCOLAMD, etc). A version number is provided here for the +// collection itself, which is also the version number of SuiteSparse_config. + +int SuiteSparse_version // returns SUITESPARSE_VERSION +( + // output, not defined on input. Not used if NULL. Returns + // the three version codes in version [0..2]: + // version [0] is SUITESPARSE_MAIN_VERSION + // version [1] is SUITESPARSE_SUB_VERSION + // version [2] is SUITESPARSE_SUBSUB_VERSION + int version [3] +) ; + +#define SUITESPARSE_HAS_VERSION_FUNCTION + +#define SUITESPARSE_DATE "@SUITESPARSE_DATE@" +#define SUITESPARSE_MAIN_VERSION @SUITESPARSE_VERSION_MAJOR@ +#define SUITESPARSE_SUB_VERSION @SUITESPARSE_VERSION_MINOR@ +#define SUITESPARSE_SUBSUB_VERSION @SUITESPARSE_VERSION_SUB@ + +#define SUITESPARSE_VER_CODE(main,sub) ((main) * 1000 + (sub)) +#define SUITESPARSE_VERSION \ + SUITESPARSE_VER_CODE(SUITESPARSE_MAIN_VERSION,SUITESPARSE_SUB_VERSION) + +//============================================================================== +// SuiteSparse interface to the BLAS and LAPACK libraries +//============================================================================== + +// Several SuiteSparse packages rely on the BLAS/LAPACK libraries (UMFPACK +// CHOLMOD, and SPQR, and likely GraphBLAS in the future). All of these +// packages are written in C/C++, but rely on the Fortran interface to +// BLAS/LAPACK. SuiteSparse does not use the cblas / lapacke interfaces to +// these libraries, mainly because FindBLAS.cmake does not locate them (or at +// least does not locate their respective cblas.h and lapacke.h files). In +// addition, the original definition of these files do not include a different +// name space for 64-bit integer versions. Finally, Intel renames cblas.h as +// mkl_cblas.h. As a result of these many portability issues, different +// implementations of those libraries extend them in different ways. Thus, +// SuiteSparse simply calls the Fortran functions directly. + +// However, the method for how C/C++ calling Fortran depends on the compilers +// involved. This connection is handled by the FortranCInterface.cmake module +// of CMake. + +// On typical systems (Linux with the GCC compiler for example, or on the Mac +// with clang) the Fortan name "dgemm" is called by C as "dgemm_", Other +// systems do not append the underscore. + +//------------------------------------------------------------------------------ +// SUITESPARSE_FORTRAN: macros created by CMake describing how C calls Fortran +//------------------------------------------------------------------------------ + +// SUITESPARSE_FORTAN: for Fortran routines with no "_" in their names +// SUITESPARSE__FORTAN: for Fortran routines with "_" in their names + +// The decision on which of these macros to use is based on the presence of +// underscores in the original Fortran names, not the (commonly) appended +// underscore needed for C to all the corresponding Fortran routine. + +// These two macros are created by the CMake module, FortranCInterface.cmake, +// which is then used by CMake to configure this file. + +// The CMAKE decision can be superceded by setting -DBLAS_NO_UNDERSCORE, so +// that "dgemm" remains "dgemm" (for MS Visual Studio for example). Setting +// -DBLAS_UNDERSCORE changes "dgemm" to "dgemm_", the common case for Mac and +// Linux. + +#if defined ( BLAS_NO_UNDERSCORE ) + + // no name mangling, use lower case + #define SUITESPARSE_FORTRAN(name,NAME) name + #define SUITESPARSE__FORTRAN(name,NAME) name + +#elif defined ( BLAS_UNDERSCORE ) + + // append an undescore, use lower case + #define SUITESPARSE_FORTRAN(name,NAME) name ## _ + #define SUITESPARSE__FORTRAN(name,NAME) name ## _ + +#else + + // let CMake decide how C calls Fortran + #define SUITESPARSE_FORTRAN@FortranCInterface_GLOBAL_MACRO@ + #define SUITESPARSE__FORTRAN@FortranCInterface_GLOBAL__MACRO@ + +#endif + +//------------------------------------------------------------------------------ +// SUITESPARSE_BLAS_INT: the BLAS/LAPACK integer (int32_t or int64_t) +//------------------------------------------------------------------------------ + +// CMake 3.22 and later allow the selection of the BLAS/LAPACK integer size. +// This information is then used to configure this file with the definition of +// this integer: int32_t or int64_t. + +// When compiling SuiteSparse for a MATLAB mexFunction, the MATLAB libmwblas is +// used, which is a 64-bit integer version of the BLAS. CMake is not used to +// configure SuiteSparse in this case. The flag -DBLAS64 can be used to ensure +// a 64-bit BLAS is used. Likewise, -DBLAS32 ensures a 32-bit BLAS is used. + +#if defined ( BLAS64 ) + + // override the BLAS found by CMake, and force a 64-bit interface + #define SUITESPARSE_BLAS_INT int64_t + +#elif defined ( BLAS32 ) + + // override the BLAS found by CMake, and force a 32-bit interface + #define SUITESPARSE_BLAS_INT int32_t + +#else + + // let CMake determine the size of the integer in the Fortran BLAS + #define SUITESPARSE_BLAS_INT @SuiteSparse_BLAS_integer@ + +#endif + +// SUITESPARSE_TO_BLAS_INT: convert an integer k to a BLAS integer K and set ok +// to false if the conversion changes its value. This is implemented as a +// macro so that can work with any type of the integer k. +#define SUITESPARSE_TO_BLAS_INT(K,k,ok) \ + SUITESPARSE_BLAS_INT K = (k) ; \ + ok = ok && ((sizeof (K) >= sizeof (k)) || ((int64_t)(K) == (int64_t)(k))) ; + +//------------------------------------------------------------------------------ +// SUITESPARSE_BLAS_SUFFIX: modify the name of a Fortran BLAS/LAPACK routine +//------------------------------------------------------------------------------ + +// OpenBLAS can be compiled by appending a suffix to each routine, so that the +// Fortan routine dgemm becomes dgemm_64, which denotes a version of dgemm with +// 64-bit integer parameters. The Sun Performance library does the same thing, +// but without the internal underscore, as dgemm64. + +// If the suffix does not contain "_", use (Sun Perf., for example): + +// cd build ; cmake -DBLAS64_SUFFIX="64" .. + +// If the suffix contains "_" (OpenBLAS in spack for example), use the +// following: + +// 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 ) + + // The suffix includes an undersore (such as "_64"), so the Fortran name + // must be processed with the SUITESPARSE__FORTRAN macro. + #define SUITESPARSE_G(name,NAME) SUITESPARSE__FORTRAN(name,NAME) + #define SUITESPARSE_F(name,NAME) \ + SUITESPARSE_G (SUITESPARSE_CAT (name, BLAS64__SUFFIX), \ + SUITESPARSE_CAT (NAME, BLAS64__SUFFIX)) + #define SUITESPARSE_BLAS(name,NAME) SUITESPARSE_F(name,NAME) + +#elif defined ( BLAS64_SUFFIX ) + + // The suffix does not include an undersore, and neither do the original + // names of the BLAS and LAPACK routines. Thus, the Fortran name must be + // processed with the SUITESPARSE_FORTRAN macro. + #define SUITESPARSE_G(name,NAME) SUITESPARSE_FORTRAN(name,NAME) + #define SUITESPARSE_F(name,NAME) \ + SUITESPARSE_G (SUITESPARSE_CAT (name, BLAS64_SUFFIX), \ + SUITESPARSE_CAT (NAME, BLAS64_SUFFIX)) + #define SUITESPARSE_BLAS(name,NAME) SUITESPARSE_F(name,NAME) + +#else + + // No suffix is need, so the final Fortran name includes no suffix. + #define SUITESPARSE_BLAS(name,NAME) SUITESPARSE_FORTRAN(name,NAME) + +#endif + +//------------------------------------------------------------------------------ +// C names of Fortan BLAS and LAPACK functions used by SuiteSparse +//------------------------------------------------------------------------------ + +#define SUITESPARSE_BLAS_DTRSV SUITESPARSE_BLAS ( dtrsv , DTRSV ) +#define SUITESPARSE_BLAS_DGEMV SUITESPARSE_BLAS ( dgemv , DGEMV ) +#define SUITESPARSE_BLAS_DTRSM SUITESPARSE_BLAS ( dtrsm , DTRSM ) +#define SUITESPARSE_BLAS_DGEMM SUITESPARSE_BLAS ( dgemm , DGEMM ) +#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_LAPACK_DPOTRF SUITESPARSE_BLAS ( dpotrf , DPOTRF ) + +#define SUITESPARSE_BLAS_ZTRSV SUITESPARSE_BLAS ( ztrsv , ZTRSV ) +#define SUITESPARSE_BLAS_ZGEMV SUITESPARSE_BLAS ( zgemv , ZGEMV ) +#define SUITESPARSE_BLAS_ZTRSM SUITESPARSE_BLAS ( ztrsm , ZTRSM ) +#define SUITESPARSE_BLAS_ZGEMM SUITESPARSE_BLAS ( zgemm , ZGEMM ) +#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_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 ) + +//------------------------------------------------------------------------------ +// prototypes of BLAS and SUITESPARSE_LAPACK functions +//------------------------------------------------------------------------------ + +// For complex functions, the (void *) parameters are actually pointers to +// arrays of complex values. They are prototyped here as (void *) to allow +// them to be called from both C and C++. + +// See https://netlib.org/blas/ and https://netlib.org/lapack/ for the +// definitions of the inputs/outputs of these functions. + +// These prototypes need to be found by UMFPACK, CHOLMOD, and SPQR, and to do +// so, they need to appear in this public header to ensure the correct BLAS +// library and integer size is used. However, these definitions should not +// (normally) be exposed to the user application. + +// If a user application wishes to use these definitions, simply add + +// #define SUITESPARSE_BLAS_DEFINITIONS +// #include "SuiteSparse_config.h" + +// prior to #include'ing any SuiteSparse headers (amd.h, and so on). + +#if defined ( SUITESPARSE_BLAS_DEFINITIONS ) + +void SUITESPARSE_BLAS_DGEMV // Y = alpha*A*x + beta*Y +( + // input: + const char *trans, + const SUITESPARSE_BLAS_INT *m, + const SUITESPARSE_BLAS_INT *n, + const double *alpha, + const double *A, + const SUITESPARSE_BLAS_INT *lda, + const double *X, + const SUITESPARSE_BLAS_INT *incx, + const double *beta, + // input/output: + double *Y, + // input: + const SUITESPARSE_BLAS_INT *incy +) ; + +#define SUITESPARSE_BLAS_dgemv(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_DGEMV (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 // Y = alpha*A*X + beta*Y +( + // 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_zgemv(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_ZGEMV (trans, &M_blas_int, &N_blas_int, alpha, A, \ + &LDA_blas_int, X, &INCX_blas_int, beta, Y, &INCY_blas_int) ; \ + } \ +} + +void SUITESPARSE_BLAS_DTRSV // solve Lx=b, Ux=b, L'x=b, or U'x=b +( + // input: + const char *uplo, + const char *trans, + const char *diag, + const SUITESPARSE_BLAS_INT *n, + const double *A, + const SUITESPARSE_BLAS_INT *lda, + // input/output: + double *X, + // input: + const SUITESPARSE_BLAS_INT *incx +) ; + +#define SUITESPARSE_BLAS_dtrsv(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_DTRSV (uplo, trans, diag, &N_blas_int, A, \ + &LDA_blas_int, X, &INCX_blas_int) ; \ + } \ +} + +void SUITESPARSE_BLAS_ZTRSV // solve (L, L', L^H, U, U', or U^H)x=b +( + // 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_ztrsv(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_ZTRSV (uplo, trans, diag, &N_blas_int, A, \ + &LDA_blas_int, X, &INCX_blas_int) ; \ + } \ +} + +void SUITESPARSE_BLAS_DTRSM // solve LX=B, UX=B, L'X=B, or U'X=B +( + // input: + const char *side, + const char *uplo, + const char *transa, + const char *diag, + const SUITESPARSE_BLAS_INT *m, + const SUITESPARSE_BLAS_INT *n, + const double *alpha, + const double *A, + const SUITESPARSE_BLAS_INT *lda, + // input/output: + double *B, + // input: + const SUITESPARSE_BLAS_INT *ldb +) ; + +#define SUITESPARSE_BLAS_dtrsm(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_DTRSM (side, uplo, transa, diag, &M_blas_int, \ + &N_blas_int, alpha, A, &LDA_blas_int, B, &LDB_blas_int) ; \ + } \ +} + +void SUITESPARSE_BLAS_ZTRSM // solve (L, L', L^H, U, U', or U^H)X=B +( + // 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_ztrsm(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_ZTRSM (side, uplo, transa, diag, &M_blas_int, \ + &N_blas_int, alpha, A, &LDA_blas_int, B, &LDB_blas_int) ; \ + } \ +} + +void SUITESPARSE_BLAS_DGEMM // C = alpha*A*B + beta*C +( + // input: + 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 SUITESPARSE_BLAS_INT *lda, + const double *B, + const SUITESPARSE_BLAS_INT *ldb, + const double *beta, + // input/output: + double *C, + // input: + const SUITESPARSE_BLAS_INT *ldc +) ; + +#define SUITESPARSE_BLAS_dgemm(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_DGEMM (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_ZGEMM // C = alpha*A*B + beta*C +( + // input: + 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, + // input: + const SUITESPARSE_BLAS_INT *ldc +) ; + +#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_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_DSYRK // C = alpha*A*A' + beta*C, or A'A +( + // 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 *C, + // input: + const SUITESPARSE_BLAS_INT *ldc +) ; + +#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) ; \ + 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) ; \ + } \ +} + +void SUITESPARSE_BLAS_ZHERK // C = alpha*A*A^H + beta*C, or A^H*A +( + // 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_LAPACK_DPOTRF // Cholesky factorization +( + // 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_ZPOTRF // Cholesky factorization +( + // 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_zpotrf(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_ZPOTRF (uplo, &N_blas_int, A, &LDA_blas_int, \ + &LAPACK_Info) ; \ + info = LAPACK_Info ; \ + } \ +} + +void SUITESPARSE_BLAS_DSCAL // Y = alpha*Y +( + // input: + const SUITESPARSE_BLAS_INT *n, + const double *alpha, + // input/output: + double *Y, + // input: + const SUITESPARSE_BLAS_INT *incy +) ; + +#define SUITESPARSE_BLAS_dscal(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_DSCAL (&N_blas_int, alpha, Y, &INCY_blas_int) ; \ + } \ +} + +void SUITESPARSE_BLAS_ZSCAL // Y = alpha*Y +( + // input: + const SUITESPARSE_BLAS_INT *n, + const void *alpha, + // input/output: + void *Y, + // input: + const SUITESPARSE_BLAS_INT *incy +) ; + +#define SUITESPARSE_BLAS_zscal(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_ZSCAL (&N_blas_int, alpha, Y, &INCY_blas_int) ; \ + } \ +} + +void SUITESPARSE_BLAS_DGER // A = alpha*x*y' + A +( + // input: + const SUITESPARSE_BLAS_INT *m, + const SUITESPARSE_BLAS_INT *n, + const double *alpha, + const double *X, + const SUITESPARSE_BLAS_INT *incx, + const double *Y, + const SUITESPARSE_BLAS_INT *incy, + // input/output: + double *A, + // input: + const SUITESPARSE_BLAS_INT *lda +) ; + +#define SUITESPARSE_BLAS_dger(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_DGER (&M_blas_int, &N_blas_int, alpha, X, \ + &INCX_blas_int, Y, &INCY_blas_int, A, &LDA_blas_int) ; \ + } \ +} + +void SUITESPARSE_BLAS_ZGERU // A = alpha*x*y' + A +( + // 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_zgeru(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_ZGERU (&M_blas_int, &N_blas_int, alpha, X, \ + &INCX_blas_int, Y, &INCY_blas_int, A, &LDA_blas_int) ; \ + } \ +} + +void SUITESPARSE_LAPACK_DLARFT // T = block Householder factor +( + // input: + const char *direct, + const char *storev, + const SUITESPARSE_BLAS_INT *n, + const SUITESPARSE_BLAS_INT *k, + const double *V, + const SUITESPARSE_BLAS_INT *ldv, + const double *Tau, + // output: + double *T, + // input: + const SUITESPARSE_BLAS_INT *ldt +) ; + +#define SUITESPARSE_LAPACK_dlarft(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_DLARFT (direct, storev, &N_blas_int, &K_blas_int, \ + V, &LDV_blas_int, Tau, T, &LDT_blas_int) ; \ + } \ +} + +void SUITESPARSE_LAPACK_ZLARFT // T = block Householder factor +( + // 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_zlarft(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_ZLARFT (direct, storev, &N_blas_int, &K_blas_int, \ + V, &LDV_blas_int, Tau, T, &LDT_blas_int) ; \ + } \ +} + +void SUITESPARSE_LAPACK_DLARFB // apply block Householder reflector +( + // 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 double *V, + const SUITESPARSE_BLAS_INT *ldv, + const double *T, + const SUITESPARSE_BLAS_INT *ldt, + // input/output: + double *C, + // input: + const SUITESPARSE_BLAS_INT *ldc, + // workspace: + double *Work, + // input: + const SUITESPARSE_BLAS_INT *ldwork +) ; + +#define SUITESPARSE_LAPACK_dlarfb(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_DLARFB (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 // apply block Householder reflector +( + // 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_zlarfb(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_ZLARFB (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) ; \ + } \ +} + +double SUITESPARSE_BLAS_DNRM2 // vector 2-norm +( + // input: + const SUITESPARSE_BLAS_INT *n, + const double *X, + const SUITESPARSE_BLAS_INT *incx +) ; + +#define SUITESPARSE_BLAS_dnrm2(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_DNRM2 (&N_blas_int, X, &INCX_blas_int) ; \ + } \ +} + +double SUITESPARSE_BLAS_DZNRM2 // vector 2-norm +( + // input: + const SUITESPARSE_BLAS_INT *n, + const void *X, + const SUITESPARSE_BLAS_INT *incx +) ; + +#define SUITESPARSE_BLAS_dznrm2(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_DZNRM2 (&N_blas_int, X, &INCX_blas_int) ; \ + } \ +} + +void SUITESPARSE_LAPACK_DLARFG // generate Householder reflector +( + // input: + const SUITESPARSE_BLAS_INT *n, + // input/output: + double *alpha, + double *X, + // input: + const SUITESPARSE_BLAS_INT *incx, + // output: + double *tau +) ; + +#define SUITESPARSE_LAPACK_dlarfg(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_DLARFG (&N_blas_int, alpha, X, &INCX_blas_int, \ + tau) ; \ + } \ +} + +void SUITESPARSE_LAPACK_ZLARFG // generate Householder reflector +( + // input: + const SUITESPARSE_BLAS_INT *n, + // input/output: + void *alpha, + void *X, + // input: + const SUITESPARSE_BLAS_INT *incx, + // output: + void *tau +) ; + +#define SUITESPARSE_LAPACK_zlarfg(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_ZLARFG (&N_blas_int, alpha, X, &INCX_blas_int, \ + tau) ; \ + } \ +} + +void SUITESPARSE_LAPACK_DLARF // apply Householder reflector +( + // input: + const char *side, + const SUITESPARSE_BLAS_INT *m, + const SUITESPARSE_BLAS_INT *n, + const double *V, + const SUITESPARSE_BLAS_INT *incv, + const double *tau, + // input/output: + double *C, + // input: + const SUITESPARSE_BLAS_INT *ldc, + // workspace: + double *Work +) ; + +#define SUITESPARSE_LAPACK_dlarf(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_DLARF (side, &M_blas_int, &N_blas_int, V, \ + &INCV_blas_int, tau, C, &LDC_blas_int, Work) ; \ + } \ +} + +void SUITESPARSE_LAPACK_ZLARF // apply Householder reflector +( + // 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_zlarf(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_ZLARF (side, &M_blas_int, &N_blas_int, V, \ + &INCV_blas_int, tau, C, &LDC_blas_int, Work) ; \ + } \ +} + +#endif + +//------------------------------------------------------------------------------ +// SuiteSparse_BLAS_library: return name of BLAS library found +//------------------------------------------------------------------------------ + +// Returns the name of the BLAS library found by SuiteSparse_config + +const char *SuiteSparse_BLAS_library ( void ) ; + +//------------------------------------------------------------------------------ +// SuiteSparse_BLAS_integer_size: return sizeof (SUITESPARSE_BLAS_INT) +//------------------------------------------------------------------------------ + +size_t SuiteSparse_BLAS_integer_size ( void ) ; + +#ifdef __cplusplus +} +#endif +#endif + diff --git a/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/Makefile b/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/Makefile index 695a327a4..9893afe78 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/Makefile +++ b/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/Makefile @@ -1,72 +1,81 @@ #------------------------------------------------------------------------------- -# SuiteSparse_config Makefile +# SuiteSparse/SuiteSparse_config/Makefile #------------------------------------------------------------------------------- -SUITESPARSE ?= $(realpath $(CURDIR)/..) -export SUITESPARSE +# SuiteSparse_config, Copyright (c) 2012-2023, Timothy A. Davis. +# All Rights Reserved. +# SPDX-License-Identifier: BSD-3-clause -# version of SuiteSparse_config is also version of SuiteSparse meta-package -LIBRARY = libsuitesparseconfig -VERSION = 5.4.0 -SO_VERSION = 5 +#------------------------------------------------------------------------------- -default: library +# A simple Makefile for SuiteSparse_config, which relies on cmake to do the +# actual build. All the work is done in cmake so this Makefile is just for +# convenience. + +# To compile with an alternate compiler: +# +# make CC=gcc CXX=g++ +# +# To compile/install for system-wide usage and for local usage in +# SuiteSparse/lib and SuiteSparse/include: +# +# make +# sudo make install +# +# To compile/install for local usage (SuiteSparse/lib and SuiteSparse/include): +# +# make local +# make install +# +# To clean up the files: +# +# make clean + +JOBS ?= 8 -include SuiteSparse_config.mk +default: library -ccode: all +# default is to install only in /usr/local +library: + ( cd build && cmake $(CMAKE_OPTIONS) .. && cmake --build . -j${JOBS} ) -all: library +# install only in SuiteSparse/lib and SuiteSparse/include +local: + ( cd build && cmake $(CMAKE_OPTIONS) -DLOCAL_INSTALL=1 .. && cmake --build . -j${JOBS} ) -# compile and install in SuiteSparse/lib -library: $(AR_TARGET) - $(MAKE) install INSTALL=$(SUITESPARSE) +# install only in /usr/local (default) +global: + ( cd build && cmake $(CMAKE_OPTIONS) -DLOCAL_INSTALL=0 .. && cmake --build . -j${JOBS} ) -OBJ = SuiteSparse_config.o +debug: + ( cd build ; cmake $(CMAKE_OPTIONS) -DCMAKE_BUILD_TYPE=Debug .. ; cmake --build . ) -SuiteSparse_config.o: SuiteSparse_config.c SuiteSparse_config.h - $(CC) $(CF) -c SuiteSparse_config.c +all: library -static: $(AR_TARGET) +demos: library -$(AR_TARGET): $(OBJ) - $(ARCHIVE) $(AR_TARGET) SuiteSparse_config.o - $(RANLIB) $(AR_TARGET) +# just compile after running cmake; do not run cmake again +remake: + ( cd build ; cmake --build . ) -distclean: purge +# just run cmake to set things up +setup: + ( cd build ; cmake $(CMAKE_OPTIONS) .. ) -purge: clean - ( cd xerbla ; $(MAKE) purge ) - - $(RM) -r $(PURGE) +install: + ( cd build ; cmake --install . ) -clean: - ( cd xerbla ; $(MAKE) clean ) - - $(RM) -r $(CLEAN) +# remove any installed libraries and #include files +uninstall: + - xargs rm < build/install_manifest.txt -# install SuiteSparse_config -install: $(AR_TARGET) $(INSTALL_LIB)/$(SO_TARGET) +# remove all files not in the distribution +clean: distclean -$(INSTALL_LIB)/$(SO_TARGET): $(OBJ) - @mkdir -p $(INSTALL_LIB) - @mkdir -p $(INSTALL_INCLUDE) - @mkdir -p $(INSTALL_DOC) - $(CC) $(SO_OPTS) $^ -o $@ $(LDLIBS) - ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_PLAIN) ) - ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_MAIN) ) - $(CP) SuiteSparse_config.h $(INSTALL_INCLUDE) - $(CP) README.txt $(INSTALL_DOC)/SUITESPARSECONFIG_README.txt - chmod 755 $(INSTALL_LIB)/$(SO_TARGET) - chmod 755 $(INSTALL_LIB)/$(SO_PLAIN) - chmod 644 $(INSTALL_INCLUDE)/SuiteSparse_config.h - chmod 644 $(INSTALL_DOC)/SUITESPARSECONFIG_README.txt +purge: distclean -# uninstall SuiteSparse_config -uninstall: - $(RM) $(INSTALL_LIB)/$(SO_TARGET) - $(RM) $(INSTALL_LIB)/$(SO_PLAIN) - $(RM) $(INSTALL_LIB)/$(SO_MAIN) - $(RM) $(INSTALL_INCLUDE)/SuiteSparse_config.h - $(RM) $(INSTALL_DOC)/SUITESPARSECONFIG_README.txt - ( cd xerbla ; $(MAKE) uninstall ) +distclean: + - $(RM) -rf build/* Config/*.tmp +docs: diff --git a/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/README.txt b/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/README.txt index 8555cc459..4ff01953f 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/README.txt +++ b/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/README.txt @@ -1,15 +1,10 @@ -SuiteSparse_config, 2018, Timothy A. Davis, http://www.suitesparse.com -(formerly the UFconfig package) +SuiteSparse_config, Copyright (c) 2012-2023, Timothy A. Davis. +All Rights Reserved. +SPDX-License-Identifier: BSD-3-clause -This directory contains a default SuiteSparse_config.mk file. It tries to -detect your system (Linux, SunOS, or Mac), which compiler to use (icc or cc), -which BLAS and LAPACK library to use (OpenBLAS or MKL), and whether or not to -compile with CUDA. - -For alternatives, see the comments in the SuiteSparse_config.mk file. - -License: No licensing restrictions apply to this file or to the -SuiteSparse_config directory. +The version of SuiteSparse_config always matches the version of the SuiteSparse +meta-package. See the top-level SuiteSparse/ChangeLog for changes to this +package. -------------------------------------------------------------------------------- @@ -17,6 +12,59 @@ SuiteSparse_config contains configuration settings for all many of the software packages that I develop or co-author. Note that older versions of some of these packages do not require SuiteSparse_config. +Files in SuiteSparse_config: + + CMakeLists.txt for compiling SuiteSparse_config + Makefile simple Makefile to control cmake (optional) + README.txt this file + SuiteSparse_config.c SuiteSparse-wide utilities + SuiteSparse_config.h SuiteSparse-wide include file + (created from Config/SuiteSparse_config.h) + + build/ where SuiteSparse_config is compiled + + Config/SuiteSparse_config.h.in source for SuiteSparse_config.h + Config/README.md.in README.md for all of SuiteSparse + + cmake_modules/FindSuiteSparse_config.cmake how to find SuiteSparse_config + cmake_modules/SuiteSparseBLAS.cmake find BLAS for SuiteSparse + cmake_modules/SuiteSparseBLAS32.cmake when a 32-bit BLAS is found + cmake_modules/SuiteSparseBLAS64.cmake when a 64-bit BLAS is found + cmake_modules/SuiteSparseLAPACK.cmake find LAPACK for SuiteSparse + cmake_modules/SuiteSparsePolicy.cmake SuiteSparse-wide policies + cmake_modules/SuiteSparseReport.cmake SuiteSparse-wide reporting + +For packages that use cmake and require SuiteSparse_config, see: + + ../SuiteSparse_config/cmake_modules/FindSuiteSparse_config.cmake + +To compile/install SuiteSparse_config on Linux/MacOS, in this directory do: + + make + sudo make install + +To compile all of SuiteSparse for installation only in ../lib +and ../include instead: + + make local + +Within each package, to install only in ../lib and ../include, +for example for UMFPACK: + + cd UMFPACK + make local + make install + +To clean up: + + make clean + +See the SuiteSparse/SuiteSparse_config/Makefile for more options. + +-------------------------------------------------------------------------------- +SuiteSparse packages: +-------------------------------------------------------------------------------- + Package Description ------- ----------- AMD approximate minimum degree ordering @@ -32,6 +80,7 @@ these packages do not require SuiteSparse_config. LPDASA LP Dual Active Set Algorithm RBio read/write files in Rutherford/Boeing format SPQR sparse QR factorization (full name: SuiteSparseQR) + SPEX sparse left-looking integer-preserving LU factorization SuiteSparse_config is not required by these packages: @@ -39,14 +88,7 @@ SuiteSparse_config is not required by these packages: MATLAB_Tools toolboxes for use in MATLAB GraphBLAS graph algorithms in the language of linear algebra -In addition, the xerbla/ directory contains Fortan and C versions of the -BLAS/LAPACK xerbla routine, which is called when an invalid input is passed to -the BLAS or LAPACK. The xerbla provided here does not print any message, so -the entire Fortran I/O library does not need to be linked into a C application. -Most versions of the BLAS contain xerbla, but those from K. Goto do not. Use -this if you need too. - -If you edit this directory (SuiteSparse_config.mk in particular) then you -must do "make purge ; make" in the parent directory to recompile all of -SuiteSparse. Otherwise, the changes will not necessarily be applied. +If you edit this directory then you should do "make purge ; make" in the parent +directory to recompile all of SuiteSparse. Otherwise, the changes will not +necessarily be applied. diff --git a/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/SuiteSparse_config.c b/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/SuiteSparse_config.c index 595e46781..079093716 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/SuiteSparse_config.c +++ b/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/SuiteSparse_config.c @@ -1,44 +1,34 @@ -/* ========================================================================== */ -/* === SuiteSparse_config =================================================== */ -/* ========================================================================== */ +//------------------------------------------------------------------------------ +// SuiteSparse_config/SuiteSparse_config.c: common utilites for SuiteSparse +//------------------------------------------------------------------------------ -/* SuiteSparse configuration : memory manager and printf functions. */ +// SuiteSparse_config, Copyright (c) 2012-2023, Timothy A. Davis. +// All Rights Reserved. +// SPDX-License-Identifier: BSD-3-clause -/* Copyright (c) 2013-2018, Timothy A. Davis. No licensing restrictions - * apply to this file or to the SuiteSparse_config directory. - * Author: Timothy A. Davis. - */ - -#include -#include - -#ifndef NPRINT -#include -#endif - -#ifdef MATLAB_MEX_FILE -#include "mex.h" -#include "matrix.h" -#endif +//------------------------------------------------------------------------------ -#ifndef NULL -#define NULL ((void *) 0) -#endif +/* SuiteSparse configuration : memory manager and printf functions. + */ +#define SUITESPARSE_LIBRARY #include "SuiteSparse_config.h" /* -------------------------------------------------------------------------- */ -/* SuiteSparse_config : a global extern struct */ +/* SuiteSparse_config : a static struct */ /* -------------------------------------------------------------------------- */ -/* The SuiteSparse_config struct is available to all SuiteSparse functions and - to all applications that use those functions. It must be modified with - care, particularly in a multithreaded context. Normally, the application - will initialize this object once, via SuiteSparse_start, possibily followed - by application-specific modifications if the applications wants to use - alternative memory manager functions. +/* The SuiteSparse_config struct is indirectly available to all SuiteSparse + functions and to all applications that use those functions. In v6.x and + earlier, it was globally visible, but it is now hidden and accessible only + by functions in this file (SuiteSparse v7.0.0 and later). + + It must be modified with care, particularly in a multithreaded context. + Normally, the application will initialize this object once, via + SuiteSparse_start, possibily followed by application-specific modifications + if the applications wants to use alternative memory manager functions. - The user can redefine these global pointers at run-time to change the + The user can redefine these pointers at run-time to change the memory manager and printf function used by SuiteSparse. If -DNMALLOC is defined at compile-time, then no memory-manager is @@ -49,7 +39,18 @@ SuiteSparse will not use printf. */ -struct SuiteSparse_config_struct SuiteSparse_config = +struct SuiteSparse_config_struct +{ + void *(*malloc_func) (size_t) ; // pointer to malloc + void *(*calloc_func) (size_t, size_t) ; // pointer to calloc + void *(*realloc_func) (void *, size_t) ; // pointer to realloc + void (*free_func) (void *) ; // pointer to free + int (*printf_func) (const char *, ...) ; // pointer to printf + double (*hypot_func) (double, double) ; // pointer to hypot + int (*divcomplex_func) (double, double, double, double, double *, double *); +} ; + +static struct SuiteSparse_config_struct SuiteSparse_config = { /* memory management functions */ @@ -80,11 +81,133 @@ struct SuiteSparse_config_struct SuiteSparse_config = NULL, #endif - SuiteSparse_hypot, + hypot, // was SuiteSparse_hypot in v5 and earlier SuiteSparse_divcomplex } ; +//------------------------------------------------------------------------------ +// SuiteSparse_config_*_get methods +//------------------------------------------------------------------------------ + +// Methods that return the contents of the SuiteSparse_config struct. + +void *(*SuiteSparse_config_malloc_func_get (void)) (size_t) +{ + return (SuiteSparse_config.malloc_func) ; +} + +void *(*SuiteSparse_config_calloc_func_get (void)) (size_t, size_t) +{ + return (SuiteSparse_config.calloc_func) ; +} + +void *(*SuiteSparse_config_realloc_func_get (void)) (void *, size_t) +{ + return (SuiteSparse_config.realloc_func) ; +} + +void (*SuiteSparse_config_free_func_get (void)) (void *) +{ + return (SuiteSparse_config.free_func) ; +} + +int (*SuiteSparse_config_printf_func_get (void)) (const char *, ...) +{ + return (SuiteSparse_config.printf_func) ; +} + +double (*SuiteSparse_config_hypot_func_get (void)) (double, double) +{ + return (SuiteSparse_config.hypot_func) ; +} + +int (*SuiteSparse_config_divcomplex_func_get (void)) (double, double, double, double, double *, double *) +{ + return (SuiteSparse_config.divcomplex_func) ; +} + +//------------------------------------------------------------------------------ +// SuiteSparse_config_*_set methods +//------------------------------------------------------------------------------ + +// Methods that set the contents of the SuiteSparse_config struct. + +void SuiteSparse_config_malloc_func_set (void *(*malloc_func) (size_t)) +{ + SuiteSparse_config.malloc_func = malloc_func ; +} + +void SuiteSparse_config_calloc_func_set (void *(*calloc_func) (size_t, size_t)) +{ + SuiteSparse_config.calloc_func = calloc_func ; +} + +void SuiteSparse_config_realloc_func_set (void *(*realloc_func) (void *, size_t)) +{ + SuiteSparse_config.realloc_func = realloc_func ; +} + +void SuiteSparse_config_free_func_set (void (*free_func) (void *)) +{ + SuiteSparse_config.free_func = free_func ; +} + +void SuiteSparse_config_printf_func_set (int (*printf_func) (const char *, ...)) +{ + SuiteSparse_config.printf_func = printf_func ; +} + +void SuiteSparse_config_hypot_func_set (double (*hypot_func) (double, double)) +{ + SuiteSparse_config.hypot_func = hypot_func ; +} + +void SuiteSparse_config_divcomplex_func_set (int (*divcomplex_func) (double, double, double, double, double *, double *)) +{ + SuiteSparse_config.divcomplex_func = divcomplex_func ; +} + +//------------------------------------------------------------------------------ +// SuiteSparse_config_*_call methods +//------------------------------------------------------------------------------ + +// Methods that directly call the functions in the SuiteSparse_config struct. +// Note that there is no wrapper for the printf_func. + +void *SuiteSparse_config_malloc (size_t s) +{ + return (SuiteSparse_config.malloc_func (s)) ; +} + +void *SuiteSparse_config_calloc (size_t n, size_t s) +{ + return (SuiteSparse_config.calloc_func (n, s)) ; +} + +void *SuiteSparse_config_realloc (void *p, size_t s) +{ + return (SuiteSparse_config.realloc_func (p, s)) ; +} + +void SuiteSparse_config_free (void *p) +{ + SuiteSparse_config.free_func (p) ; +} + +double SuiteSparse_config_hypot (double x, double y) +{ + return (SuiteSparse_config.hypot_func (x, y)) ; +} + +int SuiteSparse_config_divcomplex +( + double xr, double xi, double yr, double yi, double *zr, double *zi +) +{ + return (SuiteSparse_config.divcomplex_func (xr, xi, yr, yi, zr, zi)) ; +} + /* -------------------------------------------------------------------------- */ /* SuiteSparse_start */ /* -------------------------------------------------------------------------- */ @@ -143,7 +266,7 @@ void SuiteSparse_start ( void ) #endif /* math functions */ - SuiteSparse_config.hypot_func = SuiteSparse_hypot ; + SuiteSparse_config.hypot_func = hypot ; // was SuiteSparse_hypot in v5 SuiteSparse_config.divcomplex_func = SuiteSparse_divcomplex ; } @@ -194,7 +317,6 @@ void *SuiteSparse_malloc /* pointer to allocated block of memory */ return (p) ; } - /* -------------------------------------------------------------------------- */ /* SuiteSparse_calloc: calloc wrapper */ /* -------------------------------------------------------------------------- */ @@ -312,7 +434,6 @@ void *SuiteSparse_free /* always returns NULL */ return (NULL) ; } - /* -------------------------------------------------------------------------- */ /* SuiteSparse_tic: return current wall clock time */ /* -------------------------------------------------------------------------- */ @@ -344,36 +465,57 @@ void *SuiteSparse_free /* always returns NULL */ * include file. */ -#ifdef SUITESPARSE_TIMER_ENABLED +#if !defined ( SUITESPARSE_TIMER_ENABLED ) -#include + /* ---------------------------------------------------------------------- */ + /* no timer */ + /* ---------------------------------------------------------------------- */ -void SuiteSparse_tic -( - double tic [2] /* output, contents undefined on input */ -) -{ - /* POSIX C 1993 timer, requires -librt */ - struct timespec t ; - clock_gettime (CLOCK_MONOTONIC, &t) ; - tic [0] = (double) (t.tv_sec) ; - tic [1] = (double) (t.tv_nsec) ; -} + void SuiteSparse_tic + ( + double tic [2] /* output, contents undefined on input */ + ) + { + /* no timer installed */ + tic [0] = 0 ; + tic [1] = 0 ; + } -#else +#elif defined ( _OPENMP ) -void SuiteSparse_tic -( - double tic [2] /* output, contents undefined on input */ -) -{ - /* no timer installed */ - tic [0] = 0 ; - tic [1] = 0 ; -} + /* ---------------------------------------------------------------------- */ + /* OpenMP timer */ + /* ---------------------------------------------------------------------- */ -#endif + void SuiteSparse_tic + ( + double tic [2] /* output, contents undefined on input */ + ) + { + tic [0] = omp_get_wtime ( ) ; + tic [1] = 0 ; + } + +#else + + /* ---------------------------------------------------------------------- */ + /* POSIX timer */ + /* ---------------------------------------------------------------------- */ + + #include + void SuiteSparse_tic + ( + double tic [2] /* output, contents undefined on input */ + ) + { + /* POSIX C 1993 timer, requires -lrt */ + struct timespec t ; + clock_gettime (CLOCK_MONOTONIC, &t) ; + tic [0] = (double) (t.tv_sec) ; + tic [1] = (double) (t.tv_nsec) ; + } +#endif /* -------------------------------------------------------------------------- */ /* SuiteSparse_toc: return time since last tic */ @@ -396,7 +538,6 @@ double SuiteSparse_toc /* returns time in seconds since last tic */ return ((toc [0] - tic [0]) + 1e-9 * (toc [1] - tic [1])) ; } - /* -------------------------------------------------------------------------- */ /* SuiteSparse_time: return current wallclock time in seconds */ /* -------------------------------------------------------------------------- */ @@ -413,7 +554,6 @@ double SuiteSparse_time /* returns current wall clock time in seconds */ return (toc [0] + 1e-9 * toc [1]) ; } - /* -------------------------------------------------------------------------- */ /* SuiteSparse_version: return the current version of SuiteSparse */ /* -------------------------------------------------------------------------- */ @@ -432,26 +572,22 @@ int SuiteSparse_version return (SUITESPARSE_VERSION) ; } -/* -------------------------------------------------------------------------- */ -/* SuiteSparse_hypot */ -/* -------------------------------------------------------------------------- */ +//------------------------------------------------------------------------------ +// SuiteSparse_hypot +//------------------------------------------------------------------------------ -/* There is an equivalent routine called hypot in , which conforms - * to ANSI C99. However, SuiteSparse does not assume that ANSI C99 is - * available. You can use the ANSI C99 hypot routine with: - * - * #include - *i SuiteSparse_config.hypot_func = hypot ; - * - * Default value of the SuiteSparse_config.hypot_func pointer is - * SuiteSparse_hypot, defined below. - * - * s = hypot (x,y) computes s = sqrt (x*x + y*y) but does so more accurately. - * The NaN cases for the double relops x >= y and x+y == x are safely ignored. - * - * Source: Algorithm 312, "Absolute value and square root of a complex number," - * P. Friedland, Comm. ACM, vol 10, no 10, October 1967, page 665. - */ +// SuiteSparse_config v5 and earlier used SuiteSparse_hypot, defined below. +// SuiteSparse_config v6 now uses the hypot method in , by default. +// The hypot function appears in ANSI C99 and later, and SuiteSparse now +// assumes ANSI C11. + +// s = hypot (x,y) computes s = sqrt (x*x + y*y) but does so more accurately. +// The NaN cases for the double relops x >= y and x+y == x are safely ignored. + +// Source: Algorithm 312, "Absolute value and square root of a complex number," +// P. Friedland, Comm. ACM, vol 10, no 10, October 1967, page 665. + +// This method below is kept for historical purposes. double SuiteSparse_hypot (double x, double y) { @@ -485,47 +621,162 @@ double SuiteSparse_hypot (double x, double y) return (s) ; } -/* -------------------------------------------------------------------------- */ -/* SuiteSparse_divcomplex */ -/* -------------------------------------------------------------------------- */ - -/* c = a/b where c, a, and b are complex. The real and imaginary parts are - * passed as separate arguments to this routine. The NaN case is ignored - * for the double relop br >= bi. Returns 1 if the denominator is zero, - * 0 otherwise. - * - * This uses ACM Algo 116, by R. L. Smith, 1962, which tries to avoid - * underflow and overflow. - * - * c can be the same variable as a or b. - * - * Default value of the SuiteSparse_config.divcomplex_func pointer is - * SuiteSparse_divcomplex. - */ +//------------------------------------------------------------------------------ +// SuiteSparse_divcomplex +//------------------------------------------------------------------------------ + +// z = x/y where z, x, and y are complex. The real and imaginary parts are +// passed as separate arguments to this routine. The NaN case is ignored +// for the double relop yr >= yi. Returns 1 if the denominator is zero, +// 0 otherwise. +// +// This uses ACM Algo 116, by R. L. Smith, 1962, which tries to avoid +// underflow and overflow. +// +// z can be the same variable as x or y. +// +// Default value of the SuiteSparse_config.divcomplex_func pointer is +// SuiteSparse_divcomplex. +// +// This function is identical to GB_divcomplex in GraphBLAS/Source/GB_math.h. +// The only difference is the name of the function. int SuiteSparse_divcomplex ( - double ar, double ai, /* real and imaginary parts of a */ - double br, double bi, /* real and imaginary parts of b */ - double *cr, double *ci /* real and imaginary parts of c */ + double xr, double xi, // real and imaginary parts of x + double yr, double yi, // real and imaginary parts of y + double *zr, double *zi // real and imaginary parts of z ) { double tr, ti, r, den ; - if (fabs (br) >= fabs (bi)) + + int yr_class = fpclassify (yr) ; + int yi_class = fpclassify (yi) ; + + if (yi_class == FP_ZERO) + { + den = yr ; + if (xi == 0) + { + tr = xr / den ; + ti = 0 ; + } + else if (xr == 0) + { + tr = 0 ; + ti = xi / den ; + } + else + { + tr = xr / den ; + ti = xi / den ; + } + } + else if (yr_class == FP_ZERO) { - r = bi / br ; - den = br + r * bi ; - tr = (ar + ai * r) / den ; - ti = (ai - ar * r) / den ; + den = yi ; + if (xr == 0) + { + tr = xi / den ; + ti = 0 ; + } + else if (xi == 0) + { + tr = 0 ; + ti = -xr / den ; + } + else + { + tr = xi / den ; + ti = -xr / den ; + } + } + else if (yi_class == FP_INFINITE && yr_class == FP_INFINITE) + { + + if (signbit (yr) == signbit (yi)) + { + // r = 1 + den = yr + yi ; + tr = (xr + xi) / den ; + ti = (xi - xr) / den ; + } + else + { + // r = -1 + den = yr - yi ; + tr = (xr - xi) / den ; + ti = (xi + xr) / den ; + } + } else { - r = br / bi ; - den = r * br + bi ; - tr = (ar * r + ai) / den ; - ti = (ai * r - ar) / den ; + + if (fabs (yr) >= fabs (yi)) + { + r = yi / yr ; + den = yr + r * yi ; + tr = (xr + xi * r) / den ; + ti = (xi - xr * r) / den ; + } + else + { + r = yr / yi ; + den = r * yr + yi ; + tr = (xr * r + xi) / den ; + ti = (xi * r - xr) / den ; + } + } - *cr = tr ; - *ci = ti ; - return (den == 0.) ; + (*zr) = tr ; + (*zi) = ti ; + return (den == 0) ; +} + +//------------------------------------------------------------------------------ +// SuiteSparse_BLAS_library: return name of BLAS library found +//------------------------------------------------------------------------------ + +// Returns the name of the BLAS library found by SuiteSparse_config + +const char *SuiteSparse_BLAS_library ( void ) +{ + #if defined ( BLAS_Intel10_64ilp ) + return ("Intel MKL 64ilp BLAS (64-bit integers)") ; + #elif defined ( BLAS_Intel10_64lp ) + return ("Intel MKL 64lp BLAS (32-bit integers)") ; + #elif defined ( BLAS_Apple ) + return ("Apple Accelerate Framework BLAS (32-bit integers)") ; + #elif defined ( BLAS_Arm_ilp64_mp ) + return ("ARM MP BLAS (64-bit integers)") ; + #elif defined ( BLAS_Arm_mp ) + return ("ARM MP BLAS (32-bit integers)") ; + #elif defined ( BLAS_IBMESSL_SMP ) + return ((sizeof (SUITESPARSE_BLAS_INT) == 8) ? + "IBMESSL_SMP BLAS (64-bit integers)" : + "IBMESSL_SMP BLAS (32-bit integers)") ; + #elif defined ( BLAS_OpenBLAS ) + return ((sizeof (SUITESPARSE_BLAS_INT) == 8) ? + "OpenBLAS (64-bit integers)" : + "OpenBLAS (32-bit integers)") ; + #elif defined ( BLAS_Generic ) + return ((sizeof (SUITESPARSE_BLAS_INT) == 8) ? + "Reference BLAS (64-bit integers)" : + "Reference BLAS (32-bit integers)") ; + #else + return ((sizeof (SUITESPARSE_BLAS_INT) == 8) ? + "Other BLAS (64-bit integers)" : + "Other BLAS (32-bit integers)") ; + #endif } + +//------------------------------------------------------------------------------ +// SuiteSparse_BLAS_integer: return size of BLAS integer +//------------------------------------------------------------------------------ + +size_t SuiteSparse_BLAS_integer_size ( void ) +{ + return (sizeof (SUITESPARSE_BLAS_INT)) ; +} + diff --git a/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/SuiteSparse_config.h b/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/SuiteSparse_config.h index 9e28c0530..2b917bded 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/SuiteSparse_config.h +++ b/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/SuiteSparse_config.h @@ -1,216 +1,1408 @@ -/* ========================================================================== */ -/* === SuiteSparse_config =================================================== */ -/* ========================================================================== */ - -/* Configuration file for SuiteSparse: a Suite of Sparse matrix packages - * (AMD, COLAMD, CCOLAMD, CAMD, CHOLMOD, UMFPACK, CXSparse, and others). - * - * SuiteSparse_config.h provides the definition of the long integer. On most - * systems, a C program can be compiled in LP64 mode, in which long's and - * pointers are both 64-bits, and int's are 32-bits. Windows 64, however, uses - * the LLP64 model, in which int's and long's are 32-bits, and long long's and - * pointers are 64-bits. - * - * SuiteSparse packages that include long integer versions are - * intended for the LP64 mode. However, as a workaround for Windows 64 - * (and perhaps other systems), the long integer can be redefined. - * - * If _WIN64 is defined, then the __int64 type is used instead of long. - * - * The long integer can also be defined at compile time. For example, this - * could be added to SuiteSparse_config.mk: - * - * CFLAGS = -O -D'SuiteSparse_long=long long' \ - * -D'SuiteSparse_long_max=9223372036854775801' -D'SuiteSparse_long_idd="lld"' - * - * This file defines SuiteSparse_long as either long (on all but _WIN64) or - * __int64 on Windows 64. The intent is that a SuiteSparse_long is always a - * 64-bit integer in a 64-bit code. ptrdiff_t might be a better choice than - * long; it is always the same size as a pointer. - * - * This file also defines the SUITESPARSE_VERSION and related definitions. - * - * Copyright (c) 2012, Timothy A. Davis. No licensing restrictions apply - * to this file or to the SuiteSparse_config directory. - * Author: Timothy A. Davis. - */ +//------------------------------------------------------------------------------ +// SuiteSparse_config/SuiteSparse_config.h: common utilites for SuiteSparse +//------------------------------------------------------------------------------ + +// SuiteSparse_config, Copyright (c) 2012-2023, Timothy A. Davis. +// All Rights Reserved. +// SPDX-License-Identifier: BSD-3-clause + +//------------------------------------------------------------------------------ + +// Configuration file for SuiteSparse: a Suite of Sparse matrix packages: AMD, +// COLAMD, CCOLAMD, CAMD, CHOLMOD, UMFPACK, CXSparse, SuiteSparseQR, ParU, ... + +// The SuiteSparse_config.h file is configured by CMake to be specific to the +// C/C++ compiler and BLAS library being used for SuiteSparse. The original +// file is SuiteSparse_config/SuiteSparse_config.h.in. Do not edit the +// SuiteSparse_config.h file directly. #ifndef SUITESPARSE_CONFIG_H #define SUITESPARSE_CONFIG_H #ifdef __cplusplus -extern "C" { +extern "C" +{ #endif -#include +//------------------------------------------------------------------------------ +// SuiteSparse-wide ANSI C11 #include files +//------------------------------------------------------------------------------ + +#include +#include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include + +//------------------------------------------------------------------------------ +// SuiteSparse_long is now int64_t in SuiteSparse v6.0.0 and later +//------------------------------------------------------------------------------ -/* ========================================================================== */ -/* === SuiteSparse_long ===================================================== */ -/* ========================================================================== */ +// The use of SuiteSparse_long is deprecated. User applications should use +// int64_t instead. -#ifndef SuiteSparse_long +#undef SuiteSparse_long +#undef SuiteSparse_long_max +#undef SuiteSparse_long_idd +#undef SuiteSparse_long_id -#ifdef _WIN64 +#define SuiteSparse_long int64_t +#define SuiteSparse_long_max INT64_MAX +#define SuiteSparse_long_idd PRId64 +#define SuiteSparse_long_id "%" SuiteSparse_long_idd + +//------------------------------------------------------------------------------ +// OpenMP +//------------------------------------------------------------------------------ + +#if defined ( _OPENMP ) -#define SuiteSparse_long __int64 -#define SuiteSparse_long_max _I64_MAX -#define SuiteSparse_long_idd "I64d" + #include + #define SUITESPARSE_OPENMP_MAX_THREADS omp_get_max_threads ( ) + #define SUITESPARSE_OPENMP_GET_NUM_THREADS omp_get_num_threads ( ) + #define SUITESPARSE_OPENMP_GET_WTIME omp_get_wtime ( ) + #define SUITESPARSE_OPENMP_GET_THREAD_ID omp_get_thread_num ( ) #else -#define SuiteSparse_long long -#define SuiteSparse_long_max LONG_MAX -#define SuiteSparse_long_idd "ld" + // OpenMP not available + #define SUITESPARSE_OPENMP_MAX_THREADS (1) + #define SUITESPARSE_OPENMP_GET_NUM_THREADS (1) + #define SUITESPARSE_OPENMP_GET_WTIME (0) + #define SUITESPARSE_OPENMP_GET_THREAD_ID (0) #endif -#define SuiteSparse_long_id "%" SuiteSparse_long_idd + +//------------------------------------------------------------------------------ +// MATLAB/Octave +//------------------------------------------------------------------------------ + +#if defined ( MATLAB_MEX_FILE ) +#include "mex.h" +#include "matrix.h" #endif -/* ========================================================================== */ -/* === SuiteSparse_config parameters and functions ========================== */ -/* ========================================================================== */ +//------------------------------------------------------------------------------ +// string and token handling macros +//------------------------------------------------------------------------------ -/* SuiteSparse-wide parameters are placed in this struct. It is meant to be - an extern, globally-accessible struct. It is not meant to be updated - frequently by multiple threads. Rather, if an application needs to modify - SuiteSparse_config, it should do it once at the beginning of the application, - before multiple threads are launched. +// SUITESPARSE_STR: convert the content of x into a string "x" +#define SUITESPARSE_XSTR(x) SUITESPARSE_STR(x) +#define SUITESPARSE_STR(x) #x - The intent of these function pointers is that they not be used in your - application directly, except to assign them to the desired user-provided - functions. Rather, you should use the - */ +// SUITESPARSE_CAT(x,y): concatenate two tokens +#define SUITESPARSE_CAT2(x,y) x ## y +#define SUITESPARSE_CAT(x,y) SUITESPARSE_CAT2(x,y) -struct SuiteSparse_config_struct -{ - void *(*malloc_func) (size_t) ; /* pointer to malloc */ - void *(*calloc_func) (size_t, size_t) ; /* pointer to calloc */ - void *(*realloc_func) (void *, size_t) ; /* pointer to realloc */ - void (*free_func) (void *) ; /* pointer to free */ - int (*printf_func) (const char *, ...) ; /* pointer to printf */ - double (*hypot_func) (double, double) ; /* pointer to hypot */ - int (*divcomplex_func) (double, double, double, double, double *, double *); -} ; +//------------------------------------------------------------------------------ +// determine which compiler is in use +//------------------------------------------------------------------------------ + +#define SUITESPARSE_COMPILER_NVCC 0 +#define SUITESPARSE_COMPILER_ICX 0 +#define SUITESPARSE_COMPILER_ICC 0 +#define SUITESPARSE_COMPILER_CLANG 0 +#define SUITESPARSE_COMPILER_GCC 0 +#define SUITESPARSE_COMPILER_MSC 0 +#define SUITESPARSE_COMPILER_XLC 0 + +#if defined ( __NVCC__ ) + + // NVIDIA nvcc compiler + #undef SUITESPARSE_COMPILER_NVCC + #define SUITESPARSE_COMPILER_NVCC 1 + + #define SUITESPARSE_COMPILER_MAJOR __CUDACC_VER_MAJOR__ + #define SUITESPARSE_COMPILER_MINOR __CUDACC_VER_MINOR__ + #define SUITESPARSE_COMPILER_SUB __CUDACC_VER_BUILD__ + #define SUITESPARSE_COMPILER_NAME "nvcc" + +#elif defined ( __INTEL_CLANG_COMPILER ) + + // Intel icx compiler, 2022.0.0 based on clang/llvm 14.0.0 + #undef SUITESPARSE_COMPILER_ICX + #define SUITESPARSE_COMPILER_ICX 1 + + #define SUITESPARSE_COMPILER_MAJOR __INTEL_CLANG_COMPILER + #define SUITESPARSE_COMPILER_MINOR 0 + #define SUITESPARSE_COMPILER_SUB 0 + #define SUITESPARSE_COMPILER_NAME __VERSION__ + +#elif defined ( __INTEL_COMPILER ) + + // Intel icc compiler: 2021.5.0 uses "gcc 7.5 mode" + #undef SUITESPARSE_COMPILER_ICC + #define SUITESPARSE_COMPILER_ICC 1 + + #define SUITESPARSE_COMPILER_MAJOR __INTEL_COMPILER + #define SUITESPARSE_COMPILER_MINOR __INTEL_COMPILER_UPDATE + #define SUITESPARSE_COMPILER_SUB 0 + #define SUITESPARSE_COMPILER_NAME __VERSION__ + +#elif defined ( __clang__ ) + + // clang + #undef SUITESPARSE_COMPILER_CLANG + #define SUITESPARSE_COMPILER_CLANG 1 + + #define SUITESPARSE_COMPILER_MAJOR __clang_major__ + #define SUITESPARSE_COMPILER_MINOR __clang_minor__ + #define SUITESPARSE_COMPILER_SUB __clang_patchlevel__ + #define SUITESPARSE_COMPILER_NAME "clang " __clang_version__ + +#elif defined ( __xlC__ ) + + // xlc + #undef SUITESPARSE_COMPILER_XLC + #define SUITESPARSE_COMPILER_XLC 1 + + #define SUITESPARSE_COMPILER_MAJOR ( __xlC__ / 256 ) + #define SUITESPARSE_COMPILER_MINOR \ + ( __xlC__ - 256 * SUITESPARSE_COMPILER_MAJOR) + #define SUITESPARSE_COMPILER_SUB 0 + #define SUITESPARSE_COMPILER_NAME "IBM xlc " SUITESPARSE_XSTR (__xlC__) + +#elif defined ( __GNUC__ ) + + // gcc + #undef SUITESPARSE_COMPILER_GCC + #define SUITESPARSE_COMPILER_GCC 1 + + #define SUITESPARSE_COMPILER_MAJOR __GNUC__ + #define SUITESPARSE_COMPILER_MINOR __GNUC_MINOR__ + #define SUITESPARSE_COMPILER_SUB __GNUC_PATCHLEVEL__ + #define SUITESPARSE_COMPILER_NAME "GNU gcc " \ + SUITESPARSE_XSTR (__GNUC__) "." \ + SUITESPARSE_XSTR (__GNUC_MINOR__) "." \ + SUITESPARSE_XSTR (__GNUC_PATCHLEVEL__) + +#elif defined ( _MSC_VER ) + + // Microsoft Visual Studio (cl compiler) + #undef SUITESPARSE_COMPILER_MSC + #define SUITESPARSE_COMPILER_MSC 1 + + #define SUITESPARSE_COMPILER_MAJOR ( _MSC_VER / 100 ) + #define SUITESPARSE_COMPILER_MINOR \ + ( _MSC_VER - 100 * SUITESPARSE_COMPILER_MAJOR) + #define SUITESPARSE_COMPILER_SUB 0 + #define SUITESPARSE_COMPILER_NAME \ + "Microsoft Visual Studio " SUITESPARSE_XSTR (_MSC_VER) + +#else + + // other compiler + #define SUITESPARSE_COMPILER_MAJOR 0 + #define SUITESPARSE_COMPILER_MINOR 0 + #define SUITESPARSE_COMPILER_SUB 0 + #define SUITESPARSE_COMPILER_NAME "other C compiler" + +#endif + +//------------------------------------------------------------------------------ +// malloc.h: required include file for Microsoft Visual Studio +//------------------------------------------------------------------------------ + +#if SUITESPARSE_COMPILER_MSC + #include +#endif + +// this was formerly "extern", or "__declspec ..." for Windows. +#define SUITESPARSE_PUBLIC + +//------------------------------------------------------------------------------ +// determine the ANSI C version +//------------------------------------------------------------------------------ + +#ifdef __STDC_VERSION__ +// ANSI C17: 201710L +// ANSI C11: 201112L +// ANSI C99: 199901L +// ANSI C95: 199409L +#define SUITESPARSE_STDC_VERSION __STDC_VERSION__ +#else +// assume ANSI C90 / C89 +#define SUITESPARSE_STDC_VERSION 199001L +#endif + +//------------------------------------------------------------------------------ +// handle the restrict keyword +//------------------------------------------------------------------------------ + +#if defined ( __cplusplus ) + + // C++ does not have the "restrict" keyword + #define SUITESPARSE_RESTRICT + +#elif SUITESPARSE_COMPILER_MSC + + // MS Visual Studio + #define SUITESPARSE_RESTRICT __restrict + +#elif SUITESPARSE_COMPILER_NVCC + + // NVIDIA nvcc + #define SUITESPARSE_RESTRICT __restrict__ + +#elif SUITESPARSE_STDC_VERSION >= 199901L + + // ANSI C99 or later + #define SUITESPARSE_RESTRICT restrict + +#else + + // ANSI C95 and earlier: no restrict keyword + #define SUITESPARSE_RESTRICT + +#endif + +//============================================================================== +// SuiteSparse_config parameters and functions +//============================================================================== + +// SuiteSparse-wide parameters are placed in a single static struct, defined +// locally in SuiteSparse_config.c. It is not meant to be updated frequently +// by multiple threads. Rather, if an application needs to modify +// SuiteSparse_config, it should do it once at the beginning of the +// application, before multiple threads are launched. + +// The intent of these function pointers is that they not be used in your +// application directly, except to assign them to the desired user-provided +// functions. Rather, you should use the SuiteSparse_malloc/calloc, etc +// wrappers defined below to access them. + +// The SuiteSparse_config_*_get methods return the contents of the struct: +void *(*SuiteSparse_config_malloc_func_get (void)) (size_t); +void *(*SuiteSparse_config_calloc_func_get (void)) (size_t, size_t); +void *(*SuiteSparse_config_realloc_func_get (void)) (void *, size_t); +void (*SuiteSparse_config_free_func_get (void)) (void *); +int (*SuiteSparse_config_printf_func_get (void)) (const char *, ...); +double (*SuiteSparse_config_hypot_func_get (void)) (double, double); +int (*SuiteSparse_config_divcomplex_func_get (void)) (double, double, double, double, double *, double *); + +// The SuiteSparse_config_*_set methods modify the contents of the struct: +void SuiteSparse_config_malloc_func_set (void *(*malloc_func) (size_t)); +void SuiteSparse_config_calloc_func_set (void *(*calloc_func) (size_t, size_t)); +void SuiteSparse_config_realloc_func_set (void *(*realloc_func) (void *, size_t)); +void SuiteSparse_config_free_func_set (void (*free_func) (void *)); +void SuiteSparse_config_printf_func_set (int (*printf_func) (const char *, ...)); +void SuiteSparse_config_hypot_func_set (double (*hypot_func) (double, double)); +void SuiteSparse_config_divcomplex_func_set (int (*divcomplex_func) (double, double, double, double, double *, double *)); -extern struct SuiteSparse_config_struct SuiteSparse_config ; +// The SuiteSparse_config_*_func methods are wrappers that call the function +// pointers in the struct. Note that there is no wrapper for the printf_func. +// See the SUITESPARSE_PRINTF macro instead. +void *SuiteSparse_config_malloc (size_t s) ; +void *SuiteSparse_config_calloc (size_t n, size_t s) ; +void *SuiteSparse_config_realloc (void *, size_t s) ; +void SuiteSparse_config_free (void *) ; +double SuiteSparse_config_hypot (double x, double y) ; +int SuiteSparse_config_divcomplex +( + double xr, double xi, double yr, double yi, double *zr, double *zi +) ; -void SuiteSparse_start ( void ) ; /* called to start SuiteSparse */ +void SuiteSparse_start ( void ) ; // called to start SuiteSparse -void SuiteSparse_finish ( void ) ; /* called to finish SuiteSparse */ +void SuiteSparse_finish ( void ) ; // called to finish SuiteSparse -void *SuiteSparse_malloc /* pointer to allocated block of memory */ +void *SuiteSparse_malloc // pointer to allocated block of memory ( - size_t nitems, /* number of items to malloc (>=1 is enforced) */ - size_t size_of_item /* sizeof each item */ + size_t nitems, // number of items to malloc (>=1 is enforced) + size_t size_of_item // sizeof each item ) ; -void *SuiteSparse_calloc /* pointer to allocated block of memory */ +void *SuiteSparse_calloc // pointer to allocated block of memory ( - size_t nitems, /* number of items to calloc (>=1 is enforced) */ - size_t size_of_item /* sizeof each item */ + size_t nitems, // number of items to calloc (>=1 is enforced) + size_t size_of_item // sizeof each item ) ; -void *SuiteSparse_realloc /* pointer to reallocated block of memory, or - to original block if the realloc failed. */ +void *SuiteSparse_realloc // pointer to reallocated block of memory, or + ///to original block if the realloc failed. ( - size_t nitems_new, /* new number of items in the object */ - size_t nitems_old, /* old number of items in the object */ - size_t size_of_item, /* sizeof each item */ - void *p, /* old object to reallocate */ - int *ok /* 1 if successful, 0 otherwise */ + size_t nitems_new, // new number of items in the object + size_t nitems_old, // old number of items in the object + size_t size_of_item, // sizeof each item + void *p, // old object to reallocate + int *ok // 1 if successful, 0 otherwise ) ; -void *SuiteSparse_free /* always returns NULL */ +void *SuiteSparse_free // always returns NULL ( - void *p /* block to free */ + void *p // block to free ) ; -void SuiteSparse_tic /* start the timer */ +void SuiteSparse_tic // start the timer ( - double tic [2] /* output, contents undefined on input */ + double tic [2] // output, contents undefined on input ) ; -double SuiteSparse_toc /* return time in seconds since last tic */ +double SuiteSparse_toc // return time in seconds since last tic ( - double tic [2] /* input: from last call to SuiteSparse_tic */ + double tic [2] // input: from last call to SuiteSparse_tic ) ; -double SuiteSparse_time /* returns current wall clock time in seconds */ +double SuiteSparse_time // returns current wall clock time in seconds ( void ) ; -/* returns sqrt (x^2 + y^2), computed reliably */ +// returns sqrt (x^2 + y^2), computed reliably double SuiteSparse_hypot (double x, double y) ; -/* complex division of c = a/b */ +// complex division of c = a/b int SuiteSparse_divcomplex ( - double ar, double ai, /* real and imaginary parts of a */ - double br, double bi, /* real and imaginary parts of b */ - double *cr, double *ci /* real and imaginary parts of c */ + double ar, double ai, // real and imaginary parts of a + double br, double bi, // real and imaginary parts of b + double *cr, double *ci // real and imaginary parts of c ) ; -/* determine which timer to use, if any */ +// determine which timer to use, if any #ifndef NTIMER -#ifdef _POSIX_C_SOURCE -#if _POSIX_C_SOURCE >= 199309L -#define SUITESPARSE_TIMER_ENABLED -#endif -#endif + #if defined ( _OPENMP ) + #define SUITESPARSE_TIMER_ENABLED + #elif defined ( _POSIX_C_SOURCE ) + #if _POSIX_C_SOURCE >= 199309L + #define SUITESPARSE_TIMER_ENABLED + #endif + #endif #endif -/* SuiteSparse printf macro */ -#define SUITESPARSE_PRINTF(params) \ -{ \ - if (SuiteSparse_config.printf_func != NULL) \ - { \ - (void) (SuiteSparse_config.printf_func) params ; \ - } \ -} - -/* ========================================================================== */ -/* === SuiteSparse version ================================================== */ -/* ========================================================================== */ - -/* SuiteSparse is not a package itself, but a collection of packages, some of - * which must be used together (UMFPACK requires AMD, CHOLMOD requires AMD, - * COLAMD, CAMD, and CCOLAMD, etc). A version number is provided here for the - * collection itself, which is also the version number of SuiteSparse_config. - */ - -int SuiteSparse_version /* returns SUITESPARSE_VERSION */ -( - /* output, not defined on input. Not used if NULL. Returns - the three version codes in version [0..2]: - version [0] is SUITESPARSE_MAIN_VERSION - version [1] is SUITESPARSE_SUB_VERSION - version [2] is SUITESPARSE_SUBSUB_VERSION - */ +// SuiteSparse printf macro +#define SUITESPARSE_PRINTF(params) \ +{ \ + int (*printf_func) (const char *, ...) ; \ + printf_func = SuiteSparse_config_printf_func_get ( ) ; \ + if (printf_func != NULL) \ + { \ + (void) (printf_func) params ; \ + } \ +} + +//============================================================================== +// SuiteSparse version +//============================================================================== + +// SuiteSparse is not a package itself, but a collection of packages, some of +// which must be used together (UMFPACK requires AMD, CHOLMOD requires AMD, +// COLAMD, CAMD, and CCOLAMD, etc). A version number is provided here for the +// collection itself, which is also the version number of SuiteSparse_config. + +int SuiteSparse_version // returns SUITESPARSE_VERSION +( + // output, not defined on input. Not used if NULL. Returns + // the three version codes in version [0..2]: + // version [0] is SUITESPARSE_MAIN_VERSION + // version [1] is SUITESPARSE_SUB_VERSION + // version [2] is SUITESPARSE_SUBSUB_VERSION int version [3] ) ; -/* Versions prior to 4.2.0 do not have the above function. The following - code fragment will work with any version of SuiteSparse: - - #ifdef SUITESPARSE_HAS_VERSION_FUNCTION - v = SuiteSparse_version (NULL) ; - #else - v = SUITESPARSE_VERSION ; - #endif -*/ #define SUITESPARSE_HAS_VERSION_FUNCTION -#define SUITESPARSE_DATE "Dec 28, 2018" +#define SUITESPARSE_DATE "Jan 20, 2023" +#define SUITESPARSE_MAIN_VERSION 7 +#define SUITESPARSE_SUB_VERSION 0 +#define SUITESPARSE_SUBSUB_VERSION 1 + #define SUITESPARSE_VER_CODE(main,sub) ((main) * 1000 + (sub)) -#define SUITESPARSE_MAIN_VERSION 5 -#define SUITESPARSE_SUB_VERSION 4 -#define SUITESPARSE_SUBSUB_VERSION 0 #define SUITESPARSE_VERSION \ SUITESPARSE_VER_CODE(SUITESPARSE_MAIN_VERSION,SUITESPARSE_SUB_VERSION) +//============================================================================== +// SuiteSparse interface to the BLAS and LAPACK libraries +//============================================================================== + +// Several SuiteSparse packages rely on the BLAS/LAPACK libraries (UMFPACK +// CHOLMOD, and SPQR, and likely GraphBLAS in the future). All of these +// packages are written in C/C++, but rely on the Fortran interface to +// BLAS/LAPACK. SuiteSparse does not use the cblas / lapacke interfaces to +// these libraries, mainly because FindBLAS.cmake does not locate them (or at +// least does not locate their respective cblas.h and lapacke.h files). In +// addition, the original definition of these files do not include a different +// name space for 64-bit integer versions. Finally, Intel renames cblas.h as +// mkl_cblas.h. As a result of these many portability issues, different +// implementations of those libraries extend them in different ways. Thus, +// SuiteSparse simply calls the Fortran functions directly. + +// However, the method for how C/C++ calling Fortran depends on the compilers +// involved. This connection is handled by the FortranCInterface.cmake module +// of CMake. + +// On typical systems (Linux with the GCC compiler for example, or on the Mac +// with clang) the Fortan name "dgemm" is called by C as "dgemm_", Other +// systems do not append the underscore. + +//------------------------------------------------------------------------------ +// SUITESPARSE_FORTRAN: macros created by CMake describing how C calls Fortran +//------------------------------------------------------------------------------ + +// SUITESPARSE_FORTAN: for Fortran routines with no "_" in their names +// SUITESPARSE__FORTAN: for Fortran routines with "_" in their names + +// The decision on which of these macros to use is based on the presence of +// underscores in the original Fortran names, not the (commonly) appended +// underscore needed for C to all the corresponding Fortran routine. + +// These two macros are created by the CMake module, FortranCInterface.cmake, +// which is then used by CMake to configure this file. + +// The CMAKE decision can be superceded by setting -DBLAS_NO_UNDERSCORE, so +// that "dgemm" remains "dgemm" (for MS Visual Studio for example). Setting +// -DBLAS_UNDERSCORE changes "dgemm" to "dgemm_", the common case for Mac and +// Linux. + +#if defined ( BLAS_NO_UNDERSCORE ) + + // no name mangling, use lower case + #define SUITESPARSE_FORTRAN(name,NAME) name + #define SUITESPARSE__FORTRAN(name,NAME) name + +#elif defined ( BLAS_UNDERSCORE ) + + // append an undescore, use lower case + #define SUITESPARSE_FORTRAN(name,NAME) name ## _ + #define SUITESPARSE__FORTRAN(name,NAME) name ## _ + +#else + + // let CMake decide how C calls Fortran + #define SUITESPARSE_FORTRAN(name,NAME) name##_ + #define SUITESPARSE__FORTRAN(name,NAME) name##_ + +#endif + +//------------------------------------------------------------------------------ +// SUITESPARSE_BLAS_INT: the BLAS/LAPACK integer (int32_t or int64_t) +//------------------------------------------------------------------------------ + +// CMake 3.22 and later allow the selection of the BLAS/LAPACK integer size. +// This information is then used to configure this file with the definition of +// this integer: int32_t or int64_t. + +// When compiling SuiteSparse for a MATLAB mexFunction, the MATLAB libmwblas is +// used, which is a 64-bit integer version of the BLAS. CMake is not used to +// configure SuiteSparse in this case. The flag -DBLAS64 can be used to ensure +// a 64-bit BLAS is used. Likewise, -DBLAS32 ensures a 32-bit BLAS is used. + +#if defined ( BLAS64 ) + + // override the BLAS found by CMake, and force a 64-bit interface + #define SUITESPARSE_BLAS_INT int64_t + +#elif defined ( BLAS32 ) + + // override the BLAS found by CMake, and force a 32-bit interface + #define SUITESPARSE_BLAS_INT int32_t + +#else + + // let CMake determine the size of the integer in the Fortran BLAS + #define SUITESPARSE_BLAS_INT int64_t + +#endif + +// SUITESPARSE_TO_BLAS_INT: convert an integer k to a BLAS integer K and set ok +// to false if the conversion changes its value. This is implemented as a +// macro so that can work with any type of the integer k. +#define SUITESPARSE_TO_BLAS_INT(K,k,ok) \ + SUITESPARSE_BLAS_INT K = (k) ; \ + ok = ok && ((sizeof (K) >= sizeof (k)) || ((int64_t)(K) == (int64_t)(k))) ; + +//------------------------------------------------------------------------------ +// SUITESPARSE_BLAS_SUFFIX: modify the name of a Fortran BLAS/LAPACK routine +//------------------------------------------------------------------------------ + +// OpenBLAS can be compiled by appending a suffix to each routine, so that the +// Fortan routine dgemm becomes dgemm_64, which denotes a version of dgemm with +// 64-bit integer parameters. The Sun Performance library does the same thing, +// but without the internal underscore, as dgemm64. + +// If the suffix does not contain "_", use (Sun Perf., for example): + +// cd build ; cmake -DBLAS64_SUFFIX="64" .. + +// If the suffix contains "_" (OpenBLAS in spack for example), use the +// following: + +// 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 ) + + // The suffix includes an undersore (such as "_64"), so the Fortran name + // must be processed with the SUITESPARSE__FORTRAN macro. + #define SUITESPARSE_G(name,NAME) SUITESPARSE__FORTRAN(name,NAME) + #define SUITESPARSE_F(name,NAME) \ + SUITESPARSE_G (SUITESPARSE_CAT (name, BLAS64__SUFFIX), \ + SUITESPARSE_CAT (NAME, BLAS64__SUFFIX)) + #define SUITESPARSE_BLAS(name,NAME) SUITESPARSE_F(name,NAME) + +#elif defined ( BLAS64_SUFFIX ) + + // The suffix does not include an undersore, and neither do the original + // names of the BLAS and LAPACK routines. Thus, the Fortran name must be + // processed with the SUITESPARSE_FORTRAN macro. + #define SUITESPARSE_G(name,NAME) SUITESPARSE_FORTRAN(name,NAME) + #define SUITESPARSE_F(name,NAME) \ + SUITESPARSE_G (SUITESPARSE_CAT (name, BLAS64_SUFFIX), \ + SUITESPARSE_CAT (NAME, BLAS64_SUFFIX)) + #define SUITESPARSE_BLAS(name,NAME) SUITESPARSE_F(name,NAME) + +#else + + // No suffix is need, so the final Fortran name includes no suffix. + #define SUITESPARSE_BLAS(name,NAME) SUITESPARSE_FORTRAN(name,NAME) + +#endif + +//------------------------------------------------------------------------------ +// C names of Fortan BLAS and LAPACK functions used by SuiteSparse +//------------------------------------------------------------------------------ + +#define SUITESPARSE_BLAS_DTRSV SUITESPARSE_BLAS ( dtrsv , DTRSV ) +#define SUITESPARSE_BLAS_DGEMV SUITESPARSE_BLAS ( dgemv , DGEMV ) +#define SUITESPARSE_BLAS_DTRSM SUITESPARSE_BLAS ( dtrsm , DTRSM ) +#define SUITESPARSE_BLAS_DGEMM SUITESPARSE_BLAS ( dgemm , DGEMM ) +#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_LAPACK_DPOTRF SUITESPARSE_BLAS ( dpotrf , DPOTRF ) + +#define SUITESPARSE_BLAS_ZTRSV SUITESPARSE_BLAS ( ztrsv , ZTRSV ) +#define SUITESPARSE_BLAS_ZGEMV SUITESPARSE_BLAS ( zgemv , ZGEMV ) +#define SUITESPARSE_BLAS_ZTRSM SUITESPARSE_BLAS ( ztrsm , ZTRSM ) +#define SUITESPARSE_BLAS_ZGEMM SUITESPARSE_BLAS ( zgemm , ZGEMM ) +#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_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 ) + +//------------------------------------------------------------------------------ +// prototypes of BLAS and SUITESPARSE_LAPACK functions +//------------------------------------------------------------------------------ + +// For complex functions, the (void *) parameters are actually pointers to +// arrays of complex values. They are prototyped here as (void *) to allow +// them to be called from both C and C++. + +// See https://netlib.org/blas/ and https://netlib.org/lapack/ for the +// definitions of the inputs/outputs of these functions. + +// These prototypes need to be found by UMFPACK, CHOLMOD, and SPQR, and to do +// so, they need to appear in this public header to ensure the correct BLAS +// library and integer size is used. However, these definitions should not +// (normally) be exposed to the user application. + +// If a user application wishes to use these definitions, simply add + +// #define SUITESPARSE_BLAS_DEFINITIONS +// #include "SuiteSparse_config.h" + +// prior to #include'ing any SuiteSparse headers (amd.h, and so on). + +#if defined ( SUITESPARSE_BLAS_DEFINITIONS ) + +void SUITESPARSE_BLAS_DGEMV // Y = alpha*A*x + beta*Y +( + // input: + const char *trans, + const SUITESPARSE_BLAS_INT *m, + const SUITESPARSE_BLAS_INT *n, + const double *alpha, + const double *A, + const SUITESPARSE_BLAS_INT *lda, + const double *X, + const SUITESPARSE_BLAS_INT *incx, + const double *beta, + // input/output: + double *Y, + // input: + const SUITESPARSE_BLAS_INT *incy +) ; + +#define SUITESPARSE_BLAS_dgemv(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_DGEMV (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 // Y = alpha*A*X + beta*Y +( + // 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_zgemv(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_ZGEMV (trans, &M_blas_int, &N_blas_int, alpha, A, \ + &LDA_blas_int, X, &INCX_blas_int, beta, Y, &INCY_blas_int) ; \ + } \ +} + +void SUITESPARSE_BLAS_DTRSV // solve Lx=b, Ux=b, L'x=b, or U'x=b +( + // input: + const char *uplo, + const char *trans, + const char *diag, + const SUITESPARSE_BLAS_INT *n, + const double *A, + const SUITESPARSE_BLAS_INT *lda, + // input/output: + double *X, + // input: + const SUITESPARSE_BLAS_INT *incx +) ; + +#define SUITESPARSE_BLAS_dtrsv(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_DTRSV (uplo, trans, diag, &N_blas_int, A, \ + &LDA_blas_int, X, &INCX_blas_int) ; \ + } \ +} + +void SUITESPARSE_BLAS_ZTRSV // solve (L, L', L^H, U, U', or U^H)x=b +( + // 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_ztrsv(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_ZTRSV (uplo, trans, diag, &N_blas_int, A, \ + &LDA_blas_int, X, &INCX_blas_int) ; \ + } \ +} + +void SUITESPARSE_BLAS_DTRSM // solve LX=B, UX=B, L'X=B, or U'X=B +( + // input: + const char *side, + const char *uplo, + const char *transa, + const char *diag, + const SUITESPARSE_BLAS_INT *m, + const SUITESPARSE_BLAS_INT *n, + const double *alpha, + const double *A, + const SUITESPARSE_BLAS_INT *lda, + // input/output: + double *B, + // input: + const SUITESPARSE_BLAS_INT *ldb +) ; + +#define SUITESPARSE_BLAS_dtrsm(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_DTRSM (side, uplo, transa, diag, &M_blas_int, \ + &N_blas_int, alpha, A, &LDA_blas_int, B, &LDB_blas_int) ; \ + } \ +} + +void SUITESPARSE_BLAS_ZTRSM // solve (L, L', L^H, U, U', or U^H)X=B +( + // 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_ztrsm(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_ZTRSM (side, uplo, transa, diag, &M_blas_int, \ + &N_blas_int, alpha, A, &LDA_blas_int, B, &LDB_blas_int) ; \ + } \ +} + +void SUITESPARSE_BLAS_DGEMM // C = alpha*A*B + beta*C +( + // input: + 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 SUITESPARSE_BLAS_INT *lda, + const double *B, + const SUITESPARSE_BLAS_INT *ldb, + const double *beta, + // input/output: + double *C, + // input: + const SUITESPARSE_BLAS_INT *ldc +) ; + +#define SUITESPARSE_BLAS_dgemm(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_DGEMM (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_ZGEMM // C = alpha*A*B + beta*C +( + // input: + 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, + // input: + const SUITESPARSE_BLAS_INT *ldc +) ; + +#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_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_DSYRK // C = alpha*A*A' + beta*C, or A'A +( + // 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 *C, + // input: + const SUITESPARSE_BLAS_INT *ldc +) ; + +#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) ; \ + 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) ; \ + } \ +} + +void SUITESPARSE_BLAS_ZHERK // C = alpha*A*A^H + beta*C, or A^H*A +( + // 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_LAPACK_DPOTRF // Cholesky factorization +( + // 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_ZPOTRF // Cholesky factorization +( + // 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_zpotrf(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_ZPOTRF (uplo, &N_blas_int, A, &LDA_blas_int, \ + &LAPACK_Info) ; \ + info = LAPACK_Info ; \ + } \ +} + +void SUITESPARSE_BLAS_DSCAL // Y = alpha*Y +( + // input: + const SUITESPARSE_BLAS_INT *n, + const double *alpha, + // input/output: + double *Y, + // input: + const SUITESPARSE_BLAS_INT *incy +) ; + +#define SUITESPARSE_BLAS_dscal(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_DSCAL (&N_blas_int, alpha, Y, &INCY_blas_int) ; \ + } \ +} + +void SUITESPARSE_BLAS_ZSCAL // Y = alpha*Y +( + // input: + const SUITESPARSE_BLAS_INT *n, + const void *alpha, + // input/output: + void *Y, + // input: + const SUITESPARSE_BLAS_INT *incy +) ; + +#define SUITESPARSE_BLAS_zscal(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_ZSCAL (&N_blas_int, alpha, Y, &INCY_blas_int) ; \ + } \ +} + +void SUITESPARSE_BLAS_DGER // A = alpha*x*y' + A +( + // input: + const SUITESPARSE_BLAS_INT *m, + const SUITESPARSE_BLAS_INT *n, + const double *alpha, + const double *X, + const SUITESPARSE_BLAS_INT *incx, + const double *Y, + const SUITESPARSE_BLAS_INT *incy, + // input/output: + double *A, + // input: + const SUITESPARSE_BLAS_INT *lda +) ; + +#define SUITESPARSE_BLAS_dger(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_DGER (&M_blas_int, &N_blas_int, alpha, X, \ + &INCX_blas_int, Y, &INCY_blas_int, A, &LDA_blas_int) ; \ + } \ +} + +void SUITESPARSE_BLAS_ZGERU // A = alpha*x*y' + A +( + // 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_zgeru(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_ZGERU (&M_blas_int, &N_blas_int, alpha, X, \ + &INCX_blas_int, Y, &INCY_blas_int, A, &LDA_blas_int) ; \ + } \ +} + +void SUITESPARSE_LAPACK_DLARFT // T = block Householder factor +( + // input: + const char *direct, + const char *storev, + const SUITESPARSE_BLAS_INT *n, + const SUITESPARSE_BLAS_INT *k, + const double *V, + const SUITESPARSE_BLAS_INT *ldv, + const double *Tau, + // output: + double *T, + // input: + const SUITESPARSE_BLAS_INT *ldt +) ; + +#define SUITESPARSE_LAPACK_dlarft(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_DLARFT (direct, storev, &N_blas_int, &K_blas_int, \ + V, &LDV_blas_int, Tau, T, &LDT_blas_int) ; \ + } \ +} + +void SUITESPARSE_LAPACK_ZLARFT // T = block Householder factor +( + // 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_zlarft(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_ZLARFT (direct, storev, &N_blas_int, &K_blas_int, \ + V, &LDV_blas_int, Tau, T, &LDT_blas_int) ; \ + } \ +} + +void SUITESPARSE_LAPACK_DLARFB // apply block Householder reflector +( + // 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 double *V, + const SUITESPARSE_BLAS_INT *ldv, + const double *T, + const SUITESPARSE_BLAS_INT *ldt, + // input/output: + double *C, + // input: + const SUITESPARSE_BLAS_INT *ldc, + // workspace: + double *Work, + // input: + const SUITESPARSE_BLAS_INT *ldwork +) ; + +#define SUITESPARSE_LAPACK_dlarfb(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_DLARFB (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 // apply block Householder reflector +( + // 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_zlarfb(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_ZLARFB (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) ; \ + } \ +} + +double SUITESPARSE_BLAS_DNRM2 // vector 2-norm +( + // input: + const SUITESPARSE_BLAS_INT *n, + const double *X, + const SUITESPARSE_BLAS_INT *incx +) ; + +#define SUITESPARSE_BLAS_dnrm2(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_DNRM2 (&N_blas_int, X, &INCX_blas_int) ; \ + } \ +} + +double SUITESPARSE_BLAS_DZNRM2 // vector 2-norm +( + // input: + const SUITESPARSE_BLAS_INT *n, + const void *X, + const SUITESPARSE_BLAS_INT *incx +) ; + +#define SUITESPARSE_BLAS_dznrm2(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_DZNRM2 (&N_blas_int, X, &INCX_blas_int) ; \ + } \ +} + +void SUITESPARSE_LAPACK_DLARFG // generate Householder reflector +( + // input: + const SUITESPARSE_BLAS_INT *n, + // input/output: + double *alpha, + double *X, + // input: + const SUITESPARSE_BLAS_INT *incx, + // output: + double *tau +) ; + +#define SUITESPARSE_LAPACK_dlarfg(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_DLARFG (&N_blas_int, alpha, X, &INCX_blas_int, \ + tau) ; \ + } \ +} + +void SUITESPARSE_LAPACK_ZLARFG // generate Householder reflector +( + // input: + const SUITESPARSE_BLAS_INT *n, + // input/output: + void *alpha, + void *X, + // input: + const SUITESPARSE_BLAS_INT *incx, + // output: + void *tau +) ; + +#define SUITESPARSE_LAPACK_zlarfg(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_ZLARFG (&N_blas_int, alpha, X, &INCX_blas_int, \ + tau) ; \ + } \ +} + +void SUITESPARSE_LAPACK_DLARF // apply Householder reflector +( + // input: + const char *side, + const SUITESPARSE_BLAS_INT *m, + const SUITESPARSE_BLAS_INT *n, + const double *V, + const SUITESPARSE_BLAS_INT *incv, + const double *tau, + // input/output: + double *C, + // input: + const SUITESPARSE_BLAS_INT *ldc, + // workspace: + double *Work +) ; + +#define SUITESPARSE_LAPACK_dlarf(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_DLARF (side, &M_blas_int, &N_blas_int, V, \ + &INCV_blas_int, tau, C, &LDC_blas_int, Work) ; \ + } \ +} + +void SUITESPARSE_LAPACK_ZLARF // apply Householder reflector +( + // 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_zlarf(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_ZLARF (side, &M_blas_int, &N_blas_int, V, \ + &INCV_blas_int, tau, C, &LDC_blas_int, Work) ; \ + } \ +} + +#endif + +//------------------------------------------------------------------------------ +// SuiteSparse_BLAS_library: return name of BLAS library found +//------------------------------------------------------------------------------ + +// Returns the name of the BLAS library found by SuiteSparse_config + +const char *SuiteSparse_BLAS_library ( void ) ; + +//------------------------------------------------------------------------------ +// SuiteSparse_BLAS_integer_size: return sizeof (SUITESPARSE_BLAS_INT) +//------------------------------------------------------------------------------ + +size_t SuiteSparse_BLAS_integer_size ( void ) ; + #ifdef __cplusplus } #endif #endif + diff --git a/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/SuiteSparse_config.mk b/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/SuiteSparse_config.mk deleted file mode 100644 index 19a39032a..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/SuiteSparse_config.mk +++ /dev/null @@ -1,621 +0,0 @@ -#=============================================================================== -# SuiteSparse_config.mk: common configuration file for the SuiteSparse -#=============================================================================== - -# This file contains all configuration settings for all packages in SuiteSparse, -# except for CSparse (which is stand-alone), the packages in MATLAB_Tools, -# and GraphBLAS. The configuration settings for GraphBLAS are determined by -# GraphBLAS/CMakeLists.txt - -SUITESPARSE_VERSION = 5.4.0 - -#=============================================================================== -# Options you can change without editing this file: -#=============================================================================== - - # To list the options you can modify at the 'make' command line, type - # 'make config', which also lists their default values. You can then - # change them with 'make OPTION=value'. For example, to use an INSTALL - # path of /my/path, and to use your own BLAS and LAPACK libraries, do: - # - # make install INSTALL=/my/path BLAS=-lmyblas LAPACK=-lmylapackgoeshere - # - # which will install the package into /my/path/lib and /my/path/include, - # and use -lmyblas -lmylapackgoes here when building the demo program. - -#=============================================================================== -# Defaults for any system -#=============================================================================== - - #--------------------------------------------------------------------------- - # SuiteSparse root directory - #--------------------------------------------------------------------------- - - # Most Makefiles are in SuiteSparse/Pkg/Lib or SuiteSparse/Pkg/Demo, so - # the top-level of SuiteSparse is in ../.. unless otherwise specified. - # This is true for all but the SuiteSparse_config package. - SUITESPARSE ?= $(realpath $(CURDIR)/../..) - - #--------------------------------------------------------------------------- - # installation location - #--------------------------------------------------------------------------- - - # For "make install" and "make uninstall", the default location is - # SuiteSparse/lib, SuiteSparse/include, and - # SuiteSparse/share/doc/suitesparse-x.y.z - # If you do this: - # make install INSTALL=/usr/local - # then the libraries are installed in /usr/local/lib, include files in - # /usr/local/include, and documentation in - # /usr/local/share/doc/suitesparse-x.y.z. - # You can instead specify the install location of each of these 3 components - # separately, via (for example): - # make install INSTALL_LIB=/yada/mylibs INSTALL_INCLUDE=/yoda/myinc \ - # INSTALL_DOC=/solo/mydox - # which puts the libraries in /yada/mylibs, include files in /yoda/myinc, - # and documentation in /solo/mydox. - INSTALL ?= $(SUITESPARSE) - INSTALL_LIB ?= $(INSTALL)/lib - INSTALL_INCLUDE ?= $(INSTALL)/include - INSTALL_DOC ?= $(INSTALL)/share/doc/suitesparse-$(SUITESPARSE_VERSION) - - CMAKE_OPTIONS ?= -DCMAKE_INSTALL_PREFIX=$(INSTALL) - - #--------------------------------------------------------------------------- - # parallel make - #--------------------------------------------------------------------------- - - # sequential make's by default - JOBS ?= 1 - - #--------------------------------------------------------------------------- - # optimization level - #--------------------------------------------------------------------------- - - OPTIMIZATION ?= -O3 - - #--------------------------------------------------------------------------- - # statement coverage for */Tcov - #--------------------------------------------------------------------------- - - ifeq ($(TCOV),yes) - # Each package has a */Tcov directory for extensive testing, including - # statement coverage. The Tcov tests require Linux and gcc, and use - # the vanilla BLAS. For those tests, the packages use 'make TCOV=yes', - # which overrides the following settings: - MKLROOT = - AUTOCC = no - CC = gcc - CXX = g++ - BLAS = -lrefblas -lgfortran -lstdc++ - LAPACK = -llapack - CFLAGS += --coverage - OPTIMIZATION = -g - LDFLAGS += --coverage - endif - - #--------------------------------------------------------------------------- - # OpenMP is used in CHOLMOD - #--------------------------------------------------------------------------- - - # with gcc, enable OpenMP directives via -fopenmp - # This is not supported on Darwin, so this string is cleared, below. - CFOPENMP ?= -fopenmp - - #--------------------------------------------------------------------------- - # compiler - #--------------------------------------------------------------------------- - - # By default, look for the Intel compilers. If present, they are used - # instead of $(CC), $(CXX), and $(F77). To disable this feature and - # use the $(CC), $(CXX), and $(F77) compilers, use 'make AUTOCC=no' - - AUTOCC ?= yes - - ifneq ($(AUTOCC),no) - ifneq ($(shell which icc 2>/dev/null),) - # use the Intel icc compiler for C codes, and -qopenmp for OpenMP - CC = icc - CFLAGS += -D_GNU_SOURCE - CXX = icpc - CFOPENMP = -qopenmp -I$(MKLROOT)/include - LDFLAGS += -qopenmp - LDLIBS += -lm -lirc - endif - ifneq ($(shell which ifort 2>/dev/null),) - # use the Intel ifort compiler for Fortran codes - F77 = ifort - endif - endif - - CMAKE_OPTIONS += -DCMAKE_CXX_COMPILER=$(CXX) -DCMAKE_C_COMPILER=$(CC) - - #--------------------------------------------------------------------------- - # CFLAGS for the C/C++ compiler - #--------------------------------------------------------------------------- - - # The CF macro is used by SuiteSparse Makefiles as a combination of - # CFLAGS, CPPFLAGS, TARGET_ARCH, and system-dependent settings. - CF ?= $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) $(OPTIMIZATION) -fexceptions -fPIC - - #--------------------------------------------------------------------------- - # code formatting (for Tcov on Linux only) - #--------------------------------------------------------------------------- - - PRETTY ?= grep -v "^\#" | indent -bl -nce -bli0 -i4 -sob -l120 - - #--------------------------------------------------------------------------- - # required libraries - #--------------------------------------------------------------------------- - - # SuiteSparse requires the BLAS, LAPACK, and -lm (Math) libraries. - # It places its shared *.so libraries in SuiteSparse/lib. - # Linux also requires the -lrt library (see below) - LDLIBS ?= -lm - LDFLAGS += -L$(INSTALL_LIB) - - # See http://www.openblas.net for a recent and freely available optimzed - # BLAS. LAPACK is at http://www.netlib.org/lapack/ . You can use the - # standard Fortran LAPACK along with OpenBLAS to obtain very good - # performance. This script can also detect if the Intel MKL BLAS is - # installed. - - LAPACK ?= -llapack - - ifndef BLAS - ifdef MKLROOT - # use the Intel MKL for BLAS and LAPACK - # using static linking: - # BLAS = -Wl,--start-group \ - # $(MKLROOT)/lib/intel64/libmkl_intel_lp64.a \ - # $(MKLROOT)/lib/intel64/libmkl_core.a \ - # $(MKLROOT)/lib/intel64/libmkl_intel_thread.a \ - # -Wl,--end-group -lpthread -lm - # using dynamic linking: - BLAS = -lmkl_intel_lp64 -lmkl_core -lmkl_intel_thread -liomp5 -lpthread -lm - LAPACK = - else - # use the OpenBLAS at http://www.openblas.net - BLAS = -lopenblas - endif - endif - - # For ACML, use this instead: - # make BLAS='-lacml -lgfortran' - - #--------------------------------------------------------------------------- - # shell commands - #--------------------------------------------------------------------------- - - # ranlib, and ar, for generating libraries. If you don't need ranlib, - # just change it to RANLAB = echo - RANLIB ?= ranlib - ARCHIVE ?= $(AR) $(ARFLAGS) - CP ?= cp -f - MV ?= mv -f - - #--------------------------------------------------------------------------- - # Fortran compiler (not required for 'make' or 'make library') - #--------------------------------------------------------------------------- - - # A Fortran compiler is optional. Only required for the optional Fortran - # interfaces to AMD and UMFPACK. Not needed by 'make' or 'make install' - F77 ?= gfortran - F77FLAGS ?= $(FFLAGS) $(OPTIMIZATION) - - #--------------------------------------------------------------------------- - # NVIDIA CUDA configuration for CHOLMOD and SPQR - #--------------------------------------------------------------------------- - - # CUDA is detected automatically, and used if found. To disable CUDA, - # use CUDA=no - - ifneq ($(CUDA),no) - CUDA_PATH = $(shell which nvcc 2>/dev/null | sed "s/\/bin\/nvcc//") - endif - - ifeq ($(wildcard $(CUDA_PATH)),) - # CUDA is not present - CUDA_PATH = - GPU_BLAS_PATH = - GPU_CONFIG = - CUDART_LIB = - CUBLAS_LIB = - CUDA_INC_PATH = - CUDA_INC = - NVCC = echo - NVCCFLAGS = - else - # with CUDA for CHOLMOD and SPQR - GPU_BLAS_PATH = $(CUDA_PATH) - # GPU_CONFIG must include -DGPU_BLAS to compile SuiteSparse for the - # GPU. You can add additional GPU-related flags to it as well. - # with 4 cores (default): - GPU_CONFIG = -DGPU_BLAS - # For example, to compile CHOLMOD for 10 CPU cores when using the GPU: - # GPU_CONFIG = -DGPU_BLAS -DCHOLMOD_OMP_NUM_THREADS=10 - CUDART_LIB = $(CUDA_PATH)/lib64/libcudart.so - CUBLAS_LIB = $(CUDA_PATH)/lib64/libcublas.so - CUDA_INC_PATH = $(CUDA_PATH)/include/ - CUDA_INC = -I$(CUDA_INC_PATH) - MAGMA_INC = -I/opt/magma-2.4.0/include/ - MAGMA_LIB = -L/opt/magma-2.4.0/lib/ -lmagma - NVCC = $(CUDA_PATH)/bin/nvcc - NVCCFLAGS = -Xcompiler -fPIC -O3 \ - -gencode=arch=compute_30,code=sm_30 \ - -gencode=arch=compute_35,code=sm_35 \ - -gencode=arch=compute_50,code=sm_50 \ - -gencode=arch=compute_53,code=sm_53 \ - -gencode=arch=compute_53,code=sm_53 \ - -gencode=arch=compute_60,code=compute_60 - endif - - #--------------------------------------------------------------------------- - # UMFPACK configuration: - #--------------------------------------------------------------------------- - - # Configuration for UMFPACK. See UMFPACK/Source/umf_config.h for details. - # - # -DNBLAS do not use the BLAS. UMFPACK will be very slow. - # -D'LONGBLAS=long' or -DLONGBLAS='long long' defines the integers used by - # LAPACK and the BLAS (defaults to 'int') - # -DNSUNPERF do not use the Sun Perf. Library on Solaris - # -DNRECIPROCAL do not multiply by the reciprocal - # -DNO_DIVIDE_BY_ZERO do not divide by zero - # -DNCHOLMOD do not use CHOLMOD as a ordering method. If -DNCHOLMOD is - # included in UMFPACK_CONFIG, then UMFPACK does not rely on - # CHOLMOD, CAMD, CCOLAMD, COLAMD, and METIS. - - UMFPACK_CONFIG ?= - - # For example, uncomment this line to compile UMFPACK without CHOLMOD: - # UMFPACK_CONFIG = -DNCHOLMOD - # or use 'make UMFPACK_CONFIG=-DNCHOLMOD' - - #--------------------------------------------------------------------------- - # CHOLMOD configuration - #--------------------------------------------------------------------------- - - # CHOLMOD Library Modules, which appear in -lcholmod - # Core requires: none - # Check requires: Core - # Cholesky requires: Core, AMD, COLAMD. optional: Partition, Supernodal - # MatrixOps requires: Core - # Modify requires: Core - # Partition requires: Core, CCOLAMD, METIS. optional: Cholesky - # Supernodal requires: Core, BLAS, LAPACK - # - # CHOLMOD test/demo Modules (these do not appear in -lcholmod): - # Tcov requires: Core, Check, Cholesky, MatrixOps, Modify, Supernodal - # optional: Partition - # Valgrind same as Tcov - # Demo requires: Core, Check, Cholesky, MatrixOps, Supernodal - # optional: Partition - # - # Configuration flags: - # -DNCHECK do not include the Check module. - # -DNCHOLESKY do not include the Cholesky module. - # -DNPARTITION do not include the Partition module. - # also do not include METIS. - # -DNCAMD do not use CAMD & CCOLAMD in Parition Module. - # -DNMATRIXOPS do not include the MatrixOps module. - # -DNMODIFY do not include the Modify module. - # -DNSUPERNODAL do not include the Supernodal module. - # - # -DNPRINT do not print anything. - # -D'LONGBLAS=long' or -DLONGBLAS='long long' defines the integers used by - # LAPACK and the BLAS (defaults to 'int') - # -DNSUNPERF for Solaris only. If defined, do not use the Sun - # Performance Library - # -DGPU_BLAS enable the use of the CUDA BLAS - - CHOLMOD_CONFIG ?= $(GPU_CONFIG) - - #--------------------------------------------------------------------------- - # SuiteSparseQR configuration: - #--------------------------------------------------------------------------- - - # The SuiteSparseQR library can be compiled with the following options: - # - # -DNPARTITION do not include the CHOLMOD partition module - # -DNEXPERT do not include the functions in SuiteSparseQR_expert.cpp - # -DHAVE_TBB enable the use of Intel's Threading Building Blocks - # -DGPU_BLAS enable the use of the CUDA BLAS - - SPQR_CONFIG ?= $(GPU_CONFIG) - - # to compile with Intel's TBB, use TBB=-ltbb -DSPQR_CONFIG=-DHAVE_TBB - TBB ?= - # TBB = -ltbb -DSPQR_CONFIG=-DHAVE_TBB - - # TODO: this *mk file should auto-detect the presence of Intel's TBB, - # and set the compiler flags accordingly. - -#=============================================================================== -# System-dependent configurations -#=============================================================================== - - #--------------------------------------------------------------------------- - # determine what system we are on - #--------------------------------------------------------------------------- - - # To disable these auto configurations, use 'make UNAME=custom' - - ifndef UNAME - ifeq ($(OS),Windows_NT) - # Cygwin Make on Windows has an $(OS) variable, but not uname. - # Note that this option is untested. - UNAME = Windows - else - # Linux and Darwin (Mac OSX) have been tested. - UNAME := $(shell uname) - endif - endif - - #--------------------------------------------------------------------------- - # Linux - #--------------------------------------------------------------------------- - - ifeq ($(UNAME),Linux) - # add the realtime library, librt, and SuiteSparse/lib - LDLIBS += -lrt -Wl,-rpath=$(INSTALL_LIB) - endif - - #--------------------------------------------------------------------------- - # Mac - #--------------------------------------------------------------------------- - - ifeq ($(UNAME), Darwin) - # To compile on the Mac, you must install Xcode. Then do this at the - # command line in the Terminal, before doing 'make': - # xcode-select --install - CF += -fno-common - BLAS = -framework Accelerate - LAPACK = -framework Accelerate - # OpenMP is not yet supported by default in clang - CFOPENMP = - endif - - #--------------------------------------------------------------------------- - # Solaris - #--------------------------------------------------------------------------- - - ifeq ($(UNAME), SunOS) - # Using the Sun compiler and the Sun Performance Library - # This hasn't been tested recently. - # I leave it here in case you need it. It likely needs updating. - CF += -fast -KPIC -xc99=%none -xlibmieee -xlibmil -m64 -Xc - F77FLAGS = -O -fast -KPIC -dalign -xlibmil -m64 - BLAS = -xlic_lib=sunperf - LAPACK = - # Using the GCC compiler and the reference BLAS - ## CC = gcc - ## CXX = g++ - ## MAKE = gmake - ## BLAS = -lrefblas -lgfortran - ## LAPACK = -llapack - endif - - #--------------------------------------------------------------------------- - # IBM AIX - #--------------------------------------------------------------------------- - - ifeq ($(UNAME), AIX) - # hasn't been tested for a very long time... - # I leave it here in case you need it. It likely needs updating. - CF += -O4 -qipa -qmaxmem=16384 -q64 -qproto -DBLAS_NO_UNDERSCORE - F77FLAGS = -O4 -qipa -qmaxmem=16384 -q64 - BLAS = -lessl - LAPACK = - endif - -#=============================================================================== -# finalize the CF compiler flags -#=============================================================================== - - CF += $(CFOPENMP) - -#=============================================================================== -# internal configuration -#=============================================================================== - - # The user should not have to change these definitions, and they are - # not displayed by 'make config' - - #--------------------------------------------------------------------------- - # for removing files not in the distribution - #--------------------------------------------------------------------------- - - # remove object files, but keep compiled libraries via 'make clean' - CLEAN = *.o *.obj *.ln *.bb *.bbg *.da *.tcov *.gcov gmon.out *.bak *.d \ - *.gcda *.gcno *.aux *.bbl *.blg *.log *.toc *.dvi *.lof *.lot - - # also remove compiled libraries, via 'make distclean' - PURGE = *.so* *.a *.dll *.dylib *.dSYM - - # location of TCOV test output - TCOV_TMP ?= /tmp - -#=============================================================================== -# Building the shared and static libraries -#=============================================================================== - -# How to build/install shared and static libraries for Mac and Linux/Unix. -# This assumes that LIBRARY and VERSION have already been defined by the -# Makefile that includes this file. - -SO_OPTS = $(LDFLAGS) - -ifeq ($(UNAME),Windows) - # Cygwin Make on Windows (untested) - AR_TARGET = $(LIBRARY).lib - SO_PLAIN = $(LIBRARY).dll - SO_MAIN = $(LIBRARY).$(SO_VERSION).dll - SO_TARGET = $(LIBRARY).$(VERSION).dll - SO_INSTALL_NAME = echo -else - # Mac or Linux/Unix - AR_TARGET = $(LIBRARY).a - ifeq ($(UNAME),Darwin) - # Mac - SO_PLAIN = $(LIBRARY).dylib - SO_MAIN = $(LIBRARY).$(SO_VERSION).dylib - SO_TARGET = $(LIBRARY).$(VERSION).dylib - SO_OPTS += -dynamiclib -compatibility_version $(SO_VERSION) \ - -current_version $(VERSION) \ - -shared -undefined dynamic_lookup - # When a Mac *.dylib file is moved, this command is required - # to change its internal name to match its location in the filesystem: - SO_INSTALL_NAME = install_name_tool -id - else - # Linux and other variants of Unix - SO_PLAIN = $(LIBRARY).so - SO_MAIN = $(LIBRARY).so.$(SO_VERSION) - SO_TARGET = $(LIBRARY).so.$(VERSION) - SO_OPTS += -shared -Wl,-soname -Wl,$(SO_MAIN) -Wl,--no-undefined - # Linux/Unix *.so files can be moved without modification: - SO_INSTALL_NAME = echo - endif -endif - -#=============================================================================== -# Configure CHOLMOD/Partition module with METIS, CAMD, and CCOLAMD -#=============================================================================== - -# By default, SuiteSparse uses METIS 5.1.0 in the SuiteSparse/metis-5.1.0 -# directory. SuiteSparse's interface to METIS is only through the -# SuiteSparse/CHOLMOD/Partition module, which also requires SuiteSparse/CAMD -# and SuiteSparse/CCOLAMD. -# -# If you wish to use your own pre-installed copy of METIS, use the MY_METIS_LIB -# and MY_METIS_INC options passed to 'make'. For example: -# make MY_METIS_LIB=-lmetis -# make MY_METIS_LIB=/home/myself/mylibraries/libmetis.so -# make MY_METIS_LIB='-L/home/myself/mylibraries -lmetis' -# If you need to tell the compiler where to find the metis.h include file, -# then add MY_METIS_INC=/home/myself/metis-5.1.0/include as well, which points -# to the directory containing metis.h. If metis.h is already installed in -# a location known to the compiler (/usr/local/include/metis.h for example) -# then you do not need to add MY_METIS_INC. - -I_WITH_PARTITION = -LIB_WITH_PARTITION = -CONFIG_PARTITION = -DNPARTITION -DNCAMD -# check if CAMD/CCOLAMD and METIS are requested and available -ifeq (,$(findstring -DNCAMD, $(CHOLMOD_CONFIG))) - # CAMD and CCOLAMD are requested. See if they are available in - # SuiteSparse/CAMD and SuiteSparse/CCOLAMD - ifneq (, $(wildcard $(SUITESPARSE)/CAMD)) - ifneq (, $(wildcard $(SUITESPARSE)/CCOLAMD)) - # CAMD and CCOLAMD are requested and available - LIB_WITH_PARTITION = -lccolamd -lcamd - I_WITH_PARTITION = -I$(SUITESPARSE)/CCOLAMD/Include -I$(SUITESPARSE)/CAMD/Include - CONFIG_PARTITION = -DNPARTITION - # check if METIS is requested and available - ifeq (,$(findstring -DNPARTITION, $(CHOLMOD_CONFIG))) - # METIS is requested. See if it is available. - ifneq (,$(MY_METIS_LIB)) - # METIS 5.1.0 is provided elsewhere, and we are not using - # SuiteSparse/metis-5.1.0. To do so, we link with - # $(MY_METIS_LIB) and add the -I$(MY_METIS_INC) option for - # the compiler. The latter can be empty if you have METIS - # installed in a place where the compiler can find the - # metis.h include file by itself without any -I option - # (/usr/local/include/metis.h for example). - LIB_WITH_PARTITION += $(MY_METIS_LIB) - ifneq (,$(MY_METIS_INC)) - I_WITH_PARTITION += -I$(MY_METIS_INC) - endif - CONFIG_PARTITION = - else - # see if METIS is in SuiteSparse/metis-5.1.0 - ifneq (, $(wildcard $(SUITESPARSE)/metis-5.1.0)) - # SuiteSparse/metis5.1.0 is available - ifeq ($(UNAME), Darwin) - LIB_WITH_PARTITION += $(SUITESPARSE)/lib/libmetis.dylib - else - LIB_WITH_PARTITION += -lmetis - endif - I_WITH_PARTITION += -I$(SUITESPARSE)/metis-5.1.0/include - CONFIG_PARTITION = - endif - endif - endif - endif - endif -endif - -#=============================================================================== -# display configuration -#=============================================================================== - -ifeq ($(LIBRARY),) - # placeholders, for 'make config' in the top-level SuiteSparse - LIBRARY=PackageNameWillGoHere - VERSION=x.y.z - SO_VERSION=x -endif - -# 'make config' lists the primary installation options -config: - @echo ' ' - @echo '----------------------------------------------------------------' - @echo 'SuiteSparse package compilation options:' - @echo '----------------------------------------------------------------' - @echo ' ' - @echo 'SuiteSparse Version: ' '$(SUITESPARSE_VERSION)' - @echo 'SuiteSparse top folder: ' '$(SUITESPARSE)' - @echo 'Package: LIBRARY= ' '$(LIBRARY)' - @echo 'Version: VERSION= ' '$(VERSION)' - @echo 'SO version: SO_VERSION= ' '$(SO_VERSION)' - @echo 'System: UNAME= ' '$(UNAME)' - @echo 'Install directory: INSTALL= ' '$(INSTALL)' - @echo 'Install libraries in: INSTALL_LIB= ' '$(INSTALL_LIB)' - @echo 'Install include files in: INSTALL_INCLUDE=' '$(INSTALL_INCLUDE)' - @echo 'Install documentation in: INSTALL_DOC= ' '$(INSTALL_DOC)' - @echo 'Optimization level: OPTIMIZATION= ' '$(OPTIMIZATION)' - @echo 'parallel make jobs: JOBS= ' '$(JOBS)' - @echo 'BLAS library: BLAS= ' '$(BLAS)' - @echo 'LAPACK library: LAPACK= ' '$(LAPACK)' - @echo 'Intel TBB library: TBB= ' '$(TBB)' - @echo 'Other libraries: LDLIBS= ' '$(LDLIBS)' - @echo 'static library: AR_TARGET= ' '$(AR_TARGET)' - @echo 'shared library (full): SO_TARGET= ' '$(SO_TARGET)' - @echo 'shared library (main): SO_MAIN= ' '$(SO_MAIN)' - @echo 'shared library (short): SO_PLAIN= ' '$(SO_PLAIN)' - @echo 'shared library options: SO_OPTS= ' '$(SO_OPTS)' - @echo 'shared library name tool: SO_INSTALL_NAME=' '$(SO_INSTALL_NAME)' - @echo 'ranlib, for static libs: RANLIB= ' '$(RANLIB)' - @echo 'static library command: ARCHIVE= ' '$(ARCHIVE)' - @echo 'copy file: CP= ' '$(CP)' - @echo 'move file: MV= ' '$(MV)' - @echo 'remove file: RM= ' '$(RM)' - @echo 'pretty (for Tcov tests): PRETTY= ' '$(PRETTY)' - @echo 'C compiler: CC= ' '$(CC)' - @echo 'C++ compiler: CXX= ' '$(CXX)' - @echo 'CUDA compiler: NVCC= ' '$(NVCC)' - @echo 'CUDA root directory: CUDA_PATH= ' '$(CUDA_PATH)' - @echo 'OpenMP flags: CFOPENMP= ' '$(CFOPENMP)' - @echo 'C/C++ compiler flags: CF= ' '$(CF)' - @echo 'LD flags: LDFLAGS= ' '$(LDFLAGS)' - @echo 'Fortran compiler: F77= ' '$(F77)' - @echo 'Fortran flags: F77FLAGS= ' '$(F77FLAGS)' - @echo 'Intel MKL root: MKLROOT= ' '$(MKLROOT)' - @echo 'Auto detect Intel icc: AUTOCC= ' '$(AUTOCC)' - @echo 'UMFPACK config: UMFPACK_CONFIG= ' '$(UMFPACK_CONFIG)' - @echo 'CHOLMOD config: CHOLMOD_CONFIG= ' '$(CHOLMOD_CONFIG)' - @echo 'SuiteSparseQR config: SPQR_CONFIG= ' '$(SPQR_CONFIG)' - @echo 'CUDA library: CUDART_LIB= ' '$(CUDART_LIB)' - @echo 'CUBLAS library: CUBLAS_LIB= ' '$(CUBLAS_LIB)' - @echo 'METIS and CHOLMOD/Partition configuration:' - @echo 'Your METIS library: MY_METIS_LIB= ' '$(MY_METIS_LIB)' - @echo 'Your metis.h is in: MY_METIS_INC= ' '$(MY_METIS_INC)' - @echo 'METIS is used via the CHOLMOD/Partition module, configured as follows.' - @echo 'If the next line has -DNPARTITION then METIS will not be used:' - @echo 'CHOLMOD Partition config: ' '$(CONFIG_PARTITION)' - @echo 'CHOLMOD Partition libs: ' '$(LIB_WITH_PARTITION)' - @echo 'CHOLMOD Partition include:' '$(I_WITH_PARTITION)' -ifeq ($(TCOV),yes) - @echo 'TCOV=yes, for extensive testing only (gcc, g++, vanilla BLAS)' -endif - diff --git a/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/build/.gitignore b/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/build/.gitignore new file mode 100644 index 000000000..52e15321b --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/build/.gitignore @@ -0,0 +1,4 @@ +# Ignore all files except this file. +* +*/ +!.gitignore 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 new file mode 100644 index 000000000..2b75390cc --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/FindSuiteSparse_config.cmake @@ -0,0 +1,141 @@ +#------------------------------------------------------------------------------- +# 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 new file mode 100644 index 000000000..9e4b779ac --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparseBLAS.cmake @@ -0,0 +1,218 @@ +#------------------------------------------------------------------------------- +# SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparseBLAS.cmake +#------------------------------------------------------------------------------- + +# SuiteSparse_config, Copyright (c) 2012-2023, Timothy A. Davis. +# All Rights Reserved. +# SPDX-License-Identifier: BSD-3-clause + +#------------------------------------------------------------------------------- + +# SuiteSparse interface to the Fortran BLAS library. +# cmake 3.22 is required because BLA_SIZEOF_INTEGER is used. + +# The Intel MKL BLAS is highly recommended. It is free to download (but be +# sure to check their license to make sure you accept it). See: +# https://www.intel.com/content/www/us/en/developer/tools/oneapi/onemkl.htm + +cmake_minimum_required ( VERSION 3.22 ) + +# To select a specific BLAS: set to the BLA_VENDOR options from FindBLAS.cmake +if ( DEFINED ENV{BLA_VENDOR} ) + set ( BLA_VENDOR $ENV{BLA_VENDOR} ) +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 ) + +# dynamic/static linking with BLAS +option ( BLA_STATIC + "OFF (default): dynamic linking of BLAS. ON: static linking of BLAS" off ) + +#------------------------------------------------------------------------------- +# look for a specific BLAS library +#------------------------------------------------------------------------------- + +# To request specific BLAS, use either (for example): +# +# CMAKE_OPTIONS="-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. +# +# 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*). +# +# The default for ALLOW_64BIT_BLAS is false. + +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 ) + endif ( ) + if ( ALLOW_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 ) + endif ( ) + find_package ( BLAS REQUIRED ) + if ( BLA_SIZEOF_INTEGER EQUAL 8 ) + include ( SuiteSparseBLAS64 ) + if ( BLA_VENDOR STREQUAL "Intel10_64ilp" ) + add_compile_definitions ( MKL_ILP64 ) + endif ( ) + else ( BLA_SIZEOF_INTEGER EQUAL 4 ) + include ( SuiteSparseBLAS32 ) + endif ( ) + message ( STATUS "Specific BLAS: ${BLA_VENDOR} found: ${BLAS_FOUND}" ) + return ( ) +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 ( ALLOW_64BIT_BLAS ) + + # Look for Intel MKL BLAS with 64-bit integers + message ( STATUS "Looking for Intel 64-bit BLAS" ) + set ( BLA_VENDOR Intel10_64ilp ) + set ( BLA_SIZEOF_INTEGER 8 ) + find_package ( BLAS ) + if ( BLAS_FOUND ) + include ( SuiteSparseBLAS64 ) + add_compile_definitions ( MKL_ILP64 ) + return ( ) + endif ( ) + + # Look for ARM BLAS with 64-bit integers + message ( STATUS "Looking for ARM 64-bit BLAS" ) + set ( BLA_VENDOR Arm_ilp64_mp ) + set ( BLA_SIZEOF_INTEGER 8 ) + find_package ( BLAS ) + if ( BLAS_FOUND ) + include ( SuiteSparseBLAS64 ) + return ( ) + endif ( ) + + # Look for IBM BLAS with 64-bit integers + message ( STATUS "Looking for IBM ESSL 64-bit BLAS" ) + set ( BLA_VENDOR IBMESSL_SMP ) + set ( BLA_SIZEOF_INTEGER 8 ) + find_package ( BLAS ) + if ( BLAS_FOUND ) + include ( SuiteSparseBLAS64 ) + return ( ) + endif ( ) + + # Look for OpenBLAS with 64-bit integers + message ( STATUS "Looking for 64-bit OpenBLAS" ) + set ( BLA_VENDOR OpenBLAS ) + set ( BLA_SIZEOF_INTEGER 8 ) + find_package ( BLAS ) + if ( BLAS_FOUND ) + include ( SuiteSparseBLAS64 ) + return ( ) + endif ( ) + + # Look for any 64-bit BLAS + unset ( BLA_VENDOR ) + message ( STATUS "Looking for any 64-bit BLAS" ) + set ( BLA_SIZEOF_INTEGER 8 ) + find_package ( BLAS ) + if ( BLAS_FOUND ) + include ( SuiteSparseBLAS64 ) + return ( ) + endif ( ) + +endif ( ) + +#------------------------------------------------------------------------------- +# Look for a 32-bit BLAS, if no 64-bit BLAS has been found +#------------------------------------------------------------------------------- + +# Look for Intel MKL BLAS with 32-bit integers (and 64-bit pointer) +message ( STATUS "Looking for Intel 32-bit BLAS" ) +set ( BLA_VENDOR Intel10_64lp ) +set ( BLA_SIZEOF_INTEGER 4 ) +find_package ( BLAS ) +if ( BLAS_FOUND ) + include ( SuiteSparseBLAS32 ) + return ( ) +endif ( ) + +# Look for Apple Accelerate Framework (32-bit only) +message ( STATUS "Looking for 32-bit Apple BLAS" ) +set ( BLA_VENDOR Apple ) +set ( BLA_SIZEOF_INTEGER 4 ) +find_package ( BLAS ) +if ( BLAS_FOUND ) + include ( SuiteSparseBLAS32 ) + return ( ) +endif ( ) + +# Look for ARM BLAS with 32-bit integers +message ( STATUS "Looking for ARM 32-bit BLAS" ) +set ( BLA_VENDOR Arm_mp ) +set ( BLA_SIZEOF_INTEGER 4 ) +find_package ( BLAS ) +if ( BLAS_FOUND ) + include ( SuiteSparseBLAS32 ) + return ( ) +endif ( ) + +# Look for IBM BLAS with 32-bit integers +message ( STATUS "Looking for IBM ESSL 32-bit BLAS" ) +set ( BLA_VENDOR IBMESSL_SMP ) +set ( BLA_SIZEOF_INTEGER 4 ) +find_package ( BLAS ) +if ( BLAS_FOUND ) + include ( SuiteSparseBLAS32 ) + return ( ) +endif ( ) + +# Look for OpenBLAS with 32-bit integers +message ( STATUS "Looking for 32-bit OpenBLAS" ) +set ( BLA_VENDOR OpenBLAS ) +set ( BLA_SIZEOF_INTEGER 4 ) +find_package ( BLAS ) +if ( BLAS_FOUND ) + include ( SuiteSparseBLAS32 ) + return ( ) +endif ( ) + +# Look for FLAME BLAS(32-bit only) +message ( STATUS "Looking for 32-bit FLAME (BLIS) BLAS" ) +set ( BLA_VENDOR FLAME ) +set ( BLA_SIZEOF_INTEGER 4 ) +find_package ( BLAS ) +if ( BLAS_FOUND ) + include ( SuiteSparseBLAS32 ) + return ( ) +endif ( ) + +#------------------------------------------------------------------------------- +# do not change the following +#------------------------------------------------------------------------------- + +# Look for any 32-bit BLAS (this is required) +unset ( BLA_VENDOR ) +message ( STATUS "Looking for any 32-bit BLAS" ) +set ( BLA_SIZEOF_INTEGER 4 ) +find_package ( BLAS REQUIRED ) +include ( SuiteSparseBLAS32 ) + diff --git a/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparseBLAS32.cmake b/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparseBLAS32.cmake new file mode 100644 index 000000000..550642e30 --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparseBLAS32.cmake @@ -0,0 +1,16 @@ +#------------------------------------------------------------------------------- +# SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparseBLAS32.cmake +#------------------------------------------------------------------------------- + +# SuiteSparse_config, Copyright (c) 2012-2023, Timothy A. Davis. +# All Rights Reserved. +# SPDX-License-Identifier: BSD-3-clause + +#------------------------------------------------------------------------------- + +# actions taken when a 32-bit BLAS has been found + +message ( STATUS "Found ${BLA_VENDOR} 32-bit BLAS" ) +add_compile_definitions ( BLAS_${BLA_VENDOR} ) +set ( SuiteSparse_BLAS_integer "int32_t" ) + diff --git a/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparseBLAS64.cmake b/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparseBLAS64.cmake new file mode 100644 index 000000000..744aaef91 --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparseBLAS64.cmake @@ -0,0 +1,51 @@ +#------------------------------------------------------------------------------- +# SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparseBLAS64.cmake +#------------------------------------------------------------------------------- + +# SuiteSparse_config, Copyright (c) 2012-2023, Timothy A. Davis. +# All Rights Reserved. +# SPDX-License-Identifier: BSD-3-clause + +#------------------------------------------------------------------------------- + +# actions taken when a 64-bit BLAS has been found + +message ( STATUS "Found ${BLA_VENDOR} 64-bit BLAS" ) +add_compile_definitions ( BLAS_${BLA_VENDOR} ) +add_compile_definitions ( BLAS64 ) +set ( SuiteSparse_BLAS_integer "int64_t" ) + +#------------------------------------------------------------------------------- +# Examine the suffix appended to the Fortran 64-bit BLAS+LAPACK functions +#------------------------------------------------------------------------------- + +# OpenBLAS can be compiled by appending a suffix to each routine, so that the +# Fortan routine dgemm becomes dgemm_64, which denotes a version of dgemm with +# 64-bit integer parameters. The Sun Performance library does the same thing. + +# If the suffix does not contain "_", use (Sun Perf., for example): + +# cd build ; cmake -DBLAS64_SUFFIX="64" .. + +# If the suffix contains "_" (OpenBLAS in spack for example), use the +# following: + +# 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 + string ( FIND ${BLAS64_SUFFIX} "_" HAS_UNDERSCORE ) + message ( STATUS "BLAS64_suffix: ${BLAS64_SUFFIX}" ) + if ( HAS_UNDERSCORE EQUAL -1 ) + message ( STATUS "BLAS64 suffix has no underscore" ) + add_compile_definitions ( BLAS64_SUFFIX=${BLAS64_SUFFIX} ) + else ( ) + message ( STATUS "BLAS64 suffix has an underscore" ) + add_compile_definitions ( BLAS64__SUFFIX=${BLAS64_SUFFIX} ) + endif ( ) +endif ( ) + diff --git a/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparseLAPACK.cmake b/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparseLAPACK.cmake new file mode 100644 index 000000000..27ff73cad --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparseLAPACK.cmake @@ -0,0 +1,74 @@ +#------------------------------------------------------------------------------- +# SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparseLAPACK.cmake +#------------------------------------------------------------------------------- + +# SuiteSparse_config, Copyright (c) 2012-2023, Timothy A. Davis. +# All Rights Reserved. +# SPDX-License-Identifier: BSD-3-clause + +#------------------------------------------------------------------------------- + +# SuiteSparse interface to the Fortran LAPACK library. +# cmake 3.22 is required because BLA_SIZEOF_INTEGER is used. + +# The Intel MKL BLAS is highly recommended. It is free to download (but be +# sure to check their license to make sure you accept it). See: +# https://www.intel.com/content/www/us/en/developer/tools/oneapi/onemkl.htm + +# The use of this package must be preceded with: +# include ( SuiteSparseBLAS ) + +cmake_minimum_required ( VERSION 3.22 ) + +if ( BLA_VENDOR STREQUAL "FLAME" ) + # FLAME has the BLAS but not LAPACK + + set ( BLA_VENDOR "Generic" ) + message ( STATUS "Looking for generic LAPACK to use with BLIS/FLAME BLAS" ) + + # look for the generic dynamic LAPACK library (usually liblagraph.so) + find_library ( LAPACK_LIBRARY + NAMES lapack + PATH_SUFFIXES lib build ) + + # look for the static LAPACK library (usually liblagraph.a) + if ( MSVC ) + set ( STATIC_SUFFIX .lib ) + else ( ) + set ( STATIC_SUFFIX .a ) + endif ( ) + set ( save ${CMAKE_FIND_LIBRARY_SUFFIXES} ) + set ( CMAKE_FIND_LIBRARY_SUFFIXES ${STATIC_SUFFIX} ${CMAKE_FIND_LIBRARY_SUFFIXES} ) + find_library ( LAPACK_STATIC + NAMES lapack + PATH_SUFFIXES lib build) + set ( CMAKE_FIND_LIBRARY_SUFFIXES ${save} ) + + set ( LAPACK_LIBRARIES ${LAPACK_LIBRARY} ) + + include (FindPackageHandleStandardArgs) + + find_package_handle_standard_args ( LAPACK + REQUIRED_VARS LAPACK_LIBRARY + ) + + mark_as_advanced ( + LAPACK_LIBRARY + LAPACK_STATIC + LAPACK_LIBRARIES + ) + + set ( BLA_VENDOR "FLAME" ) + + if ( LAPACK_FOUND ) + message ( STATUS "LAPACK library: ${LAPACK_LIBRARY}" ) + message ( STATUS "LAPACK static: ${LAPACK_STATIC}" ) + else ( ) + message ( STATUS "LAPACK not found" ) + endif ( ) + +else ( ) + # all other cases: BLA_VENDOR works fine for LAPACK + find_package ( LAPACK REQUIRED ) +endif ( ) + diff --git a/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparsePolicy.cmake b/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparsePolicy.cmake new file mode 100644 index 000000000..8c18fa758 --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparsePolicy.cmake @@ -0,0 +1,269 @@ +#------------------------------------------------------------------------------- +# SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparsePolicy.cmake +#------------------------------------------------------------------------------- + +# Copyright (c) 2022-2023, Timothy A. Davis. All Rights Reserved. +# SPDX-License-Identifier: BSD-3-clause + +#------------------------------------------------------------------------------- + +# SuiteSparse CMake policies. The following parameters can be defined prior +# to including this file: +# +# CMAKE_BUILD_TYPE: if not set, it is set below to "Release". +# 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). +# +# 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 and BLA_SIZEOF_INTEGER: By default, SuiteSparse searches for +# the BLAS library in a specific order. If you wish to +# use a specific BLAS library, set both of these with +# (for example): +# -DBLA_VENDOR=Intel10_64lp -DBLA_SIZEOF_INTEGER=4 +# Both settings must appear, or neither. +# Default: neither are defined. +# +# BLA_STATIC: if true, use static linkage for BLAS and LAPACK. +# Default: false +# +# 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 +# BLA_SIZEOF_INTEGER are defined. +# +# SUITESPARSE_C_TO_FORTRAN: a string that defines how C calls Fortran. +# Defaults to "(name,NAME) name" for Windows (lower case, +# no underscore appended to the name), which is the +# system that is most likely not to have a Fortran +# compiler. Defaults to "(name,NAME) name##_" otherwise. +# 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 + +# AMICI +set(CMAKE_POSITION_INDEPENDENT_CODE ON) + +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 ) + +# add the cmake_modules folder for this package to the module path +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 ) +else ( ) + option ( NSTATIC "ON: do not build static libraries. OFF (default): build static libraries" off ) +endif ( ) + +# installation options +option ( LOCAL_INSTALL "Install in SuiteSparse/lib" off ) + +if ( SUITESPARSE_SECOND_LEVEL ) + # some packages in SuiteSparse are in SuiteSparse/Package/Package + set ( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} + ${CMAKE_SOURCE_DIR}/../../lib/cmake ) +else ( ) + # most packages in SuiteSparse are located in SuiteSparse/Package + set ( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} + ${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 ) + 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 ( ) + + 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 ( ) + +if ( LOCAL_INSTALL ) + set ( SUITESPARSE_LIBDIR ${SUITESPARSE_LOCAL_PREFIX}/lib ) + set ( SUITESPARSE_INCLUDEDIR ${SUITESPARSE_LOCAL_PREFIX}/include ) + set ( SUITESPARSE_BINDIR ${SUITESPARSE_LOCAL_PREFIX}/bin ) +else ( ) + set ( SUITESPARSE_LIBDIR ${CMAKE_INSTALL_LIBDIR} ) + set ( SUITESPARSE_INCLUDEDIR ${CMAKE_INSTALL_INCLUDEDIR} ) + 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} ) +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}" ) + +if ( NOT CMAKE_BUILD_TYPE ) + set ( CMAKE_BUILD_TYPE Release ) +endif ( ) + +message ( STATUS "Build type: ${CMAKE_BUILD_TYPE} ") + +set ( CMAKE_INCLUDE_CURRENT_DIR ON ) + +#------------------------------------------------------------------------------- +# 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 ( ) + check_language ( Fortran ) + if ( CMAKE_Fortran_COMPILER ) + enable_language ( Fortran ) + message ( STATUS "Fortran: ${CMAKE_Fortran_COMPILER}" ) + else ( ) + # Fortran not available: + set ( NFORTRAN true ) + message ( STATUS "Fortran: not available" ) + endif ( ) +endif ( ) + +# default C-to-Fortran name mangling if Fortran compiler not found +if ( MSVC ) + # MS Visual Studio Fortran compiler does not mangle the Fortran name + set ( SUITESPARSE_C_TO_FORTRAN "(name,NAME) name" + CACHE STRING "C to Fortan name mangling" ) +else ( ) + # Other systems (Linux, Mac) typically append an underscore + set ( SUITESPARSE_C_TO_FORTRAN "(name,NAME) name##_" + CACHE STRING "C to Fortan name mangling" ) +endif ( ) + +#------------------------------------------------------------------------------- +# find CUDA +#------------------------------------------------------------------------------- + +if ( ENABLE_CUDA ) + + # try finding CUDA + check_language ( CUDA ) + message ( STATUS "Looking for CUDA" ) + if ( CMAKE_CUDA_COMPILER ) + # with CUDA: + 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} ) + 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 ) + else ( ) + # CUDA 11.2 or later present + enable_language ( CUDA ) + set ( SUITESPARSE_CUDA on ) + endif ( ) + else ( ) + # without CUDA: + message ( STATUS "CUDA: not found" ) + set ( SUITESPARSE_CUDA off ) + endif ( ) + +else ( ) + + # CUDA is disabled + set ( SUITESPARSE_CUDA off ) + +endif ( ) + +if ( SUITESPARSE_CUDA ) + message ( STATUS "CUDA: enabled" ) + add_compile_definitions ( SUITESPARSE_CUDA ) + set ( SUITESPARSE_CUDA_ARCHITECTURES "52;75;80" CACHE STRING "CUDA architectures" ) + set ( CMAKE_CUDA_ARCHITECTURES ${SUITESPARSE_CUDA_ARCHITECTURES} ) +else ( ) + message ( STATUS "CUDA: not enabled" ) +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 new file mode 100644 index 000000000..9271c4a92 --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparseReport.cmake @@ -0,0 +1,55 @@ +#------------------------------------------------------------------------------- +# SuiteSparse/SuiteSparse_config/SuiteSparseReport.cmake +#------------------------------------------------------------------------------- + +# Copyright (c) 2012-2023, Timothy A. Davis. All Rights Reserved. +# SPDX-License-Identifier: BSD-3-clause + +#------------------------------------------------------------------------------- +# report status and compile flags +#------------------------------------------------------------------------------- + +message ( STATUS "------------------------------------------------------------------------" ) +message ( STATUS "SuiteSparse CMAKE report for: ${CMAKE_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 ") +endif ( ) +message ( STATUS "C compiler: ${CMAKE_C_COMPILER} ") +message ( STATUS "C flags: ${CMAKE_C_FLAGS}" ) +message ( STATUS "C++ compiler: ${CMAKE_CXX_COMPILER}" ) +message ( STATUS "C++ flags: ${CMAKE_CXX_FLAGS}" ) +if ( ${CMAKE_BUILD_TYPE} STREQUAL "Debug" ) + message ( STATUS "C Flags debug: ${CMAKE_C_FLAGS_DEBUG} ") + message ( STATUS "C++ Flags debug: ${CMAKE_CXX_FLAGS_DEBUG} ") +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 ( ) + message ( STATUS "Fortran compiler: ${CMAKE_Fortran_COMPILER} " ) +endif ( ) +get_property ( CDEFN DIRECTORY PROPERTY COMPILE_DEFINITIONS ) +message ( STATUS "compile definitions: ${CDEFN}") +if ( DEFINED SuiteSparse_BLAS_integer ) + message ( STATUS "BLAS integer: ${SuiteSparse_BLAS_integer}" ) +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_ssize_t.cmake b/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparse_ssize_t.cmake new file mode 100644 index 000000000..2b74bae83 --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparse_ssize_t.cmake @@ -0,0 +1,32 @@ +#------------------------------------------------------------------------------- +# 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/SuiteSparse_config/xerbla/Makefile b/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/xerbla/Makefile deleted file mode 100644 index db68a2ea8..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/xerbla/Makefile +++ /dev/null @@ -1,75 +0,0 @@ -# Makefile for null-output xerbla, both C and Fortran versions. -# By default, the C version (libcerbla.a and *.so) is compiled and installed. -# Set the USE_FORTRAN option to 1 to create the Fortran instead (libxerbla): - USE_FORTRAN = 0 -# USE_FORTRAN = 1 - -VERSION = 1.0.3 -SO_VERSION = 1 - -default: library - -# compile and install in SuiteSparse/lib -library: - $(MAKE) install INSTALL=$(SUITESPARSE) - -all: library - -ifeq ($(USE_FORTRAN),0) - LIBRARY = libcerbla -else - LIBRARY = libxerbla -endif - -include ../SuiteSparse_config.mk - -ifeq ($(USE_FORTRAN),0) - COMPILE = $(CC) $(CF) -c xerbla.c - DEPENDS = xerbla.c xerbla.h -else - COMPILE = $(F77) $(F77FLAGS) -c xerbla.f - DEPENDS = xerbla.f -endif - -ccode: all - -fortran: all - -static: $(AR_TARGET) - -$(AR_TARGET): $(DEPENDS) - $(COMPILE) - $(ARCHIVE) $(AR_TARGET) xerbla.o - - $(RANLIB) $(AR_TARGET) - - $(RM) xerbla.o - -# install libcerbla / libxerbla -install: $(AR_TARGET) $(INSTALL_LIB)/$(SO_TARGET) - -$(INSTALL_LIB)/$(SO_TARGET): $(DEPENDS) - @mkdir -p $(INSTALL_LIB) - @mkdir -p $(INSTALL_INCLUDE) - @mkdir -p $(INSTALL_DOC) - $(COMPILE) - $(CC) $(SO_OPTS) xerbla.o -o $@ - - $(RM) xerbla.o - ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_PLAIN) ) - ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_MAIN) ) - $(CP) xerbla.h $(INSTALL_INCLUDE) - chmod 755 $(INSTALL_LIB)/$(SO_TARGET) - chmod 644 $(INSTALL_INCLUDE)/xerbla.h - -# uninstall libcerbla / libxerbla -uninstall: - $(RM) $(INSTALL_LIB)/$(SO_TARGET) - $(RM) $(INSTALL_LIB)/$(SO_PLAIN) - $(RM) $(INSTALL_INCLUDE)/xerbla.h - -distclean: purge - -purge: clean - - $(RM) -r $(PURGE) - -clean: - - $(RM) -r $(CLEAN) - diff --git a/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/xerbla/xerbla.c b/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/xerbla/xerbla.c deleted file mode 100644 index 5107f0353..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/xerbla/xerbla.c +++ /dev/null @@ -1,12 +0,0 @@ - -void xerbla_ (char *srname, int *info) -{ - /* do nothing */ ; -} - - -void xerbla (char *srname, int *info) -{ - /* do nothing */ ; -} - diff --git a/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/xerbla/xerbla.f b/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/xerbla/xerbla.f deleted file mode 100644 index 42720048a..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/xerbla/xerbla.f +++ /dev/null @@ -1,46 +0,0 @@ - SUBROUTINE XERBLA( SRNAME, INFO ) -* -* -- LAPACK auxiliary routine (version 3.0) -- -* Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., -* Courant Institute, Argonne National Lab, and Rice University -* September 30, 1994 -* -* .. Scalar Arguments .. - CHARACTER*6 SRNAME - INTEGER INFO -* .. -* -* Purpose -* ======= -* -* XERBLA is an error handler for the LAPACK routines. -* It is called by an LAPACK routine if an input parameter has an -* invalid value. A message is printed and execution stops. -* -* Installers may consider modifying the STOP statement in order to -* call system-specific exception-handling facilities. -* -* Arguments -* ========= -* -* SRNAME (input) CHARACTER*6 -* The name of the routine which called XERBLA. -* -* INFO (input) INTEGER -* The position of the invalid parameter in the parameter list -* of the calling routine. -* -* ===================================================================== -* -* .. Executable Statements .. -* -***** WRITE( *, FMT = 9999 )SRNAME, INFO -* -***** STOP -* -***** 9999 FORMAT( ' ** On entry to ', A6, ' parameter number ', I2, ' had ', -***** $ 'an illegal value' ) -* -* End of XERBLA -* - END diff --git a/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/xerbla/xerbla.h b/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/xerbla/xerbla.h deleted file mode 100644 index b332eb3da..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/xerbla/xerbla.h +++ /dev/null @@ -1,2 +0,0 @@ -void xerbla_ (char *srname, int *info) ; -void xerbla (char *srname, int *info) ; diff --git a/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_demo.m b/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_demo.m deleted file mode 100644 index b5fce6ad1..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_demo.m +++ /dev/null @@ -1,212 +0,0 @@ -function SuiteSparse_demo (matrixpath, dopause) -%SUITESPARSE_DEMO a demo of all packages in SuiteSparse -% -% Example: -% SuiteSparse_demo -% -% See also umfpack, cholmod, amd, camd, colamd, ccolamd, btf, klu, spqr, -% CSparse, CXSparse, ldlsparse, mongoose - -% Copyright 2016, Timothy A. Davis, http://www.suitesparse.com. - -if (nargin < 1 || isempty (matrixpath) || ~ischar (matrixpath)) - try - % older versions of MATLAB do not have an input argument to mfilename - p = mfilename ('fullpath') ; - t = strfind (p, '/') ; - matrixpath = [ p(1:t(end)) 'CXSparse/Matrix' ] ; - catch me %#ok - % mfilename failed, assume we're in the SuiteSparse directory - matrixpath = 'CXSparse/Matrix' ; - end -end - -if (nargin < 2) - dopause = false ; -end - -if (dopause) - input ('Hit enter to run the CXSparse demo: ', 's') ; -end -try - cs_demo (0, matrixpath) -catch me - disp (me.message) ; - fprintf ('\nIf you have an older version of MATLAB, you must run the\n') ; - fprintf ('SuiteSparse_demo while in the SuiteSparse directory.\n\n') ; - fprintf ('CXSparse demo failed\n' ) -end - -if (dopause) - input ('Hit enter to run the UMFPACK demo: ', 's') ; -end -try - umfpack_demo (1) -catch me - disp (me.message) ; - fprintf ('UMFPACK demo failed\n' ) -end - -if (dopause) - input ('Hit enter to run the CHOLMOD demo: ', 's') ; -end -try - cholmod_demo -catch me - disp (me.message) ; - fprintf ('CHOLMOD demo failed\n' ) -end - -if (dopause) - input ('Hit enter to run the CHOLMOD graph partitioning demo: ', 's') ; -end -try - graph_demo -catch me - disp (me.message) ; - fprintf ('graph_demo failed, probably because METIS not installed\n') ; -end - -if (dopause) - input ('Hit enter to run the AMD demo: ', 's') ; -end -try - amd_demo -catch me - disp (me.message) ; - fprintf ('AMD demo failed\n' ) -end - -if (dopause) - input ('Hit enter to run the CAMD demo: ', 's') ; -end -try - camd_demo -catch me - disp (me.message) ; - fprintf ('CAMD demo failed\n' ) -end - -if (dopause) - input ('Hit enter to run the COLAMD demo: ', 's') ; -end -try - colamd_demo -catch me - disp (me.message) ; - fprintf ('COLAMD demo failed\n' ) -end - -if (dopause) - input ('Hit enter to run the CCOLAMD demo: ', 's') ; -end -try - ccolamd_demo -catch me - disp (me.message) ; - fprintf ('CCOLAMD demo failed\n' ) -end - -if (dopause) - input ('Hit enter to run the BTF demo: ', 's') ; -end -try - btf_demo -catch me - disp (me.message) ; - fprintf ('BTF demo failed\n' ) -end - -if (dopause) - input ('Hit enter to run the KLU demo: ', 's') ; -end -try - klu_demo -catch me - disp (me.message) ; - fprintf ('KLU demo failed\n' ) -end - -if (dopause) - input ('Hit enter to run the LDL demo: ', 's') ; -end -try - ldldemo -catch me - disp (me.message) ; - fprintf ('LDL demo failed\n' ) -end - -if (dopause) - input ('Hit enter to run the SSMULT demo: ', 's') ; -end -try - ssmult_demo -catch me - disp (me.message) ; - fprintf ('SSMULT demo failed\n' ) -end - -if (dopause) - input ('Hit enter to run the MESHND demo: ', 's') ; -end -try - meshnd_example -catch me - disp (me.message) ; - fprintf ('MESHND demo failed\n' ) -end - -if (dopause) - input ('Hit enter to run the SPARSEINV demo: ', 's') ; -end -try - sparseinv_test -catch me - disp (me.message) ; - fprintf ('SPARSEINV demo failed\n' ) -end - -if (dopause) - input ('Hit enter to run the SuiteSparseQR demo: ', 's') ; -end -try - spqr_demo -catch me - disp (me.message) ; - fprintf ('SuiteSparseQR demo failed\n' ) -end - -if (dopause) - input ('Hit enter to run the quick spqr_rank demo: ', 's') ; -end -try - quickdemo_spqr_rank -catch me - disp (me.message) ; - fprintf ('spqr_rank demo failed\n' ) -end - -if (dopause) - input ('Hit enter to run the Mongoose demo: ', 's') ; -end -try - mongoose_demo -catch me - disp (me.message) ; - fprintf ('Mongoose demo failed\n' ) -end - -%{ -if (dopause) - input ('Hit enter to run the piro_band demo: ', 's') ; -end -try - piro_band_demo -catch me - disp (me.message) ; - fprintf ('piro_band_demo failed\n' ) -end -%} - -fprintf ('\n\n---- SuiteSparse demos complete\n') ; diff --git a/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_install.m b/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_install.m deleted file mode 100644 index 630854975..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_install.m +++ /dev/null @@ -1,344 +0,0 @@ -function SuiteSparse_install (do_demo) -%SuiteSparse_install: compiles and installs all of SuiteSparse -% A Suite of Sparse matrix packages, authored or co-authored by Tim Davis. -% -% Packages in SuiteSparse: -% -% GraphBLAS graph algorithms via sparse linear algebra (graphblas.org) -% (does not yet have a MATLAB interface) -% Mongoose graph partitioner -% UMFPACK sparse LU factorization (multifrontal) -% CHOLMOD sparse Cholesky factorization, and many other operations -% AMD sparse symmetric approximate minimum degree ordering -% COLAMD sparse column approximate minimum degree ordering -% CAMD constrained AMD -% CCOLAMD constrained COLAMD -% CSparse a Concise Sparse matrix package (32-bit or 64-bit, real only) -% CXSparse extended version of CSparse (32-bit/64-bit/real/complex) -% ssget interface to SuiteSparse Matrix Collection -% KLU sparse LU factorization (left-looking) -% BTF permutation to block triangular form (like dmperm) -% LDL sparse LDL' factorization -% SuiteSparseCollection tools for managing the SuiteSparse Matrix Collection -% RBio read/write Rutherford/Boeing files -% SSMULT sparse matrix times sparse matrix -% MESHND 2D and 3D regular mesh generation and nested dissection -% FACTORIZE an object-oriented solver for x=A\b -% SPARSEINV sparse inverse subset; computes entries of inv(sparse(A)) -% MATLAB_Tools various simple m-files and demos -% SuiteSparseQR sparse QR factorization -% spqr_rank MATLAB toolbox for sparse rank deficient matrices -% -% Example: -% SuiteSparse_install % compile and prompt to run each package's demo -% SuiteSparse_install(0) % compile but do not run the demo -% SuiteSparse_install(1) % compile and run the demos with no prompts -% help SuiteSparse % for more details -% -% See also AMD, COLAMD, CAMD, CCOLAMD, CHOLMOD, UMFPACK, CSPARSE, CXSPARSE, -% ssget, RBio, SuiteSparseCollection, KLU, BTF, MESHND, SSMULT, LINFACTOR, -% SPOK, SPQR_RANK, SuiteSparse, SPQR, PATHTOOL, PATH, FACTORIZE, -% SPARSEINV, Mongoose. -% -% This script installs the full-featured CXSparse rather than CSparse. -% -% Copyright 1990-2018, Timothy A. Davis, http://www.suitesparse.com. -% In collaboration with (in alphabetical order): Patrick Amestoy, David -% Bateman, Yanqing Chen, Iain Duff, Les Foster, William Hager, Scott Kolodziej, -% Stefan Larimore, Ekanathan Palamadai Natarajan, Sivasankaran Rajamanickam, -% Sanjay Ranka, Wissam Sid-Lakhdar, and Nuri Yeralan. - -%------------------------------------------------------------------------------- -% initializations -%------------------------------------------------------------------------------- - -paths = { } ; -SuiteSparse = pwd ; - -% determine the MATLAB version (6.1, 6.5, 7.0, ...) -v = version ; -pc = ispc ; - -% print the introduction -help SuiteSparse_install - -fprintf ('\nInstalling SuiteSparse for MATLAB version %s\n\n', v) ; - -% add SuiteSparse to the path -paths = add_to_path (paths, SuiteSparse) ; - -%------------------------------------------------------------------------------- -% compile and install the packages -%------------------------------------------------------------------------------- - -% compile and install UMFPACK -try - paths = add_to_path (paths, [SuiteSparse '/UMFPACK/MATLAB']) ; - umfpack_make ; -catch me - disp (me.message) ; - fprintf ('UMFPACK not installed\n') ; -end - -% compile and install CHOLMOD -try - paths = add_to_path (paths, [SuiteSparse '/CHOLMOD/MATLAB']) ; - cholmod_make ; -catch me - disp (me.message) ; - fprintf ('CHOLMOD not installed\n') ; -end - -% compile and install AMD -try - paths = add_to_path (paths, [SuiteSparse '/AMD/MATLAB']) ; - amd_make ; -catch me - disp (me.message) ; - fprintf ('AMD not installed\n') ; -end - -% compile and install COLAMD -try - paths = add_to_path (paths, [SuiteSparse '/COLAMD/MATLAB']) ; - colamd_make ; -catch me - disp (me.message) ; - fprintf ('COLAMD not installed\n') ; -end - -% compile and install CCOLAMD -try - paths = add_to_path (paths, [SuiteSparse '/CCOLAMD/MATLAB']) ; - ccolamd_make ; -catch me - disp (me.message) ; - fprintf ('CCOLAMD not installed\n') ; -end - -% compile and install CAMD -try - paths = add_to_path (paths, [SuiteSparse '/CAMD/MATLAB']) ; - camd_make ; -catch me - disp (me.message) ; - fprintf ('CAMD not installed\n') ; -end - -% install ssget, unless it's already in the path -try - % if this fails, then ssget is not yet installed - index = ssget ; - fprintf ('ssget already installed:\n') ; - which ssget -catch - index = [ ] ; -end -if (isempty (index)) - % ssget is not installed. Use SuiteSparse/ssget - fprintf ('Installing SuiteSparse/ssget\n') ; - try - paths = add_to_path (paths, [SuiteSparse '/ssget']) ; - catch me - disp (me.message) ; - fprintf ('ssget not installed\n') ; - end -end - -% compile and install CXSparse -try - paths = add_to_path (paths, [SuiteSparse '/CXSparse/MATLAB/Demo']) ; - paths = add_to_path (paths, [SuiteSparse '/CXSparse/MATLAB/CSparse']) ; - fprintf ('Compiling CXSparse:\n') ; - if (pc) - % Windows does not support ANSI C99 complex, which CXSparse requires - cs_make (1, 0) ; - else - cs_make (1) ; - end -catch me - disp (me.message) ; - fprintf ('CXSparse not installed\n') ; -end - -% compile and install LDL -try - paths = add_to_path (paths, [SuiteSparse '/LDL/MATLAB']) ; - ldl_make ; -catch me - disp (me.message) ; - fprintf ('LDL not installed\n') ; -end - -% compile and install BTF -try - paths = add_to_path (paths, [SuiteSparse '/BTF/MATLAB']) ; - btf_make ; -catch me - disp (me.message) ; - fprintf ('BTF not installed\n') ; -end - -% compile and install KLU -try - paths = add_to_path (paths, [SuiteSparse '/KLU/MATLAB']) ; - klu_make ; -catch me - disp (me.message) ; - fprintf ('KLU not installed\n') ; -end - -% compile and install SuiteSparseQR -try - if (pc) - fprintf ('Note that SuiteSparseQR will not compile with the lcc\n') ; - fprintf ('compiler provided with MATLAB on Windows\n') ; - end - paths = add_to_path (paths, [SuiteSparse '/SPQR/MATLAB']) ; - spqr_make ; -catch me - disp (me.message) ; - fprintf ('SuiteSparseQR not installed\n') ; -end - -% compile and install RBio -try - paths = add_to_path (paths, [SuiteSparse '/RBio/RBio']) ; - RBmake ; -catch me - disp (me.message) ; - fprintf ('RBio not installed.\n') ; -end - -% install MATLAB_Tools/* -try - paths = add_to_path (paths, [SuiteSparse '/MATLAB_Tools']) ; - paths = add_to_path (paths, [SuiteSparse '/MATLAB_Tools/Factorize']) ; - paths = add_to_path (paths, [SuiteSparse '/MATLAB_Tools/MESHND']) ; - paths = add_to_path (paths, [SuiteSparse '/MATLAB_Tools/LINFACTOR']) ; - paths = add_to_path (paths, [SuiteSparse '/MATLAB_Tools/find_components']) ; - paths = add_to_path (paths, [SuiteSparse '/MATLAB_Tools/GEE']) ; - paths = add_to_path (paths, [SuiteSparse '/MATLAB_Tools/shellgui']) ; - paths = add_to_path (paths, [SuiteSparse '/MATLAB_Tools/waitmex']) ; - paths = add_to_path (paths, [SuiteSparse '/MATLAB_Tools/spqr_rank']) ; - paths = add_to_path (paths, [SuiteSparse '/MATLAB_Tools/spqr_rank/SJget']) ; - fprintf ('MATLAB_Tools installed\n') ; -catch me - disp (me.message) ; - fprintf ('MATLAB_Tools not installed\n') ; -end - -% compile and install SuiteSparseCollection -try - % do not try to compile with large-file I/O for MATLAB 6.5 or earlier - paths = add_to_path (paths, [SuiteSparse '/MATLAB_Tools/SuiteSparseCollection']) ; - ss_install (verLessThan ('matlab', '7.0')) ; -catch me - disp (me.message) ; - fprintf ('SuiteSparseCollection not installed\n') ; -end - -% compile and install SSMULT -try - paths = add_to_path (paths, [SuiteSparse '/MATLAB_Tools/SSMULT']) ; - ssmult_install ; -catch me - disp (me.message) ; - fprintf ('SSMULT not installed\n') ; -end - -% compile and install dimacs10 -try - paths = add_to_path (paths, [SuiteSparse '/MATLAB_Tools/dimacs10']) ; - dimacs10_install (0) ; -catch me - disp (me.message) ; - fprintf ('MATLAB_Tools/dimacs10 not installed\n') ; -end - -% compile and install spok -try - paths = add_to_path (paths, [SuiteSparse '/MATLAB_Tools/spok']) ; - spok_install ; -catch me - disp (me.message) ; - fprintf ('MATLAB_Tools/spok not installed\n') ; -end - -%{ -% compile and install PIRO_BAND -try - paths = add_to_path (paths, [SuiteSparse '/PIRO_BAND/MATLAB']) ; - piro_band_make ; -catch me - disp (me.message) ; - fprintf ('PIRO_BAND not installed\n') ; -end -%} - -% compile and install sparsinv -try - paths = add_to_path (paths, [SuiteSparse '/MATLAB_Tools/sparseinv']) ; - sparseinv_install ; -catch me - disp (me.message) ; - fprintf ('MATLAB_Tools/sparseinv not installed\n') ; -end - -% compile and install Mongoose -try - paths = add_to_path (paths, [SuiteSparse '/Mongoose/MATLAB']) ; - mongoose_make (0) ; -catch me - disp (me.message) ; - fprintf ('Mongoose not installed\n') ; -end - -%------------------------------------------------------------------------------- -% post-install wrapup -%------------------------------------------------------------------------------- - -cd (SuiteSparse) -fprintf ('SuiteSparse is now installed.\n\n') ; - -% run the demo, if requested -if (nargin < 1) - % ask if demo should be run - y = input ('Hit enter to run the SuiteSparse demo (or "n" to quit): ', 's'); - if (isempty (y)) - y = 'y' ; - end - do_demo = (y (1) ~= 'n') ; - do_pause = true ; -else - % run the demo without pausing - do_pause = false ; -end -if (do_demo) - try - SuiteSparse_demo ([ ], do_pause) ; - catch me - disp (me.message) ; - fprintf ('SuiteSparse demo failed\n') ; - end -end - -% print the list of new directories added to the path -fprintf ('\nSuiteSparse installation is complete. The following paths\n') ; -fprintf ('have been added for this session. Use pathtool to add them\n') ; -fprintf ('permanently. If you cannot save the new path because of file\n'); -fprintf ('permissions, then add these commands to your startup.m file.\n') ; -fprintf ('Type "doc startup" and "doc pathtool" for more information.\n\n') ; -for k = 1:length (paths) - fprintf ('addpath %s\n', paths {k}) ; -end -cd (SuiteSparse) - -fprintf ('\nSuiteSparse for MATLAB %s installation complete\n', v) ; - -%------------------------------------------------------------------------------- -function paths = add_to_path (paths, newpath) -% add a path -cd (newpath) ; -addpath (newpath) ; -paths = [paths { newpath } ] ; diff --git a/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_test.m b/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_test.m deleted file mode 100644 index 24d22fa29..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_test.m +++ /dev/null @@ -1,237 +0,0 @@ -function SuiteSparse_test -% SuiteSparse_test exhaustive test of all SuiteSparse packages -% -% Your current directory must be SuiteSparse for this function to work. -% SuiteSparse_install must be run prior to running this test. Warning: -% this test takes a *** long **** time. -% -% Example: -% SuiteSparse_test -% -% See also SuiteSparse_install, SuiteSparse_demo. - -% Copyright 1990-2015, Timothy A. Davis, http://www.suitesparse.com. - -help SuiteSparse_test - -npackages = 19 ; -h = waitbar (0, 'SuiteSparse test:') ; -SuiteSparse = pwd ; -package = 0 ; - -try - - %--------------------------------------------------------------------------- - % CSparse - %--------------------------------------------------------------------------- - - % compile and install CSparse (not installed by SuiteSparse_install) - package = package + 1 ; - waitbar (package/(npackages+1), h, 'SuiteSparse test: CSparse') ; - addpath ([SuiteSparse '/CSparse/MATLAB/CSparse']) ; - addpath ([SuiteSparse '/CSparse/MATLAB/Demo']) ; - cd ([SuiteSparse '/CSparse/MATLAB/CSparse']) ; - cs_make ; - % test CSparse - cd ([SuiteSparse '/CSparse/MATLAB/Test']) ; - testall ; - % uninstall CSparse by removing it from path - rmpath ([SuiteSparse '/CSparse/MATLAB/CSparse']) ; - rmpath ([SuiteSparse '/CSparse/MATLAB/Demo']) ; - rmpath ([SuiteSparse '/CSparse/MATLAB/ssget']) ; - - %--------------------------------------------------------------------------- - % CXSparse - %--------------------------------------------------------------------------- - - package = package + 1 ; - waitbar (package/(npackages+1), h, 'SuiteSparse test: CXSparse') ; - cd ([SuiteSparse '/CXSparse/MATLAB/Test']) ; - testall ; - - %--------------------------------------------------------------------------- - % COLAMD - %--------------------------------------------------------------------------- - - package = package + 1 ; - waitbar (package/(npackages+1), h, 'SuiteSparse test: COLAMD') ; - cd ([SuiteSparse '/COLAMD/MATLAB']) ; - colamd_test ; - - %--------------------------------------------------------------------------- - % CCOLAMD - %--------------------------------------------------------------------------- - - package = package + 1 ; - waitbar (package/(npackages+1), h, 'SuiteSparse test: CCOLAMD') ; - cd ([SuiteSparse '/CCOLAMD/MATLAB']) ; - ccolamd_test ; - - %--------------------------------------------------------------------------- - % UMFPACK - %--------------------------------------------------------------------------- - - package = package + 1 ; - waitbar (package/(npackages+1), h, 'SuiteSparse test: UMFPACK') ; - cd ([SuiteSparse '/UMFPACK/MATLAB']) ; - umfpack_test (100) ; - - %--------------------------------------------------------------------------- - % CHOLMOD - %--------------------------------------------------------------------------- - - package = package + 1 ; - waitbar (package/(npackages+1), h, 'SuiteSparse test: CHOLMOD') ; - cd ([SuiteSparse '/CHOLMOD/MATLAB/Test']) ; - cholmod_test ; - - %--------------------------------------------------------------------------- - % BTF - %--------------------------------------------------------------------------- - - package = package + 1 ; - waitbar (package/(npackages+1), h, 'SuiteSparse test: BTF') ; - cd ([SuiteSparse '/BTF/MATLAB/Test']) ; - btf_test ; - - %--------------------------------------------------------------------------- - % KLU - %--------------------------------------------------------------------------- - - package = package + 1 ; - waitbar (package/(npackages+1), h, 'SuiteSparse test: KLU') ; - cd ([SuiteSparse '/KLU/MATLAB/Test']) ; - klu_test (100) ; - - %--------------------------------------------------------------------------- - % LDL - %--------------------------------------------------------------------------- - - package = package + 1 ; - waitbar (package/(npackages+1), h, 'SuiteSparse test: LDL') ; - cd ([SuiteSparse '/LDL/MATLAB']) ; - ldlmain2 ; - ldltest ; - - %--------------------------------------------------------------------------- - % LINFACTOR: MATLAB 7.3 (R2006b) or later required - %--------------------------------------------------------------------------- - - package = package + 1 ; - if (verLessThan ('matlab', '7.3')) - % skip test of LINFACTOR - else - waitbar (package/(npackages+1), h, 'SuiteSparse test: LINFACTOR') ; - cd ([SuiteSparse '/MATLAB_Tools/LINFACTOR']) ; - lintests ; - end - - %--------------------------------------------------------------------------- - % MESHND - %--------------------------------------------------------------------------- - - package = package + 1 ; - waitbar (package/(npackages+1), h, 'SuiteSparse test: MESHND') ; - cd ([SuiteSparse '/MATLAB_Tools/MESHND']) ; - meshnd_quality ; - - %--------------------------------------------------------------------------- - % SSMULT - %--------------------------------------------------------------------------- - - package = package + 1 ; - waitbar (package/(npackages+1), h, 'SuiteSparse test: SSMULT') ; - cd ([SuiteSparse '/MATLAB_Tools/SSMULT']) ; - sstest3 ; - - %--------------------------------------------------------------------------- - % other MATLAB_Tools - %--------------------------------------------------------------------------- - - package = package + 1 ; - waitbar (package/(npackages+1), h, 'SuiteSparse test: MATLAB Tools') ; - cd ([SuiteSparse '/MATLAB_Tools']) ; - fprintf ('getversion: %g\n', getversion) ; - seashell ; - shellgui ; - cd ([SuiteSparse '/MATLAB_Tools/waitmex']) ; - waitmex ; - url = 'http://www.suitesparse.com' ; - fprintf ('Click here for more details\n', url) ; - hprintf ('or see \n', url) ; - cd ([SuiteSparse '/MATLAB_Tools/find_components']) ; - find_components_example (1, 0) ; - cd ([SuiteSparse '/MATLAB_Tools/spok']) ; - spok_test ; - - %--------------------------------------------------------------------------- - % FACTORIZE - %--------------------------------------------------------------------------- - - package = package + 1 ; - waitbar (package/(npackages+1), h, 'SuiteSparse test: FACTORIZE') ; - cd ([SuiteSparse '/MATLAB_Tools/Factorize/Test']) ; - test_all ; - - %--------------------------------------------------------------------------- - % SPARSEINV - %--------------------------------------------------------------------------- - - package = package + 1 ; - waitbar (package/(npackages+1), h, 'SuiteSparse test: SPARSEINV') ; - cd ([SuiteSparse '/MATLAB_Tools/sparseinv']) ; - sparseinv_test - - %--------------------------------------------------------------------------- - % SPQR_RANK - %--------------------------------------------------------------------------- - - package = package + 1 ; - waitbar (package/(npackages+1), h, 'SuiteSparse test: spqr_rank') ; - cd ([SuiteSparse '/MATLAB_Tools/spqr_rank']) ; - demo_spqr_rank ; - - %--------------------------------------------------------------------------- - % Mongoose - %--------------------------------------------------------------------------- - - package = package + 1 ; - waitbar (package/(npackages+1), h, 'SuiteSparse test: mongoose') ; - cd ([SuiteSparse '/Mongoose/MATLAB']) ; - mongoose_test ; - - %--------------------------------------------------------------------------- - % PIRO_BAND - %--------------------------------------------------------------------------- - -% package = package + 1 ; -% waitbar (package/(npackages+1), h, 'SuiteSparse test: PIRO_BAND') ; -% cd ([SuiteSparse '/PIRO_BAND/MATLAB/Test']) ; -% demo_spqr_rank ; - - %--------------------------------------------------------------------------- - % AMD, CAMD, SuiteSparseCollection, ssget - %--------------------------------------------------------------------------- - - % no exhaustive tests; tested via other packages - -catch - - %--------------------------------------------------------------------------- - % test failure - %--------------------------------------------------------------------------- - - cd (SuiteSparse) ; - disp (lasterr) ; %#ok - fprintf ('SuiteSparse test: FAILED\n') ; - return - -end - -%------------------------------------------------------------------------------- -% test OK -%------------------------------------------------------------------------------- - -close (h) ; -fprintf ('SuiteSparse test: OK\n') ; -cd (SuiteSparse) ; diff --git a/deps/AMICI/ThirdParty/SuiteSparse/include/SuiteSparse_config.h b/deps/AMICI/ThirdParty/SuiteSparse/include/SuiteSparse_config.h deleted file mode 100644 index 9e28c0530..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/include/SuiteSparse_config.h +++ /dev/null @@ -1,216 +0,0 @@ -/* ========================================================================== */ -/* === SuiteSparse_config =================================================== */ -/* ========================================================================== */ - -/* Configuration file for SuiteSparse: a Suite of Sparse matrix packages - * (AMD, COLAMD, CCOLAMD, CAMD, CHOLMOD, UMFPACK, CXSparse, and others). - * - * SuiteSparse_config.h provides the definition of the long integer. On most - * systems, a C program can be compiled in LP64 mode, in which long's and - * pointers are both 64-bits, and int's are 32-bits. Windows 64, however, uses - * the LLP64 model, in which int's and long's are 32-bits, and long long's and - * pointers are 64-bits. - * - * SuiteSparse packages that include long integer versions are - * intended for the LP64 mode. However, as a workaround for Windows 64 - * (and perhaps other systems), the long integer can be redefined. - * - * If _WIN64 is defined, then the __int64 type is used instead of long. - * - * The long integer can also be defined at compile time. For example, this - * could be added to SuiteSparse_config.mk: - * - * CFLAGS = -O -D'SuiteSparse_long=long long' \ - * -D'SuiteSparse_long_max=9223372036854775801' -D'SuiteSparse_long_idd="lld"' - * - * This file defines SuiteSparse_long as either long (on all but _WIN64) or - * __int64 on Windows 64. The intent is that a SuiteSparse_long is always a - * 64-bit integer in a 64-bit code. ptrdiff_t might be a better choice than - * long; it is always the same size as a pointer. - * - * This file also defines the SUITESPARSE_VERSION and related definitions. - * - * Copyright (c) 2012, Timothy A. Davis. No licensing restrictions apply - * to this file or to the SuiteSparse_config directory. - * Author: Timothy A. Davis. - */ - -#ifndef SUITESPARSE_CONFIG_H -#define SUITESPARSE_CONFIG_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include - -/* ========================================================================== */ -/* === SuiteSparse_long ===================================================== */ -/* ========================================================================== */ - -#ifndef SuiteSparse_long - -#ifdef _WIN64 - -#define SuiteSparse_long __int64 -#define SuiteSparse_long_max _I64_MAX -#define SuiteSparse_long_idd "I64d" - -#else - -#define SuiteSparse_long long -#define SuiteSparse_long_max LONG_MAX -#define SuiteSparse_long_idd "ld" - -#endif -#define SuiteSparse_long_id "%" SuiteSparse_long_idd -#endif - -/* ========================================================================== */ -/* === SuiteSparse_config parameters and functions ========================== */ -/* ========================================================================== */ - -/* SuiteSparse-wide parameters are placed in this struct. It is meant to be - an extern, globally-accessible struct. It is not meant to be updated - frequently by multiple threads. Rather, if an application needs to modify - SuiteSparse_config, it should do it once at the beginning of the application, - before multiple threads are launched. - - The intent of these function pointers is that they not be used in your - application directly, except to assign them to the desired user-provided - functions. Rather, you should use the - */ - -struct SuiteSparse_config_struct -{ - void *(*malloc_func) (size_t) ; /* pointer to malloc */ - void *(*calloc_func) (size_t, size_t) ; /* pointer to calloc */ - void *(*realloc_func) (void *, size_t) ; /* pointer to realloc */ - void (*free_func) (void *) ; /* pointer to free */ - int (*printf_func) (const char *, ...) ; /* pointer to printf */ - double (*hypot_func) (double, double) ; /* pointer to hypot */ - int (*divcomplex_func) (double, double, double, double, double *, double *); -} ; - -extern struct SuiteSparse_config_struct SuiteSparse_config ; - -void SuiteSparse_start ( void ) ; /* called to start SuiteSparse */ - -void SuiteSparse_finish ( void ) ; /* called to finish SuiteSparse */ - -void *SuiteSparse_malloc /* pointer to allocated block of memory */ -( - size_t nitems, /* number of items to malloc (>=1 is enforced) */ - size_t size_of_item /* sizeof each item */ -) ; - -void *SuiteSparse_calloc /* pointer to allocated block of memory */ -( - size_t nitems, /* number of items to calloc (>=1 is enforced) */ - size_t size_of_item /* sizeof each item */ -) ; - -void *SuiteSparse_realloc /* pointer to reallocated block of memory, or - to original block if the realloc failed. */ -( - size_t nitems_new, /* new number of items in the object */ - size_t nitems_old, /* old number of items in the object */ - size_t size_of_item, /* sizeof each item */ - void *p, /* old object to reallocate */ - int *ok /* 1 if successful, 0 otherwise */ -) ; - -void *SuiteSparse_free /* always returns NULL */ -( - void *p /* block to free */ -) ; - -void SuiteSparse_tic /* start the timer */ -( - double tic [2] /* output, contents undefined on input */ -) ; - -double SuiteSparse_toc /* return time in seconds since last tic */ -( - double tic [2] /* input: from last call to SuiteSparse_tic */ -) ; - -double SuiteSparse_time /* returns current wall clock time in seconds */ -( - void -) ; - -/* returns sqrt (x^2 + y^2), computed reliably */ -double SuiteSparse_hypot (double x, double y) ; - -/* complex division of c = a/b */ -int SuiteSparse_divcomplex -( - double ar, double ai, /* real and imaginary parts of a */ - double br, double bi, /* real and imaginary parts of b */ - double *cr, double *ci /* real and imaginary parts of c */ -) ; - -/* determine which timer to use, if any */ -#ifndef NTIMER -#ifdef _POSIX_C_SOURCE -#if _POSIX_C_SOURCE >= 199309L -#define SUITESPARSE_TIMER_ENABLED -#endif -#endif -#endif - -/* SuiteSparse printf macro */ -#define SUITESPARSE_PRINTF(params) \ -{ \ - if (SuiteSparse_config.printf_func != NULL) \ - { \ - (void) (SuiteSparse_config.printf_func) params ; \ - } \ -} - -/* ========================================================================== */ -/* === SuiteSparse version ================================================== */ -/* ========================================================================== */ - -/* SuiteSparse is not a package itself, but a collection of packages, some of - * which must be used together (UMFPACK requires AMD, CHOLMOD requires AMD, - * COLAMD, CAMD, and CCOLAMD, etc). A version number is provided here for the - * collection itself, which is also the version number of SuiteSparse_config. - */ - -int SuiteSparse_version /* returns SUITESPARSE_VERSION */ -( - /* output, not defined on input. Not used if NULL. Returns - the three version codes in version [0..2]: - version [0] is SUITESPARSE_MAIN_VERSION - version [1] is SUITESPARSE_SUB_VERSION - version [2] is SUITESPARSE_SUBSUB_VERSION - */ - int version [3] -) ; - -/* Versions prior to 4.2.0 do not have the above function. The following - code fragment will work with any version of SuiteSparse: - - #ifdef SUITESPARSE_HAS_VERSION_FUNCTION - v = SuiteSparse_version (NULL) ; - #else - v = SUITESPARSE_VERSION ; - #endif -*/ -#define SUITESPARSE_HAS_VERSION_FUNCTION - -#define SUITESPARSE_DATE "Dec 28, 2018" -#define SUITESPARSE_VER_CODE(main,sub) ((main) * 1000 + (sub)) -#define SUITESPARSE_MAIN_VERSION 5 -#define SUITESPARSE_SUB_VERSION 4 -#define SUITESPARSE_SUBSUB_VERSION 0 -#define SUITESPARSE_VERSION \ - SUITESPARSE_VER_CODE(SUITESPARSE_MAIN_VERSION,SUITESPARSE_SUB_VERSION) - -#ifdef __cplusplus -} -#endif -#endif diff --git a/deps/AMICI/ThirdParty/SuiteSparse/include/amd.h b/deps/AMICI/ThirdParty/SuiteSparse/include/amd.h deleted file mode 100644 index a72851fcf..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/include/amd.h +++ /dev/null @@ -1,400 +0,0 @@ -/* ========================================================================= */ -/* === AMD: approximate minimum degree ordering =========================== */ -/* ========================================================================= */ - -/* ------------------------------------------------------------------------- */ -/* AMD Version 2.4, Copyright (c) 1996-2013 by Timothy A. Davis, */ -/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ -/* email: DrTimothyAldenDavis@gmail.com */ -/* ------------------------------------------------------------------------- */ - -/* AMD finds a symmetric ordering P of a matrix A so that the Cholesky - * factorization of P*A*P' has fewer nonzeros and takes less work than the - * Cholesky factorization of A. If A is not symmetric, then it performs its - * ordering on the matrix A+A'. Two sets of user-callable routines are - * provided, one for int integers and the other for SuiteSparse_long integers. - * - * The method is based on the approximate minimum degree algorithm, discussed - * in Amestoy, Davis, and Duff, "An approximate degree ordering algorithm", - * SIAM Journal of Matrix Analysis and Applications, vol. 17, no. 4, pp. - * 886-905, 1996. This package can perform both the AMD ordering (with - * aggressive absorption), and the AMDBAR ordering (without aggressive - * absorption) discussed in the above paper. This package differs from the - * Fortran codes discussed in the paper: - * - * (1) it can ignore "dense" rows and columns, leading to faster run times - * (2) it computes the ordering of A+A' if A is not symmetric - * (3) it is followed by a depth-first post-ordering of the assembly tree - * (or supernodal elimination tree) - * - * For historical reasons, the Fortran versions, amd.f and amdbar.f, have - * been left (nearly) unchanged. They compute the identical ordering as - * described in the above paper. - */ - -#ifndef AMD_H -#define AMD_H - -/* make it easy for C++ programs to include AMD */ -#ifdef __cplusplus -extern "C" { -#endif - -/* get the definition of size_t: */ -#include - -#include "SuiteSparse_config.h" - -int amd_order /* returns AMD_OK, AMD_OK_BUT_JUMBLED, - * AMD_INVALID, or AMD_OUT_OF_MEMORY */ -( - int n, /* A is n-by-n. n must be >= 0. */ - const int Ap [ ], /* column pointers for A, of size n+1 */ - const int Ai [ ], /* row indices of A, of size nz = Ap [n] */ - int P [ ], /* output permutation, of size n */ - double Control [ ], /* input Control settings, of size AMD_CONTROL */ - double Info [ ] /* output Info statistics, of size AMD_INFO */ -) ; - -SuiteSparse_long amd_l_order /* see above for description of arguments */ -( - SuiteSparse_long n, - const SuiteSparse_long Ap [ ], - const SuiteSparse_long Ai [ ], - SuiteSparse_long P [ ], - double Control [ ], - double Info [ ] -) ; - -/* Input arguments (not modified): - * - * n: the matrix A is n-by-n. - * Ap: an int/SuiteSparse_long array of size n+1, containing column - * pointers of A. - * Ai: an int/SuiteSparse_long array of size nz, containing the row - * indices of A, where nz = Ap [n]. - * Control: a double array of size AMD_CONTROL, containing control - * parameters. Defaults are used if Control is NULL. - * - * Output arguments (not defined on input): - * - * P: an int/SuiteSparse_long array of size n, containing the output - * permutation. If row i is the kth pivot row, then P [k] = i. In - * MATLAB notation, the reordered matrix is A (P,P). - * Info: a double array of size AMD_INFO, containing statistical - * information. Ignored if Info is NULL. - * - * On input, the matrix A is stored in column-oriented form. The row indices - * of nonzero entries in column j are stored in Ai [Ap [j] ... Ap [j+1]-1]. - * - * If the row indices appear in ascending order in each column, and there - * are no duplicate entries, then amd_order is slightly more efficient in - * terms of time and memory usage. If this condition does not hold, a copy - * of the matrix is created (where these conditions do hold), and the copy is - * ordered. This feature is new to v2.0 (v1.2 and earlier required this - * condition to hold for the input matrix). - * - * Row indices must be in the range 0 to - * n-1. Ap [0] must be zero, and thus nz = Ap [n] is the number of nonzeros - * in A. The array Ap is of size n+1, and the array Ai is of size nz = Ap [n]. - * The matrix does not need to be symmetric, and the diagonal does not need to - * be present (if diagonal entries are present, they are ignored except for - * the output statistic Info [AMD_NZDIAG]). The arrays Ai and Ap are not - * modified. This form of the Ap and Ai arrays to represent the nonzero - * pattern of the matrix A is the same as that used internally by MATLAB. - * If you wish to use a more flexible input structure, please see the - * umfpack_*_triplet_to_col routines in the UMFPACK package, at - * http://www.suitesparse.com. - * - * Restrictions: n >= 0. Ap [0] = 0. Ap [j] <= Ap [j+1] for all j in the - * range 0 to n-1. nz = Ap [n] >= 0. Ai [0..nz-1] must be in the range 0 - * to n-1. Finally, Ai, Ap, and P must not be NULL. If any of these - * restrictions are not met, AMD returns AMD_INVALID. - * - * AMD returns: - * - * AMD_OK if the matrix is valid and sufficient memory can be allocated to - * perform the ordering. - * - * AMD_OUT_OF_MEMORY if not enough memory can be allocated. - * - * AMD_INVALID if the input arguments n, Ap, Ai are invalid, or if P is - * NULL. - * - * AMD_OK_BUT_JUMBLED if the matrix had unsorted columns, and/or duplicate - * entries, but was otherwise valid. - * - * The AMD routine first forms the pattern of the matrix A+A', and then - * computes a fill-reducing ordering, P. If P [k] = i, then row/column i of - * the original is the kth pivotal row. In MATLAB notation, the permuted - * matrix is A (P,P), except that 0-based indexing is used instead of the - * 1-based indexing in MATLAB. - * - * The Control array is used to set various parameters for AMD. If a NULL - * pointer is passed, default values are used. The Control array is not - * modified. - * - * Control [AMD_DENSE]: controls the threshold for "dense" rows/columns. - * A dense row/column in A+A' can cause AMD to spend a lot of time in - * ordering the matrix. If Control [AMD_DENSE] >= 0, rows/columns - * with more than Control [AMD_DENSE] * sqrt (n) entries are ignored - * during the ordering, and placed last in the output order. The - * default value of Control [AMD_DENSE] is 10. If negative, no - * rows/columns are treated as "dense". Rows/columns with 16 or - * fewer off-diagonal entries are never considered "dense". - * - * Control [AMD_AGGRESSIVE]: controls whether or not to use aggressive - * absorption, in which a prior element is absorbed into the current - * element if is a subset of the current element, even if it is not - * adjacent to the current pivot element (refer to Amestoy, Davis, - * & Duff, 1996, for more details). The default value is nonzero, - * which means to perform aggressive absorption. This nearly always - * leads to a better ordering (because the approximate degrees are - * more accurate) and a lower execution time. There are cases where - * it can lead to a slightly worse ordering, however. To turn it off, - * set Control [AMD_AGGRESSIVE] to 0. - * - * Control [2..4] are not used in the current version, but may be used in - * future versions. - * - * The Info array provides statistics about the ordering on output. If it is - * not present, the statistics are not returned. This is not an error - * condition. - * - * Info [AMD_STATUS]: the return value of AMD, either AMD_OK, - * AMD_OK_BUT_JUMBLED, AMD_OUT_OF_MEMORY, or AMD_INVALID. - * - * Info [AMD_N]: n, the size of the input matrix - * - * Info [AMD_NZ]: the number of nonzeros in A, nz = Ap [n] - * - * Info [AMD_SYMMETRY]: the symmetry of the matrix A. It is the number - * of "matched" off-diagonal entries divided by the total number of - * off-diagonal entries. An entry A(i,j) is matched if A(j,i) is also - * an entry, for any pair (i,j) for which i != j. In MATLAB notation, - * S = spones (A) ; - * B = tril (S, -1) + triu (S, 1) ; - * symmetry = nnz (B & B') / nnz (B) ; - * - * Info [AMD_NZDIAG]: the number of entries on the diagonal of A. - * - * Info [AMD_NZ_A_PLUS_AT]: the number of nonzeros in A+A', excluding the - * diagonal. If A is perfectly symmetric (Info [AMD_SYMMETRY] = 1) - * with a fully nonzero diagonal, then Info [AMD_NZ_A_PLUS_AT] = nz-n - * (the smallest possible value). If A is perfectly unsymmetric - * (Info [AMD_SYMMETRY] = 0, for an upper triangular matrix, for - * example) with no diagonal, then Info [AMD_NZ_A_PLUS_AT] = 2*nz - * (the largest possible value). - * - * Info [AMD_NDENSE]: the number of "dense" rows/columns of A+A' that were - * removed from A prior to ordering. These are placed last in the - * output order P. - * - * Info [AMD_MEMORY]: the amount of memory used by AMD, in bytes. In the - * current version, this is 1.2 * Info [AMD_NZ_A_PLUS_AT] + 9*n - * times the size of an integer. This is at most 2.4nz + 9n. This - * excludes the size of the input arguments Ai, Ap, and P, which have - * a total size of nz + 2*n + 1 integers. - * - * Info [AMD_NCMPA]: the number of garbage collections performed. - * - * Info [AMD_LNZ]: the number of nonzeros in L (excluding the diagonal). - * This is a slight upper bound because mass elimination is combined - * with the approximate degree update. It is a rough upper bound if - * there are many "dense" rows/columns. The rest of the statistics, - * below, are also slight or rough upper bounds, for the same reasons. - * The post-ordering of the assembly tree might also not exactly - * correspond to a true elimination tree postordering. - * - * Info [AMD_NDIV]: the number of divide operations for a subsequent LDL' - * or LU factorization of the permuted matrix A (P,P). - * - * Info [AMD_NMULTSUBS_LDL]: the number of multiply-subtract pairs for a - * subsequent LDL' factorization of A (P,P). - * - * Info [AMD_NMULTSUBS_LU]: the number of multiply-subtract pairs for a - * subsequent LU factorization of A (P,P), assuming that no numerical - * pivoting is required. - * - * Info [AMD_DMAX]: the maximum number of nonzeros in any column of L, - * including the diagonal. - * - * Info [14..19] are not used in the current version, but may be used in - * future versions. - */ - -/* ------------------------------------------------------------------------- */ -/* direct interface to AMD */ -/* ------------------------------------------------------------------------- */ - -/* amd_2 is the primary AMD ordering routine. It is not meant to be - * user-callable because of its restrictive inputs and because it destroys - * the user's input matrix. It does not check its inputs for errors, either. - * However, if you can work with these restrictions it can be faster than - * amd_order and use less memory (assuming that you can create your own copy - * of the matrix for AMD to destroy). Refer to AMD/Source/amd_2.c for a - * description of each parameter. */ - -void amd_2 -( - int n, - int Pe [ ], - int Iw [ ], - int Len [ ], - int iwlen, - int pfree, - int Nv [ ], - int Next [ ], - int Last [ ], - int Head [ ], - int Elen [ ], - int Degree [ ], - int W [ ], - double Control [ ], - double Info [ ] -) ; - -void amd_l2 -( - SuiteSparse_long n, - SuiteSparse_long Pe [ ], - SuiteSparse_long Iw [ ], - SuiteSparse_long Len [ ], - SuiteSparse_long iwlen, - SuiteSparse_long pfree, - SuiteSparse_long Nv [ ], - SuiteSparse_long Next [ ], - SuiteSparse_long Last [ ], - SuiteSparse_long Head [ ], - SuiteSparse_long Elen [ ], - SuiteSparse_long Degree [ ], - SuiteSparse_long W [ ], - double Control [ ], - double Info [ ] -) ; - -/* ------------------------------------------------------------------------- */ -/* amd_valid */ -/* ------------------------------------------------------------------------- */ - -/* Returns AMD_OK or AMD_OK_BUT_JUMBLED if the matrix is valid as input to - * amd_order; the latter is returned if the matrix has unsorted and/or - * duplicate row indices in one or more columns. Returns AMD_INVALID if the - * matrix cannot be passed to amd_order. For amd_order, the matrix must also - * be square. The first two arguments are the number of rows and the number - * of columns of the matrix. For its use in AMD, these must both equal n. - * - * NOTE: this routine returned TRUE/FALSE in v1.2 and earlier. - */ - -int amd_valid -( - int n_row, /* # of rows */ - int n_col, /* # of columns */ - const int Ap [ ], /* column pointers, of size n_col+1 */ - const int Ai [ ] /* row indices, of size Ap [n_col] */ -) ; - -SuiteSparse_long amd_l_valid -( - SuiteSparse_long n_row, - SuiteSparse_long n_col, - const SuiteSparse_long Ap [ ], - const SuiteSparse_long Ai [ ] -) ; - -/* ------------------------------------------------------------------------- */ -/* AMD memory manager and printf routines */ -/* ------------------------------------------------------------------------- */ - - /* moved to SuiteSparse_config.c */ - -/* ------------------------------------------------------------------------- */ -/* AMD Control and Info arrays */ -/* ------------------------------------------------------------------------- */ - -/* amd_defaults: sets the default control settings */ -void amd_defaults (double Control [ ]) ; -void amd_l_defaults (double Control [ ]) ; - -/* amd_control: prints the control settings */ -void amd_control (double Control [ ]) ; -void amd_l_control (double Control [ ]) ; - -/* amd_info: prints the statistics */ -void amd_info (double Info [ ]) ; -void amd_l_info (double Info [ ]) ; - -#define AMD_CONTROL 5 /* size of Control array */ -#define AMD_INFO 20 /* size of Info array */ - -/* contents of Control */ -#define AMD_DENSE 0 /* "dense" if degree > Control [0] * sqrt (n) */ -#define AMD_AGGRESSIVE 1 /* do aggressive absorption if Control [1] != 0 */ - -/* default Control settings */ -#define AMD_DEFAULT_DENSE 10.0 /* default "dense" degree 10*sqrt(n) */ -#define AMD_DEFAULT_AGGRESSIVE 1 /* do aggressive absorption by default */ - -/* contents of Info */ -#define AMD_STATUS 0 /* return value of amd_order and amd_l_order */ -#define AMD_N 1 /* A is n-by-n */ -#define AMD_NZ 2 /* number of nonzeros in A */ -#define AMD_SYMMETRY 3 /* symmetry of pattern (1 is sym., 0 is unsym.) */ -#define AMD_NZDIAG 4 /* # of entries on diagonal */ -#define AMD_NZ_A_PLUS_AT 5 /* nz in A+A' */ -#define AMD_NDENSE 6 /* number of "dense" rows/columns in A */ -#define AMD_MEMORY 7 /* amount of memory used by AMD */ -#define AMD_NCMPA 8 /* number of garbage collections in AMD */ -#define AMD_LNZ 9 /* approx. nz in L, excluding the diagonal */ -#define AMD_NDIV 10 /* number of fl. point divides for LU and LDL' */ -#define AMD_NMULTSUBS_LDL 11 /* number of fl. point (*,-) pairs for LDL' */ -#define AMD_NMULTSUBS_LU 12 /* number of fl. point (*,-) pairs for LU */ -#define AMD_DMAX 13 /* max nz. in any column of L, incl. diagonal */ - -/* ------------------------------------------------------------------------- */ -/* return values of AMD */ -/* ------------------------------------------------------------------------- */ - -#define AMD_OK 0 /* success */ -#define AMD_OUT_OF_MEMORY -1 /* malloc failed, or problem too large */ -#define AMD_INVALID -2 /* input arguments are not valid */ -#define AMD_OK_BUT_JUMBLED 1 /* input matrix is OK for amd_order, but - * columns were not sorted, and/or duplicate entries were present. AMD had - * to do extra work before ordering the matrix. This is a warning, not an - * error. */ - -/* ========================================================================== */ -/* === AMD version ========================================================== */ -/* ========================================================================== */ - -/* AMD Version 1.2 and later include the following definitions. - * As an example, to test if the version you are using is 1.2 or later: - * - * #ifdef AMD_VERSION - * if (AMD_VERSION >= AMD_VERSION_CODE (1,2)) ... - * #endif - * - * This also works during compile-time: - * - * #if defined(AMD_VERSION) && (AMD_VERSION >= AMD_VERSION_CODE (1,2)) - * printf ("This is version 1.2 or later\n") ; - * #else - * printf ("This is an early version\n") ; - * #endif - * - * Versions 1.1 and earlier of AMD do not include a #define'd version number. - */ - -#define AMD_DATE "May 4, 2016" -#define AMD_VERSION_CODE(main,sub) ((main) * 1000 + (sub)) -#define AMD_MAIN_VERSION 2 -#define AMD_SUB_VERSION 4 -#define AMD_SUBSUB_VERSION 6 -#define AMD_VERSION AMD_VERSION_CODE(AMD_MAIN_VERSION,AMD_SUB_VERSION) - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/deps/AMICI/ThirdParty/SuiteSparse/include/camd.h b/deps/AMICI/ThirdParty/SuiteSparse/include/camd.h deleted file mode 100644 index 21898e017..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/include/camd.h +++ /dev/null @@ -1,407 +0,0 @@ -/* ========================================================================= */ -/* === CAMD: approximate minimum degree ordering ========================== */ -/* ========================================================================= */ - -/* ------------------------------------------------------------------------- */ -/* CAMD Version 2.4, Copyright (c) 2013 by Timothy A. Davis, Yanqing Chen, */ -/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ -/* email: DrTimothyAldenDavis@gmail.com */ -/* ------------------------------------------------------------------------- */ - -/* CAMD finds a symmetric ordering P of a matrix A so that the Cholesky - * factorization of P*A*P' has fewer nonzeros and takes less work than the - * Cholesky factorization of A. If A is not symmetric, then it performs its - * ordering on the matrix A+A'. Two sets of user-callable routines are - * provided, one for int integers and the other for SuiteSparse_long integers. - * - * The method is based on the approximate minimum degree algorithm, discussed - * in Amestoy, Davis, and Duff, "An approximate degree ordering algorithm", - * SIAM Journal of Matrix Analysis and Applications, vol. 17, no. 4, pp. - * 886-905, 1996. - */ - -#ifndef CAMD_H -#define CAMD_H - -/* make it easy for C++ programs to include CAMD */ -#ifdef __cplusplus -extern "C" { -#endif - -/* get the definition of size_t: */ -#include - -#include "SuiteSparse_config.h" - -int camd_order /* returns CAMD_OK, CAMD_OK_BUT_JUMBLED, - * CAMD_INVALID, or CAMD_OUT_OF_MEMORY */ -( - int n, /* A is n-by-n. n must be >= 0. */ - const int Ap [ ], /* column pointers for A, of size n+1 */ - const int Ai [ ], /* row indices of A, of size nz = Ap [n] */ - int P [ ], /* output permutation, of size n */ - double Control [ ], /* input Control settings, of size CAMD_CONTROL */ - double Info [ ], /* output Info statistics, of size CAMD_INFO */ - const int C [ ] /* Constraint set of A, of size n; can be NULL */ -) ; - -SuiteSparse_long camd_l_order /* see above for description of arguments */ -( - SuiteSparse_long n, - const SuiteSparse_long Ap [ ], - const SuiteSparse_long Ai [ ], - SuiteSparse_long P [ ], - double Control [ ], - double Info [ ], - const SuiteSparse_long C [ ] -) ; - -/* Input arguments (not modified): - * - * n: the matrix A is n-by-n. - * Ap: an int/SuiteSparse_long array of size n+1, containing column - * pointers of A. - * Ai: an int/SuiteSparse_long array of size nz, containing the row - * indices of A, where nz = Ap [n]. - * Control: a double array of size CAMD_CONTROL, containing control - * parameters. Defaults are used if Control is NULL. - * - * Output arguments (not defined on input): - * - * P: an int/SuiteSparse_long array of size n, containing the output - * permutation. If row i is the kth pivot row, then P [k] = i. In - * MATLAB notation, the reordered matrix is A (P,P). - * Info: a double array of size CAMD_INFO, containing statistical - * information. Ignored if Info is NULL. - * - * On input, the matrix A is stored in column-oriented form. The row indices - * of nonzero entries in column j are stored in Ai [Ap [j] ... Ap [j+1]-1]. - * - * If the row indices appear in ascending order in each column, and there - * are no duplicate entries, then camd_order is slightly more efficient in - * terms of time and memory usage. If this condition does not hold, a copy - * of the matrix is created (where these conditions do hold), and the copy is - * ordered. - * - * Row indices must be in the range 0 to - * n-1. Ap [0] must be zero, and thus nz = Ap [n] is the number of nonzeros - * in A. The array Ap is of size n+1, and the array Ai is of size nz = Ap [n]. - * The matrix does not need to be symmetric, and the diagonal does not need to - * be present (if diagonal entries are present, they are ignored except for - * the output statistic Info [CAMD_NZDIAG]). The arrays Ai and Ap are not - * modified. This form of the Ap and Ai arrays to represent the nonzero - * pattern of the matrix A is the same as that used internally by MATLAB. - * If you wish to use a more flexible input structure, please see the - * umfpack_*_triplet_to_col routines in the UMFPACK package, at - * http://www.suitesparse.com. - * - * Restrictions: n >= 0. Ap [0] = 0. Ap [j] <= Ap [j+1] for all j in the - * range 0 to n-1. nz = Ap [n] >= 0. Ai [0..nz-1] must be in the range 0 - * to n-1. Finally, Ai, Ap, and P must not be NULL. If any of these - * restrictions are not met, CAMD returns CAMD_INVALID. - * - * CAMD returns: - * - * CAMD_OK if the matrix is valid and sufficient memory can be allocated to - * perform the ordering. - * - * CAMD_OUT_OF_MEMORY if not enough memory can be allocated. - * - * CAMD_INVALID if the input arguments n, Ap, Ai are invalid, or if P is - * NULL. - * - * CAMD_OK_BUT_JUMBLED if the matrix had unsorted columns, and/or duplicate - * entries, but was otherwise valid. - * - * The CAMD routine first forms the pattern of the matrix A+A', and then - * computes a fill-reducing ordering, P. If P [k] = i, then row/column i of - * the original is the kth pivotal row. In MATLAB notation, the permuted - * matrix is A (P,P), except that 0-based indexing is used instead of the - * 1-based indexing in MATLAB. - * - * The Control array is used to set various parameters for CAMD. If a NULL - * pointer is passed, default values are used. The Control array is not - * modified. - * - * Control [CAMD_DENSE]: controls the threshold for "dense" rows/columns. - * A dense row/column in A+A' can cause CAMD to spend a lot of time in - * ordering the matrix. If Control [CAMD_DENSE] >= 0, rows/columns - * with more than Control [CAMD_DENSE] * sqrt (n) entries are ignored - * during the ordering, and placed last in the output order. The - * default value of Control [CAMD_DENSE] is 10. If negative, no - * rows/columns are treated as "dense". Rows/columns with 16 or - * fewer off-diagonal entries are never considered "dense". - * - * Control [CAMD_AGGRESSIVE]: controls whether or not to use aggressive - * absorption, in which a prior element is absorbed into the current - * element if is a subset of the current element, even if it is not - * adjacent to the current pivot element (refer to Amestoy, Davis, - * & Duff, 1996, for more details). The default value is nonzero, - * which means to perform aggressive absorption. This nearly always - * leads to a better ordering (because the approximate degrees are - * more accurate) and a lower execution time. There are cases where - * it can lead to a slightly worse ordering, however. To turn it off, - * set Control [CAMD_AGGRESSIVE] to 0. - * - * Control [2..4] are not used in the current version, but may be used in - * future versions. - * - * The Info array provides statistics about the ordering on output. If it is - * not present, the statistics are not returned. This is not an error - * condition. - * - * Info [CAMD_STATUS]: the return value of CAMD, either CAMD_OK, - * CAMD_OK_BUT_JUMBLED, CAMD_OUT_OF_MEMORY, or CAMD_INVALID. - * - * Info [CAMD_N]: n, the size of the input matrix - * - * Info [CAMD_NZ]: the number of nonzeros in A, nz = Ap [n] - * - * Info [CAMD_SYMMETRY]: the symmetry of the matrix A. It is the number - * of "matched" off-diagonal entries divided by the total number of - * off-diagonal entries. An entry A(i,j) is matched if A(j,i) is also - * an entry, for any pair (i,j) for which i != j. In MATLAB notation, - * S = spones (A) ; - * B = tril (S, -1) + triu (S, 1) ; - * symmetry = nnz (B & B') / nnz (B) ; - * - * Info [CAMD_NZDIAG]: the number of entries on the diagonal of A. - * - * Info [CAMD_NZ_A_PLUS_AT]: the number of nonzeros in A+A', excluding the - * diagonal. If A is perfectly symmetric (Info [CAMD_SYMMETRY] = 1) - * with a fully nonzero diagonal, then Info [CAMD_NZ_A_PLUS_AT] = nz-n - * (the smallest possible value). If A is perfectly unsymmetric - * (Info [CAMD_SYMMETRY] = 0, for an upper triangular matrix, for - * example) with no diagonal, then Info [CAMD_NZ_A_PLUS_AT] = 2*nz - * (the largest possible value). - * - * Info [CAMD_NDENSE]: the number of "dense" rows/columns of A+A' that were - * removed from A prior to ordering. These are placed last in the - * output order P. - * - * Info [CAMD_MEMORY]: the amount of memory used by CAMD, in bytes. In the - * current version, this is 1.2 * Info [CAMD_NZ_A_PLUS_AT] + 9*n - * times the size of an integer. This is at most 2.4nz + 9n. This - * excludes the size of the input arguments Ai, Ap, and P, which have - * a total size of nz + 2*n + 1 integers. - * - * Info [CAMD_NCMPA]: the number of garbage collections performed. - * - * Info [CAMD_LNZ]: the number of nonzeros in L (excluding the diagonal). - * This is a slight upper bound because mass elimination is combined - * with the approximate degree update. It is a rough upper bound if - * there are many "dense" rows/columns. The rest of the statistics, - * below, are also slight or rough upper bounds, for the same reasons. - * The post-ordering of the assembly tree might also not exactly - * correspond to a true elimination tree postordering. - * - * Info [CAMD_NDIV]: the number of divide operations for a subsequent LDL' - * or LU factorization of the permuted matrix A (P,P). - * - * Info [CAMD_NMULTSUBS_LDL]: the number of multiply-subtract pairs for a - * subsequent LDL' factorization of A (P,P). - * - * Info [CAMD_NMULTSUBS_LU]: the number of multiply-subtract pairs for a - * subsequent LU factorization of A (P,P), assuming that no numerical - * pivoting is required. - * - * Info [CAMD_DMAX]: the maximum number of nonzeros in any column of L, - * including the diagonal. - * - * Info [14..19] are not used in the current version, but may be used in - * future versions. - */ - -/* ------------------------------------------------------------------------- */ -/* direct interface to CAMD */ -/* ------------------------------------------------------------------------- */ - -/* camd_2 is the primary CAMD ordering routine. It is not meant to be - * user-callable because of its restrictive inputs and because it destroys - * the user's input matrix. It does not check its inputs for errors, either. - * However, if you can work with these restrictions it can be faster than - * camd_order and use less memory (assuming that you can create your own copy - * of the matrix for CAMD to destroy). Refer to CAMD/Source/camd_2.c for a - * description of each parameter. */ - -void camd_2 -( - int n, - int Pe [ ], - int Iw [ ], - int Len [ ], - int iwlen, - int pfree, - int Nv [ ], - int Next [ ], - int Last [ ], - int Head [ ], - int Elen [ ], - int Degree [ ], - int W [ ], - double Control [ ], - double Info [ ], - const int C [ ], - int BucketSet [ ] -) ; - -void camd_l2 -( - SuiteSparse_long n, - SuiteSparse_long Pe [ ], - SuiteSparse_long Iw [ ], - SuiteSparse_long Len [ ], - SuiteSparse_long iwlen, - SuiteSparse_long pfree, - SuiteSparse_long Nv [ ], - SuiteSparse_long Next [ ], - SuiteSparse_long Last [ ], - SuiteSparse_long Head [ ], - SuiteSparse_long Elen [ ], - SuiteSparse_long Degree [ ], - SuiteSparse_long W [ ], - double Control [ ], - double Info [ ], - const SuiteSparse_long C [ ], - SuiteSparse_long BucketSet [ ] - -) ; - -/* ------------------------------------------------------------------------- */ -/* camd_valid */ -/* ------------------------------------------------------------------------- */ - -/* Returns CAMD_OK or CAMD_OK_BUT_JUMBLED if the matrix is valid as input to - * camd_order; the latter is returned if the matrix has unsorted and/or - * duplicate row indices in one or more columns. Returns CAMD_INVALID if the - * matrix cannot be passed to camd_order. For camd_order, the matrix must also - * be square. The first two arguments are the number of rows and the number - * of columns of the matrix. For its use in CAMD, these must both equal n. - */ - -int camd_valid -( - int n_row, /* # of rows */ - int n_col, /* # of columns */ - const int Ap [ ], /* column pointers, of size n_col+1 */ - const int Ai [ ] /* row indices, of size Ap [n_col] */ -) ; - -SuiteSparse_long camd_l_valid -( - SuiteSparse_long n_row, - SuiteSparse_long n_col, - const SuiteSparse_long Ap [ ], - const SuiteSparse_long Ai [ ] -) ; - -/* ------------------------------------------------------------------------- */ -/* camd_cvalid */ -/* ------------------------------------------------------------------------- */ - -/* Returns TRUE if the constraint set is valid as input to camd_order, - * FALSE otherwise. */ - -int camd_cvalid -( - int n, - const int C [ ] -) ; - -SuiteSparse_long camd_l_cvalid -( - SuiteSparse_long n, - const SuiteSparse_long C [ ] -) ; - -/* ------------------------------------------------------------------------- */ -/* CAMD memory manager and printf routines */ -/* ------------------------------------------------------------------------- */ - - /* moved to SuiteSparse_config.c */ - -/* ------------------------------------------------------------------------- */ -/* CAMD Control and Info arrays */ -/* ------------------------------------------------------------------------- */ - -/* camd_defaults: sets the default control settings */ -void camd_defaults (double Control [ ]) ; -void camd_l_defaults (double Control [ ]) ; - -/* camd_control: prints the control settings */ -void camd_control (double Control [ ]) ; -void camd_l_control (double Control [ ]) ; - -/* camd_info: prints the statistics */ -void camd_info (double Info [ ]) ; -void camd_l_info (double Info [ ]) ; - -#define CAMD_CONTROL 5 /* size of Control array */ -#define CAMD_INFO 20 /* size of Info array */ - -/* contents of Control */ -#define CAMD_DENSE 0 /* "dense" if degree > Control [0] * sqrt (n) */ -#define CAMD_AGGRESSIVE 1 /* do aggressive absorption if Control [1] != 0 */ - -/* default Control settings */ -#define CAMD_DEFAULT_DENSE 10.0 /* default "dense" degree 10*sqrt(n) */ -#define CAMD_DEFAULT_AGGRESSIVE 1 /* do aggressive absorption by default */ - -/* contents of Info */ -#define CAMD_STATUS 0 /* return value of camd_order and camd_l_order */ -#define CAMD_N 1 /* A is n-by-n */ -#define CAMD_NZ 2 /* number of nonzeros in A */ -#define CAMD_SYMMETRY 3 /* symmetry of pattern (1 is sym., 0 is unsym.) */ -#define CAMD_NZDIAG 4 /* # of entries on diagonal */ -#define CAMD_NZ_A_PLUS_AT 5 /* nz in A+A' */ -#define CAMD_NDENSE 6 /* number of "dense" rows/columns in A */ -#define CAMD_MEMORY 7 /* amount of memory used by CAMD */ -#define CAMD_NCMPA 8 /* number of garbage collections in CAMD */ -#define CAMD_LNZ 9 /* approx. nz in L, excluding the diagonal */ -#define CAMD_NDIV 10 /* number of fl. point divides for LU and LDL' */ -#define CAMD_NMULTSUBS_LDL 11 /* number of fl. point (*,-) pairs for LDL' */ -#define CAMD_NMULTSUBS_LU 12 /* number of fl. point (*,-) pairs for LU */ -#define CAMD_DMAX 13 /* max nz. in any column of L, incl. diagonal */ - -/* ------------------------------------------------------------------------- */ -/* return values of CAMD */ -/* ------------------------------------------------------------------------- */ - -#define CAMD_OK 0 /* success */ -#define CAMD_OUT_OF_MEMORY -1 /* malloc failed, or problem too large */ -#define CAMD_INVALID -2 /* input arguments are not valid */ -#define CAMD_OK_BUT_JUMBLED 1 /* input matrix is OK for camd_order, but - * columns were not sorted, and/or duplicate entries were present. CAMD had - * to do extra work before ordering the matrix. This is a warning, not an - * error. */ - -/* ========================================================================== */ -/* === CAMD version ========================================================= */ -/* ========================================================================== */ - -/* - * As an example, to test if the version you are using is 1.2 or later: - * - * if (CAMD_VERSION >= CAMD_VERSION_CODE (1,2)) ... - * - * This also works during compile-time: - * - * #if (CAMD_VERSION >= CAMD_VERSION_CODE (1,2)) - * printf ("This is version 1.2 or later\n") ; - * #else - * printf ("This is an early version\n") ; - * #endif - */ - -#define CAMD_DATE "May 4, 2016" -#define CAMD_VERSION_CODE(main,sub) ((main) * 1000 + (sub)) -#define CAMD_MAIN_VERSION 2 -#define CAMD_SUB_VERSION 4 -#define CAMD_SUBSUB_VERSION 6 -#define CAMD_VERSION CAMD_VERSION_CODE(CAMD_MAIN_VERSION,CAMD_SUB_VERSION) - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/deps/AMICI/ThirdParty/SuiteSparse/include/colamd.h b/deps/AMICI/ThirdParty/SuiteSparse/include/colamd.h deleted file mode 100644 index fbe959308..000000000 --- a/deps/AMICI/ThirdParty/SuiteSparse/include/colamd.h +++ /dev/null @@ -1,237 +0,0 @@ -/* ========================================================================== */ -/* === colamd/symamd prototypes and definitions ============================= */ -/* ========================================================================== */ - -/* COLAMD / SYMAMD include file - - You must include this file (colamd.h) in any routine that uses colamd, - symamd, or the related macros and definitions. - - Authors: - - The authors of the code itself are Stefan I. Larimore and Timothy A. - Davis (DrTimothyAldenDavis@gmail.com). The algorithm was - developed in collaboration with John Gilbert, Xerox PARC, and Esmond - Ng, Oak Ridge National Laboratory. - - Acknowledgements: - - This work was supported by the National Science Foundation, under - grants DMS-9504974 and DMS-9803599. - - Notice: - - Copyright (c) 1998-2007, Timothy A. Davis, All Rights Reserved. - See COLAMD/Doc/License.txt for the license. - - Availability: - - The colamd/symamd library is available at http://www.suitesparse.com - This file is required by the colamd.c, colamdmex.c, and symamdmex.c - files, and by any C code that calls the routines whose prototypes are - listed below, or that uses the colamd/symamd definitions listed below. - -*/ - -#ifndef COLAMD_H -#define COLAMD_H - -/* make it easy for C++ programs to include COLAMD */ -#ifdef __cplusplus -extern "C" { -#endif - -/* ========================================================================== */ -/* === Include files ======================================================== */ -/* ========================================================================== */ - -#include - -/* ========================================================================== */ -/* === COLAMD version ======================================================= */ -/* ========================================================================== */ - -/* COLAMD Version 2.4 and later will include the following definitions. - * As an example, to test if the version you are using is 2.4 or later: - * - * #ifdef COLAMD_VERSION - * if (COLAMD_VERSION >= COLAMD_VERSION_CODE (2,4)) ... - * #endif - * - * This also works during compile-time: - * - * #if defined(COLAMD_VERSION) && (COLAMD_VERSION >= COLAMD_VERSION_CODE (2,4)) - * printf ("This is version 2.4 or later\n") ; - * #else - * printf ("This is an early version\n") ; - * #endif - * - * Versions 2.3 and earlier of COLAMD do not include a #define'd version number. - */ - -#define COLAMD_DATE "May 4, 2016" -#define COLAMD_VERSION_CODE(main,sub) ((main) * 1000 + (sub)) -#define COLAMD_MAIN_VERSION 2 -#define COLAMD_SUB_VERSION 9 -#define COLAMD_SUBSUB_VERSION 6 -#define COLAMD_VERSION \ - COLAMD_VERSION_CODE(COLAMD_MAIN_VERSION,COLAMD_SUB_VERSION) - -/* ========================================================================== */ -/* === Knob and statistics definitions ====================================== */ -/* ========================================================================== */ - -/* size of the knobs [ ] array. Only knobs [0..1] are currently used. */ -#define COLAMD_KNOBS 20 - -/* number of output statistics. Only stats [0..6] are currently used. */ -#define COLAMD_STATS 20 - -/* knobs [0] and stats [0]: dense row knob and output statistic. */ -#define COLAMD_DENSE_ROW 0 - -/* knobs [1] and stats [1]: dense column knob and output statistic. */ -#define COLAMD_DENSE_COL 1 - -/* knobs [2]: aggressive absorption */ -#define COLAMD_AGGRESSIVE 2 - -/* stats [2]: memory defragmentation count output statistic */ -#define COLAMD_DEFRAG_COUNT 2 - -/* stats [3]: colamd status: zero OK, > 0 warning or notice, < 0 error */ -#define COLAMD_STATUS 3 - -/* stats [4..6]: error info, or info on jumbled columns */ -#define COLAMD_INFO1 4 -#define COLAMD_INFO2 5 -#define COLAMD_INFO3 6 - -/* error codes returned in stats [3]: */ -#define COLAMD_OK (0) -#define COLAMD_OK_BUT_JUMBLED (1) -#define COLAMD_ERROR_A_not_present (-1) -#define COLAMD_ERROR_p_not_present (-2) -#define COLAMD_ERROR_nrow_negative (-3) -#define COLAMD_ERROR_ncol_negative (-4) -#define COLAMD_ERROR_nnz_negative (-5) -#define COLAMD_ERROR_p0_nonzero (-6) -#define COLAMD_ERROR_A_too_small (-7) -#define COLAMD_ERROR_col_length_negative (-8) -#define COLAMD_ERROR_row_index_out_of_bounds (-9) -#define COLAMD_ERROR_out_of_memory (-10) -#define COLAMD_ERROR_internal_error (-999) - - -/* ========================================================================== */ -/* === Prototypes of user-callable routines ================================= */ -/* ========================================================================== */ - -#include "SuiteSparse_config.h" - -size_t colamd_recommended /* returns recommended value of Alen, */ - /* or 0 if input arguments are erroneous */ -( - int nnz, /* nonzeros in A */ - int n_row, /* number of rows in A */ - int n_col /* number of columns in A */ -) ; - -size_t colamd_l_recommended /* returns recommended value of Alen, */ - /* or 0 if input arguments are erroneous */ -( - SuiteSparse_long nnz, /* nonzeros in A */ - SuiteSparse_long n_row, /* number of rows in A */ - SuiteSparse_long n_col /* number of columns in A */ -) ; - -void colamd_set_defaults /* sets default parameters */ -( /* knobs argument is modified on output */ - double knobs [COLAMD_KNOBS] /* parameter settings for colamd */ -) ; - -void colamd_l_set_defaults /* sets default parameters */ -( /* knobs argument is modified on output */ - double knobs [COLAMD_KNOBS] /* parameter settings for colamd */ -) ; - -int colamd /* returns (1) if successful, (0) otherwise*/ -( /* A and p arguments are modified on output */ - int n_row, /* number of rows in A */ - int n_col, /* number of columns in A */ - int Alen, /* size of the array A */ - int A [], /* row indices of A, of size Alen */ - int p [], /* column pointers of A, of size n_col+1 */ - double knobs [COLAMD_KNOBS],/* parameter settings for colamd */ - int stats [COLAMD_STATS] /* colamd output statistics and error codes */ -) ; - -SuiteSparse_long colamd_l /* returns (1) if successful, (0) otherwise*/ -( /* A and p arguments are modified on output */ - SuiteSparse_long n_row, /* number of rows in A */ - SuiteSparse_long n_col, /* number of columns in A */ - SuiteSparse_long Alen, /* size of the array A */ - SuiteSparse_long A [], /* row indices of A, of size Alen */ - SuiteSparse_long p [], /* column pointers of A, of size n_col+1 */ - double knobs [COLAMD_KNOBS],/* parameter settings for colamd */ - SuiteSparse_long stats [COLAMD_STATS] /* colamd output statistics - * and error codes */ -) ; - -int symamd /* return (1) if OK, (0) otherwise */ -( - int n, /* number of rows and columns of A */ - int A [], /* row indices of A */ - int p [], /* column pointers of A */ - int perm [], /* output permutation, size n_col+1 */ - double knobs [COLAMD_KNOBS], /* parameters (uses defaults if NULL) */ - int stats [COLAMD_STATS], /* output statistics and error codes */ - void * (*allocate) (size_t, size_t), - /* pointer to calloc (ANSI C) or */ - /* mxCalloc (for MATLAB mexFunction) */ - void (*release) (void *) - /* pointer to free (ANSI C) or */ - /* mxFree (for MATLAB mexFunction) */ -) ; - -SuiteSparse_long symamd_l /* return (1) if OK, (0) otherwise */ -( - SuiteSparse_long n, /* number of rows and columns of A */ - SuiteSparse_long A [], /* row indices of A */ - SuiteSparse_long p [], /* column pointers of A */ - SuiteSparse_long perm [], /* output permutation, size n_col+1 */ - double knobs [COLAMD_KNOBS], /* parameters (uses defaults if NULL) */ - SuiteSparse_long stats [COLAMD_STATS], /* output stats and error codes */ - void * (*allocate) (size_t, size_t), - /* pointer to calloc (ANSI C) or */ - /* mxCalloc (for MATLAB mexFunction) */ - void (*release) (void *) - /* pointer to free (ANSI C) or */ - /* mxFree (for MATLAB mexFunction) */ -) ; - -void colamd_report -( - int stats [COLAMD_STATS] -) ; - -void colamd_l_report -( - SuiteSparse_long stats [COLAMD_STATS] -) ; - -void symamd_report -( - int stats [COLAMD_STATS] -) ; - -void symamd_l_report -( - SuiteSparse_long stats [COLAMD_STATS] -) ; - -#ifdef __cplusplus -} -#endif - -#endif /* COLAMD_H */ diff --git a/deps/AMICI/ThirdParty/sundials/CMakeLists.txt b/deps/AMICI/ThirdParty/sundials/CMakeLists.txt index 40a650ea3..f535def9d 100644 --- a/deps/AMICI/ThirdParty/sundials/CMakeLists.txt +++ b/deps/AMICI/ThirdParty/sundials/CMakeLists.txt @@ -25,16 +25,31 @@ cmake_minimum_required(VERSION 3.12) # sets PROJECT_SOURCE_DIR and PROJECT_BINARY_DIR variables. 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 + ) + +# MACRO definitions +include(SundialsCMakeMacros) +include(FindPackageHandleStandardArgs) +include(CMakePrintHelpers) + # Set some variables with info on the SUNDIALS project set(PACKAGE_BUGREPORT "sundials-users@llnl.gov") set(PACKAGE_NAME "SUNDIALS") -set(PACKAGE_STRING "SUNDIALS 5.7.0") +set(PACKAGE_STRING "SUNDIALS 5.8.0") set(PACKAGE_TARNAME "sundials") -# set SUNDIALS version numbers +# Set SUNDIALS version numbers +sundials_git_version() # sets SUNDIALS_GIT_VERSION +message(STATUS "SUNDIALS_GIT_VERSION: ${SUNDIALS_GIT_VERSION}") + # (use "" for the version label if none is needed) set(PACKAGE_VERSION_MAJOR "5") -set(PACKAGE_VERSION_MINOR "7") +set(PACKAGE_VERSION_MINOR "8") set(PACKAGE_VERSION_PATCH "0") set(PACKAGE_VERSION_LABEL "") @@ -50,37 +65,37 @@ endif() # Specify the VERSION and SOVERSION for shared libraries -set(arkodelib_VERSION "4.7.0") +set(arkodelib_VERSION "4.8.0") set(arkodelib_SOVERSION "4") -set(cvodelib_VERSION "5.7.0") +set(cvodelib_VERSION "5.8.0") set(cvodelib_SOVERSION "5") -set(cvodeslib_VERSION "5.7.0") +set(cvodeslib_VERSION "5.8.0") set(cvodeslib_SOVERSION "5") -set(idalib_VERSION "5.7.0") +set(idalib_VERSION "5.8.0") set(idalib_SOVERSION "5") -set(idaslib_VERSION "4.7.0") +set(idaslib_VERSION "4.8.0") set(idaslib_SOVERSION "4") -set(kinsollib_VERSION "5.7.0") +set(kinsollib_VERSION "5.8.0") set(kinsollib_SOVERSION "5") set(cpodeslib_VERSION "0.0.0") set(cpodeslib_SOVERSION "0") -set(nveclib_VERSION "5.7.0") +set(nveclib_VERSION "5.8.0") set(nveclib_SOVERSION "5") -set(sunmatrixlib_VERSION "3.7.0") +set(sunmatrixlib_VERSION "3.8.0") set(sunmatrixlib_SOVERSION "3") -set(sunlinsollib_VERSION "3.7.0") +set(sunlinsollib_VERSION "3.8.0") set(sunlinsollib_SOVERSION "3") -set(sunnonlinsollib_VERSION "2.7.0") +set(sunnonlinsollib_VERSION "2.8.0") set(sunnonlinsollib_SOVERSION "2") set(sundialslib_VERSION @@ -88,20 +103,6 @@ set(sundialslib_VERSION ) set(sundialslib_SOVERSION "${PACKAGE_VERSION_MAJOR}") -# =============================================================== -# SUNDIALS CMake Macros -# =============================================================== - -# 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 - ) - -# MACRO definitions -include(SundialsCMakeMacros) - # =============================================================== # Initial Setup # =============================================================== @@ -192,8 +193,6 @@ if(_BUILD_EXAMPLES) add_subdirectory(examples) endif() - - # =============================================================== # Install configuration header files and license file. # =============================================================== @@ -254,9 +253,10 @@ install( ) # install SUNDIALSConfig.cmake -configure_file( - ${PROJECT_SOURCE_DIR}/cmake/SUNDIALSConfig.cmake.in SUNDIALSConfig.cmake - @ONLY +configure_package_config_file( + "${PROJECT_SOURCE_DIR}/cmake/SUNDIALSConfig.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/SUNDIALSConfig.cmake" + INSTALL_DESTINATION "${SUNDIALS_INSTALL_CMAKEDIR}" ) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/SUNDIALSConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/SUNDIALSConfigVersion.cmake" diff --git a/deps/AMICI/ThirdParty/sundials/README.md b/deps/AMICI/ThirdParty/sundials/README.md index 9c597a212..aa87e594f 100644 --- a/deps/AMICI/ThirdParty/sundials/README.md +++ b/deps/AMICI/ThirdParty/sundials/README.md @@ -1,5 +1,5 @@ # SUNDIALS: SUite of Nonlinear and DIfferential/ALgebraic equation Solvers # -### Version 5.7.0 (Jan 2021) ### +### Version 5.8.0 (Sep 2021) ### **Center for Applied Scientific Computing, Lawrence Livermore National Laboratory** diff --git a/deps/AMICI/ThirdParty/sundials/cmake/SUNDIALSConfig.cmake.in b/deps/AMICI/ThirdParty/sundials/cmake/SUNDIALSConfig.cmake.in index ba2b24d92..6846c5353 100644 --- a/deps/AMICI/ThirdParty/sundials/cmake/SUNDIALSConfig.cmake.in +++ b/deps/AMICI/ThirdParty/sundials/cmake/SUNDIALSConfig.cmake.in @@ -12,10 +12,34 @@ # SUNDIALS Copyright End # --------------------------------------------------------------- +@PACKAGE_INIT@ + include(CMakeFindDependencyMacro) + +### ------- Set FOUND status for SUNDIALS components + +set(_installed_components "@_SUNDIALS_INSTALLED_COMPONENTS@") + +set(_comp_not_found "") +foreach(_comp ${SUNDIALS_FIND_COMPONENTS}) + if(_comp IN_LIST _installed_components) + set(SUNDIALS_${_comp}_FOUND TRUE) + else() + set(SUNDIALS_${_comp}_FOUND FALSE) + set(_comp_not_found "${_comp} ${_comp_not_found}") + endif() +endforeach() + +if(_comp_not_found) + set(SUNDIALS_NOT_FOUND_MESSAGE "Component(s) not found: ${_comp_not_found}") +endif() + +### ------- Import SUNDIALS targets + include("${CMAKE_CURRENT_LIST_DIR}/SUNDIALSTargets.cmake") ### ------- Alias targets + set(_SUNDIALS_ALIAS_TARGETS "@_SUNDIALS_ALIAS_TARGETS@") foreach(ptr ${_SUNDIALS_ALIAS_TARGETS}) string(REGEX REPLACE "sundials_" "" ptr "${ptr}") @@ -23,20 +47,22 @@ foreach(ptr ${_SUNDIALS_ALIAS_TARGETS}) _matches "${ptr}") set(_pointer ${CMAKE_MATCH_1}) set(_pointee ${CMAKE_MATCH_2}) - set_target_properties(SUNDIALS::${_pointee} PROPERTIES IMPORTED_GLOBAL TRUE) - add_library(SUNDIALS::${_pointer} ALIAS SUNDIALS::${_pointee}) + if(NOT TARGET SUNDIALS::${_pointer}) + add_library(SUNDIALS::${_pointer} INTERFACE IMPORTED) + target_link_libraries(SUNDIALS::${_pointer} INTERFACE SUNDIALS::${_pointee}) + endif() endforeach() ### ------- Create TPL imported targets if(@ENABLE_HYPRE@ AND NOT TARGET SUNDIALS::HYPRE) - add_library(SUNDIALS::HYPRE INTERFACE IMPORTED GLOBAL) + add_library(SUNDIALS::HYPRE INTERFACE IMPORTED) target_link_libraries(SUNDIALS::HYPRE INTERFACE "@HYPRE_LIBRARIES@") set_target_properties(SUNDIALS::HYPRE PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "@HYPRE_INCLUDE_DIR@") endif() if(@ENABLE_KLU@ AND NOT TARGET SUNDIALS::KLU) - add_library(SUNDIALS::KLU INTERFACE IMPORTED GLOBAL) + add_library(SUNDIALS::KLU INTERFACE IMPORTED) target_link_libraries(SUNDIALS::KLU INTERFACE "@KLU_LIBRARIES@") set_target_properties(SUNDIALS::KLU PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "@KLU_INCLUDE_DIR@") endif() @@ -93,19 +119,23 @@ if(@ENABLE_PETSC@) endif() if(@ENABLE_MAGMA@ AND NOT TARGET SUNDIALS::MAGMA) - add_library(SUNDIALS::MAGMA INTERFACE IMPORTED GLOBAL) + add_library(SUNDIALS::MAGMA INTERFACE IMPORTED) target_link_libraries(SUNDIALS::MAGMA INTERFACE "@MAGMA_LIBRARIES@") set_target_properties(SUNDIALS::MAGMA PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "@MAGMA_INCLUDE_DIR@") endif() +if(@ENABLE_ONEMKL@ AND NOT TARGET MKL) + find_package(MKL PATHS @ONEMKL_DIR@) +endif() + if(@ENABLE_SUPERLUDIST@ AND NOT TARGET SUNDIALS::SUPERLUDIST) - add_library(SUNDIALS::SUPERLUDIST INTERFACE IMPORTED GLOBAL) + add_library(SUNDIALS::SUPERLUDIST INTERFACE IMPORTED) target_link_libraries(SUNDIALS::SUPERLUDIST INTERFACE "@SUPERLUDIST_LIBRARIES@") set_target_properties(SUNDIALS::SUPERLUDIST PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "@SUPERLUDIST_INCLUDE_DIR@") endif() if(@ENABLE_SUPERLUMT@ AND NOT TARGET SUNDIALS::SUPERLUMT) - add_library(SUNDIALS::SUPERLUMT INTERFACE IMPORTED GLOBAL) + add_library(SUNDIALS::SUPERLUMT INTERFACE IMPORTED) target_link_libraries(SUNDIALS::SUPERLUMT INTERFACE "@SUPERLUMT_LIBRARIES@") set_target_properties(SUNDIALS::SUPERLUMT PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "@SUPERLUMT_INCLUDE_DIR@") endif() @@ -115,13 +145,17 @@ if(@ENABLE_RAJA@ AND NOT TARGET RAJA) endif() if(@ENABLE_TRILINOS@ AND NOT TARGET SUNDIALS::TRILINOS) - add_library(SUNDIALS::TRILINOS INTERFACE IMPORTED GLOBAL) + add_library(SUNDIALS::TRILINOS INTERFACE IMPORTED) target_link_libraries(SUNDIALS::TRILINOS INTERFACE "@Trilinos_LIBRARIES@") set_target_properties(SUNDIALS::TRILINOS PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "@Trilinos_INCLUDE_DIRS@") endif() if(@ENABLE_XBRAID@ AND NOT TARGET SUNDIALS::XBRAID) - add_library(SUNDIALS::XBRAID INTERFACE IMPORTED GLOBAL) + add_library(SUNDIALS::XBRAID INTERFACE IMPORTED) target_link_libraries(SUNDIALS::XBRAID INTERFACE "@XBRAID_LIBRARIES@") set_target_properties(SUNDIALS::XBRAID PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "@XBRAID_INCLUDE_DIR@") endif() + +### ------- Check if required components were found + +check_required_components(SUNDIALS) diff --git a/deps/AMICI/ThirdParty/sundials/cmake/SundialsBuildOptionsPost.cmake b/deps/AMICI/ThirdParty/sundials/cmake/SundialsBuildOptionsPost.cmake index 6af5f8f00..d39b51803 100644 --- a/deps/AMICI/ThirdParty/sundials/cmake/SundialsBuildOptionsPost.cmake +++ b/deps/AMICI/ThirdParty/sundials/cmake/SundialsBuildOptionsPost.cmake @@ -20,10 +20,10 @@ # Currently only available in CVODE. # --------------------------------------------------------------- -sundials_option(SUNDIALS_BUILD_PACKAGE_FUSED_KERNELS BOOL "Build specialized fused CUDA kernels" OFF - DEPENDS_ON ENABLE_CUDA CMAKE_CUDA_COMPILER BUILD_CVODE +sundials_option(SUNDIALS_BUILD_PACKAGE_FUSED_KERNELS BOOL "Build specialized fused GPU kernels" OFF + DEPENDS_ON BUILD_CVODE DEPENDS_ON_THROW_ERROR - SHOW_IF ENABLE_CUDA CMAKE_CUDA_COMPILER BUILD_CVODE) + SHOW_IF BUILD_CVODE) # --------------------------------------------------------------- # Options to enable/disable build for NVECTOR modules. @@ -124,16 +124,21 @@ sundials_option(BUILD_SUNMATRIX_CUSPARSE BOOL "Build the SUNMATRIX_CUSPARSE modu ADVANCED) list(APPEND SUNDIALS_BUILD_LIST "BUILD_SUNMATRIX_CUSPARSE") -sundials_option(BUILD_SUNMATRIX_SLUNRLOC BOOL "Build the SUNMATRIX_SLUNRLOC module (requires SuperLU_DIST)" ON - DEPENDS_ON ENABLE_SUPERLUDIST SUPERLUDIST_WORKS - ADVANCED) -list(APPEND SUNDIALS_BUILD_LIST "BUILD_SUNMATRIX_SLUNRLOC") - sundials_option(BUILD_SUNMATRIX_MAGMADENSE BOOL "Build the SUNMATRIX_MAGMADENSE module (requires MAGMA)" ON DEPENDS_ON ENABLE_MAGMA MAGMA_WORKS ADVANCED) list(APPEND SUNDIALS_BUILD_LIST "BUILD_SUNMATRIX_MAGMADENSE") +sundials_option(BUILD_SUNMATRIX_ONEMKLDENSE BOOL "Build the SUNMATRIX_ONEMKLDENSE module (requires oneMKL)" ON + DEPENDS_ON ENABLE_ONEMKL ONEMKL_WORKS + ADVANCED) +list(APPEND SUNDIALS_BUILD_LIST "BUILD_SUNMATRIX_ONEMKLDENSE") + +sundials_option(BUILD_SUNMATRIX_SLUNRLOC BOOL "Build the SUNMATRIX_SLUNRLOC module (requires SuperLU_DIST)" ON + DEPENDS_ON ENABLE_SUPERLUDIST SUPERLUDIST_WORKS + ADVANCED) +list(APPEND SUNDIALS_BUILD_LIST "BUILD_SUNMATRIX_SLUNRLOC") + # --------------------------------------------------------------- # Options to enable/disable build for SUNLINSOL modules. # --------------------------------------------------------------- @@ -179,6 +184,11 @@ sundials_option(BUILD_SUNLINSOL_MAGMADENSE BOOL "Build the SUNLINSOL_MAGMADENSE ADVANCED) list(APPEND SUNDIALS_BUILD_LIST "BUILD_SUNLINSOL_MAGMADENSE") +sundials_option(BUILD_SUNLINSOL_ONEMKLDENSE BOOL "Build the SUNLINSOL_ONEMKLDENSE module (requires oneMKL)" ON + DEPENDS_ON ENABLE_ONEMKL ONEMKL_WORKS + ADVANCED) +list(APPEND SUNDIALS_BUILD_LIST "BUILD_SUNLINSOL_ONEMKLDENSE") + sundials_option(BUILD_SUNLINSOL_SUPERLUDIST BOOL "Build the SUNLINSOL_SUPERLUDIST module (requires SUPERLUDIST)" ON DEPENDS_ON ENABLE_SUPERLUDIST SUPERLUDIST_WORKS BUILD_SUNMATRIX_SLUNRLOC ADVANCED) diff --git a/deps/AMICI/ThirdParty/sundials/cmake/SundialsExampleOptions.cmake b/deps/AMICI/ThirdParty/sundials/cmake/SundialsExampleOptions.cmake index 6bc39e53b..442faff3f 100644 --- a/deps/AMICI/ThirdParty/sundials/cmake/SundialsExampleOptions.cmake +++ b/deps/AMICI/ThirdParty/sundials/cmake/SundialsExampleOptions.cmake @@ -22,7 +22,8 @@ sundials_option(EXAMPLES_ENABLE_C BOOL "Build SUNDIALS C examples" ON) # Some TPLs only have C++ examples. Default the C++ examples to ON if any of # these are enabled on the initial configuration pass. -if (ENABLE_TRILINOS OR ENABLE_SUPERLUDIST OR ENABLE_XBRAID OR ENABLE_HIP OR ENABLE_MAGMA) +if (ENABLE_TRILINOS OR ENABLE_SUPERLUDIST OR ENABLE_XBRAID OR ENABLE_HIP OR + ENABLE_MAGMA OR ENABLE_SYCL OR ENABLE_ONEMKL OR ENABLE_RAJA) sundials_option(EXAMPLES_ENABLE_CXX BOOL "Build SUNDIALS C++ examples" ON) else() sundials_option(EXAMPLES_ENABLE_CXX BOOL "Build SUNDIALS C++ examples" OFF) diff --git a/deps/AMICI/ThirdParty/sundials/cmake/SundialsSetupCXX.cmake b/deps/AMICI/ThirdParty/sundials/cmake/SundialsSetupCXX.cmake index 412ba5079..0d2646a12 100644 --- a/deps/AMICI/ThirdParty/sundials/cmake/SundialsSetupCXX.cmake +++ b/deps/AMICI/ThirdParty/sundials/cmake/SundialsSetupCXX.cmake @@ -28,4 +28,9 @@ set(DOCSTR "The C++ standard to use if C++ is enabled (98, 11, 14, 17, 20)") sundials_option(CMAKE_CXX_STANDARD STRING "${DOCSTR}" "11" OPTIONS "98;11;14;17;20") +# SYCL requries C++17 +if(ENABLE_SYCL AND (CMAKE_CXX_STANDARD LESS "17")) + set(CMAKE_CXX_STANDARD "17" CACHE STRING "${DOCSTR}" FORCE) +endif() + message(STATUS "CXX standard set to ${CMAKE_CXX_STANDARD}") diff --git a/deps/AMICI/ThirdParty/sundials/cmake/SundialsSetupCompilers.cmake b/deps/AMICI/ThirdParty/sundials/cmake/SundialsSetupCompilers.cmake index a3d6fb501..162d30884 100644 --- a/deps/AMICI/ThirdParty/sundials/cmake/SundialsSetupCompilers.cmake +++ b/deps/AMICI/ThirdParty/sundials/cmake/SundialsSetupCompilers.cmake @@ -213,11 +213,22 @@ if(ENABLE_HIP) list(APPEND SUNDIALS_TPL_LIST "HIP") endif() +# =============================================================== +# Default flags for build types +# =============================================================== + +set(CMAKE_C_FLAGS_DEV "${CMAKE_C_FLAGS_DEV} -g -O0 -Wall -Wpedantic -Wextra -Wno-unused-parameter -Werror") +set(CMAKE_CXX_FLAGS_DEV "${CMAKE_CXX_FLAGS_DEV} -g -O0 -Wall -Wpedantic -Wextra -Wno-unused-parameter -Werror") +set(CMAKE_Fortran_FLAGS_DEV "${CMAKE_Fortran_FLAGS_DEV} -g -O0 -Wall -Wpedantic -ffpe-summary=none") +set(CMAKE_C_FLAGS_DEVSTRICT "-std=c89 ${CMAKE_C_FLAGS_DEV}") +set(CMAKE_CXX_FLAGS_DEVSTRICT "${CMAKE_CXX_FLAGS_DEV}") +set(CMAKE_Fortran_FLAGS_DEVSTRICT "${CMAKE_Fortran_FLAGS_DEV}") + # =============================================================== # Configure presentation of language options # =============================================================== -set(build_types DEBUG RELEASE RELWITHDEBINFO MINSIZEREL) +set(build_types DEBUG RELEASE RELWITHDEBINFO MINSIZEREL DEV DEVSTRICT) set(_SUNDIALS_ENABLED_LANGS "C") if(CXX_FOUND) diff --git a/deps/AMICI/ThirdParty/sundials/cmake/SundialsSetupCuda.cmake b/deps/AMICI/ThirdParty/sundials/cmake/SundialsSetupCuda.cmake deleted file mode 100644 index f7c04df03..000000000 --- a/deps/AMICI/ThirdParty/sundials/cmake/SundialsSetupCuda.cmake +++ /dev/null @@ -1,106 +0,0 @@ -# --------------------------------------------------------------- -# Programmer(s): Cody J. Balos @ LLNL -# --------------------------------------------------------------- -# SUNDIALS Copyright Start -# Copyright (c) 2002-2021, Lawrence Livermore National Security -# and Southern Methodist University. -# All rights reserved. -# -# See the top-level LICENSE and NOTICE files for details. -# -# SPDX-License-Identifier: BSD-3-Clause -# SUNDIALS Copyright End -# --------------------------------------------------------------- -# Setup the CUDA languge and CUDA libraries. -# --------------------------------------------------------------- - -# =============================================================== -# Configure options needed prior to enabling the CUDA language -# =============================================================== - -if(NOT CMAKE_CUDA_HOST_COMPILER) - # If a user did not provide the host compiler, then we - # assume that they want to use the CXX compiler that was set. - set(CMAKE_CUDA_HOST_COMPILER ${CMAKE_CXX_COMPILER} CACHE FILEPATH "NVCC host compiler") -endif() - -# =============================================================== -# Configure the CUDA flags -# =============================================================== - -set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} --expt-extended-lambda") - -if(${CMAKE_VERSION} VERSION_LESS "3.18.0") - if(CMAKE_CUDA_ARCHITECTURES) - foreach(arch ${CMAKE_CUDA_ARCHITECTURES}) - # Remove real/virtual specifiers - string(REGEX MATCH "[0-9]+" arch_name "${arch}") - string(APPEND _nvcc_arch_flags " -gencode=arch=compute_${arch_name},code=sm_${arch_name}") - endforeach() - - set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} ${_nvcc_arch_flags}") - endif() -endif() - -if( (CMAKE_CXX_COMPILER_ID MATCHES GNU) - OR (CMAKE_CXX_COMPILER_ID MATCHES Clang) - AND (CMAKE_SYSTEM_PROCESSOR MATCHES ppc64le) ) - include(CheckCXXCompilerFlag) - check_cxx_compiler_flag(-mno-float128 _hasflag) - if(_hasflag) - set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Xcompiler=-mno-float128") - endif() -endif() - -# Need c++11 for the CUDA compiler check. -set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -std=c++11") - -# =============================================================== -# Enable CUDA lang and find the CUDA libraries. -# =============================================================== - -enable_language(CUDA) -set(CUDA_FOUND TRUE) - -# Need this as long as CUDA libraries like cuSOLVER are not available -# through some other way. -find_package(CUDA REQUIRED) - -# Hide legacy FindCUDA variables -get_cmake_property(_variables VARIABLES) -foreach(_var ${_variables}) - if("${_var}" MATCHES "^CUDA_[A-z]+_LIBRARY") - # do nothing - elseif("${_var}" MATCHES "^CUDA_.*") - mark_as_advanced(${_var}) - endif() -endforeach() - -# Make the CUDA_rt_LIBRARY advanced like the other CUDA_*_LIBRARY variables -mark_as_advanced(FORCE CUDA_rt_LIBRARY) - -# Show CUDA flags -mark_as_advanced(CLEAR CMAKE_CUDA_FLAGS) - -# We need c++11 for the CUDA compiler check, but if we don't remove it, -# then we will get a redefinition error. CMAKE_CUDA_STANDARD ends up -# setting the proper version. -if(CMAKE_CUDA_FLAGS) - STRING(REPLACE "-std=c++11" " " CMAKE_CUDA_FLAGS ${CMAKE_CUDA_FLAGS}) -endif() -set(CMAKE_CUDA_STANDARD ${CMAKE_CXX_STANDARD}) - -# =============================================================== -# Print out information about CUDA. -# =============================================================== - -message(STATUS "CUDA Version: ${CUDA_VERSION_STRING}") -message(STATUS "CUDA Architectures: ${CMAKE_CUDA_ARCHITECTURES}") -message(STATUS "CUDA Compiler: ${CMAKE_CUDA_COMPILER}") -message(STATUS "CUDA Host Compiler: ${CMAKE_CUDA_HOST_COMPILER}") -message(STATUS "CUDA Include Path: ${CUDA_INCLUDE_DIRS}") -message(STATUS "CUDA Libraries: ${CUDA_LIBRARIES}") -message(STATUS "CUDA Compile Flags: ${CMAKE_CUDA_FLAGS}") -message(STATUS "CUDA Link Flags: ${CMAKE_CUDA_LINK_FLAGS}") -message(STATUS "CUDA Link Executable: ${CMAKE_CUDA_LINK_EXECUTABLE}") -message(STATUS "CUDA Separable Compilation: ${CMAKE_CUDA_SEPARABLE_COMPILATION}") diff --git a/deps/AMICI/ThirdParty/sundials/cmake/SundialsSetupFortran.cmake b/deps/AMICI/ThirdParty/sundials/cmake/SundialsSetupFortran.cmake deleted file mode 100644 index 06f388a83..000000000 --- a/deps/AMICI/ThirdParty/sundials/cmake/SundialsSetupFortran.cmake +++ /dev/null @@ -1,339 +0,0 @@ -# --------------------------------------------------------------- -# Programmer(s): Radu Serban, David Gardner, Cody J. Balos @ LLNL -# --------------------------------------------------------------- -# SUNDIALS Copyright Start -# Copyright (c) 2002-2021, Lawrence Livermore National Security -# and Southern Methodist University. -# All rights reserved. -# -# See the top-level LICENSE and NOTICE files for details. -# -# SPDX-License-Identifier: BSD-3-Clause -# SUNDIALS Copyright End -# --------------------------------------------------------------- -# Module which enables Fortran and tests for support of necessary -# compiler features for the current SUNDIALS configuration. -# Will define the variables: -# Fortran_FOUND - TRUE if a Fortran compiler is found -# F77_FOUND - equivalent to Fortran_FOUND -# F90_FOUND - TRUE if the Fortran compiler supports Fortran 90 -# F2003_FOUND - TRUE if the Fortran compiler supports the -# Fortran 2003 standard -# --------------------------------------------------------------- - -# If the Fortran compiler flags are set using environemnt variables (i.e., -# CMAKE_Fortran_FLAGS is not set), then check if both FFLAGS and FCFLAGS are -# set. If both are set and not the same then a fatal error occurs. -# -# NOTE: This check must occur before 'enable_language(Fortran)' as it will use -# the value of FFLAGS to set CMAKE_Fortran_FLAGS -set(ENV_FFLAGS "$ENV{FFLAGS}") -set(ENV_FCFLAGS "$ENV{FCFLAGS}") - -# check if environment variables are used and CMAKE_Fortran_FLAGS is not -if ((NOT "${ENV_FFLAGS}" STREQUAL "") AND (NOT "${ENV_FCFLAGS}" STREQUAL "") - AND ("${CMAKE_Fortran_FLAGS}" STREQUAL "")) - - # check if environment variables are equal - if (NOT "${ENV_FFLAGS}" STREQUAL "${ENV_FCFLAGS}") - print_error("FFLAGS='${ENV_FFLAGS}' and FCFLAGS='${ENV_FCFLAGS}' are both set but are not equal.") - endif() -endif() - -# ----------------------------------------------------------------------------- -# Enable Fortran -# ----------------------------------------------------------------------------- -enable_language(Fortran) -set(Fortran_FOUND TRUE) -set(F77_FOUND TRUE) - -# ----------------------------------------------------------------------------- -# Check if Fortran 90 is supported -# ----------------------------------------------------------------------------- -if(CMAKE_Fortran_COMPILER_SUPPORTS_F90) - set(F90_FOUND TRUE) -else() - set(F90_FOUND FALSE) - print_warning("Fortran compiler does not support F90" "F90 support will not be provided") -endif() - -# ----------------------------------------------------------------------------- -# Check if Fortran 2003 is supported -# ----------------------------------------------------------------------------- -if(BUILD_FORTRAN_MODULE_INTERFACE) - if(NOT F2003_FOUND) - message(STATUS "Checking whether ${CMAKE_Fortran_COMPILER} supports F2003") - - set(F2003Test_DIR ${PROJECT_BINARY_DIR}/F2003Test_DIR) - file(MAKE_DIRECTORY ${F2003Test_DIR}) - - # Create a CMakeLists.txt file - file(WRITE ${F2003Test_DIR}/CMakeLists.txt - "CMAKE_MINIMUM_REQUIRED(VERSION 3.1.3)\n" - "PROJECT(ftest Fortran)\n" - "SET(CMAKE_VERBOSE_MAKEFILE ON)\n" - "SET(CMAKE_BUILD_TYPE \"${CMAKE_BUILD_TYPE}\")\n" - "SET(CMAKE_Fortran_COMPILER \"${CMAKE_Fortran_COMPILER}\")\n" - "SET(CMAKE_Fortran_FLAGS \"${CMAKE_Fortran_FLAGS}\")\n" - "SET(CMAKE_Fortran_FLAGS_RELEASE \"${CMAKE_Fortran_FLAGS_RELEASE}\")\n" - "SET(CMAKE_Fortran_FLAGS_DEBUG \"${CMAKE_Fortran_FLAGS_DEBUG}\")\n" - "SET(CMAKE_Fortran_FLAGS_RELWITHDEBUGINFO \"${CMAKE_Fortran_FLAGS_RELWITHDEBUGINFO}\")\n" - "SET(CMAKE_Fortran_FLAGS_MINSIZE \"${CMAKE_Fortran_FLAGS_MINSIZE}\")\n" - "ADD_EXECUTABLE(ftest ftest.f90)\n") - - # Create a Fortran source file which tries to use iso_c_binding - file(WRITE ${F2003Test_DIR}/ftest.f90 - "program main\n" - "use, intrinsic :: iso_c_binding\n" - "end program main\n") - - # Attempt compile the executable - try_compile(FTEST_OK ${F2003Test_DIR} ${F2003Test_DIR} - ftest OUTPUT_VARIABLE COMPILE_OUTPUT) - - # To ensure we do not use stuff from the previous attempts, - # we must remove the CMakeFiles directory. - file(REMOVE_RECURSE ${F2003Test_DIR}/CMakeFiles) - - if(FTEST_OK) - message(STATUS "Checking whether ${CMAKE_Fortran_COMPILER} supports F2003 -- yes") - set(F2003_FOUND TRUE CACHE BOOL "${CMAKE_Fortran_COMPILER} supports F2003" FORCE) - else() - message(STATUS "Checking whether ${CMAKE_Fortran_COMPILER} supports F2003 -- no") - message(STATUS "Check output:") - message("${COMPILE_OUTPUT}") - print_error("BUILD_FORTRAN_MODULE_INTERFACE is set to ON, but the CMAKE_Fortran_COMPILER does not support F2003") - endif() - else() - message(STATUS "Skipped F2003 tests, assuming ${CMAKE_Fortran_COMPILER} supports the f2003 standard. To rerun the F2003 tests, set F2003_FOUND to FALSE.") - endif() -endif() - -# Ensure that F90 compiler is found if F90 examples are enabled -if (EXAMPLES_ENABLE_F90 AND (NOT F90_FOUND)) - print_error("Compiler with F90 support not found" "Disabling F90 Examples") - set(DOCSTR "Build SUNDIALS F90 examples") - force_variable(EXAMPLES_ENABLE_F90 BOOL "${DOCSTR}" OFF) -endif() - -# Put all F2003 modules into one build directory -set(CMAKE_Fortran_MODULE_DIRECTORY "${CMAKE_BINARY_DIR}/fortran") - -# --------------------------------------------------------------- -# Determining the name-mangling scheme if needed -# --------------------------------------------------------------- -# In general, names of symbols with and without underscore may be mangled -# differently (e.g. g77 mangles mysub to mysub_ and my_sub to my_sub__), -# we have to consider both cases. -# -# Method: -# 1) create a library from a Fortran source file which defines a function "mysub" -# 2) attempt to link with this library a C source file which calls the "mysub" -# function using various possible schemes (6 different schemes, corresponding -# to all combinations lower/upper case and none/one/two underscores). -# 3) define the name-mangling scheme based on the test that was successful. -# -# On exit, if we were able to infer the scheme, the variables -# CMAKE_Fortran_SCHEME_NO_UNDERSCORES and CMAKE_Fortran_SCHEME_WITH_UNDERSCORES -# contain the mangled names for "mysub" and "my_sub", respectively. -# --------------------------------------------------------------- -if(NEED_FORTRAN_NAME_MANGLING) - - set(CMAKE_Fortran_SCHEME_NO_UNDERSCORES "") - set(CMAKE_Fortran_SCHEME_WITH_UNDERSCORES "") - - # Create the FortranTest directory - set(FortranTest_DIR ${PROJECT_BINARY_DIR}/FortranTest) - file(MAKE_DIRECTORY ${FortranTest_DIR}) - - # Create a CMakeLists.txt file which will generate the "flib" library - # and an executable "ftest" - file(WRITE ${FortranTest_DIR}/CMakeLists.txt - "CMAKE_MINIMUM_REQUIRED(VERSION 3.0.2)\n" - "PROJECT(ftest Fortran)\n" - "SET(CMAKE_VERBOSE_MAKEFILE ON)\n" - "SET(CMAKE_BUILD_TYPE \"${CMAKE_BUILD_TYPE}\")\n" - "SET(CMAKE_Fortran_COMPILER \"${CMAKE_Fortran_COMPILER}\")\n" - "SET(CMAKE_Fortran_FLAGS \"${CMAKE_Fortran_FLAGS}\")\n" - "SET(CMAKE_Fortran_FLAGS_RELEASE \"${CMAKE_Fortran_FLAGS_RELEASE}\")\n" - "SET(CMAKE_Fortran_FLAGS_DEBUG \"${CMAKE_Fortran_FLAGS_DEBUG}\")\n" - "SET(CMAKE_Fortran_FLAGS_RELWITHDEBUGINFO \"${CMAKE_Fortran_FLAGS_RELWITHDEBUGINFO}\")\n" - "SET(CMAKE_Fortran_FLAGS_MINSIZE \"${CMAKE_Fortran_FLAGS_MINSIZE}\")\n" - "ADD_LIBRARY(flib flib.f)\n" - "ADD_EXECUTABLE(ftest ftest.f)\n" - "TARGET_LINK_LIBRARIES(ftest flib)\n") - - # Create the Fortran source flib.f which defines two subroutines, "mysub" and "my_sub" - file(WRITE ${FortranTest_DIR}/flib.f - " SUBROUTINE mysub\n" - " RETURN\n" - " END\n" - " SUBROUTINE my_sub\n" - " RETURN\n" - " END\n") - - # Create the Fortran source ftest.f which calls "mysub" and "my_sub" - file(WRITE ${FortranTest_DIR}/ftest.f - " PROGRAM ftest\n" - " CALL mysub()\n" - " CALL my_sub()\n" - " END\n") - - # Use TRY_COMPILE to make the targets "flib" and "ftest" - try_compile(FTEST_OK ${FortranTest_DIR} ${FortranTest_DIR} - ftest OUTPUT_VARIABLE MY_OUTPUT) - - # To ensure we do not use stuff from the previous attempts, - # we must remove the CMakeFiles directory. - file(REMOVE_RECURSE ${FortranTest_DIR}/CMakeFiles) - - # Proceed based on test results - if(FTEST_OK) - - # Infer Fortran name-mangling scheme for symbols WITHOUT underscores. - # Overwrite CMakeLists.txt with one which will generate the "ctest1" executable - file(WRITE ${FortranTest_DIR}/CMakeLists.txt - "CMAKE_MINIMUM_REQUIRED(VERSION 3.0.2)\n" - "PROJECT(ctest1 C)\n" - "SET(CMAKE_VERBOSE_MAKEFILE ON)\n" - "SET(CMAKE_BUILD_TYPE \"${CMAKE_BUILD_TYPE}\")\n" - "SET(CMAKE_C_COMPILER \"${CMAKE_C_COMPILER}\")\n" - "SET(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS}\")\n" - "SET(CMAKE_C_FLAGS_RELEASE \"${CMAKE_C_FLAGS_RELEASE}\")\n" - "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" - "ADD_EXECUTABLE(ctest1 ctest1.c)\n" - "FIND_LIBRARY(FLIB flib \"${FortranTest_DIR}\")\n" - "TARGET_LINK_LIBRARIES(ctest1 \${FLIB})\n") - - # Define the list "options" of all possible schemes that we want to consider - # Get its length and initialize the counter "iopt" to zero - set(options mysub mysub_ mysub__ MYSUB MYSUB_ MYSUB__) - list(LENGTH options imax) - set(iopt 0) - - # We will attempt to sucessfully generate the "ctest1" executable as long as - # there still are entries in the "options" list - while(${iopt} LESS ${imax}) - # Get the current list entry (current scheme) - list(GET options ${iopt} opt) - # Generate C source which calls the "mysub" function using the current scheme - file(WRITE ${FortranTest_DIR}/ctest1.c - "extern void ${opt}();\n" - "int main(){${opt}();return(0);}\n") - # Use TRY_COMPILE to make the "ctest1" executable from the current C source - # and linking to the previously created "flib" library. - try_compile(CTEST_OK ${FortranTest_DIR} ${FortranTest_DIR} - ctest1 OUTPUT_VARIABLE MY_OUTPUT) - # Write output compiling the test code - file(WRITE ${FortranTest_DIR}/ctest1_${opt}.out "${MY_OUTPUT}") - # To ensure we do not use stuff from the previous attempts, - # we must remove the CMakeFiles directory. - file(REMOVE_RECURSE ${FortranTest_DIR}/CMakeFiles) - # Test if we successfully created the "ctest" executable. - # If yes, save the current scheme, and set the counter "iopt" to "imax" - # so that we exit the while loop. - # Otherwise, increment the counter "iopt" and go back in the while loop. - if(CTEST_OK) - set(CMAKE_Fortran_SCHEME_NO_UNDERSCORES ${opt}) - set(iopt ${imax}) - else(CTEST_OK) - math(EXPR iopt ${iopt}+1) - endif() - endwhile(${iopt} LESS ${imax}) - - # Infer Fortran name-mangling scheme for symbols WITH underscores. - # Practically a duplicate of the previous steps. - file(WRITE ${FortranTest_DIR}/CMakeLists.txt - "CMAKE_MINIMUM_REQUIRED(VERSION 3.0.2)\n" - "PROJECT(ctest2 C)\n" - "SET(CMAKE_VERBOSE_MAKEFILE ON)\n" - "SET(CMAKE_BUILD_TYPE \"${CMAKE_BUILD_TYPE}\")\n" - "SET(CMAKE_C_COMPILER \"${CMAKE_C_COMPILER}\")\n" - "SET(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS}\")\n" - "SET(CMAKE_C_FLAGS_RELEASE \"${CMAKE_C_FLAGS_RELEASE}\")\n" - "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" - "ADD_EXECUTABLE(ctest2 ctest2.c)\n" - "FIND_LIBRARY(FLIB flib ${FortranTest_DIR})\n" - "TARGET_LINK_LIBRARIES(ctest2 \${FLIB})\n") - - set(options my_sub my_sub_ my_sub__ MY_SUB MY_SUB_ MY_SUB__) - list(LENGTH options imax) - set(iopt 0) - while(${iopt} LESS ${imax}) - list(GET options ${iopt} opt) - file(WRITE ${FortranTest_DIR}/ctest2.c - "extern void ${opt}();\n" - "int main(){${opt}();return(0);}\n") - try_compile(CTEST_OK ${FortranTest_DIR} ${FortranTest_DIR} - ctest2 OUTPUT_VARIABLE MY_OUTPUT) - file(WRITE ${FortranTest_DIR}/ctest2_${opt}.out "${MY_OUTPUT}") - file(REMOVE_RECURSE ${FortranTest_DIR}/CMakeFiles) - if(CTEST_OK) - set(CMAKE_Fortran_SCHEME_WITH_UNDERSCORES ${opt}) - set(iopt ${imax}) - else(CTEST_OK) - math(EXPR iopt ${iopt}+1) - endif() - endwhile(${iopt} LESS ${imax}) - - # If a name-mangling scheme was found set the C preprocessor macros to use - # that scheme. Otherwise default to lower case with one underscore. - if(CMAKE_Fortran_SCHEME_NO_UNDERSCORES AND CMAKE_Fortran_SCHEME_WITH_UNDERSCORES) - message(STATUS "Determining Fortran name-mangling scheme... OK") - else() - message(STATUS "Determining Fortran name-mangling scheme... DEFAULT") - set(CMAKE_Fortran_SCHEME_NO_UNDERSCORES "mysub_") - set(CMAKE_Fortran_SCHEME_WITH_UNDERSCORES "my_sub_") - endif() - - # Symbols NO underscores - if(${CMAKE_Fortran_SCHEME_NO_UNDERSCORES} MATCHES "mysub") - set(F77_MANGLE_MACRO1 "#define SUNDIALS_F77_FUNC(name,NAME) name") - endif() - if(${CMAKE_Fortran_SCHEME_NO_UNDERSCORES} MATCHES "mysub_") - set(F77_MANGLE_MACRO1 "#define SUNDIALS_F77_FUNC(name,NAME) name ## _") - endif() - if(${CMAKE_Fortran_SCHEME_NO_UNDERSCORES} MATCHES "mysub__") - set(F77_MANGLE_MACRO1 "#define SUNDIALS_F77_FUNC(name,NAME) name ## __") - endif() - if(${CMAKE_Fortran_SCHEME_NO_UNDERSCORES} MATCHES "MYSUB") - set(F77_MANGLE_MACRO1 "#define SUNDIALS_F77_FUNC(name,NAME) NAME") - endif() - if(${CMAKE_Fortran_SCHEME_NO_UNDERSCORES} MATCHES "MYSUB_") - set(F77_MANGLE_MACRO1 "#define SUNDIALS_F77_FUNC(name,NAME) NAME ## _") - endif() - if(${CMAKE_Fortran_SCHEME_NO_UNDERSCORES} MATCHES "MYSUB__") - set(F77_MANGLE_MACRO1 "#define SUNDIALS_F77_FUNC(name,NAME) NAME ## __") - endif() - - # Symbols WITH underscores - if(${CMAKE_Fortran_SCHEME_WITH_UNDERSCORES} MATCHES "my_sub") - set(F77_MANGLE_MACRO2 "#define SUNDIALS_F77_FUNC_(name,NAME) name") - endif() - if(${CMAKE_Fortran_SCHEME_WITH_UNDERSCORES} MATCHES "my_sub_") - set(F77_MANGLE_MACRO2 "#define SUNDIALS_F77_FUNC_(name,NAME) name ## _") - endif() - if(${CMAKE_Fortran_SCHEME_WITH_UNDERSCORES} MATCHES "my_sub__") - set(F77_MANGLE_MACRO2 "#define SUNDIALS_F77_FUNC_(name,NAME) name ## __") - endif() - if(${CMAKE_Fortran_SCHEME_WITH_UNDERSCORES} MATCHES "MY_SUB") - set(F77_MANGLE_MACRO2 "#define SUNDIALS_F77_FUNC_(name,NAME) NAME") - endif() - if(${CMAKE_Fortran_SCHEME_WITH_UNDERSCORES} MATCHES "MY_SUB_") - set(F77_MANGLE_MACRO2 "#define SUNDIALS_F77_FUNC_(name,NAME) NAME ## _") - endif() - if(${CMAKE_Fortran_SCHEME_WITH_UNDERSCORES} MATCHES "MY_SUB__") - set(F77_MANGLE_MACRO2 "#define SUNDIALS_F77_FUNC_(name,NAME) NAME ## __") - endif() - - # name-mangling scheme has been set - set(NEED_FORTRAN_NAME_MANGLING FALSE) - else(FTEST_OK) - message(STATUS "Determining Fortran name-mangling scheme... FAILED") - endif() - -endif() \ No newline at end of file diff --git a/deps/AMICI/ThirdParty/sundials/cmake/SundialsSetupHIP.cmake b/deps/AMICI/ThirdParty/sundials/cmake/SundialsSetupHIP.cmake deleted file mode 100644 index f12008037..000000000 --- a/deps/AMICI/ThirdParty/sundials/cmake/SundialsSetupHIP.cmake +++ /dev/null @@ -1,56 +0,0 @@ -# --------------------------------------------------------------- -# Programmer(s): Cody J. Balos @ LLNL -# --------------------------------------------------------------- -# SUNDIALS Copyright Start -# Copyright (c) 2002-2021, Lawrence Livermore National Security -# and Southern Methodist University. -# All rights reserved. -# -# See the top-level LICENSE and NOTICE files for details. -# -# SPDX-License-Identifier: BSD-3-Clause -# SUNDIALS Copyright End -# --------------------------------------------------------------- -# Setup the HIP language and libraries. -# --------------------------------------------------------------- - -if(NOT DEFINED ROCM_PATH) - if(NOT DEFINED ENV{ROCM_PATH}) - set(ROCM_PATH "/opt/rocm/" CACHE PATH "Path to which ROCm has been installed") - else() - set(ROCM_PATH "$ENV{ROCM_PATH}" CACHE PATH "Path to which ROCm has been installed") - endif() -endif() - -if(NOT DEFINED HIP_PATH) - if(NOT DEFINED ENV{HIP_PATH}) - set(HIP_PATH "/opt/rocm/hip" CACHE PATH "Path to which HIP has been installed") - else() - set(HIP_PATH "$ENV{HIP_PATH}" CACHE PATH "Path to which HIP has been installed") - endif() -endif() - -if(NOT DEFINED HIP_PLATFORM) - if(NOT DEFINED ENV{HIP_PLATFORM}) - set(HIP_PLATFORM "hcc" CACHE STRING "HIP platform (hcc, nvcc)") - else() - set(HIP_PLATFORM "$ENV{HIP_PLATFORM}" CACHE STRING "HIP platform (hcc, nvcc)") - endif() -endif() - -# Set CMAKE_PREFIX_PATH as the hip-config.cmake has some find_package calls -# which don't have the proper path set (not sure if this is a bug or -# intentional), so without this they will fail even if we provide the PATH -# option to find_package(HIP). -set(CMAKE_PREFIX_PATH "${ROCM_PATH};${HIP_PATH}") -find_package(HIP REQUIRED) - -if("${HIP_COMPILER}" STREQUAL "hcc") - print_error("Deprecated HCC compiler is not supported" "Please update ROCm") -endif() - -message(STATUS "HIP version: ${HIP_VERSION}") -message(STATUS "HIP platform: ${HIP_PLATFORM}") -message(STATUS "HIP compiler: ${HIP_COMPILER}") -message(STATUS "HIP linker: ${CMAKE_CXX_LINK_EXECUTABLE}") -message(STATUS "AMD targets: ${AMDGPU_TARGETS}") diff --git a/deps/AMICI/ThirdParty/sundials/cmake/SundialsSetupTPLs.cmake b/deps/AMICI/ThirdParty/sundials/cmake/SundialsSetupTPLs.cmake index 786022130..0b8339340 100644 --- a/deps/AMICI/ThirdParty/sundials/cmake/SundialsSetupTPLs.cmake +++ b/deps/AMICI/ThirdParty/sundials/cmake/SundialsSetupTPLs.cmake @@ -68,6 +68,15 @@ if(ENABLE_MAGMA) list(APPEND SUNDIALS_TPL_LIST "MAGMA") endif() +# --------------------------------------------------------------- +# Find (and test) the oneMKL libraries +# --------------------------------------------------------------- + +if(ENABLE_ONEMKL) + include(SundialsONEMKL) + list(APPEND SUNDIALS_TPL_LIST "ONEMKL") +endif() + # --------------------------------------------------------------- # Find (and test) the SuperLUDIST libraries # --------------------------------------------------------------- @@ -144,4 +153,4 @@ endif() # Check for POSIX timers # --------------------------------------------------------------- -include(SundialsPOSIXTimers) \ No newline at end of file +include(SundialsPOSIXTimers) diff --git a/deps/AMICI/ThirdParty/sundials/cmake/SundialsTPLOptions.cmake b/deps/AMICI/ThirdParty/sundials/cmake/SundialsTPLOptions.cmake index 9bf9ecff8..b8c7d8b85 100644 --- a/deps/AMICI/ThirdParty/sundials/cmake/SundialsTPLOptions.cmake +++ b/deps/AMICI/ThirdParty/sundials/cmake/SundialsTPLOptions.cmake @@ -81,7 +81,7 @@ sundials_option(ENABLE_MAGMA BOOL "Enable MAGMA support" OFF) sundials_option(MAGMA_DIR PATH "Path to the root of a MAGMA installation" "${MAGMA_DIR}" SHOW_IF ENABLE_MAGMA) -sundials_option(SUNDIALS_MAGMA_BACKENDS STRING "Which MAGMA backend under the SUNDIALS MAGMA interfaces (CUDA, HIP)" "CUDA" +sundials_option(SUNDIALS_MAGMA_BACKENDS STRING "Which MAGMA backend to use under the SUNDIALS MAGMA interfaces (CUDA, HIP)" "CUDA" OPTIONS "CUDA;HIP" SHOW_IF ENABLE_MAGMA) @@ -193,8 +193,8 @@ sundials_option(ENABLE_RAJA BOOL "Enable RAJA support" OFF) sundials_option(RAJA_DIR PATH "Path to root of RAJA installation" "${RAJA_DIR}" SHOW_IF ENABLE_RAJA) -sundials_option(SUNDIALS_RAJA_BACKENDS STRING "Which RAJA backend under the SUNDIALS RAJA interfaces (CUDA, HIP)" "CUDA" - OPTIONS "CUDA;HIP" +sundials_option(SUNDIALS_RAJA_BACKENDS STRING "Which RAJA backend under the SUNDIALS RAJA interfaces (CUDA, HIP, SYCL)" "CUDA" + OPTIONS "CUDA;HIP;SYCL" SHOW_IF ENABLE_RAJA) # --------------------------------------------------------------- @@ -252,5 +252,15 @@ sundials_option(XBRAID_INCLUDES STRING "Semi-colon separated list of XBraid incl ADVANCED) sundials_option(XBRAID_WORKS BOOL "Set to ON to force CMake to accept a given XBraid configuration" OFF - DEPENDS_ON ENABLE_XBRAID + SHOW_IF ENABLE_XBRAID + ADVANCED) + +# ------------------------------------------------------------- +# Enable oneMKL support? +# ------------------------------------------------------------- + +sundials_option(ENABLE_ONEMKL BOOL "Enable oneMKL support" OFF) + +sundials_option(ONEMKL_WORKS BOOL "Set to ON to force CMake to accept a given oneMKL configuration" OFF + SHOW_IF ENABLE_ONEMKL ADVANCED) diff --git a/deps/AMICI/ThirdParty/sundials/cmake/macros/SundialsAddLibrary.cmake b/deps/AMICI/ThirdParty/sundials/cmake/macros/SundialsAddLibrary.cmake index 1d78918b3..32d33167f 100644 --- a/deps/AMICI/ThirdParty/sundials/cmake/macros/SundialsAddLibrary.cmake +++ b/deps/AMICI/ThirdParty/sundials/cmake/macros/SundialsAddLibrary.cmake @@ -357,6 +357,15 @@ macro(sundials_add_library target) endif() endif() + # -------------------------------------------------------------------------- + # List of installed SUNDIALS components + # -------------------------------------------------------------------------- + + if(NOT sundials_add_library_OBJECT_LIB_ONLY) + string(REPLACE "sundials_" "" _comp_name "${target}") + set(_SUNDIALS_INSTALLED_COMPONENTS "${_comp_name};${_SUNDIALS_INSTALLED_COMPONENTS}" CACHE INTERNAL "" FORCE) + endif() + endmacro(sundials_add_library) diff --git a/deps/AMICI/ThirdParty/sundials/cmake/macros/SundialsCMakeMacros.cmake b/deps/AMICI/ThirdParty/sundials/cmake/macros/SundialsCMakeMacros.cmake index df6c47b5d..85a252a6c 100644 --- a/deps/AMICI/ThirdParty/sundials/cmake/macros/SundialsCMakeMacros.cmake +++ b/deps/AMICI/ThirdParty/sundials/cmake/macros/SundialsCMakeMacros.cmake @@ -65,7 +65,7 @@ macro(print_warning message action) message(${_mode} ${MSG}) endmacro() -# Macro to print error messages. Takes +# Macro to print error messages. macro(print_error message) set(options ) @@ -111,9 +111,28 @@ macro(EXAMPLES2STRING example_list example_string) list2string(tmp_list ${example_string}) endmacro(EXAMPLES2STRING) +# Sets the SUNDIALS_GIT_VERSION variable + +function(sundials_git_version) + find_package(Git QUIET) + + set(_tmp "") + + if(EXISTS ${CMAKE_CURRENT_LIST_DIR}/.git AND ${GIT_FOUND}) + execute_process(COMMAND git describe --abbrev=12 --dirty --always --tags + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} + OUTPUT_VARIABLE _tmp) + string(STRIP "${_tmp}" _tmp) + endif() + + set(SUNDIALS_GIT_VERSION "${_tmp}" CACHE INTERNAL "") + unset(_tmp) +endfunction() + # Macros from other files + include(SundialsAddLibrary) include(SundialsAddTest) include(SundialsAddTestInstall) include(SundialsInstallExamples) -include(SundialsOption) \ No newline at end of file +include(SundialsOption) diff --git a/deps/AMICI/ThirdParty/sundials/cmake/macros/SundialsInstallExamples.cmake b/deps/AMICI/ThirdParty/sundials/cmake/macros/SundialsInstallExamples.cmake index e14ef7c17..5441b08d1 100644 --- a/deps/AMICI/ThirdParty/sundials/cmake/macros/SundialsInstallExamples.cmake +++ b/deps/AMICI/ThirdParty/sundials/cmake/macros/SundialsInstallExamples.cmake @@ -40,6 +40,8 @@ # # The SUNDIALS_TARGETS option is a list of CMake targets in the SUNDIALS:: namespace that the examples need to be linked to. # +# The OTHER_TARGETS option is a list of CMake targets that the examples need to be linked to. +# # The EXAMPLE_DEPENDENCIES option is a list of additional source files that the examples are dependent on. # # The EXTRA_FILES option is a list of files to install that are not example source code. @@ -51,7 +53,7 @@ macro(sundials_install_examples MODULE EXAMPLES_VAR) set(options ) set(oneValueArgs SOLVER_LIBRARY DESTINATION CMAKE_TEMPLATE MAKE_TEMPLATE TEST_INSTALL) - set(multiValueArgs SUNDIALS_TARGETS EXAMPLES_DEPENDENCIES EXTRA_FILES EXTRA_INCLUDES) + set(multiValueArgs SUNDIALS_TARGETS OTHER_TARGETS EXAMPLES_DEPENDENCIES EXTRA_FILES EXTRA_INCLUDES) # Parse keyword arguments/options cmake_parse_arguments(sundials_install_examples @@ -81,10 +83,18 @@ macro(sundials_install_examples MODULE EXAMPLES_VAR) string(TOUPPER "${MODULE}" SOLVER) set(SOLVER_LIB "${sundials_install_examples_SOLVER_LIBRARY}") set(EXAMPLES_DEPENDENCIES "${sundials_install_examples_EXAMPLES_DEPENDENCIES}") - set(EXAMPLES_CMAKE_TARGETS "${sundials_install_examples_SUNDIALS_TARGETS}") set(EXTRA_INCLUDES "${sundials_install_examples_EXTRA_INCLUDES}") examples2string(${EXAMPLES_VAR} EXAMPLES) + set(target_list "") + foreach(target ${sundials_install_examples_SUNDIALS_TARGETS}) + list(APPEND target_list SUNDIALS::${target}) + endforeach() + foreach(target ${sundials_install_examples_OTHER_TARGETS}) + list(APPEND target_list ${target}) + endforeach() + list2string(target_list EXAMPLES_CMAKE_TARGETS) + # Regardless of the platform we're on, we will generate and install # CMakeLists.txt file for building the examples. This file can then # be used as a template for the user's own programs. @@ -125,4 +135,4 @@ macro(sundials_install_examples MODULE EXAMPLES_VAR) sundials_add_test_install(${MODULE} ${sundials_install_examples_TEST_INSTALL}) endif() -endmacro() \ No newline at end of file +endmacro() diff --git a/deps/AMICI/ThirdParty/sundials/cmake/tpl/FindHYPRE.cmake b/deps/AMICI/ThirdParty/sundials/cmake/tpl/FindHYPRE.cmake deleted file mode 100644 index 168b8da62..000000000 --- a/deps/AMICI/ThirdParty/sundials/cmake/tpl/FindHYPRE.cmake +++ /dev/null @@ -1,91 +0,0 @@ -# --------------------------------------------------------------- -# Programmer(s): Eddy Banks, Slaven Peles, Cody J. Balos, and -# Jean Sexton @ LLNL -# --------------------------------------------------------------- -# SUNDIALS Copyright Start -# Copyright (c) 2002-2021, Lawrence Livermore National Security -# and Southern Methodist University. -# All rights reserved. -# -# See the top-level LICENSE and NOTICE files for details. -# -# SPDX-License-Identifier: BSD-3-Clause -# SUNDIALS Copyright End -# --------------------------------------------------------------- -# HYPRE find module that creates an imported target for HYPRE. -# The target is SUNDIALS::HYPRE. -# -# The variable HYPRE_LIBRARY_DIR can be used to control -# where the module looks for the library. -# -# The variable HYPRE_INCLUDE_DIR can be used to set the -# include path for the library. -# -# This module also defines variables, but it is best to use -# the defined target to ensure includes and compile/link -# options are correctly passed to consumers. -# -# HYPRE_FOUND - system has HYPRE library -# HYPRE_LIBRARY - the HYPRE library -# HYPRE_INCLUDE_DIR - the HYPRE include path -# HYPRE_LIBRARIES - all of the libraries needed for HYPRE -# --------------------------------------------------------------- - -### Find include dir -find_path(temp_HYPRE_INCLUDE_DIR hypre.h ${HYPRE_INCLUDE_DIR}) -if (temp_HYPRE_INCLUDE_DIR) - set(HYPRE_INCLUDE_DIR ${temp_HYPRE_INCLUDE_DIR}) -endif() -unset(temp_HYPRE_INCLUDE_DIR CACHE) - -if (HYPRE_LIBRARY) - # We have (or were given) HYPRE_LIBRARY - get path to use for any related libs - get_filename_component(HYPRE_LIBRARY_DIR ${HYPRE_LIBRARY} PATH) - - # force CACHE update to show user DIR that will be used - set(HYPRE_LIBRARY_DIR ${HYPRE_LIBRARY_DIR} CACHE PATH "" FORCE) -else () - # find library with user provided directory path - set(HYPRE_LIBRARY_NAMES hypre HYPRE) - find_library(HYPRE_LIBRARY - NAMES ${HYPRE_LIBRARY_NAMES} - PATHS ${HYPRE_LIBRARY_DIR} NO_DEFAULT_PATH - ) -endif () -mark_as_advanced(HYPRE_LIBRARY) - -list(FIND HYPRE_LIBRARIES ${HYPRE_LIBRARY} _idx) -if (_idx EQUAL -1) - set(HYPRE_LIBRARIES "${HYPRE_LIBRARY};${HYPRE_LIBRARIES}" CACHE STRING "" FORCE) -endif () - -# set a more informative error message in case the library was not found -set(HYPRE_NOT_FOUND_MESSAGE "\ -************************************************************************\n\ -ERROR: Could not find HYPRE. Please check the variables:\n\ - HYPRE_INCLUDE_DIR and HYPRE_LIBRARY_DIR\n\ -************************************************************************") - -# set package variables including HYPRE_FOUND -find_package_handle_standard_args(HYPRE - REQUIRED_VARS - HYPRE_LIBRARY - HYPRE_LIBRARIES - HYPRE_INCLUDE_DIR - FAIL_MESSAGE - "${HYPRE_NOT_FOUND_MESSAGE}" - ) - -# Create target for HYPRE -if(HYPRE_FOUND) - - if(NOT TARGET SUNDIALS::HYPRE) - add_library(SUNDIALS::HYPRE UNKNOWN IMPORTED) - endif() - - set_target_properties(SUNDIALS::HYPRE PROPERTIES - INTERFACE_INCLUDE_DIRECTORIES "${HYPRE_INCLUDE_DIR}" - INTERFACE_LINK_LIBRARIES "${HYPRE_LIBRARIES}" - IMPORTED_LOCATION "${HYPRE_LIBRARY}") - -endif() diff --git a/deps/AMICI/ThirdParty/sundials/cmake/tpl/FindKLU.cmake b/deps/AMICI/ThirdParty/sundials/cmake/tpl/FindKLU.cmake index b4fba99c6..9e25883c4 100644 --- a/deps/AMICI/ThirdParty/sundials/cmake/tpl/FindKLU.cmake +++ b/deps/AMICI/ThirdParty/sundials/cmake/tpl/FindKLU.cmake @@ -35,7 +35,11 @@ if(WIN32) set(CMAKE_FIND_LIBRARY_PREFIXES lib ${CMAKE_FIND_LIBRARY_PREFIXES}) set(CMAKE_FIND_LIBRARY_SUFFIXES d.lib ${CMAKE_FIND_LIBRARY_SUFFIXES}) elseif(APPLE) - set(CMAKE_FIND_LIBRARY_SUFFIXES d.a ${CMAKE_FIND_LIBRARY_SUFFIXES}) + # AMICI + # set(CMAKE_FIND_LIBRARY_SUFFIXES d.a ${CMAKE_FIND_LIBRARY_SUFFIXES}) + set(CMAKE_FIND_LIBRARY_SUFFIXES .a d.a ${CMAKE_FIND_LIBRARY_SUFFIXES}) +else() + set(CMAKE_FIND_LIBRARY_SUFFIXES .a ${CMAKE_FIND_LIBRARY_SUFFIXES}) endif() ### Find include dir @@ -55,25 +59,33 @@ if (KLU_LIBRARY) else () # find library with user provided directory path set(KLU_LIBRARY_NAME klu) - find_library(KLU_LIBRARY ${KLU_LIBRARY_NAME} ${KLU_LIBRARY_DIR} NO_DEFAULT_PATH) + # AMICI + # find_library(KLU_LIBRARY ${KLU_LIBRARY_NAME} ${KLU_LIBRARY_DIR} NO_DEFAULT_PATH) + find_library(KLU_LIBRARY NAMES ${KLU_LIBRARY_NAME} klu_static.lib klu.lib PATHS ${KLU_LIBRARY_DIR} NO_DEFAULT_PATH) endif () mark_as_advanced(KLU_LIBRARY) if (NOT AMD_LIBRARY) set(AMD_LIBRARY_NAME amd) - find_library(AMD_LIBRARY ${AMD_LIBRARY_NAME} ${KLU_LIBRARY_DIR} NO_DEFAULT_PATH) + # AMICI + # find_library(AMD_LIBRARY ${AMD_LIBRARY_NAME} ${KLU_LIBRARY_DIR} NO_DEFAULT_PATH) + find_library(AMD_LIBRARY NAMES ${AMD_LIBRARY_NAME} amd_static.lib amd.lib PATHS ${KLU_LIBRARY_DIR} NO_DEFAULT_PATH) mark_as_advanced(AMD_LIBRARY) endif () if (NOT COLAMD_LIBRARY) set(COLAMD_LIBRARY_NAME colamd) - find_library(COLAMD_LIBRARY ${COLAMD_LIBRARY_NAME} ${KLU_LIBRARY_DIR} NO_DEFAULT_PATH) + # AMICI + # find_library(COLAMD_LIBRARY ${COLAMD_LIBRARY_NAME} ${KLU_LIBRARY_DIR} NO_DEFAULT_PATH) + find_library(COLAMD_LIBRARY NAMES ${COLAMD_LIBRARY_NAME} colamd_static.lib colamd.lib PATHS ${KLU_LIBRARY_DIR} NO_DEFAULT_PATH) mark_as_advanced(COLAMD_LIBRARY) endif () if (NOT BTF_LIBRARY) set(BTF_LIBRARY_NAME btf) - find_library( BTF_LIBRARY ${BTF_LIBRARY_NAME} ${KLU_LIBRARY_DIR} NO_DEFAULT_PATH) + # AMICI + # find_library( BTF_LIBRARY ${BTF_LIBRARY_NAME} ${KLU_LIBRARY_DIR} NO_DEFAULT_PATH) + find_library(BTF_LIBRARY NAMES ${BTF_LIBRARY_NAME} btf_static.lib btf.lib PATHS ${KLU_LIBRARY_DIR} NO_DEFAULT_PATH) mark_as_advanced(BTF_LIBRARY) endif () @@ -83,7 +95,9 @@ if (NOT SUITESPARSECONFIG_LIBRARY) if(WIN32 AND NOT MSYS) set(CMAKE_FIND_LIBRARY_PREFIXES "") endif() - find_library( SUITESPARSECONFIG_LIBRARY ${SUITESPARSECONFIG_LIBRARY_NAME} ${KLU_LIBRARY_DIR} NO_DEFAULT_PATH) + # AMICI + # find_library( SUITESPARSECONFIG_LIBRARY ${SUITESPARSECONFIG_LIBRARY_NAME} ${KLU_LIBRARY_DIR} NO_DEFAULT_PATH) + find_library(SUITESPARSECONFIG_LIBRARY NAMES suitesparseconfig_static.lib ${SUITESPARSECONFIG_LIBRARY_NAME} suitesparseconfig.lib PATHS ${KLU_LIBRARY_DIR} NO_DEFAULT_PATH) mark_as_advanced(SUITESPARSECONFIG_LIBRARY) endif () diff --git a/deps/AMICI/ThirdParty/sundials/cmake/tpl/FindMAGMA.cmake b/deps/AMICI/ThirdParty/sundials/cmake/tpl/FindMAGMA.cmake deleted file mode 100644 index c621b8820..000000000 --- a/deps/AMICI/ThirdParty/sundials/cmake/tpl/FindMAGMA.cmake +++ /dev/null @@ -1,111 +0,0 @@ -# ----------------------------------------------------------------------------- -# Programmer(s): Cody J. Balos @ LLNL -# ----------------------------------------------------------------------------- -# SUNDIALS Copyright Start -# Copyright (c) 2002-2021, Lawrence Livermore National Security -# and Southern Methodist University. -# All rights reserved. -# -# See the top-level LICENSE and NOTICE files for details. -# -# SPDX-License-Identifier: BSD-3-Clause -# SUNDIALS Copyright End -# ----------------------------------------------------------------------------- -# Find module that locates the MAGMA linear algebra library. -# ----------------------------------------------------------------------------- - -# find the MAGMA include path -find_path(MAGMA_INCLUDE_DIR magma_v2.h - NAMES magma_v2.h - HINTS ${MAGMA_DIR} $ENV{MAGMA_DIR} - PATH_SUFFIXES include - NO_DEFAULT_PATH - DOC "Directory with MAGMA header" -) - -# find the main MAGMA library -find_library(MAGMA_LIBRARY - NAMES magma - HINTS ${MAGMA_DIR} $ENV{MAGMA_DIR} - PATH_SUFFIXES lib lib64 - NO_DEFAULT_PATH - DOC "The MAGMA library.") - -# Find the optional sparse component -if("SPARSE" IN_LIST MAGMA_FIND_COMPONENTS) - set(_sparse_required MAGMA_SPARSE_LIBRARY) - find_library(MAGMA_SPARSE_LIBRARY - NAMES magma_sparse - HINTS ${MAGMA_DIR} $ENV{MAGMA_DIR} - PATH_SUFFIXES lib lib64 - NO_DEFAULT_PATH - DOC "The MAGMA sparse library.") -else() - set(_sparse_required ) -endif() - -# Determine MAGMA version and libraries it depends on -if(MAGMA_LIBRARY AND MAGMA_INCLUDE_DIR) - get_filename_component(libdir ${MAGMA_LIBRARY} DIRECTORY) - find_file(MAGMA_PKG_CONFIG_PATH magma.pc PATHS "${libdir}/pkgconfig") - - if(MAGMA_PKG_CONFIG_PATH) - file(STRINGS ${MAGMA_PKG_CONFIG_PATH} _version_string REGEX "Version: [0-9].[0-9].[0-9]") - string(REGEX MATCHALL "[0-9]" _version_full "${_version_string}") - - list(GET _version_full 0 _version_major) - list(GET _version_full 1 _version_minor) - list(GET _version_full 2 _version_patch) - - set(MAGMA_VERSION "${_version_major}.${_version_minor}.${_version_patch}") - - file(STRINGS ${MAGMA_PKG_CONFIG_PATH} _libraries_string REGEX "Libs:.*") - string(REPLACE " " ";" _libraries_list ${_libraries_string}) - list(SUBLIST _libraries_list 1 -1 _libraries_list) # remove 'Libs:' part - - set(_interface_libraires ) - foreach(lib ${_libraries_list}) - if(NOT (lib STREQUAL "-lmagma" OR lib STREQUAL "-lmagma_sparse" OR lib STREQUAL "-L\${libdir}" OR lib STREQUAL "") ) - string(REPLACE "-l" "" lib ${lib}) - list(APPEND _interface_libraires ${lib}) - endif() - endforeach() - endif() -endif() - -set(MAGMA_LIBRARIES "${MAGMA_LIBRARY};${_interface_libraires}") - -find_package_handle_standard_args(MAGMA - REQUIRED_VARS - MAGMA_LIBRARY - MAGMA_LIBRARIES - MAGMA_INCLUDE_DIR - ${_sparse_required} - VERSION_VAR - MAGMA_VERSION - ) - -# Create target for MAGMA -if(MAGMA_FOUND) - - if(NOT TARGET SUNDIALS::MAGMA) - add_library(SUNDIALS::MAGMA UNKNOWN IMPORTED) - endif() - - set_target_properties(SUNDIALS::MAGMA PROPERTIES - INTERFACE_INCLUDE_DIRECTORIES "${MAGMA_INCLUDE_DIR}" - INTERFACE_LINK_LIBRARIES "${_interface_libraires}" - IMPORTED_LOCATION "${MAGMA_LIBRARY}") - - if(MAGMA_SPARSE_LIBRARY) - if(NOT TARGET SUNDIALS::MAGMA_SPARSE) - add_library(SUNDIALS::MAGMA_SPARSE UNKNOWN IMPORTED) - endif() - - set_target_properties(SUNDIALS::MAGMA_SPARSE PROPERTIES - INTERFACE_INCLUDE_DIRECTORIES "${MAGMA_INCLUDE_DIR}" - INTERFACE_LINK_LIBRARIES "${MAGMA_LIBRARY};${_interface_libraires}" - IMPORTED_LOCATION "${MAGMA_SPARSE_LIBRARY}") - endif() - -endif() diff --git a/deps/AMICI/ThirdParty/sundials/cmake/tpl/FindPETSC.cmake b/deps/AMICI/ThirdParty/sundials/cmake/tpl/FindPETSC.cmake deleted file mode 100644 index 1ef09d53d..000000000 --- a/deps/AMICI/ThirdParty/sundials/cmake/tpl/FindPETSC.cmake +++ /dev/null @@ -1,777 +0,0 @@ -# ------------------------------------------------------------------------------ -# Programmer(s): Cody J. Balos and David J. Gardner @ LLNL -# ------------------------------------------------------------------------------ -# Based on the FindPETSC module by Jed Brown. -# ------------------------------------------------------------------------------ -# SUNDIALS Copyright Start -# Copyright (c) 2002-2021, Lawrence Livermore National Security -# and Southern Methodist University. -# All rights reserved. -# -# See the top-level LICENSE and NOTICE files for details. -# -# SPDX-License-Identifier: BSD-3-Clause -# SUNDIALS Copyright End -# ------------------------------------------------------------------------------ -# Copyright Jed Brown -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# * 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. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS -# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR -# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# ------------------------------------------------------------------------------ -# Try to find PETSC. This has three usage modes. -# -# The first usage mode is to find PETSC by introspection. -# This case is triggered when PETSC_DIR is not set by the user. -# Setting the variables below change the behavior of the search in this mode: -# PETSC_DIR - directory in which PETSC resides -# PETSC_ARCH - build architecture -# PETSC_CURRENT - (advanced) redo the find stage and executable tests -# PETSC_WORKS - (advanced) set to ON to ignore the output of the -# executable tests (not recommended) -# -# The second usage mode is to find PETSC based on the user-provided -# PETSC_DIR, and optionally PETSC_ARCH, variables. This case is triggered -# when just PETSC_DIR, and optionally PETSC_ARCH, are set by the user. -# Setting the variables below change the behavior of the search in this mode: -# PETSC_DIR - directory in which PETSC resides -# PETSC_ARCH - build architecture -# PETSC_CURRENT - (advanced) redo the find stage and executable tests -# PETSC_WORKS - (advanced) set to ON to ignore the output of the -# executable tests (not recommended) -# -# The third usage mode is to 'find' PETSC based on the user-provided list -# of include directories and libraries. This mode will only use the includes -# and libraries provided in the PETSC_INCLUDES and PETSC_LIBRARIES variable. -# This case is triggered when PETSC_INCLUDES, and PETSC_LIBRARIES are set. -# Setting the variables below change the behavior of the search in this mode: -# PETSC_LIBRARIES - (advanced) link these to use PETSC -# PETSC_INCLUDES - (advanced) the PETSC include directories -# PETSC_CURRENT - (advanced) redo the executable tests -# PETSC_WORKS - (advanced) set to ON to ignore the output of the -# executable tests (not recommended) -# -# Note that setting PETSC_LIBRARIES and PETSC_INCLUDES takes precedence over -# setting PETSC_DIR. -# -# Once done this will define the targets: -# -# SUNDIALS::PETSC_ALL - a CMake target for all of PETSc -# SUNDIALS::PETSC_SYS - a CMake target for the main PETSc library -# SUNDIALS::PETSC_VEC - a CMake target for the PETSc vector library -# SUNDIALS::PETSC_MAT - a CMake target for the PETSc matrix library -# SUNDIALS::PETSC_DM - a CMake target for the PETSc DM library -# SUNDIALS::PETSC_KSP - a CMake target for the PETSc KSP library -# SUNDIALS::PETSC_SNES - a CMake target for the PETSc SNES library -# SUNDIALS::PETSC_TS - a CMake target for the PETSc TS library -# -# It will also define the following, potentially useful, variables: -# -# PETSC_COMPILER - (advanced) Compiler used by PETSC, helpful to find a compatible MPI -# PETSC_DEFINITIONS - (advanced) Compiler switches for using PETSC -# PETSC_MPIEXEC - (advanced) Executable for running MPI programs -# PETSC_INDEX_SIZE - (internal) the size of indices in PETSC -# PETSC_PRECISION - (internal) the real type precision in PETSC -# PETSC_VERSION - (internal) Version string (MAJOR.MINOR.SUBMINOR) -# -# Usage: -# find_package(PETSC COMPONENTS CXX) - required if build --with-clanguage=C++ --with-c-support=0 -# find_package(PETSC COMPONENTS C) - standard behavior of checking build using a C compiler -# find_package(PETSC) - same as above -# -# Redistribution and use is allowed according to the terms of the BSD license. -# ------------------------------------------------------------------------------ - -# ------------------------------------------------------------------------------ -# helper macros and functions -# ------------------------------------------------------------------------------ - -function (PETSC_GET_VERSION) - if (EXISTS "${PETSC_INCLUDE_DIR}/petscversion.h") - file (STRINGS "${PETSC_INCLUDE_DIR}/petscversion.h" vstrings REGEX "#define PETSC_VERSION_(RELEASE|MAJOR|MINOR|SUBMINOR|PATCH) ") - foreach (line ${vstrings}) - string (REGEX REPLACE " +" ";" fields ${line}) # break line into three fields (the first is always "#define") - list (GET fields 1 var) - list (GET fields 2 val) - set (${var} ${val} PARENT_SCOPE) - set (${var} ${val}) # Also in local scope so we have access below - endforeach () - if (PETSC_VERSION_RELEASE) - if ($(PETSC_VERSION_PATCH) GREATER 0) - set (PETSC_VERSION "${PETSC_VERSION_MAJOR}.${PETSC_VERSION_MINOR}.${PETSC_VERSION_SUBMINOR}p${PETSC_VERSION_PATCH}" CACHE INTERNAL "PETSC version" FORCE) - else () - set (PETSC_VERSION "${PETSC_VERSION_MAJOR}.${PETSC_VERSION_MINOR}.${PETSC_VERSION_SUBMINOR}" CACHE INTERNAL "PETSC version" FORCE) - endif () - else () - # make dev version compare higher than any patch level of a released version - set (PETSC_VERSION "${PETSC_VERSION_MAJOR}.${PETSC_VERSION_MINOR}.${PETSC_VERSION_SUBMINOR}.99" CACHE INTERNAL "PETSC version" FORCE) - endif () - else () - message (SEND_ERROR "${PETSC_INCLUDE_DIR}/petscversion.h does not exist") - endif () -endfunction () - -macro (PETSC_GET_VARIABLE name var) - if (NOT DEFINED MAKE_EXECUTABLE) - # need to find the make executable the first time this macro is used - find_program (MAKE_EXECUTABLE NAMES make gmake) - if (MAKE_EXECUTABLE MATCHES "NOTFOUND") - message(SEND_ERROR "MAKE_EXECUTABLE could not be found (looked for `make` and `gmake`)") - endif () - endif () - set (${var} "NOTFOUND" CACHE INTERNAL "Cleared" FORCE) - execute_process (COMMAND ${MAKE_EXECUTABLE} --no-print-directory -f ${petsc_config_makefile} show VARIABLE=${name} - OUTPUT_VARIABLE ${var} - RESULT_VARIABLE petsc_return) -endmacro (PETSC_GET_VARIABLE) - -macro (PETSC_TEST_RUNS includes libraries runs) - if (PETSC_VERSION VERSION_GREATER 3.1) - set (_PETSC_TSDestroy "TSDestroy(&ts)") - else () - set (_PETSC_TSDestroy "TSDestroy(ts)") - endif () - - set (_PETSC_TEST_SOURCE " -static const char help[] = \"PETSC test program.\"; -#include -int main(int argc,char *argv[]) { - PetscErrorCode ierr; - TS ts; - - ierr = PetscInitialize(&argc,&argv,0,help);CHKERRQ(ierr); - ierr = TSCreate(PETSC_COMM_WORLD,&ts);CHKERRQ(ierr); - ierr = TSSetFromOptions(ts);CHKERRQ(ierr); - ierr = ${_PETSC_TSDestroy};CHKERRQ(ierr); - ierr = PetscFinalize();CHKERRQ(ierr); - return 0; -} -") - - multipass_source_runs ("${includes}" "${libraries}" "${_PETSC_TEST_SOURCE}" ${runs} "${PETSC_LANGUAGE_BINDINGS}") - - if (${${runs}}) - set (PETSC_EXECUTABLE_RUNS "YES" CACHE INTERNAL - "The system can successfully run a PETSC executable" FORCE) - else() - set (PETSC_EXECUTABLE_RUNS "NO" CACHE INTERNAL - "The system can NOT successfully run a PETSC executable" FORCE) - endif () -endmacro (PETSC_TEST_RUNS) - -macro (PETSC_FIND_LIBRARY suffix name) - # Clear any stale value, if we got here, we need to find it again - set (PETSC_LIBRARY_${suffix} "NOTFOUND" CACHE INTERNAL "Cleared" FORCE) - - if (WIN32) - set (libname lib${name}) # windows expects "libfoo", linux expects "foo" - else (WIN32) - set (libname ${name}) - endif (WIN32) - - find_library (PETSC_LIBRARY_${suffix} NAMES ${libname} HINTS ${petsc_lib_dir} NO_DEFAULT_PATH) - set (PETSC_LIBRARIES_${suffix} "${PETSC_LIBRARY_${suffix}}" CACHE INTERNAL "PETSC ${suffix} libraries" FORCE) - mark_as_advanced(PETSC_LIBRARY_${suffix}) -endmacro (PETSC_FIND_LIBRARY suffix name) - -macro (PETSC_FIND_LIBRARY_IN_LIST suffix names liblist) - # Clear any stale value, if we got here, we need to find it again - set (PETSC_LIBRARY_${suffix} "NOTFOUND" CACHE INTERNAL "Cleared" FORCE) - - foreach (name ${names}) - if (WIN32) - set (libname lib${name}) # windows expects "libfoo", linux expects "foo" - else (WIN32) - set (libname ${name}) - endif (WIN32) - foreach (lib ${${liblist}}) - if ("${lib}" MATCHES "${libname}[.].*") - set (PETSC_LIBRARY_${suffix} ${lib} CACHE INTERNAL "" FORCE) - list (REMOVE_ITEM ${liblist} ${lib}) - break () - endif () - endforeach () - endforeach () - set (PETSC_LIBRARIES_${suffix} "${PETSC_LIBRARY_${suffix}}" CACHE INTERNAL "PETSC ${suffix} libraries" FORCE) - mark_as_advanced(PETSC_LIBRARY_${suffix}) - -endmacro (PETSC_FIND_LIBRARY_IN_LIST suffix names liblist) - -macro (PETSC_JOIN libs deps) - list (APPEND PETSC_LIBRARIES_${libs} ${PETSC_LIBRARIES_${deps}}) - # since list APPEND creates a new local variable in the current scope we need - # to set the cache variable value to propagate the changes upwards - set (PETSC_LIBRARIES_${libs} ${PETSC_LIBRARIES_${libs}} CACHE INTERNAL "PETSC ${libs} libraries" FORCE) -endmacro (PETSC_JOIN libs deps) - -# ------------------------------------------------------------------------------ -# FindPETSC -# ------------------------------------------------------------------------------ - -set (PETSC_VALID_COMPONENTS C CXX) - -if (NOT PETSC_FIND_COMPONENTS) - - get_property (_enabled_langs GLOBAL PROPERTY ENABLED_LANGUAGES) - list(FIND _enabled_langs "C" _c_index) - if (${_c_index} GREATER -1) - set (PETSC_LANGUAGE_BINDINGS "C") - else () - set (PETSC_LANGUAGE_BINDINGS "CXX") - endif () - -else() - - # Right now, this is designed for compatability with the --with-clanguage option, so - # only allow one item in the components list. - list(LENGTH ${PETSC_FIND_COMPONENTS} components_length) - if(${components_length} GREATER 1) - message(FATAL_ERROR "Only one component for PETSC is allowed to be specified") - endif() - # This is a stub for allowing multiple components should that time ever come. Perhaps - # to also test Fortran bindings? - foreach(component ${PETSC_FIND_COMPONENTS}) - list(FIND PETSC_VALID_COMPONENTS ${component} component_location) - if(${component_location} EQUAL -1) - message(FATAL_ERROR "\"${component}\" is not a valid PETSC component.") - else() - list(APPEND PETSC_LANGUAGE_BINDINGS ${component}) - endif() - endforeach() - -endif() - -# Set which state variables to check to determine if the PETSC configuration is -# current and clear the other state variables -if (PETSC_INCLUDES OR PETSC_LIBRARIES) - - if (PETSC_INCLUDES AND PETSC_LIBRARIES) - - set (PETSC_STATES "LIBRARIES;INCLUDES" CACHE INTERNAL "" FORCE) - set (PETSC_DIR "" CACHE PATH "Path to the root of a PETSc installation" FORCE) - set (PETSC_ARCH "" CACHE STRING "PETSc architecture" FORCE) - - else () - - string (CONCAT msg - "Both PETSC_INCLUDES and PETSC_LIBRARIES must be provided:\n" - " PETSC_INCLUDES=${PETSC_INCLUDES}\n" - " PETSC_LIBRARIES=${PETSC_LIBRARIES}") - message (FATAL_ERROR ${msg}) - - endif () - -else () - - set (PETSC_STATES "DIR;ARCH" CACHE INTERNAL "" FORCE) - set (PETSC_INCLUDES "" CACHE STRING "Semi-colon separated list of PETSc include directories" FORCE) - set (PETSC_LIBRARIES "" CACHE STRING "Semi-colon separated list of PETSc link libraries" FORCE) - -endif () - -# Keep track of FindPETSC state so that we do not do the complete -# set of tests and variable lookups every time cmake is run. -include (FindPackageMultipass) -set (petsc_slaves LIBRARIES_SYS LIBRARIES_VEC LIBRARIES_MAT LIBRARIES_DM LIBRARIES_KSP LIBRARIES_SNES LIBRARIES_TS) -set (petsc_deps LIBRARY_DIR INCLUDE_DIR LIBRARIES_ INCLUDES_ COMPILER MPIEXEC EXECUTABLE_RUNS ${petsc_slaves}) -find_package_multipass (PETSC petsc_config_current STATES ${PETSC_STATES} DEPENDENTS ${petsc_deps}) - -# This runs anytime the current configuration is not current. -# This happens either when a user sets PETSC_CURRENT=FALSE, -# or when one of the dependents given to find_package_multipass changes. -if (NOT petsc_config_current) - - if (PETSC_INCLUDES AND PETSC_LIBRARIES) - - message (STATUS "Finding PETSC using PETSC_INCLUDES and PETSC_LIBRARIES") - - # extract path from PETSC_INCLUDES - foreach (_include_dir ${PETSC_INCLUDES}) - if (EXISTS "${_include_dir}/petsc.h") - set (PETSC_INCLUDE_DIR "${_include_dir}" CACHE INTERNAL "Internal PETSc include directory" FORCE) - break () - endif () - endforeach () - - # check if the include directory was found - if (NOT PETSC_INCLUDE_DIR) - string (CONCAT msg - "Could not determine PETSc include directory from PETSC_INCLUDES:\n" - " PETSC_INCLUDES=${PETSC_INCLUDES}\n") - message (FATAL_ERROR ${msg}) - endif() - - # extract path from PETSC_LIBRARIES - foreach (_library_path ${PETSC_LIBRARIES}) - get_filename_component (_library_name "${_library_path}" NAME) - if (_library_name MATCHES "petsc") - get_filename_component (_library_dir "${_library_path}" DIRECTORY) - set (PETSC_LIBRARY_DIR "${_library_dir}" CACHE INTERNAL "Internal PETSc library directory" FORCE) - break () - endif () - endforeach () - - # check if the library directory was found - if (NOT PETSC_LIBRARY_DIR) - string (CONCAT msg - "Could not DETERMINE PETSc library directory from PETSC_LIBRARIES:\n" - " PETSC_LIBRARIES=${PETSC_LIBRARIES}") - message (FATAL_ERROR ${msg}) - endif() - - # set internal PETSC_DIR and PETSC_ARCH variables - set (PETSC_DIR_ "${PETSC_LIBRARY_DIR}/.." CACHE INTERNAL "Internal PETSC_DIR" FORCE) - set (PETSC_ARCH_ "" CACHE INTERNAL "Internal PETSC_ARCH" FORCE) - - else() - - message (STATUS "Finding PETSC using PETSC_DIR") - - # find PETSC_DIR - if (NOT PETSC_DIR) - - message (STATUS "Looking for PETSc in common install locations") - - # Debian uses versioned paths e.g /usr/lib/petscdir/3.5/ - file (GLOB DEB_PATHS "/usr/lib/petscdir/*") - - find_path (PETSC_DIR include/petsc.h - HINTS ENV PETSC_DIR - PATHS - /usr/lib/petsc - # Debian paths - ${DEB_PATHS} - # Arch Linux path - /opt/petsc/linux-c-opt - # MacPorts path - /opt/local/lib/petsc - $ENV{HOME}/petsc - DOC "PETSC Directory") - - # check if PETSC_DIR was set/found - if (NOT PETSC_DIR) - - string (CONCAT msg - "Could not locate PETSc install directory please set:\n" - " - PETSC_DIR and (optionally) PETSC_ARCH\n" - "or used the advanced options\n" - " - PETSC_INCLUDES and PETSC_LIBRARIES.") - message (FATAL_ERROR ${msg}) - - endif () - - endif() - - # find PETSC_ARCH - if (NOT PETSC_ARCH) - - set (_petsc_arches - $ENV{PETSC_ARCH} # If set, use environment variable first - linux-gnu-c-debug linux-gnu-c-opt # Debian defaults - x86_64-unknown-linux-gnu i386-unknown-linux-gnu) - set (PETSCCONF "NOTFOUND" CACHE INTERNAL "Cleared" FORCE) - foreach (arch ${_petsc_arches}) - find_path (PETSCCONF petscconf.h - HINTS ${PETSC_DIR} - PATH_SUFFIXES ${arch}/include bmake/${arch} - NO_DEFAULT_PATH) - if (PETSCCONF) - set (PETSC_ARCH "${arch}" CACHE STRING "PETSC build architecture" FORCE) - break () - endif () - endforeach () - set (PETSCCONF "NOTFOUND" CACHE INTERNAL "Scratch variable" FORCE) - - endif () - - if (PETSC_ARCH) - set (PETSC_INCLUDE_DIR "${PETSC_DIR}/${PETSC_ARCH}/include" CACHE INTERNAL "Internal PETSc include directory" FORCE) - set (PETSC_LIBRARY_DIR "${PETSC_DIR}/${PETSC_ARCH}/lib" CACHE INTERNAL "Internal PETSc library directory" FORCE) - else () - set (PETSC_INCLUDE_DIR "${PETSC_DIR}/include" CACHE INTERNAL "Internal PETSc include directory" FORCE) - set (PETSC_LIBRARY_DIR "${PETSC_DIR}/lib" CACHE INTERNAL "Internal PETSc library directory" FORCE) - endif () - - # set internal PETSC_DIR and PETSC_ARCH variables - set (PETSC_DIR_ "${PETSC_DIR}" CACHE INTERNAL "Internal PETS_DIR" FORCE) - set (PETSC_ARCH_ "${PETSC_ARCH}" CACHE INTERNAL "Internal PETS_ARCH" FORCE) - - endif () - - # Resolve the conf/rules and conf/variables files. - # The location of these files has changed with different PETSc versions, - # so look in a few different locations for them. - if (EXISTS "${PETSC_LIBRARY_DIR}/petsc/conf/petscvariables") # > 3.5 - set (petsc_conf_rules "${PETSC_LIBRARY_DIR}/petsc/conf/rules") - set (petsc_conf_variables "${PETSC_LIBRARY_DIR}/petsc/conf/variables") - elseif (EXISTS "${PETSC_INCLUDE_DIR}/petscconf.h") # > 2.3.3 - set (petsc_conf_rules "${PETSC_DIR_}/conf/rules") - set (petsc_conf_variables "${PETSC_DIR_}/conf/variables") - elseif (EXISTS "${PETSC_DIR_}/bmake/${PETSC_ARCH_}/petscconf.h") # <= 2.3.3 - set (petsc_conf_rules "${PETSC_DIR_}/bmake/common/rules") - set (petsc_conf_variables "${PETSC_DIR_}/bmake/common/variables") - elseif (PETSC_LIBRARIES AND PETSC_INCLUDES) - message (FATAL_ERROR "PETSC_LIBRARIES=${PETSC_LIBRARIES} and PETSC_INCLUDES=${PETSC_INCLUDES} do not specify a valid PETSC installation") - else () - message (FATAL_ERROR "PETSC_DIR=${PETSC_DIR} and PETSC_ARCH=${PETSC_ARCH} do not specify a valid PETSC installation") - endif () - - # ---------------------------------------------------------------------------- - # Probe the PETSc installation for information about how it was configured. - # ---------------------------------------------------------------------------- - - # Get the PETSc version - petsc_get_version() - - # Put variables into environment since they are needed to get - # configuration (petscvariables) in the PETSC makefile - set (ENV{PETSC_DIR} "${PETSC_DIR_}") - set (ENV{PETSC_ARCH} "${PETSC_ARCH_}") - - # A temporary makefile to probe the PETSC configuration - set (petsc_config_makefile "${PROJECT_BINARY_DIR}/Makefile.petsc") - file (WRITE "${petsc_config_makefile}" -"## This file was autogenerated by FindPETSC.cmake -# PETSC_DIR = ${PETSC_DIR_} -# PETSC_ARCH = ${PETSC_ARCH_} -include ${petsc_conf_rules} -include ${petsc_conf_variables} -show : -\t-@echo -n \${\${VARIABLE}} -") - - # Extract information about the PETSC configuration - petsc_get_variable (PETSC_LIB_DIR petsc_lib_dir) - petsc_get_variable (PETSC_EXTERNAL_LIB_BASIC petsc_libs_external) - petsc_get_variable (PETSC_CCPPFLAGS petsc_cpp_line) - petsc_get_variable (PETSC_INCLUDE petsc_include) - petsc_get_variable (PCC petsc_cc) - petsc_get_variable (PCC_FLAGS petsc_cc_flags) - petsc_get_variable (MPIEXEC petsc_mpiexec) - petsc_get_variable (PETSC_INDEX_SIZE petsc_index_size) - petsc_get_variable (PETSC_PRECISION petsc_precision) - - # We are done with the temporary Makefile, calling PETSC_GET_VARIABLE after this point is invalid! - file (REMOVE ${petsc_config_makefile}) - - # ---------------------------------------------------------------------------- - # Determine what libraries and includes are needed. - # ---------------------------------------------------------------------------- - - if (PETSC_INCLUDES AND PETSC_LIBRARIES) - - # If the user manually set PETSC_INCUDES and PETSC_LIBRARIES, we work off of - # what they provided. - - # Make a copy of the user-provided library list to modify as libraries are - # found and extracted - set (PETSC_LIBRARIES_REMAINING ${PETSC_LIBRARIES}) - - # Look for petscvec first, if it doesn't exist, we must be using single-library - petsc_find_library_in_list (VEC petscvec PETSC_LIBRARIES_REMAINING) - - if (PETSC_LIBRARY_VEC) - - # libpetscsys is called libpetsc prior to 3.1 (when single-library was introduced) - petsc_find_library_in_list (SYS "petscsys;petsc" PETSC_LIBRARIES_REMAINING) - petsc_find_library_in_list (MAT petscmat PETSC_LIBRARIES_REMAINING) - petsc_find_library_in_list (DM petscdm PETSC_LIBRARIES_REMAINING) - petsc_find_library_in_list (KSP petscksp PETSC_LIBRARIES_REMAINING) - petsc_find_library_in_list (SNES petscsnes PETSC_LIBRARIES_REMAINING) - petsc_find_library_in_list (TS petscts PETSC_LIBRARIES_REMAINING) - petsc_join (SYS REMAINING) - petsc_join (VEC SYS) - petsc_join (MAT VEC) - petsc_join (DM MAT) - petsc_join (KSP DM) - petsc_join (SNES KSP) - petsc_join (TS SNES) - - set (PETSC_LIBRARY_ALL ${PETSC_LIBRARY_TS} CACHE INTERNAL "All PETSC libraries" FORCE) - set (PETSC_LIBRARIES_ALL ${PETSC_LIBRARIES_TS} CACHE INTERNAL "All PETSC libraries" FORCE) - - message (STATUS "Recognized PETSC install with separate libraries for each package") - - else () - - # There is no libpetscvec - set (PETSC_LIBRARY_VEC "NOTFOUND" CACHE INTERNAL "Cleared" FORCE) - - petsc_find_library_in_list (SINGLE petsc PETSC_LIBRARIES_REMAINING) - # Debian 9/Ubuntu 16.04 uses _real and _complex extensions when using libraries in /usr/lib/petsc. - if (NOT PETSC_LIBRARY_SINGLE) - petsc_find_library_in_list (SINGLE petsc_real PETSC_LIBRARIES_REMAINING) - endif() - if (NOT PETSC_LIBRARY_SINGLE) - petsc_find_library_in_list (SINGLE petsc_complex PETSC_LIBRARIES_REMAINING) - endif() - - foreach (pkg SYS VEC MAT DM KSP SNES TS ALL) - set (PETSC_LIBRARIES_${pkg} "${PETSC_LIBRARY_SINGLE}" CACHE INTERNAL "PETSC ${pkg} libraries" FORCE) - endforeach () - - message (STATUS "Recognized PETSC install with single library for all packages") - - endif () - - # At this point PETSC_LIBRARIES_REMAINING should only contain external - # libraries needed by PETSc. These may (e.g., static build) or may not - # (e.g., shared build) be needed to compile but are added to the package - # libraries regardless. - foreach (pkg SYS VEC MAT DM KSP SNES TS ALL) - list (APPEND PETSC_LIBRARIES_${pkg} ${PETSC_LIBRARIES_REMAINING}) - # since list APPEND creates a new local variable in the current scope we need - # to set the cache variable value to propagate the changes upwards - set (PETSC_LIBRARIES_${pkg} ${PETSC_LIBRARIES_${pkg}} CACHE INTERNAL "PETSC ${pkg} libraries" FORCE) - endforeach () - - # Try to run a simple executable - petsc_test_runs ("${PETSC_INCLUDES}" "${PETSC_LIBRARIES_TS}" petsc_works_userprovided) - if (petsc_works_userprovided) - message (STATUS "PETSC works with the includes and libraries given.") - else () - message (STATUS "PETSC could not be used, maybe the install is broken.") - endif () - - # set include and library variables needed to create targets below - set (petsc_includes_needed ${PETSC_INCLUDES}) - set (petsc_libraries_needed ${PETSC_LIBRARIES}) - - else () - - include (ResolveCompilerPaths) - # Extract include paths and libraries from compile command line - resolve_includes (petsc_includes_all "${petsc_cpp_line}") - - # On windows we need to make sure we're linking against the right - # runtime library - if (WIN32) - if (petsc_cc_flags MATCHES "-MT") - - set (using_md False) - foreach(flag_var - CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE - CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO - CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE - CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) - if(${flag_var} MATCHES "/MD") - set (using_md True) - endif(${flag_var} MATCHES "/MD") - endforeach(flag_var) - if(${using_md} MATCHES "True") - string(CONCAT msg "PETSC was built with /MT, but /MD is currently set.\n" - "See http://www.cmake.org/Wiki/CMake_FAQ#How_can_I_build_my_MSVC_application_with_a_static_runtime.3F") - message(WARNING ${msg}) - endif(${using_md} MATCHES "True") - - endif (petsc_cc_flags MATCHES "-MT") - endif (WIN32) - - include (CorrectWindowsPaths) - convert_cygwin_path(petsc_lib_dir) - - # Look for petscvec first, if it doesn't exist, we must be using single-library - petsc_find_library (VEC petscvec) - if (PETSC_LIBRARY_VEC) - - petsc_find_library (SYS "petscsys;petsc") # libpetscsys is called libpetsc prior to 3.1 (when single-library was introduced) - petsc_find_library (MAT petscmat) - petsc_find_library (DM petscdm) - petsc_find_library (KSP petscksp) - petsc_find_library (SNES petscsnes) - petsc_find_library (TS petscts) - petsc_join (VEC SYS) - petsc_join (MAT VEC) - petsc_join (DM MAT) - petsc_join (KSP DM) - petsc_join (SNES KSP) - petsc_join (TS SNES) - - set (PETSC_LIBRARY_ALL ${PETSC_LIBRARY_TS} CACHE INTERNAL "All PETSC libraries" FORCE) - set (PETSC_LIBRARIES_ALL ${PETSC_LIBRARIES_TS} CACHE INTERNAL "All PETSC libraries" FORCE) - - message (STATUS "Recognized PETSC install with separate libraries for each package") - - else () - - # There is no libpetscvec - set (PETSC_LIBRARY_VEC "NOTFOUND" CACHE INTERNAL "Cleared" FORCE) - - petsc_find_library (SINGLE petsc) - # Debian 9/Ubuntu 16.04 uses _real and _complex extensions when using libraries in /usr/lib/petsc. - if (NOT PETSC_LIBRARY_SINGLE) - petsc_find_library (SINGLE petsc_real) - endif() - if (NOT PETSC_LIBRARY_SINGLE) - petsc_find_library (SINGLE petsc_complex) - endif() - - foreach (pkg SYS VEC MAT DM KSP SNES TS ALL) - set (PETSC_LIBRARIES_${pkg} "${PETSC_LIBRARY_SINGLE}" CACHE INTERNAL "PETSC ${pkg} libraries" FORCE) - endforeach () - - message (STATUS "Recognized PETSC install with single library for all packages") - - endif () - - # determine the include and library variables needed to create targets below - - find_path (PETSC_INCLUDE_CONF petscconf.h HINTS "${PETSC_INCLUDE_DIR}" "${PETSC_DIR_}/bmake/${PETSC_ARCH_}" NO_DEFAULT_PATH) - mark_as_advanced (PETSC_INCLUDE_CONF) - - set (petsc_includes_minimal ${PETSC_INCLUDE_CONF} ${PETSC_INCLUDE_DIR}) - - petsc_test_runs ("${petsc_includes_minimal}" "${PETSC_LIBRARIES_TS}" petsc_works_minimal) - if (petsc_works_minimal) - - message (STATUS "Minimal PETSC includes and libraries work. This probably means we are building with shared libs.") - set (petsc_includes_needed "${petsc_includes_minimal}") - - else (petsc_works_minimal) # Minimal includes fail, see if just adding full includes fixes it - - petsc_test_runs ("${petsc_includes_all}" "${PETSC_LIBRARIES_TS}" petsc_works_allincludes) - if (petsc_works_allincludes) # It does, we just need all the includes - - string (CONCAT msg "PETSC requires extra include paths, but links correctly with only interface libraries.\n" - "This is an unexpected configuration (but it seems to work fine).") - message (STATUS ${msg}) - set (petsc_includes_needed ${petsc_includes_all}) - - else (petsc_works_allincludes) # We are going to need to link the external libs explicitly - - resolve_libraries (petsc_libraries_external "${petsc_libs_external}") - foreach (pkg SYS VEC MAT DM KSP SNES TS ALL) - list (APPEND PETSC_LIBRARIES_${pkg} ${petsc_libraries_external}) - # since list APPEND creates a new local variable in the current scope we need - # to set the cache variable value to propagate the changes upwards - set (PETSC_LIBRARIES_${pkg} ${PETSC_LIBRARIES_${pkg}} CACHE INTERNAL "PETSC ${pkg} libraries" FORCE) - endforeach (pkg) - - petsc_test_runs ("${petsc_includes_minimal}" "${PETSC_LIBRARIES_TS}" petsc_works_alllibraries) - if (petsc_works_alllibraries) - - string (CONCAT msg "PETSC only need minimal includes, but requires explicit linking to all dependencies.\n" - "This is expected when PETSC is built with static libraries.") - message(STATUS ${msg}) - set (petsc_includes_needed ${petsc_includes_minimal}) - - else (petsc_works_alllibraries) - - # It looks like we really need everything, should have listened to Matt - set (petsc_includes_needed ${petsc_includes_all}) - petsc_test_runs ("${petsc_includes_all}" "${PETSC_LIBRARIES_TS}" petsc_works_all) - if (petsc_works_all) # We fail anyways - string (CONCAT msg "PETSC requires extra include paths and explicit linking to all dependencies.\n" - "This probably means you have static libraries and something unexpected in PETSC headers.") - message (STATUS ${msg}) - else (petsc_works_all) # We fail anyways - message (STATUS "PETSC could not be used, maybe the install is broken.") - endif (petsc_works_all) - - endif (petsc_works_alllibraries) - - endif (petsc_works_allincludes) - - endif (petsc_works_minimal) - - set (petsc_libraries_needed ${PETSC_LIBRARIES_ALL}) - - endif () - - # ---------------------------------------------------------------------------- - # Now we set all of the variables needed to build targets. - # ---------------------------------------------------------------------------- - - # If PETSC_WORKS is set override the executable test results. This variable - # can be manually set to ON to force CMake to accept a given PETSC - # configuration, but this will almost always result in a broken build. - if (PETSC_WORKS) - message (STATUS "Overwriting PETSc test results with PETSC_WORKS = ${PETSC_WORKS}") - set (PETSC_EXECUTABLE_RUNS ${PETSC_WORKS} CACHE INTERNAL "Overwritten by PETSC_WORKS" FORCE) - endif () - - # We do an out-of-source build so __FILE__ will be an absolute path, hence __INSDIR__ is superfluous - if (${PETSC_VERSION} VERSION_LESS 3.1) - set (PETSC_DEFINITIONS "-D__SDIR__=\"\"" CACHE STRING "PETSC definitions" FORCE) - else () - set (PETSC_DEFINITIONS "-D__INSDIR__=\"\"" CACHE STRING "PETSC definitions" FORCE) - endif () - - # Sometimes this can be used to assist FindMPI.cmake - set (PETSC_COMPILER ${petsc_cc} CACHE FILEPATH "PETSC compiler" FORCE) - set (PETSC_MPIEXEC ${petsc_mpiexec} CACHE FILEPATH "Executable for running PETSC MPI programs" FORCE) - - # Internal variables needed for configuring targets - set (PETSC_INDEX_SIZE ${petsc_index_size} CACHE INTERNAL "PETSC index size" FORCE) - set (PETSC_PRECISION ${petsc_precision} CACHE INTERNAL "PETSC real type precision" FORCE) - set (PETSC_INCLUDES_ ${petsc_includes_needed} CACHE INTERNAL "PETSC include paths to be used" FORCE) - set (PETSC_LIBRARIES_ ${petsc_libraries_needed} CACHE INTERNAL "PETSC libraries to be used" FORCE) - - # Note that we have forced values for all these choices. If you - # change these, you are telling the system to trust you that they - # work. It is likely that you will end up with a broken build. - mark_as_advanced (PETSC_CURRENT PETSC_COMPILER PETSC_DEFINITIONS PETSC_MPIEXEC PETSC_EXECUTABLE_RUNS) - -endif () - -include (FindPackageHandleStandardArgs) -find_package_handle_standard_args (PETSC - REQUIRED_VARS PETSC_EXECUTABLE_RUNS - VERSION_VAR PETSC_VERSION - FAIL_MESSAGE "PETSC could not be found.") - -# Create targets -if (PETSC_FOUND) - if (PETSC_LIBRARY_SINGLE) - foreach (suffix SYS VEC MAT DM KSP SNES TS ALL) - if (NOT TARGET SUNDIALS::PETSC_${suffix}) - add_library (SUNDIALS::PETSC_${suffix} UNKNOWN IMPORTED) - # add properties one-by-one for easier debugging - set_target_properties (SUNDIALS::PETSC_${suffix} PROPERTIES - INTERFACE_INCLUDE_DIRECTORIES "${PETSC_INCLUDES_}") - set_target_properties (SUNDIALS::PETSC_${suffix} PROPERTIES - INTERFACE_LINK_LIBRARIES "${PETSC_LIBRARIES_}") - set_target_properties (SUNDIALS::PETSC_${suffix} PROPERTIES - INTERFACE_COMPILE_OPTIONS ${PETSC_DEFINITIONS}) - set_target_properties (SUNDIALS::PETSC_${suffix} PROPERTIES - IMPORTED_LOCATION ${PETSC_LIBRARY_SINGLE}) - endif () - endforeach () - else () - foreach (suffix SYS VEC MAT DM KSP SNES TS ALL) - if (PETSC_LIBRARY_${suffix} AND (NOT TARGET SUNDIALS::PETSC_${suffix})) - add_library (SUNDIALS::PETSC_${suffix} UNKNOWN IMPORTED) - # add properties one-by-one for easier debugging - set_target_properties (SUNDIALS::PETSC_${suffix} PROPERTIES - INTERFACE_INCLUDE_DIRECTORIES "${PETSC_INCLUDES_}") - set_target_properties (SUNDIALS::PETSC_${suffix} PROPERTIES - INTERFACE_LINK_LIBRARIES "${PETSC_LIBRARIES_${suffix}}") - set_target_properties (SUNDIALS::PETSC_${suffix} PROPERTIES - INTERFACE_COMPILE_OPTIONS ${PETSC_DEFINITIONS}) - set_target_properties (SUNDIALS::PETSC_${suffix} PROPERTIES - IMPORTED_LOCATION ${PETSC_LIBRARY_${suffix}}) - endif () - endforeach () - endif () -endif (PETSC_FOUND) diff --git a/deps/AMICI/ThirdParty/sundials/cmake/tpl/FindTrilinos.cmake b/deps/AMICI/ThirdParty/sundials/cmake/tpl/FindTrilinos.cmake deleted file mode 100644 index eb78f834e..000000000 --- a/deps/AMICI/ThirdParty/sundials/cmake/tpl/FindTrilinos.cmake +++ /dev/null @@ -1,45 +0,0 @@ -# ----------------------------------------------------------------------------- -# Programmer(s): Slaven Peles and Cody J. Balos @ LLNL -# ----------------------------------------------------------------------------- -# SUNDIALS Copyright Start -# Copyright (c) 2002-2021, Lawrence Livermore National Security -# and Southern Methodist University. -# All rights reserved. -# -# See the top-level LICENSE and NOTICE files for details. -# -# SPDX-License-Identifier: BSD-3-Clause -# SUNDIALS Copyright End -# ----------------------------------------------------------------------------- -# Find module for Trilinos that uses the TrilinosConfig.cmake that is installed -# with Trilinos. The module will also create a SUNDIALS::TRILINOS target. -# ----------------------------------------------------------------------------- - -# First try and find Trilinos using Trilinos_DIR only. -find_package(Trilinos - NAMES Trilinos TRILINOS - PATHS - ${Trilinos_DIR}/lib/cmake/Trilinos - ${Trilinos_DIR} - NO_DEFAULT_PATH - QUIET) - -# set package variables including Trilinos_FOUND -find_package_handle_standard_args(Trilinos - REQUIRED_VARS - Trilinos_LIBRARIES # defined in TrilinosConfig.cmake - Trilinos_INCLUDE_DIRS # defined in TrilinosConfig.cmake - ) - -# Create Trilinos target -if(Trilinos_FOUND) - - if(NOT TARGET SUNDIALS::TRILINOS) - add_library(SUNDIALS::TRILINOS IMPORTED INTERFACE) - endif() - - set_target_properties(SUNDIALS::TRILINOS PROPERTIES - INTERFACE_INCLUDE_DIRECTORIES "${Trilinos_INCLUDE_DIRS}" - INTERFACE_LINK_LIBRARIES "${Trilinos_LIBRARIES}") - -endif() diff --git a/deps/AMICI/ThirdParty/sundials/cmake/tpl/FindXBRAID.cmake b/deps/AMICI/ThirdParty/sundials/cmake/tpl/FindXBRAID.cmake deleted file mode 100644 index 6b2d8b052..000000000 --- a/deps/AMICI/ThirdParty/sundials/cmake/tpl/FindXBRAID.cmake +++ /dev/null @@ -1,196 +0,0 @@ -# ------------------------------------------------------------------------------ -# Programmer(s): David J. Gardner @ LLNL -# ------------------------------------------------------------------------------ -# SUNDIALS Copyright Start -# Copyright (c) 2002-2021, Lawrence Livermore National Security -# and Southern Methodist University. -# All rights reserved. -# -# See the top-level LICENSE and NOTICE files for details. -# -# SPDX-License-Identifier: BSD-3-Clause -# SUNDIALS Copyright End -# ------------------------------------------------------------------------------ -# XBRAID find module that creates an imported target for XBRAID. -# The target is SUNDIALS::XBRAID. -# -# The variable XBRAID_DIR can be used to control where the module -# looks for the library. -# -# XBRAID_LIBRARIES - (advanced) the libraries to link against -# XBRAID_INCLUDES - (advanced) the directories to include -# -# This module also defines variables, but it is best to use -# the defined target to ensure includes and compile/link -# options are correctly passed to consumers. -# -# XBRAID_FOUND - system has the XBRAID library -# ------------------------------------------------------------------------------ - -# Check if we are locating XBraid using the root install directory or a list of -# include directories and link libraries -if (XBRAID_INCLUDES OR XBRAID_LIBRARIES) - - if (XBRAID_INCLUDES AND XBRAID_LIBRARIES) - - set(XBRAID_DIR "" CACHE PATH "Path to the root of XBraid installation" FORCE) - - else () - - string(CONCAT msg - "Both XBRAID_INCLUDES and XBRAID_LIBRARIES must be provided:\n" - " XBRAID_INCLUDES=${XBRAID_INCLUDES}\n" - " XBRAID_LIBRARIES=${XBRAID_LIBRARIES}") - message(FATAL_ERROR ${msg}) - - endif () - -else () - - set(XBRAID_INCLUDES "" CACHE STRING "Semi-colon separated list of XBraid include directories" FORCE) - set(XBRAID_LIBRARIES "" CACHE STRING "Semi-colon separated list of XBraid link libraries" FORCE) - -endif () - -# unset cache values for multiple passes -unset(XBRAID_INCLUDE_DIR CACHE) -unset(XBRAID_LIBRARY CACHE) - -unset(XBRAID_INCS CACHE) -unset(XBRAID_LIBS CACHE) - -if (XBRAID_INCLUDES AND XBRAID_LIBRARIES) - - message(STATUS "Finding XBraid using XBRAID_INCLUDES and XBRAID_LIBRARIES") - - # extract path from XBRAID_INCLUDES - foreach (include_dir ${XBRAID_INCLUDES}) - if (EXISTS "${include_dir}/braid.h") - set(XBRAID_INCLUDE_DIR "${include_dir}" CACHE "XBraid include directory") - break() - endif () - endforeach () - - # check if the include directory was found - if (NOT XBRAID_INCLUDE_DIR) - string(CONCAT msg - "Could not determine XBraid include directory from XBRAID_INCLUDES:\n" - " XBRAID_INCLUDES=${XBRAID_INCLUDES}\n") - message(FATAL_ERROR ${msg}) - endif () - - # extract library from XBRAID_LIBRARIES - foreach (library_path ${XBRAID_LIBRARIES}) - get_filename_component(library_name "${library_path}" NAME) - if (library_name MATCHES "braid") - set(XBRAID_LIBRARY "${library_path}" CACHE "XBraid library") - break() - endif () - endforeach () - - # check if the library directory was found - if (NOT XBRAID_LIBRARY) - string(CONCAT msg - "Could not determine XBraid library from XBRAID_LIBRARIES:\n" - " XBRAID_LIBRARIES=${XBRAID_LIBRARIES}") - message(FATAL_ERROR ${msg}) - endif () - -else () - - message(STATUS "Finding XBraid using XBRAID_DIR") - - # find XBRAID_DIR - if (NOT XBRAID_DIR) - - message(STATUS "Looking for XBraid in common install locations") - find_path(XBRAID_DIR include/braid.h braid/braid.h) - - endif () - - # check if XBRAID_DIR was set/found - if (NOT XBRAID_DIR) - - string(CONCAT msg - "Could not locate XBraid install directory please set:\n" - " - XBRAID_DIR\n" - "or used the advanced options\n" - " - XBRAID_INCLUDES and XBRAID_LIBRARIES.") - message(FATAL_ERROR ${msg}) - - endif () - - # Find the include dir - find_path(XBRAID_INCLUDE_DIR braid.h - PATHS - ${XBRAID_DIR} - PATH_SUFFIXES - include braid - DOC - "XBraid include directory" - NO_DEFAULT_PATH) - - # check if the include directory was found - if (NOT XBRAID_INCLUDE_DIR) - string(CONCAT msg - "Could not determine XBraid include directory from XBRAID_DIR:\n" - " XBRAID_DIR=${XBRAID_DIR}\n") - message(FATAL_ERROR ${msg}) - endif () - - # Find the library - find_library(XBRAID_LIBRARY braid - PATHS - ${XBRAID_DIR} - PATH_SUFFIXES - lib braid - DOC - "XBraid library" - NO_DEFAULT_PATH) - - # check if the library was found - if (NOT XBRAID_LIBRARY) - string(CONCAT msg - "Could not determine XBraid library from XBRAID_DIR:\n" - " XBRAID_DIR=${XBRAID_DIR}\n") - message(FATAL_ERROR ${msg}) - endif () - -endif () - -# set package variables including XBRAID_FOUND -find_package_handle_standard_args(XBRAID - REQUIRED_VARS - XBRAID_INCLUDE_DIR - XBRAID_LIBRARY - ) - -# XBraid target -if (XBRAID_FOUND) - - # create target if necessary - if (NOT TARGET SUNDIALS::XBRAID) - add_library(SUNDIALS::XBRAID UNKNOWN IMPORTED) - endif () - - # update target properties (for multiple passes) - set_target_properties(SUNDIALS::XBRAID PROPERTIES - INTERFACE_INCLUDE_DIRECTORIES "${XBRAID_INCLUDE_DIR}" - INTERFACE_LINK_LIBRARIES "${XBRAID_LIBRARIES}" - IMPORTED_LOCATION "${XBRAID_LIBRARY}") - - # set variables for output message, compile tests, and - # CMake/Makefile templates - if (XBRAID_INCLUDES AND XBRAID_LIBRARIES) - set(XBRAID_INCS "${XBRAID_INCLUDES}" CACHE INTERNAL - "Internal XBraid includes") - set(XBRAID_LIBS "${XBRAID_LIBRARIES}" CACHE INTERNAL - "Internal XBraid libraries") - else () - set(XBRAID_INCS "${XBRAID_INCLUDE_DIR}" CACHE INTERNAL - "Internal XBraid includes") - set(XBRAID_LIBS "${XBRAID_LIBRARY}" CACHE INTERNAL - "Internal XBraid libraries") - endif () - -endif () diff --git a/deps/AMICI/ThirdParty/sundials/cmake/tpl/SundialsHypre.cmake b/deps/AMICI/ThirdParty/sundials/cmake/tpl/SundialsHypre.cmake deleted file mode 100644 index b2fb7af2d..000000000 --- a/deps/AMICI/ThirdParty/sundials/cmake/tpl/SundialsHypre.cmake +++ /dev/null @@ -1,113 +0,0 @@ -# ----------------------------------------------------------------------------- -# Programmer(s): Cody J. Balos @ LLNL -# ----------------------------------------------------------------------------- -# SUNDIALS Copyright Start -# Copyright (c) 2002-2021, Lawrence Livermore National Security -# and Southern Methodist University. -# All rights reserved. -# -# See the top-level LICENSE and NOTICE files for details. -# -# SPDX-License-Identifier: BSD-3-Clause -# SUNDIALS Copyright End -# ----------------------------------------------------------------------------- -# Module to find and setup HYPRE correctly. -# Created from the SundialsTPL.cmake template. -# All SUNDIALS modules that find and setup a TPL must: -# -# 1. Check to make sure the SUNDIALS configuration and the TPL is compatible. -# 2. Find the TPL. -# 3. Check if the TPL works with SUNDIALS, UNLESS the override option -# TPL_WORKS is TRUE - in this case the tests should not be performed and it -# should be assumed that the TPL works with SUNDIALS. -# ----------------------------------------------------------------------------- - -# ----------------------------------------------------------------------------- -# Section 1: Include guard -# ----------------------------------------------------------------------------- - -if(NOT DEFINED SUNDIALS_HYPRE_INCLUDED) - set(SUNDIALS_HYPRE_INCLUDED) -else() - return() -endif() - -# ----------------------------------------------------------------------------- -# Section 2: Check to make sure options are compatible -# ----------------------------------------------------------------------------- - -# Using hypre requres building with MPI enabled -if(ENABLE_HYPRE AND NOT ENABLE_MPI) - print_error("MPI is required for hypre support. Set ENABLE_MPI to ON.") -endif() - -# ----------------------------------------------------------------------------- -# Section 3: Find the TPL -# ----------------------------------------------------------------------------- - -find_package(HYPRE REQUIRED) - -message(STATUS "HYPRE_LIBRARIES: ${HYPRE_LIBRARIES}") -message(STATUS "HYPRE_INCLUDE_DIR: ${HYPRE_INCLUDE_DIR}") - -# ----------------------------------------------------------------------------- -# Section 4: Test the TPL -# ----------------------------------------------------------------------------- - -if(HYPRE_FOUND AND (NOT HYPRE_WORKS)) - # Do any checks which don't require compilation first. - - # Create the HYPRE_TEST directory - set(HYPRE_TEST_DIR ${PROJECT_BINARY_DIR}/HYPRE_TEST) - file(MAKE_DIRECTORY ${HYPRE_TEST_DIR}) - - # Create a CMakeLists.txt file - file(WRITE ${HYPRE_TEST_DIR}/CMakeLists.txt - "CMAKE_MINIMUM_REQUIRED(VERSION 3.0.2)\n" - "PROJECT(ltest C)\n" - "SET(CMAKE_VERBOSE_MAKEFILE ON)\n" - "SET(CMAKE_BUILD_TYPE \"${CMAKE_BUILD_TYPE}\")\n" - "SET(CMAKE_C_COMPILER ${MPI_C_COMPILER})\n" - "SET(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS}\")\n" - "SET(CMAKE_C_FLAGS_RELEASE \"${CMAKE_C_FLAGS_RELEASE}\")\n" - "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" - "SET(CMAKE_EXE_LINKER_FLAGS \"${LINK_MATH_LIB}\")\n" - "INCLUDE_DIRECTORIES(${HYPRE_INCLUDE_DIR})\n" - "ADD_EXECUTABLE(ltest ltest.c)\n" - "TARGET_LINK_LIBRARIES(ltest ${HYPRE_LIBRARIES})\n") - - file(WRITE ${HYPRE_TEST_DIR}/ltest.c - "\#include \"HYPRE_parcsr_ls.h\"\n" - "int main(){\n" - "HYPRE_ParVector par_b;\n" - "HYPRE_IJVector b;\n" - "par_b = 0;\n" - "b = 0;\n" - "if (par_b != 0 || b != 0) return(1);\n" - "else return(0);\n" - "}\n") - - # To ensure we do not use stuff from the previous attempts, - # we must remove the CMakeFiles directory. - file(REMOVE_RECURSE ${HYPRE_TEST_DIR}/CMakeFiles) - - # Attempt to build and link the "ltest" executable - try_compile(COMPILE_OK ${HYPRE_TEST_DIR} ${HYPRE_TEST_DIR} ltest - OUTPUT_VARIABLE COMPILE_OUTPUT) - - # Process test result - if(COMPILE_OK) - message(STATUS "Checking if HYPRE works... OK") - set(HYPRE_WORKS TRUE CACHE BOOL "HYPRE works with SUNDIALS as configured" FORCE) - else() - message(STATUS "Checking if HYPRE works... FAILED") - message(STATUS "Check output: ") - message("${COMPILE_OUTPUT}") - print_error("SUNDIALS interface to HYPRE is not functional.") - endif() - -elseif(HYPRE_FOUND AND HYPRE_WORKS) - message(STATUS "Skipped HYPRE tests, assuming HYPRE works with SUNDIALS. Set HYPRE_WORKS=FALSE to (re)run compatibility test.") -endif() diff --git a/deps/AMICI/ThirdParty/sundials/cmake/tpl/SundialsMAGMA.cmake b/deps/AMICI/ThirdParty/sundials/cmake/tpl/SundialsMAGMA.cmake deleted file mode 100644 index 671fac1aa..000000000 --- a/deps/AMICI/ThirdParty/sundials/cmake/tpl/SundialsMAGMA.cmake +++ /dev/null @@ -1,109 +0,0 @@ -# ----------------------------------------------------------------------------- -# Programmer(s): Cody J. Balos @ LLNL -# ----------------------------------------------------------------------------- -# SUNDIALS Copyright Start -# Copyright (c) 2002-2021, Lawrence Livermore National Security -# and Southern Methodist University. -# All rights reserved. -# -# See the top-level LICENSE and NOTICE files for details. -# -# SPDX-License-Identifier: BSD-3-Clause -# SUNDIALS Copyright End -# ----------------------------------------------------------------------------- -# Module to find and setup MAGMA correctly. -# Created from the SundialsTPL.cmake template. -# All SUNDIALS modules that find and setup a TPL must: -# -# 1. Check to make sure the SUNDIALS configuration and the TPL is compatible. -# 2. Find the TPL. -# 3. Check if the TPL works with SUNDIALS, UNLESS the override option -# TPL_WORKS is TRUE - in this case the tests should not be performed and it -# should be assumed that the TPL works with SUNDIALS. -# ----------------------------------------------------------------------------- - -# ----------------------------------------------------------------------------- -# Section 1: Include guard -# ----------------------------------------------------------------------------- - -if(NOT DEFINED SUNDIALS_MAGMA_INCLUDED) - set(SUNDIALS_MAGMA_INCLUDED) -else() - return() -endif() - -# ----------------------------------------------------------------------------- -# Section 2: Check to make sure options are compatible -# ----------------------------------------------------------------------------- - -if(SUNDIALS_PRECISION MATCHES "extended") - print_error("SUNDIALS MAGMA interface is not compatible with extended precision") -endif() - -# ----------------------------------------------------------------------------- -# Section 3: Find the TPL -# ----------------------------------------------------------------------------- - -find_package(MAGMA REQUIRED) - -message(STATUS "MAGMA_VERSION: ${MAGMA_VERSION}") -message(STATUS "MAGMA_LIBRARIES: ${MAGMA_LIBRARIES}") -message(STATUS "MAGMA_INCLUDE_DIR: ${MAGMA_INCLUDE_DIR}") - -# ----------------------------------------------------------------------------- -# Section 4: Test the TPL -# ----------------------------------------------------------------------------- - -if(SUNDIALS_MAGMA_BACKENDS MATCHES "CUDA" AND NOT ENABLE_CUDA) - print_error("SUNDIALS_MAGMA_BACKENDS includes CUDA but CUDA is not enabled. Set ENABLE_CUDA=ON or change the backend.") -endif() -if(SUNDIALS_MAGMA_BACKENDS MATCHES "HIP" AND NOT ENABLE_HIP) - print_error("SUNDIALS_MAGMA_BACKENDS includes HIP but HIP is not enabled. Set ENABLE_HIP=ON or change the backend.") -endif() - -if(MAGMA_FOUND AND (NOT MAGMA_WORKS)) - # Create the MAGMA_TEST directory - set(MAGMA_TEST_DIR ${PROJECT_BINARY_DIR}/CMakeFiles/MAGMA_TEST) - file(MAKE_DIRECTORY ${MAGMA_TEST_DIR}) - - if(SUNDIALS_MAGMA_BACKENDS MATCHES "HIP") - set(lang CXX) - set(ext cxx) - set(define_have "\#define HAVE_HIP") - set(lib hip::host) - elseif(SUNDIALS_MAGMA_BACKENDS MATCHES "CUDA") - set(lang CUDA) - set(ext cu) - set(define_have "\#define HAVE_CUBLAS") - set(lib ) - endif() - - file(WRITE ${MAGMA_TEST_DIR}/ltest.${ext} - "${define_have}\n" - "\#include \"magma_v2.h\"\n" - "int main(){\n" - "magma_int_t a=0;\n" - "return(a);\n" - "}\n") - - try_compile(COMPILE_OK ${MAGMA_TEST_DIR} ${MAGMA_TEST_DIR}/ltest.${ext} - CMAKE_FLAGS - "-DINCLUDE_DIRECTORIES=${MAGMA_INCLUDE_DIR}" - LINK_LIBRARIES ${MAGMA_LIBRARIES} ${lib} - OUTPUT_VARIABLE COMPILE_OUTPUT - ${lang}_STANDARD ${CMAKE_${lang}_STANDARD} - ) - - # Process test result - if(COMPILE_OK) - message(STATUS "Checking if MAGMA works... OK") - set(MAGMA_WORKS TRUE CACHE BOOL "MAGMA works with SUNDIALS as configured" FORCE) - else() - message(STATUS "Checking if MAGMA works... FAILED") - message(STATUS "Check output: ") - message("${COMPILE_OUTPUT}") - print_error("SUNDIALS interface to MAGMA is not functional.") - endif() -elseif(MAGMA_FOUND AND MAGMA_WORKS) - message(STATUS "Skipped MAGMA tests, assuming MAGMA works with SUNDIALS.") -endif() diff --git a/deps/AMICI/ThirdParty/sundials/cmake/tpl/SundialsMPI.cmake b/deps/AMICI/ThirdParty/sundials/cmake/tpl/SundialsMPI.cmake deleted file mode 100644 index ad5d721c0..000000000 --- a/deps/AMICI/ThirdParty/sundials/cmake/tpl/SundialsMPI.cmake +++ /dev/null @@ -1,89 +0,0 @@ -# --------------------------------------------------------------------------- -# Programmer(s): David J. Gardner @ LLNL -# --------------------------------------------------------------------------- -# SUNDIALS Copyright Start -# Copyright (c) 2002-2021, Lawrence Livermore National Security -# and Southern Methodist University. -# All rights reserved. -# -# See the top-level LICENSE and NOTICE files for details. -# -# SPDX-License-Identifier: BSD-3-Clause -# SUNDIALS Copyright End -# --------------------------------------------------------------------------- -# Setup MPI for SUNDIALS CMake-based configuration. -# --------------------------------------------------------------------------- -# Prior to CMake 3.10 the CMake FindMPI module considers: -# 1. Inspect MPI wrappers (MPI__COMPILER) -# 2. Try guesses -# 3. Try the compiler (CMAKE__COMPILER) -# -# Starting with CMake 3.10 the CMake FindMPI module considers: -# 1. Try the compiler (CMAKE__COMPILER) -# 2. Inspect MPI wrappers (MPI__COMPILER) -# 3. Try guesses -# --------------------------------------------------------------------------- - -# ----------------------------------------------------------------------------- -# Section 1: Include guard -# ----------------------------------------------------------------------------- - -if(NOT DEFINED SUNDIALS_MPI_INCLUDED) - set(SUNDIALS_MPI_INCLUDED) -else() - return() -endif() - -# --------------------------------------------------------------------------- -# If MPI__COMPILER is set, FindMPI will try to set the below variables -# for the given compiler wrapper. If MPI__COMPILER is unset FindMPI -# will attempt to locate an installed MPI library and set the below -# variables. -# -# MPI__FOUND TRUE if FindMPI found MPI flags for -# MPI__COMPILER MPI Compiler wrapper for -# MPI__COMPILE_FLAGS Compilation flags for MPI programs -# MPI__INCLUDE_PATH Include path(s) for MPI header -# MPI__LINK_FLAGS Linking flags for MPI programs -# MPI__LIBRARIES All libraries to link MPI programs against -# -# MPIEXEC_EXECUTABLE Executable for running MPI programs -# MPIEXEC_NUMPROC_FLAG Flag to pass to MPIEXEC_EXECUTABLE before -# giving it the number of processors to run on -# MPIEXEC_PREFLAGS Flags to pass to MPIEXEC_EXECUTABLE directly -# before the executable to run. -# MPIEXEC_POSTFLAGS Flags to pass to MPIEXEC_EXECUTABLE after -# other flags -# --------------------------------------------------------------------------- - -mark_as_advanced(MPI_EXTRA_LIBRARY) -mark_as_advanced(MPI_LIBRARY) - -foreach(lang ${_SUNDIALS_ENABLED_LANGS}) - mark_as_advanced(CLEAR MPI_${lang}_COMPILER) - mark_as_advanced(MPI_${lang}_LIBRARIES) - mark_as_advanced(MPI_${lang}_COMPILE_FLAGS) - mark_as_advanced(MPI_${lang}_INCLUDE_PATH) - mark_as_advanced(MPI_${lang}_LIBRARIES) - mark_as_advanced(MPI_${lang}_LINK_FLAGS) -endforeach() - -find_package(MPI 2.0.0 REQUIRED) - -# --------------------------------------------------------------------------- -# Configure the presentation of MPI options in the GUI. -# --------------------------------------------------------------------------- - -mark_as_advanced(CLEAR MPIEXEC_EXECUTABLE) - -mark_as_advanced(MPI_EXTRA_LIBRARY) -mark_as_advanced(MPI_LIBRARY) - -foreach(lang ${_SUNDIALS_ENABLED_LANGS}) - mark_as_advanced(CLEAR MPI_${lang}_COMPILER) - mark_as_advanced(MPI_${lang}_LIBRARIES) - mark_as_advanced(MPI_${lang}_COMPILE_FLAGS) - mark_as_advanced(MPI_${lang}_INCLUDE_PATH) - mark_as_advanced(MPI_${lang}_LIBRARIES) - mark_as_advanced(MPI_${lang}_LINK_FLAGS) -endforeach() diff --git a/deps/AMICI/ThirdParty/sundials/cmake/tpl/SundialsPETSC.cmake b/deps/AMICI/ThirdParty/sundials/cmake/tpl/SundialsPETSC.cmake deleted file mode 100644 index 822c763fd..000000000 --- a/deps/AMICI/ThirdParty/sundials/cmake/tpl/SundialsPETSC.cmake +++ /dev/null @@ -1,91 +0,0 @@ -# ----------------------------------------------------------------------------- -# Programmer(s): Cody J. Balos @ LLNL -# ----------------------------------------------------------------------------- -# SUNDIALS Copyright Start -# Copyright (c) 2002-2021, Lawrence Livermore National Security -# and Southern Methodist University. -# All rights reserved. -# -# See the top-level LICENSE and NOTICE files for details. -# -# SPDX-License-Identifier: BSD-3-Clause -# SUNDIALS Copyright End -# ----------------------------------------------------------------------------- -# Module to find and setup PETSC correctly. -# Created from the SundialsTPL.cmake template. -# All SUNDIALS modules that find and setup a TPL must: -# -# 1. Check to make sure the SUNDIALS configuration and the TPL is compatible. -# 2. Find the TPL. -# 3. Check if the TPL works with SUNDIALS, UNLESS the override option -# TPL_WORKS is TRUE - in this case the tests should not be performed and it -# should be assumed that the TPL works with SUNDIALS. -# ----------------------------------------------------------------------------- - -# ----------------------------------------------------------------------------- -# Section 1: Include guard -# ----------------------------------------------------------------------------- - -if(NOT DEFINED SUNDIALS_PETSC_INCLUDED) - set(SUNDIALS_PETSC_INCLUDED) -else() - return() -endif() - -# ----------------------------------------------------------------------------- -# Section 2: Check to make sure options are compatible -# ----------------------------------------------------------------------------- - -# Using PETSc requires building with MPI enabled -if(ENABLE_PETSC AND NOT ENABLE_MPI) - print_error("MPI is required for PETSc support. Set ENABLE_MPI to ON.") -endif() - -if(SUNDIALS_PRECISION MATCHES "EXTENDED") - print_error("SUNDIALS is not compatible with PETSc when using ${SUNDIALS_PRECISION} precision") -endif() - -# ----------------------------------------------------------------------------- -# Section 3: Find the TPL -# ----------------------------------------------------------------------------- - -find_package(PETSC REQUIRED) - -message(STATUS "PETSC_DIR: ${PETSC_DIR}") -message(STATUS "PETSC_LIBRARIES: ${PETSC_LIBRARIES_}") -message(STATUS "PETSC_INCLUDES: ${PETSC_INCLUDES_}") -message(STATUS "PETSC_INDEX_SIZE: ${PETSC_INDEX_SIZE}") -message(STATUS "PETSC_PRECISION: ${PETSC_PRECISION}\n") - -# ----------------------------------------------------------------------------- -# Section 4: Test the TPL -# ----------------------------------------------------------------------------- - -if(PETSC_FOUND AND (NOT PETSC_WORKS)) - # No need for any compile tests because the FindPETSC module - # does compile tests already. - - if(NOT ("${SUNDIALS_INDEX_SIZE}" MATCHES "${PETSC_INDEX_SIZE}")) - string(CONCAT _err_msg_string - "PETSc not functional due to index size mismatch:\n" - "SUNDIALS_INDEX_SIZE=${SUNDIALS_INDEX_SIZE}, " - "but PETSc was built with ${PETSC_INDEX_SIZE}-bit indices\n" - "PETSC_DIR: ${PETSC_DIR}\n") - print_error("${_err_msg_string}") - endif() - - string(TOUPPER "${PETSC_PRECISION}" _petsc_precision) - string(TOUPPER "${SUNDIALS_PRECISION}" _sundials_precision) - if(NOT ("${_sundials_precision}" MATCHES "${_petsc_precision}")) - string(CONCAT _err_msg_string - "PETSc not functional due to real type precision mismatch:\n" - "SUNDIALS_PRECISION=${_sundials_precision}, " - "but PETSc was built with ${_petsc_precision} precision\n" - "PETSC_DIR: ${PETSC_DIR}\n") - print_error("${_err_msg_string}") - endif() - - set(PETSC_WORKS TRUE CACHE BOOL "PETSC works with SUNDIALS as configured" FORCE) -elseif(PETSC_FOUND AND PETSC_WORKS) - message(STATUS "Skipped PETSC tests, assuming PETSC works with SUNDIALS. Set PETSC_WORKS=FALSE to (re)run compatibility test.") -endif() diff --git a/deps/AMICI/ThirdParty/sundials/cmake/tpl/SundialsRAJA.cmake b/deps/AMICI/ThirdParty/sundials/cmake/tpl/SundialsRAJA.cmake deleted file mode 100644 index 4a7a0ab41..000000000 --- a/deps/AMICI/ThirdParty/sundials/cmake/tpl/SundialsRAJA.cmake +++ /dev/null @@ -1,87 +0,0 @@ -# ----------------------------------------------------------------------------- -# Programmer(s): Cody J. Balos @ LLNL -# ----------------------------------------------------------------------------- -# SUNDIALS Copyright Start -# Copyright (c) 2002-2021, Lawrence Livermore National Security -# and Southern Methodist University. -# All rights reserved. -# -# See the top-level LICENSE and NOTICE files for details. -# -# SPDX-License-Identifier: BSD-3-Clause -# SUNDIALS Copyright End -# ----------------------------------------------------------------------------- -# Module to find and setup RAJA correctly. -# Created from the SundialsTPL.cmake template. -# All SUNDIALS modules that find and setup a TPL must: -# -# 1. Check to make sure the SUNDIALS configuration and the TPL is compatible. -# 2. Find the TPL. -# 3. Check if the TPL works with SUNDIALS, UNLESS the override option -# TPL_WORKS is TRUE - in this case the tests should not be performed and it -# should be assumed that the TPL works with SUNDIALS. -# ----------------------------------------------------------------------------- - -# ----------------------------------------------------------------------------- -# Section 1: Include guard -# ----------------------------------------------------------------------------- - -if(NOT DEFINED SUNDIALS_RAJA_INCLUDED) - set(SUNDIALS_RAJA_INCLUDED) -else() - return() -endif() - -# ----------------------------------------------------------------------------- -# Section 2: Check to make sure options are compatible -# ----------------------------------------------------------------------------- - -# ----------------------------------------------------------------------------- -# Section 3: Find the TPL -# ----------------------------------------------------------------------------- - -# find the library configuration file -find_file(RAJA_CONFIGHPP_PATH config.hpp - HINTS "${RAJA_DIR}" - PATH_SUFFIXES include include/RAJA - NO_DEFAULT_PATH) -mark_as_advanced(FORCE RAJA_CONFIGHPP_PATH) - -# Look for CMake configuration file in RAJA installation -find_package(RAJA CONFIG - PATHS "${RAJA_DIR}" "${RAJA_DIR}/share/raja/cmake" - NO_DEFAULT_PATH - REQUIRED) - -# determine the backends -foreach(_backend CUDA HIP OPENMP TARGET_OPENMP) - file(STRINGS "${RAJA_CONFIGHPP_PATH}" _raja_has_backend REGEX "^#define RAJA_ENABLE_${_backend}\$") - if(_raja_has_backend) - set(RAJA_BACKENDS "${_backend};${RAJA_BACKENDS}") - endif() -endforeach() - -message(STATUS "RAJA Version: ${RAJA_VERSION_MAJOR}.${RAJA_VERSION_MINOR}.${RAJA_VERSION_PATCHLEVEL}") -message(STATUS "RAJA Backends: ${RAJA_BACKENDS}") - -# ----------------------------------------------------------------------------- -# Section 4: Test the TPL -# ----------------------------------------------------------------------------- - -if((SUNDIALS_RAJA_BACKENDS MATCHES "CUDA") AND - (NOT RAJA_BACKENDS MATCHES "CUDA")) - print_error("Requested that SUNDIALS uses the CUDA RAJA backend, but RAJA was not built with the CUDA backend.") -endif() - -if((SUNDIALS_RAJA_BACKENDS MATCHES "HIP") AND - (NOT RAJA_BACKENDS MATCHES "HIP")) - print_error("Requested that SUNDIALS uses the HIP RAJA backend, but RAJA was not built with the HIP backend.") -endif() - -if(NOT ENABLE_OPENMP AND RAJA_BACKENDS MATCHES "OPENMP") - print_error("RAJA was built with OpenMP, but OpenMP is not enabled. Set ENABLE_OPENMP to ON.") -endif() - -if(NOT ENABLE_OPENMP_DEVICE AND RAJA_BACKENDS MATCHES "TARGET_OPENMP") - print_error("RAJA was built with OpenMP device offloading, but OpenMP with device offloading is not enabled. Set ENABLE_OPENMP_DEVICE to ON.") -endif() diff --git a/deps/AMICI/ThirdParty/sundials/cmake/tpl/SundialsTrilinos.cmake b/deps/AMICI/ThirdParty/sundials/cmake/tpl/SundialsTrilinos.cmake deleted file mode 100644 index e995b0fec..000000000 --- a/deps/AMICI/ThirdParty/sundials/cmake/tpl/SundialsTrilinos.cmake +++ /dev/null @@ -1,140 +0,0 @@ -# ----------------------------------------------------------------------------- -# Programmer(s): Cody J. Balos @ LLNL -# ----------------------------------------------------------------------------- -# SUNDIALS Copyright Start -# Copyright (c) 2002-2021, Lawrence Livermore National Security -# and Southern Methodist University. -# All rights reserved. -# -# See the top-level LICENSE and NOTICE files for details. -# -# SPDX-License-Identifier: BSD-3-Clause -# SUNDIALS Copyright End -# ----------------------------------------------------------------------------- -# Module to find and setup Trilinos correctly. -# Created from the SundialsTPL.cmake template. -# All SUNDIALS modules that find and setup a TPL must: -# -# 1. Check to make sure the SUNDIALS configuration and the TPL is compatible. -# 2. Find the TPL. -# 3. Check if the TPL works with SUNDIALS, UNLESS the override option -# TPL_WORKS is TRUE - in this case the tests should not be performed and it -# should be assumed that the TPL works with SUNDIALS. -# ----------------------------------------------------------------------------- - -# ----------------------------------------------------------------------------- -# Section 1: Include guard -# ----------------------------------------------------------------------------- - -if(NOT DEFINED SUNDIALS_TRILINOS_INCLUDED) - set(SUNDIALS_TRILINOS_INCLUDED) -else() - return() -endif() - -# ----------------------------------------------------------------------------- -# Section 2: Check to make sure options are compatible -# ----------------------------------------------------------------------------- - -# ----------------------------------------------------------------------------- -# Section 3: Find the TPL -# ----------------------------------------------------------------------------- - -# Find Trilinos -find_package(Trilinos REQUIRED) - -# Check if Trilinos was built with MPI -if(";${Trilinos_TPL_LIST};" MATCHES ";MPI;") - set(Trilinos_MPI TRUE) -else() - set(Trilinos_MPI FALSE) -endif() - -# For XSDK compatibility, only use the user/spack provided compiler and flags to build -# SUNDIALS modules that use Trilinos. If we are not in XSDK mode, we can use the imported -# Trilinos compiler and flags by default, but allow the user to change it through CMake -# the Trilinos_INTERFACE_* options. - -if(USE_XSDK_DEFAULTS) - if(Trilinos_MPI AND MPI_CXX_FOUND) - force_variable(Trilinos_INTERFACE_CXX_COMPILER STRING "C++ compiler for Trilinos interface" "${MPI_CXX_COMPILER}") - set(Trilinos_INTERFACE_MPI_CXX_FOUND ${Trilinos_MPI} CACHE INTERNAL "Is Trilinos interface C++ compiler MPI") - else() - force_variable(Trilinos_INTERFACE_CXX_COMPILER STRING "C compiler for Trilinos interface" "${CMAKE_CXX_COMPILER}") - set(Trilinos_INTERFACE_MPI_CXX_FOUND FALSE CACHE INTERNAL "Is Trilinos interface C++ compiler MPI") - endif() - if(Trilinos_MPI AND MPI_C_FOUND) - force_variable(Trilinos_INTERFACE_C_COMPILER STRING "C compiler for Trilinos interface" "${MPI_C_COMPILER}") - set(Trilinos_INTERFACE_MPI_C_FOUND ${Trilinos_MPI} CACHE INTERNAL "Is Trilinos interface C compiler MPI") - else() - force_variable(Trilinos_INTERFACE_C_COMPILER STRING "C compiler for Trilinos interface" "${CMAKE_C_COMPILER}") - set(Trilinos_INTERFACE_MPI_C_FOUND FALSE CACHE INTERNAL "Is Trilinos interface C compiler MPI") - endif() - force_variable(Trilinos_INTERFACE_CXX_COMPILER_FLAGS STRING "C++ compiler flags specific to Trilinos interface" "") - force_variable(Trilinos_INTERFACE_C_COMPILER_FLAGS STRING "C compiler flags specific to Trilinos interface" "") - force_variable(Trilinos_INTERFACE_MPIEXEC STRING "MPI executable for Trilinos interface" "${MPIEXEC_EXECUTABLE}") -else() - force_variable(Trilinos_INTERFACE_CXX_COMPILER STRING "C++ compiler for Trilinos interface" "${Trilinos_CXX_COMPILER}") - force_variable(Trilinos_INTERFACE_C_COMPILER STRING "C compiler for Trilinos interface" "${Trilinos_C_COMPILER}") - force_variable(Trilinos_INTERFACE_CXX_COMPILER_FLAGS STRING "C++ compiler flags for Trilinos interface" "${Trilinos_CXX_COMPILER_FLAGS}") - force_variable(Trilinos_INTERFACE_C_COMPILER_FLAGS STRING "C compiler flags for Trilinos interface" "${Trilinos_C_COMPILER_FLAGS}") - force_variable(Trilinos_INTERFACE_MPIEXEC STRING "MPI executable for Trilinos interface" "${Trilinos_MPI_EXEC}") - set(Trilinos_INTERFACE_MPI_CXX_FOUND ${Trilinos_MPI} CACHE INTERNAL "Is Trilinos interface C++ compiler MPI") - set(Trilinos_INTERFACE_MPI_C_FOUND ${Trilinos_MPI} CACHE INTERNAL "Is Trilinos interface C compiler MPI") -endif() - -message(STATUS "Trilinos_MPI: ${Trilinos_MPI}") -message(STATUS "Trilinos_LIBRARIES: ${Trilinos_LIBRARIES}") -message(STATUS "Trilinos_INCLUDE_DIRS: ${Trilinos_INCLUDE_DIRS}") - -# ----------------------------------------------------------------------------- -# Section 4: Test the TPL -# ----------------------------------------------------------------------------- - -if(Trilinos_FOUND AND (NOT Trilinos_WORKS)) - # Do any checks which don't require compilation first. - - # Create the Trilinos_TEST directory - set(Trilinos_TEST_DIR ${PROJECT_BINARY_DIR}/Trilinos_TEST) - file(MAKE_DIRECTORY ${Trilinos_TEST_DIR}) - - # Create a CMakeLists.txt file - file(WRITE ${Trilinos_TEST_DIR}/CMakeLists.txt - "CMAKE_MINIMUM_REQUIRED(VERSION 3.1.3)\n" - "PROJECT(ltest CXX)\n" - "SET(CMAKE_VERBOSE_MAKEFILE ON)\n" - "SET(CMAKE_BUILD_TYPE \"${CMAKE_BUILD_TYPE}\")\n" - "SET(CMAKE_CXX_COMPILER \"${Trilinos_INTERFACE_CXX_COMPILER}\")\n" - "SET(CMAKE_CXX_FLAGS \"${Trilinos_INTERFACE_CXX_COMPILER_FLAGS}\")\n" - "SET(Trilinos_DIR \"${Trilinos_DIR}\")\n" - "INCLUDE(FindPackageHandleStandardArgs)\n" - "INCLUDE(${PROJECT_SOURCE_DIR}/cmake/tpl/FindTrilinos.cmake)\n" - "ADD_EXECUTABLE(ltest ltest.cpp)\n" - "TARGET_LINK_LIBRARIES(ltest SUNDIALS::TRILINOS)\n") - - # Create a C++ source file which calls a Trilinos function - file(WRITE ${Trilinos_TEST_DIR}/ltest.cpp - "#include \n" - "int main(){\n" - "std::cout << Tpetra::version() << std::endl;\n" - "return(0);\n" - "}\n") - - # Attempt to build and link the "ltest" executable - try_compile(COMPILE_OK ${Trilinos_TEST_DIR} ${Trilinos_TEST_DIR} ltest - OUTPUT_VARIABLE COMPILE_OUTPUT) - - # Process test result - if(COMPILE_OK) - message(STATUS "Checking if Trilinos works with SUNDIALS... OK") - set(Trilinos_WORKS TRUE CACHE BOOL "Trilinos works with SUNDIALS as configured" FORCE) - else() - message(STATUS "Checking if Trilinos works with SUNDIALS... FAILED") - message(STATUS "Check output: ") - message("${COMPILE_OUTPUT}") - print_error("SUNDIALS interface to Trilinos is not functional.") - endif() - -elseif(Trilinos_FOUND AND Trilinos_WORKS) - message(STATUS "Skipped Trilinos tests, assuming Trilinos works with SUNDIALS. Set Trilinos_WORKS=FALSE to (re)run compatibility test.") -endif() diff --git a/deps/AMICI/ThirdParty/sundials/cmake/tpl/SundialsXBRAID.cmake b/deps/AMICI/ThirdParty/sundials/cmake/tpl/SundialsXBRAID.cmake deleted file mode 100644 index 91b99669f..000000000 --- a/deps/AMICI/ThirdParty/sundials/cmake/tpl/SundialsXBRAID.cmake +++ /dev/null @@ -1,129 +0,0 @@ -# ----------------------------------------------------------------------------- -# Programmer(s): David J. Gardner @ LLNL -# ----------------------------------------------------------------------------- -# SUNDIALS Copyright Start -# Copyright (c) 2002-2021, Lawrence Livermore National Security -# and Southern Methodist University. -# All rights reserved. -# -# See the top-level LICENSE and NOTICE files for details. -# -# SPDX-License-Identifier: BSD-3-Clause -# SUNDIALS Copyright End -# ----------------------------------------------------------------------------- -# Module to find and setup correctly. -# Created from the SundialsTPL.cmake template. -# All SUNDIALS modules that find and setup a TPL must: -# -# 1. Check to make sure the SUNDIALS configuration and the TPL is compatible. -# 2. Find the TPL. -# 3. Check if the TPL works with SUNDIALS, UNLESS the override option -# TPL_WORKS is TRUE - in this case the tests should not be performed and it -# should be assumed that the TPL works with SUNDIALS. -# ----------------------------------------------------------------------------- - -# ----------------------------------------------------------------------------- -# Section 1: Include guard -# ----------------------------------------------------------------------------- - -if(NOT DEFINED SUNDIALS_XBRAID_INCLUDED) - set(SUNDIALS_XBRAID_INCLUDED) -else() - return() -endif() - -# ----------------------------------------------------------------------------- -# Section 2: Check to make sure options are compatible -# ----------------------------------------------------------------------------- - -# Using XBRAID requires building with MPI enabled -if(NOT ENABLE_MPI) - message(FATAL_ERROR - "MPI is required for XBraid support. Set ENABLE_MPI to ON.") -endif() - -# XBraid does not support single or extended precision -if(SUNDIALS_PRECISION MATCHES "SINGLE" OR SUNDIALS_PRECISION MATCHES "EXTENDED") - message(FATAL_ERROR - "XBraid is not compatible with ${SUNDIALS_PRECISION} precision") -endif() - -# XBraid does not support 64-bit index sizes -if(SUNDIALS_INDEX_SIZE MATCHES "64") - message(FATAL_ERROR - "XBraid is not compatible with ${SUNDIALS_INDEX_SIZE}-bit indices") -endif() - -# ----------------------------------------------------------------------------- -# Section 3: Find the TPL -# ----------------------------------------------------------------------------- - -find_package(XBRAID REQUIRED) - -message(STATUS "XBRAID_LIBRARIES: ${XBRAID_LIBS}") -message(STATUS "XBRAID_INCLUDES: ${XBRAID_INCS}") - -# ----------------------------------------------------------------------------- -# Section 4: Test the TPL -# ----------------------------------------------------------------------------- - -# Add works variable - -if(XBRAID_FOUND AND (NOT XBRAID_WORKS)) - - # Create the XBRAID_TEST directory - set(XBRAID_TEST_DIR ${PROJECT_BINARY_DIR}/XBRAID_TEST) - file(MAKE_DIRECTORY ${XBRAID_TEST_DIR}) - - # Create a CMakeLists.txt file - file(WRITE ${XBRAID_TEST_DIR}/CMakeLists.txt - "cmake_minimum_required(VERSION ${CMAKE_VERSION})\n" - "project(ltest C)\n" - "set(CMAKE_VERBOSE_MAKEFILE ON)\n" - "set(CMAKE_BUILD_TYPE \"${CMAKE_BUILD_TYPE}\")\n" - "set(CMAKE_C_COMPILER ${MPI_C_COMPILER})\n" - "set(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS}\")\n" - "set(CMAKE_C_FLAGS_RELEASE \"${CMAKE_C_FLAGS_RELEASE}\")\n" - "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" - "add_executable(ltest ltest.c)\n" - "target_include_directories(ltest PRIVATE \"${XBRAID_INCS}\")\n" - "target_link_libraries(ltest \"${XBRAID_LIBS}\")\n" - "target_link_libraries(ltest m)\n") - - # Create a C source file - file(WRITE ${XBRAID_TEST_DIR}/ltest.c - "\#include \n" - "\#include \"braid.h\"\n" - "int main(){\n" - "braid_Int rand;\n" - "rand = braid_Rand();\n" - "if (rand < 0) return 1;\n" - "return 0;\n" - "}\n") - - # To ensure we do not use stuff from the previous attempts, - # we must remove the CMakeFiles directory. - file(REMOVE_RECURSE ${XBRAID_TEST_DIR}/CMakeFiles) - - # Attempt to build and link the "ltest" executable - try_compile(COMPILE_OK ${XBRAID_TEST_DIR} ${XBRAID_TEST_DIR} ltest - OUTPUT_VARIABLE COMPILE_OUTPUT) - - # Process test result - if(COMPILE_OK) - message(STATUS "Checking if XBRAID works... OK") - set(XBRAID_WORKS TRUE CACHE BOOL "XBRAID works as configured" FORCE) - else() - message(STATUS "Checking if XBRAID works... FAILED") - message(STATUS "Check output: ") - message("${COMPILE_OUTPUT}") - message(FATAL_ERROR "XBRAID compile test failed.") - endif() - - message(STATUS "XBRAID tests passed") - -else() - message(STATUS "Skipped XBRAID tests, assuming XBRAID works with SUNDIALS.") -endif() diff --git a/deps/AMICI/ThirdParty/sundials/include/cvodes/cvodes.h b/deps/AMICI/ThirdParty/sundials/include/cvodes/cvodes.h index 9f7601330..58fdff67b 100644 --- a/deps/AMICI/ThirdParty/sundials/include/cvodes/cvodes.h +++ b/deps/AMICI/ThirdParty/sundials/include/cvodes/cvodes.h @@ -208,9 +208,9 @@ SUNDIALS_EXPORT int CVodeSetMaxConvFails(void *cvode_mem, int maxncf); SUNDIALS_EXPORT int CVodeSetNonlinConvCoef(void *cvode_mem, realtype nlscoef); SUNDIALS_EXPORT int CVodeSetLSetupFrequency(void *cvode_mem, long int msbp); SUNDIALS_EXPORT int CVodeSetConstraints(void *cvode_mem, N_Vector constraints); - SUNDIALS_EXPORT int CVodeSetNonlinearSolver(void *cvode_mem, SUNNonlinearSolver NLS); +SUNDIALS_EXPORT int CVodeSetNlsRhsFn(void *cvode_mem, CVRhsFn f); /* Rootfinding initialization function */ SUNDIALS_EXPORT int CVodeRootInit(void *cvode_mem, int nrtfn, CVRootFn g); @@ -223,10 +223,6 @@ SUNDIALS_EXPORT int CVodeSetNoInactiveRootWarn(void *cvode_mem); SUNDIALS_EXPORT int CVode(void *cvode_mem, realtype tout, N_Vector yout, realtype *tret, int itask); -/* Dense output function */ -SUNDIALS_EXPORT int CVodeGetDky(void *cvode_mem, realtype t, int k, - N_Vector dky); - /* Utility functions to update/compute y based on ycor */ SUNDIALS_EXPORT int CVodeComputeState(void *cvode_mem, N_Vector ycor, N_Vector y); @@ -235,6 +231,10 @@ SUNDIALS_EXPORT int CVodeComputeStateSens(void *cvode_mem, N_Vector *yScor, SUNDIALS_EXPORT int CVodeComputeStateSens1(void *cvode_mem, int idx, N_Vector yScor1, N_Vector yS1); +/* Dense output function */ +SUNDIALS_EXPORT int CVodeGetDky(void *cvode_mem, realtype t, int k, + N_Vector dky); + /* Optional output functions */ SUNDIALS_EXPORT int CVodeGetWorkSpace(void *cvode_mem, long int *lenrw, long int *leniw); diff --git a/deps/AMICI/ThirdParty/sundials/include/idas/idas.h b/deps/AMICI/ThirdParty/sundials/include/idas/idas.h index 654b6c3cd..e685cd818 100644 --- a/deps/AMICI/ThirdParty/sundials/include/idas/idas.h +++ b/deps/AMICI/ThirdParty/sundials/include/idas/idas.h @@ -221,6 +221,7 @@ SUNDIALS_EXPORT int IDASetConstraints(void *ida_mem, N_Vector constraints); SUNDIALS_EXPORT int IDASetNonlinearSolver(void *ida_mem, SUNNonlinearSolver NLS); +SUNDIALS_EXPORT int IDASetNlsResFn(void *IDA_mem, IDAResFn res); /* Rootfinding initialization function */ SUNDIALS_EXPORT int IDARootInit(void *ida_mem, int nrtfn, IDARootFn g); diff --git a/deps/AMICI/ThirdParty/sundials/include/sundials/sundials_config.h b/deps/AMICI/ThirdParty/sundials/include/sundials/sundials_config.h index ec95eb40c..674ec0df1 100644 --- a/deps/AMICI/ThirdParty/sundials/include/sundials/sundials_config.h +++ b/deps/AMICI/ThirdParty/sundials/include/sundials/sundials_config.h @@ -21,11 +21,12 @@ * -----------------------------------------------------------------*/ -#define SUNDIALS_VERSION "5.7.0" +#define SUNDIALS_VERSION "5.8.0" #define SUNDIALS_VERSION_MAJOR 5 -#define SUNDIALS_VERSION_MINOR 7 +#define SUNDIALS_VERSION_MINOR 8 #define SUNDIALS_VERSION_PATCH 0 #define SUNDIALS_VERSION_LABEL "" +#define SUNDIALS_GIT_VERSION "" /* ------------------------------------------------------------------ @@ -85,8 +86,8 @@ /* MAGMA backends */ -/* #undef SUNDIALS_MAGMA_BACKENDS_HIP */ #define SUNDIALS_MAGMA_BACKENDS_CUDA +/* #undef SUNDIALS_MAGMA_BACKENDS_HIP */ /* Set if SUNDIALS is built with MPI support, then * #define SUNDIALS_MPI_ENABLED 1 @@ -104,8 +105,9 @@ /* #undef SUNDIALS_TRILINOS_HAVE_MPI */ /* RAJA backends */ -/* #undef SUNDIALS_RAJA_BACKENDS_HIP */ #define SUNDIALS_RAJA_BACKENDS_CUDA +/* #undef SUNDIALS_RAJA_BACKENDS_HIP */ +/* #undef SUNDIALS_RAJA_BACKENDS_SYCL */ /* ------------------------------------------------------------------ * SUNDIALS modules enabled diff --git a/deps/AMICI/ThirdParty/sundials/include/sundials/sundials_config.in b/deps/AMICI/ThirdParty/sundials/include/sundials/sundials_config.in index 3a20cdc1a..b7dde7826 100644 --- a/deps/AMICI/ThirdParty/sundials/include/sundials/sundials_config.in +++ b/deps/AMICI/ThirdParty/sundials/include/sundials/sundials_config.in @@ -26,6 +26,7 @@ #define SUNDIALS_VERSION_MINOR @PACKAGE_VERSION_MINOR@ #define SUNDIALS_VERSION_PATCH @PACKAGE_VERSION_PATCH@ #define SUNDIALS_VERSION_LABEL "@PACKAGE_VERSION_LABEL@" +#define SUNDIALS_GIT_VERSION "@SUNDIALS_GIT_VERSION@" /* ------------------------------------------------------------------ @@ -85,8 +86,8 @@ @SUNDIALS_CONFIGH_TPLS@ /* MAGMA backends */ -#cmakedefine SUNDIALS_MAGMA_BACKENDS_HIP #cmakedefine SUNDIALS_MAGMA_BACKENDS_CUDA +#cmakedefine SUNDIALS_MAGMA_BACKENDS_HIP /* Set if SUNDIALS is built with MPI support, then * #define SUNDIALS_MPI_ENABLED 1 @@ -104,8 +105,9 @@ #cmakedefine SUNDIALS_TRILINOS_HAVE_MPI /* RAJA backends */ -#cmakedefine SUNDIALS_RAJA_BACKENDS_HIP #cmakedefine SUNDIALS_RAJA_BACKENDS_CUDA +#cmakedefine SUNDIALS_RAJA_BACKENDS_HIP +#cmakedefine SUNDIALS_RAJA_BACKENDS_SYCL /* ------------------------------------------------------------------ * SUNDIALS modules enabled @@ -169,4 +171,4 @@ /* Mark SUNDIALS function as static inline. */ -#define SUNDIALS_STATIC_INLINE static SUNDIALS_INLINE \ No newline at end of file +#define SUNDIALS_STATIC_INLINE static SUNDIALS_INLINE diff --git a/deps/AMICI/ThirdParty/sundials/include/sundials/sundials_export.h b/deps/AMICI/ThirdParty/sundials/include/sundials/sundials_export.h index 0fe047c3e..5ad995643 100644 --- a/deps/AMICI/ThirdParty/sundials/include/sundials/sundials_export.h +++ b/deps/AMICI/ThirdParty/sundials/include/sundials/sundials_export.h @@ -22,11 +22,13 @@ #endif #ifndef SUNDIALS_DEPRECATED -#ifdef __GNUC__ -# define SUNDIALS_DEPRECATED __attribute__ ((__deprecated__)) -#else -# define SUNDIALS_DEPRECATED -#endif +/* BEGIN changed for AMICI */ +# ifdef __GNUC__ +# define SUNDIALS_DEPRECATED __attribute__ ((__deprecated__)) +# else +# define SUNDIALS_DEPRECATED +# endif +/* END changed for AMICI */ #endif #ifndef SUNDIALS_DEPRECATED_EXPORT diff --git a/deps/AMICI/ThirdParty/sundials/include/sundials/sundials_linearsolver.h b/deps/AMICI/ThirdParty/sundials/include/sundials/sundials_linearsolver.h index bd6776581..ad3b05116 100644 --- a/deps/AMICI/ThirdParty/sundials/include/sundials/sundials_linearsolver.h +++ b/deps/AMICI/ThirdParty/sundials/include/sundials/sundials_linearsolver.h @@ -70,7 +70,8 @@ extern "C" { typedef enum { SUNLINEARSOLVER_DIRECT, SUNLINEARSOLVER_ITERATIVE, - SUNLINEARSOLVER_MATRIX_ITERATIVE + SUNLINEARSOLVER_MATRIX_ITERATIVE, + SUNLINEARSOLVER_MATRIX_EMBEDDED } SUNLinearSolver_Type; typedef enum { @@ -88,6 +89,7 @@ typedef enum { SUNLINEARSOLVER_SUPERLUMT, SUNLINEARSOLVER_CUSOLVERSP_BATCHQR, SUNLINEARSOLVER_MAGMADENSE, + SUNLINEARSOLVER_ONEMKLDENSE, SUNLINEARSOLVER_CUSTOM } SUNLinearSolver_ID; @@ -111,6 +113,7 @@ struct _generic_SUNLinearSolver_Ops { PSetupFn, PSolveFn); int (*setscalingvectors)(SUNLinearSolver, N_Vector, N_Vector); + int (*setzeroguess)(SUNLinearSolver, booleantype); int (*initialize)(SUNLinearSolver); int (*setup)(SUNLinearSolver, SUNMatrix); int (*solve)(SUNLinearSolver, SUNMatrix, N_Vector, @@ -153,6 +156,8 @@ SUNDIALS_EXPORT int SUNLinSolSetPreconditioner(SUNLinearSolver S, void* P_data, SUNDIALS_EXPORT int SUNLinSolSetScalingVectors(SUNLinearSolver S, N_Vector s1, N_Vector s2); +SUNDIALS_EXPORT int SUNLinSolSetZeroGuess(SUNLinearSolver S, booleantype onoff); + SUNDIALS_EXPORT int SUNLinSolInitialize(SUNLinearSolver S); SUNDIALS_EXPORT int SUNLinSolSetup(SUNLinearSolver S, SUNMatrix A); diff --git a/deps/AMICI/ThirdParty/sundials/include/sundials/sundials_math.h b/deps/AMICI/ThirdParty/sundials/include/sundials/sundials_math.h index eee19394a..f79344e5d 100644 --- a/deps/AMICI/ThirdParty/sundials/include/sundials/sundials_math.h +++ b/deps/AMICI/ThirdParty/sundials/include/sundials/sundials_math.h @@ -185,6 +185,42 @@ SUNDIALS_EXPORT realtype SUNRpowerI(realtype base, int exponent); SUNDIALS_EXPORT realtype SUNRpowerR(realtype base, realtype exponent); +/* + * ----------------------------------------------------------------- + * Function : SUNRCompare + * ----------------------------------------------------------------- + * Usage : int isNotEqual; + * realtype a, b; + * isNotEqual = SUNRCompare(a, b); + * ----------------------------------------------------------------- + * SUNRCompareTol returns 0 if the relative difference of a and b is + * less than or equal to 10*machine epsilon. If the relative + * difference is greater than 10*machine epsilon, it returns 1. The + * function handles the case where a or b are near zero as well as + * the case where a or b are inf/nan. + * ----------------------------------------------------------------- + */ + +SUNDIALS_EXPORT booleantype SUNRCompare(realtype a, realtype b); + +/* + * ----------------------------------------------------------------- + * Function : SUNRCompareTol + * ----------------------------------------------------------------- + * Usage : int isNotEqual; + * realtype a, b, tol; + * isNotEqual = SUNRCompareTol(a, b, tol); + * ----------------------------------------------------------------- + * SUNRCompareTol returns 0 if the relative difference of a and b is + * less than or equal to the provided tolerance. If the relative + * difference is greater than the tolerance, it returns 1. The + * function handles the case where a or b are near zero as well as + * the case where a or b are inf/nan. + * ----------------------------------------------------------------- + */ + +SUNDIALS_EXPORT booleantype SUNRCompareTol(realtype a, realtype b, realtype tol); + #ifdef __cplusplus } diff --git a/deps/AMICI/ThirdParty/sundials/include/sundials/sundials_matrix.h b/deps/AMICI/ThirdParty/sundials/include/sundials/sundials_matrix.h index b60f4f149..ebb73d528 100644 --- a/deps/AMICI/ThirdParty/sundials/include/sundials/sundials_matrix.h +++ b/deps/AMICI/ThirdParty/sundials/include/sundials/sundials_matrix.h @@ -59,6 +59,7 @@ extern "C" { typedef enum { SUNMATRIX_DENSE, SUNMATRIX_MAGMADENSE, + SUNMATRIX_ONEMKLDENSE, SUNMATRIX_BAND, SUNMATRIX_SPARSE, SUNMATRIX_SLUNRLOC, diff --git a/deps/AMICI/ThirdParty/sundials/include/sundials_debug.h b/deps/AMICI/ThirdParty/sundials/include/sundials_debug.h deleted file mode 100644 index dbf6c7b0b..000000000 --- a/deps/AMICI/ThirdParty/sundials/include/sundials_debug.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * ----------------------------------------------------------------- - * Programmer(s): Cody J. Balos @ LLNL - * ----------------------------------------------------------------- - * SUNDIALS Copyright Start - * Copyright (c) 2002-2021, Lawrence Livermore National Security - * and Southern Methodist University. - * All rights reserved. - * - * See the top-level LICENSE and NOTICE files for details. - * - * SPDX-License-Identifier: BSD-3-Clause - * SUNDIALS Copyright End - * ----------------------------------------------------------------- - * This header files defines internal utility functions and macros - * for SUNDIALS debugging. - * ----------------------------------------------------------------- - */ - -#ifndef _SUNDIALS_DEBUG_H -#define _SUNDIALS_DEBUG_H - -#include - -#ifdef __cplusplus /* wrapper to enable C++ usage */ -extern "C" { -#endif - -/* - * Macro which prints to stderr when in debug mode - */ -#ifdef SUNDIALS_DEBUG -#define SUNDIALS_DEBUG_PRINT(str) fprintf(stderr, str) -#else -#define SUNDIALS_DEBUG_PRINT(str) -#endif - -#ifdef __cplusplus /* wrapper to enable C++ usage */ -} -#endif - -#endif /* _SUNDIALS_DEBUG_H */ \ No newline at end of file diff --git a/deps/AMICI/ThirdParty/sundials/include/sunlinsol/sunlinsol_cusolversp_batchqr.h b/deps/AMICI/ThirdParty/sundials/include/sunlinsol/sunlinsol_cusolversp_batchqr.h deleted file mode 100644 index b4729f194..000000000 --- a/deps/AMICI/ThirdParty/sundials/include/sunlinsol/sunlinsol_cusolversp_batchqr.h +++ /dev/null @@ -1,111 +0,0 @@ -/* ---------------------------------------------------------------------------- - * Programmer(s): Cody J. Balos @ LLNL - * ---------------------------------------------------------------------------- - * Based on work by Donald Wilcox @ LBNL - * ---------------------------------------------------------------------------- - * SUNDIALS Copyright Start - * Copyright (c) 2002-2021, Lawrence Livermore National Security - * and Southern Methodist University. - * All rights reserved. - * - * See the top-level LICENSE and NOTICE files for details. - * - * SPDX-License-Identifier: BSD-3-Clause - * SUNDIALS Copyright End - * ---------------------------------------------------------------------------- - * Header file for cuSolverSp batched QR SUNLinearSolver interface. - * ----------------------------------------------------------------------------*/ - -#ifndef _SUNLINSOL_CUSOLVERSP_H -#define _SUNLINSOL_CUSOLVERSP_H - -#include -#include - -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - - -/* - * ---------------------------------------------------------------------------- - * PART I: cuSolverSp implementation of SUNLinearSolver - * ---------------------------------------------------------------------------- - */ - -struct _SUNLinearSolverContent_cuSolverSp_batchQR { - int last_flag; /* last return flag */ - booleantype first_factorize; /* is this the first factorization? */ - size_t internal_size; /* size of cusolver internal buffer for Q and R */ - size_t workspace_size; /* size of cusolver memory block for num. factorization */ - cusolverSpHandle_t cusolver_handle; /* cuSolverSp context */ - csrqrInfo_t info; /* opaque cusolver data structure */ - void* workspace; /* memory block used by cusolver */ - const char* desc; /* description of this linear solver */ -}; - -typedef struct _SUNLinearSolverContent_cuSolverSp_batchQR *SUNLinearSolverContent_cuSolverSp_batchQR; - - -/* - * ---------------------------------------------------------------------------- - * PART II: Functions exported by sunlinsol_sludist - * ---------------------------------------------------------------------------- - */ - -SUNDIALS_EXPORT SUNLinearSolver SUNLinSol_cuSolverSp_batchQR(N_Vector y, SUNMatrix A, - cusolverSpHandle_t cusol_handle); - - -/* - * ---------------------------------------------------------------------------- - * cuSolverSp implementations of SUNLinearSolver operations - * ---------------------------------------------------------------------------- - */ - -SUNDIALS_EXPORT SUNLinearSolver_Type SUNLinSolGetType_cuSolverSp_batchQR(SUNLinearSolver S); - -SUNDIALS_EXPORT SUNLinearSolver_ID SUNLinSolGetID_cuSolverSp_batchQR(SUNLinearSolver S); - -SUNDIALS_EXPORT int SUNLinSolInitialize_cuSolverSp_batchQR(SUNLinearSolver S); - -SUNDIALS_EXPORT int SUNLinSolSetup_cuSolverSp_batchQR(SUNLinearSolver S, - SUNMatrix A); - -SUNDIALS_EXPORT int SUNLinSolSolve_cuSolverSp_batchQR(SUNLinearSolver S, - SUNMatrix A, - N_Vector x, - N_Vector b, - realtype tol); - -SUNDIALS_EXPORT sunindextype SUNLinSolLastFlag_cuSolverSp_batchQR(SUNLinearSolver S); - -SUNDIALS_EXPORT int SUNLinSolFree_cuSolverSp_batchQR(SUNLinearSolver S); - - -/* - * ---------------------------------------------------------------------------- - * Additional get and set functions. - * ---------------------------------------------------------------------------- - */ - -SUNDIALS_EXPORT void SUNLinSol_cuSolverSp_batchQR_GetDescription(SUNLinearSolver S, - char** desc); - -SUNDIALS_EXPORT void SUNLinSol_cuSolverSp_batchQR_SetDescription(SUNLinearSolver S, - const char* desc); - -SUNDIALS_EXPORT void SUNLinSol_cuSolverSp_batchQR_GetDeviceSpace(SUNLinearSolver S, - size_t* cuSolverInternal, - size_t* cuSolverWorkspace); - - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/deps/AMICI/ThirdParty/sundials/include/sunlinsol/sunlinsol_pcg.h b/deps/AMICI/ThirdParty/sundials/include/sunlinsol/sunlinsol_pcg.h index 36441a5a7..549ed1cc8 100644 --- a/deps/AMICI/ThirdParty/sundials/include/sunlinsol/sunlinsol_pcg.h +++ b/deps/AMICI/ThirdParty/sundials/include/sunlinsol/sunlinsol_pcg.h @@ -43,6 +43,7 @@ extern "C" { struct _SUNLinearSolverContent_PCG { int maxl; int pretype; + booleantype zeroguess; int numiters; realtype resnorm; int last_flag; @@ -94,6 +95,8 @@ SUNDIALS_EXPORT int SUNLinSolSetPreconditioner_PCG(SUNLinearSolver S, SUNDIALS_EXPORT int SUNLinSolSetScalingVectors_PCG(SUNLinearSolver S, N_Vector s, N_Vector nul); +SUNDIALS_EXPORT int SUNLinSolSetZeroGuess_PCG(SUNLinearSolver S, + booleantype onoff); SUNDIALS_EXPORT int SUNLinSolSetup_PCG(SUNLinearSolver S, SUNMatrix nul); SUNDIALS_EXPORT int SUNLinSolSolve_PCG(SUNLinearSolver S, SUNMatrix nul, N_Vector x, N_Vector b, realtype tol); diff --git a/deps/AMICI/ThirdParty/sundials/include/sunlinsol/sunlinsol_spbcgs.h b/deps/AMICI/ThirdParty/sundials/include/sunlinsol/sunlinsol_spbcgs.h index f7d341bc3..9f928a4aa 100644 --- a/deps/AMICI/ThirdParty/sundials/include/sunlinsol/sunlinsol_spbcgs.h +++ b/deps/AMICI/ThirdParty/sundials/include/sunlinsol/sunlinsol_spbcgs.h @@ -47,6 +47,7 @@ extern "C" { struct _SUNLinearSolverContent_SPBCGS { int maxl; int pretype; + booleantype zeroguess; int numiters; realtype resnorm; int last_flag; @@ -102,6 +103,8 @@ SUNDIALS_EXPORT int SUNLinSolSetPreconditioner_SPBCGS(SUNLinearSolver S, SUNDIALS_EXPORT int SUNLinSolSetScalingVectors_SPBCGS(SUNLinearSolver S, N_Vector s1, N_Vector s2); +SUNDIALS_EXPORT int SUNLinSolSetZeroGuess_SPBCGS(SUNLinearSolver S, + booleantype onoff); SUNDIALS_EXPORT int SUNLinSolSetup_SPBCGS(SUNLinearSolver S, SUNMatrix A); SUNDIALS_EXPORT int SUNLinSolSolve_SPBCGS(SUNLinearSolver S, SUNMatrix A, N_Vector x, N_Vector b, realtype tol); diff --git a/deps/AMICI/ThirdParty/sundials/include/sunlinsol/sunlinsol_spfgmr.h b/deps/AMICI/ThirdParty/sundials/include/sunlinsol/sunlinsol_spfgmr.h index 543492ce8..6aec1d1a3 100644 --- a/deps/AMICI/ThirdParty/sundials/include/sunlinsol/sunlinsol_spfgmr.h +++ b/deps/AMICI/ThirdParty/sundials/include/sunlinsol/sunlinsol_spfgmr.h @@ -50,6 +50,7 @@ struct _SUNLinearSolverContent_SPFGMR { int pretype; int gstype; int max_restarts; + booleantype zeroguess; int numiters; realtype resnorm; int last_flag; @@ -111,6 +112,8 @@ SUNDIALS_EXPORT int SUNLinSolSetPreconditioner_SPFGMR(SUNLinearSolver S, SUNDIALS_EXPORT int SUNLinSolSetScalingVectors_SPFGMR(SUNLinearSolver S, N_Vector s1, N_Vector s2); +SUNDIALS_EXPORT int SUNLinSolSetZeroGuess_SPFGMR(SUNLinearSolver S, + booleantype onoff); SUNDIALS_EXPORT int SUNLinSolSetup_SPFGMR(SUNLinearSolver S, SUNMatrix A); SUNDIALS_EXPORT int SUNLinSolSolve_SPFGMR(SUNLinearSolver S, SUNMatrix A, N_Vector x, N_Vector b, realtype tol); diff --git a/deps/AMICI/ThirdParty/sundials/include/sunlinsol/sunlinsol_spgmr.h b/deps/AMICI/ThirdParty/sundials/include/sunlinsol/sunlinsol_spgmr.h index ef7cf5066..55eaef938 100644 --- a/deps/AMICI/ThirdParty/sundials/include/sunlinsol/sunlinsol_spgmr.h +++ b/deps/AMICI/ThirdParty/sundials/include/sunlinsol/sunlinsol_spgmr.h @@ -52,6 +52,7 @@ struct _SUNLinearSolverContent_SPGMR { int pretype; int gstype; int max_restarts; + booleantype zeroguess; int numiters; realtype resnorm; int last_flag; @@ -112,6 +113,8 @@ SUNDIALS_EXPORT int SUNLinSolSetPreconditioner_SPGMR(SUNLinearSolver S, SUNDIALS_EXPORT int SUNLinSolSetScalingVectors_SPGMR(SUNLinearSolver S, N_Vector s1, N_Vector s2); +SUNDIALS_EXPORT int SUNLinSolSetZeroGuess_SPGMR(SUNLinearSolver S, + booleantype onff); SUNDIALS_EXPORT int SUNLinSolSetup_SPGMR(SUNLinearSolver S, SUNMatrix A); SUNDIALS_EXPORT int SUNLinSolSolve_SPGMR(SUNLinearSolver S, SUNMatrix A, N_Vector x, N_Vector b, realtype tol); diff --git a/deps/AMICI/ThirdParty/sundials/include/sunlinsol/sunlinsol_sptfqmr.h b/deps/AMICI/ThirdParty/sundials/include/sunlinsol/sunlinsol_sptfqmr.h index f40352bc0..bbdcd13b0 100644 --- a/deps/AMICI/ThirdParty/sundials/include/sunlinsol/sunlinsol_sptfqmr.h +++ b/deps/AMICI/ThirdParty/sundials/include/sunlinsol/sunlinsol_sptfqmr.h @@ -45,6 +45,7 @@ extern "C" { struct _SUNLinearSolverContent_SPTFQMR { int maxl; int pretype; + booleantype zeroguess; int numiters; realtype resnorm; int last_flag; @@ -102,6 +103,8 @@ SUNDIALS_EXPORT int SUNLinSolSetPreconditioner_SPTFQMR(SUNLinearSolver S, SUNDIALS_EXPORT int SUNLinSolSetScalingVectors_SPTFQMR(SUNLinearSolver S, N_Vector s1, N_Vector s2); +SUNDIALS_EXPORT int SUNLinSolSetZeroGuess_SPTFQMR(SUNLinearSolver S, + booleantype onoff); SUNDIALS_EXPORT int SUNLinSolSetup_SPTFQMR(SUNLinearSolver S, SUNMatrix A); SUNDIALS_EXPORT int SUNLinSolSolve_SPTFQMR(SUNLinearSolver S, SUNMatrix A, N_Vector x, N_Vector b, realtype tol); diff --git a/deps/AMICI/ThirdParty/sundials/include/sunlinsol/sunlinsol_superludist.h b/deps/AMICI/ThirdParty/sundials/include/sunlinsol/sunlinsol_superludist.h index 34512c3f2..80c9b8c66 100644 --- a/deps/AMICI/ThirdParty/sundials/include/sunlinsol/sunlinsol_superludist.h +++ b/deps/AMICI/ThirdParty/sundials/include/sunlinsol/sunlinsol_superludist.h @@ -40,7 +40,7 @@ #include #include -#if (SUPERLU_DIST_MAJOR_VERSION >= 6) && (SUPERLU_DIST_MINOR_VERSION >= 3) +#if (SUPERLU_DIST_MAJOR_VERSION >= 7) || ((SUPERLU_DIST_MAJOR_VERSION == 6) && (SUPERLU_DIST_MINOR_VERSION >= 3)) #define xLUstructInit dLUstructInit #define xScalePermstructInit dScalePermstructInit #define xScalePermstructFree dScalePermstructFree diff --git a/deps/AMICI/ThirdParty/sundials/include/sunmatrix/sunmatrix_cusparse.h b/deps/AMICI/ThirdParty/sundials/include/sunmatrix/sunmatrix_cusparse.h deleted file mode 100644 index 458413826..000000000 --- a/deps/AMICI/ThirdParty/sundials/include/sunmatrix/sunmatrix_cusparse.h +++ /dev/null @@ -1,134 +0,0 @@ -/* - * ----------------------------------------------------------------- - * Programmer(s): Cody J. Balos @ LLNL - * ----------------------------------------------------------------- - * SUNDIALS Copyright Start - * Copyright (c) 2002-2021, Lawrence Livermore National Security - * and Southern Methodist University. - * All rights reserved. - * - * See the top-level LICENSE and NOTICE files for details. - * - * SPDX-License-Identifier: BSD-3-Clause - * SUNDIALS Copyright End - * ----------------------------------------------------------------- - * This is the header file is for the cuSPARSE implementation of the - * SUNMATRIX module. - * ----------------------------------------------------------------- - */ - -#ifndef _SUNMATRIX_CUSPARSE_H -#define _SUNMATRIX_CUSPARSE_H - -#include - -#include -#include - -#include -#include -#include - -#ifdef __cplusplus /* wrapper to enable C++ usage */ -extern "C" { -#endif - -/* ------------------------------------------ - * Implementation of SUNMATRIX_CUSPARSE - * ------------------------------------------ */ - -/* storage formats */ -#define SUNMAT_CUSPARSE_CSR 0 -#define SUNMAT_CUSPARSE_BCSR 1 - -struct _SUNMatrix_Content_cuSparse { - int M; - int N; - int NNZ; - int nblocks; - int blockrows; - int blockcols; - int blocknnz; - int sparse_type; - booleantype own_matd; - booleantype own_exec; - booleantype fixed_pattern; - booleantype matvec_issetup; - SUNMemory colind; - SUNMemory rowptrs; - SUNMemory data; - SUNMemoryHelper mem_helper; - cusparseMatDescr_t mat_descr; -#if CUDART_VERSION >= 11000 - SUNMemory dBufferMem; - size_t bufferSize; - cusparseDnVecDescr_t vecX, vecY; - cusparseSpMatDescr_t spmat_descr; -#endif - cusparseHandle_t cusp_handle; - SUNCudaExecPolicy* exec_policy; -}; - -typedef struct _SUNMatrix_Content_cuSparse *SUNMatrix_Content_cuSparse; - -/* ------------------------------------------------------------------ - * Constructors. - * ------------------------------------------------------------------ */ - -SUNDIALS_EXPORT SUNMatrix SUNMatrix_cuSparse_NewCSR(int M, int N, int NNZ, cusparseHandle_t cusp); -SUNDIALS_EXPORT SUNMatrix SUNMatrix_cuSparse_MakeCSR(cusparseMatDescr_t mat_descr, int M, int N, int NNZ, - int *rowptrs , int *colind , realtype *data, - cusparseHandle_t cusp); - -/* Creates a CSR block-diagonal matrix where each block shares the same sparsity structure. - Reduces memory usage by only storing the row pointers and column indices for one block. */ -SUNDIALS_EXPORT SUNMatrix SUNMatrix_cuSparse_NewBlockCSR(int nblocks, int blockrows, int blockcols, - int blocknnz, cusparseHandle_t cusp); - - -/* ------------------------------------------------------------------ - * Implementation specific routines. - * ------------------------------------------------------------------ */ - -SUNDIALS_EXPORT int SUNMatrix_cuSparse_SparseType(SUNMatrix A); -SUNDIALS_EXPORT int SUNMatrix_cuSparse_Rows(SUNMatrix A); -SUNDIALS_EXPORT int SUNMatrix_cuSparse_Columns(SUNMatrix A); -SUNDIALS_EXPORT int SUNMatrix_cuSparse_NNZ(SUNMatrix A); -SUNDIALS_EXPORT int* SUNMatrix_cuSparse_IndexPointers(SUNMatrix A); -SUNDIALS_EXPORT int* SUNMatrix_cuSparse_IndexValues(SUNMatrix A); -SUNDIALS_EXPORT realtype* SUNMatrix_cuSparse_Data(SUNMatrix A); - -SUNDIALS_EXPORT int SUNMatrix_cuSparse_SetFixedPattern(SUNMatrix A, booleantype yesno); -SUNDIALS_EXPORT int SUNMatrix_cuSparse_SetKernelExecPolicy(SUNMatrix A, SUNCudaExecPolicy* exec_policy); -SUNDIALS_EXPORT int SUNMatrix_cuSparse_NumBlocks(SUNMatrix A); -SUNDIALS_EXPORT int SUNMatrix_cuSparse_BlockRows(SUNMatrix A); -SUNDIALS_EXPORT int SUNMatrix_cuSparse_BlockColumns(SUNMatrix A); -SUNDIALS_EXPORT int SUNMatrix_cuSparse_BlockNNZ(SUNMatrix A); -SUNDIALS_EXPORT realtype* SUNMatrix_cuSparse_BlockData(SUNMatrix A, int blockidx); -SUNDIALS_EXPORT cusparseMatDescr_t SUNMatrix_cuSparse_MatDescr(SUNMatrix A); -SUNDIALS_EXPORT int SUNMatrix_cuSparse_CopyToDevice(SUNMatrix device, realtype* h_data, - int* h_idxptrs, int* h_idxvals); -SUNDIALS_EXPORT int SUNMatrix_cuSparse_CopyFromDevice(SUNMatrix device, realtype* h_data, - int* h_idxptrs, int* h_idxvals); - - -/* ------------------------------------------------------------------ - * SUNMatrix API routines. - * ------------------------------------------------------------------ */ - -SUNDIALS_EXPORT SUNMatrix_ID SUNMatGetID_cuSparse(SUNMatrix A); -SUNDIALS_EXPORT SUNMatrix SUNMatClone_cuSparse(SUNMatrix A); -SUNDIALS_EXPORT void SUNMatDestroy_cuSparse(SUNMatrix A); -SUNDIALS_EXPORT int SUNMatZero_cuSparse(SUNMatrix A); -SUNDIALS_EXPORT int SUNMatCopy_cuSparse(SUNMatrix A, SUNMatrix B); -SUNDIALS_EXPORT int SUNMatScaleAdd_cuSparse(realtype c, SUNMatrix A, SUNMatrix B); -SUNDIALS_EXPORT int SUNMatScaleAddI_cuSparse(realtype c, SUNMatrix A); -SUNDIALS_EXPORT int SUNMatMatvecSetup_cuSparse(SUNMatrix A); -SUNDIALS_EXPORT int SUNMatMatvec_cuSparse(SUNMatrix A, N_Vector x, N_Vector y); - - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/deps/AMICI/ThirdParty/sundials/src/cvodes/README.md b/deps/AMICI/ThirdParty/sundials/src/cvodes/README.md index 3e48f6509..392e3b198 100644 --- a/deps/AMICI/ThirdParty/sundials/src/cvodes/README.md +++ b/deps/AMICI/ThirdParty/sundials/src/cvodes/README.md @@ -1,5 +1,5 @@ # CVODES -### Version 5.7.0 (Jan 2021) +### Version 5.8.0 (Sep 2021) **Alan C. Hindmarsh, Radu Serban, Cody J. Balos, David J. Gardner, and Carol S. Woodward, Center for Applied Scientific Computing, LLNL** @@ -45,11 +45,11 @@ the "SUNDIALS Release History" appendix of the CVODES User Guide. ## References * A. C. Hindmarsh, R. Serban, C. J. Balos, D. J. Gardner, D. R. Reynolds - and C. S. Woodward, "User Documentation for CVODES v5.7.0," - LLNL technical report UCRL-SM-208111, Jan 2021. + and C. S. Woodward, "User Documentation for CVODES v5.8.0," + LLNL technical report UCRL-SM-208111, Sep 2021. -* A. C. Hindmarsh and R. Serban, "Example Programs for CVODES v5.7.0," - LLNL technical report UCRL-SM-208115, Jan 2021. +* A. C. Hindmarsh and R. Serban, "Example Programs for CVODES v5.8.0," + LLNL technical report UCRL-SM-208115, Sep 2021. * R. Serban and A. C. Hindmarsh, "CVODES: the Sensitivity-Enabled ODE solver in SUNDIALS," Proceedings of IDETC/CIE 2005, Sept. 2005, diff --git a/deps/AMICI/ThirdParty/sundials/src/cvodes/cvodes.c b/deps/AMICI/ThirdParty/sundials/src/cvodes/cvodes.c index 3033ea3de..a0d8a168e 100644 --- a/deps/AMICI/ThirdParty/sundials/src/cvodes/cvodes.c +++ b/deps/AMICI/ThirdParty/sundials/src/cvodes/cvodes.c @@ -828,6 +828,15 @@ int CVodeInit(void *cvode_mem, CVRhsFn f, realtype t0, N_Vector y0) return(CV_MEM_FAIL); } + /* Input checks complete at this point and history array allocated */ + + /* Copy the input parameters into CVODE state */ + cv_mem->cv_f = f; + cv_mem->cv_tn = t0; + + /* Initialize zn[0] in the history array */ + N_VScale(ONE, y0, cv_mem->cv_zn[0]); + /* create a Newton nonlinear solver object by default */ NLS = SUNNonlinSol_Newton(y0); @@ -855,11 +864,6 @@ int CVodeInit(void *cvode_mem, CVRhsFn f, realtype t0, N_Vector y0) /* All error checking is complete at this point */ - /* Copy the input parameters into CVODES state */ - - cv_mem->cv_f = f; - cv_mem->cv_tn = t0; - /* Set step parameters */ cv_mem->cv_q = 1; @@ -884,10 +888,6 @@ int CVodeInit(void *cvode_mem, CVRhsFn f, realtype t0, N_Vector y0) cv_mem->cv_forceSetup = SUNFALSE; - /* Initialize zn[0] in the history array */ - - N_VScale(ONE, y0, cv_mem->cv_zn[0]); - /* Initialize all the counters */ cv_mem->cv_nst = 0; diff --git a/deps/AMICI/ThirdParty/sundials/src/cvodes/cvodes_impl.h b/deps/AMICI/ThirdParty/sundials/src/cvodes/cvodes_impl.h index c2f137411..aa24dd674 100644 --- a/deps/AMICI/ThirdParty/sundials/src/cvodes/cvodes_impl.h +++ b/deps/AMICI/ThirdParty/sundials/src/cvodes/cvodes_impl.h @@ -377,6 +377,7 @@ typedef struct CVodeMemRec { booleantype sens_solve; /* flag indicating if the current solve is a staggered or staggered1 sensitivity solve */ + CVRhsFn nls_f; /* f(t,y(t)) used in the nonlinear solver */ int convfail; /* flag to indicate when a Jacobian update may be needed */ @@ -413,7 +414,7 @@ typedef struct CVodeMemRec { N_Vector vtemp1, N_Vector vtemp2, N_Vector vtemp3); int (*cv_lsolve)(struct CVodeMemRec *cv_mem, N_Vector b, N_Vector weight, - N_Vector ycur, N_Vector fcur); + N_Vector ycur, N_Vector fcur); int (*cv_lfree)(struct CVodeMemRec *cv_mem); diff --git a/deps/AMICI/ThirdParty/sundials/src/cvodes/cvodes_ls.c b/deps/AMICI/ThirdParty/sundials/src/cvodes/cvodes_ls.c index 7185ca4c2..ef368e1ad 100644 --- a/deps/AMICI/ThirdParty/sundials/src/cvodes/cvodes_ls.c +++ b/deps/AMICI/ThirdParty/sundials/src/cvodes/cvodes_ls.c @@ -167,7 +167,8 @@ int CVodeSetLinearSolver(void *cvode_mem, SUNLinearSolver LS, /* Set flags based on LS type */ iterative = (LSType != SUNLINEARSOLVER_DIRECT); - matrixbased = (LSType != SUNLINEARSOLVER_ITERATIVE); + matrixbased = ((LSType != SUNLINEARSOLVER_ITERATIVE) && + (LSType != SUNLINEARSOLVER_MATRIX_EMBEDDED)); /* Test if vector is compatible with LS interface */ if ( (cv_mem->cv_tempv->ops->nvconst == NULL) || @@ -177,6 +178,13 @@ int CVodeSetLinearSolver(void *cvode_mem, SUNLinearSolver LS, return(CVLS_ILL_INPUT); } + /* Ensure that A is NULL when LS is matrix-embedded */ + if ((LSType == SUNLINEARSOLVER_MATRIX_EMBEDDED) && (A != NULL)) { + cvProcessError(cv_mem, CVLS_ILL_INPUT, "CVLS", "CVodeSetLinearSolver", + "Incompatible inputs: matrix-embedded LS requires NULL matrix"); + return(CVLS_ILL_INPUT); + } + /* Check for compatible LS type, matrix and "atimes" support */ if (iterative) { @@ -186,13 +194,14 @@ int CVodeSetLinearSolver(void *cvode_mem, SUNLinearSolver LS, return(CVLS_ILL_INPUT); } - if (!matrixbased && LS->ops->setatimes == NULL) { + if (!matrixbased && (LSType != SUNLINEARSOLVER_MATRIX_EMBEDDED) && + (LS->ops->setatimes == NULL)) { cvProcessError(cv_mem, CVLS_ILL_INPUT, "CVSLS", "CVodeSetLinearSolver", "Incompatible inputs: iterative LS must support ATimes routine"); return(CVLS_ILL_INPUT); } - if (matrixbased && A == NULL) { + if (matrixbased && (A == NULL)) { cvProcessError(cv_mem, CVLS_ILL_INPUT, "CVSLS", "CVodeSetLinearSolver", "Incompatible inputs: matrix-iterative LS requires non-NULL matrix"); return(CVLS_ILL_INPUT); @@ -320,7 +329,7 @@ int CVodeSetLinearSolver(void *cvode_mem, SUNLinearSolver LS, if (iterative) cvls_mem->nrmfac = SUNRsqrt( N_VGetLength(cvls_mem->ytemp) ); - /* Check if soltuion scaling should be enabled */ + /* Check if solution scaling should be enabled */ if (matrixbased && cv_mem->cv_lmm == CV_BDF) cvls_mem->scalesol = SUNTRUE; else @@ -1494,6 +1503,12 @@ int cvLsInitialize(CVodeMem cv_mem) if ( (cvls_mem->A == NULL) && (cvls_mem->pset == NULL) ) cv_mem->cv_lsetup = NULL; + /* When using a matrix-embedded linear solver, disable lsetup call and solution scaling */ + if (SUNLinSolGetType(cvls_mem->LS) == SUNLINEARSOLVER_MATRIX_EMBEDDED) { + cv_mem->cv_lsetup = NULL; + cvls_mem->scalesol = SUNFALSE; + } + /* Call LS initialize routine, and return result */ cvls_mem->last_flag = SUNLinSolInitialize(cvls_mem->LS); return(cvls_mem->last_flag); @@ -1530,6 +1545,12 @@ int cvLsSetup(CVodeMem cv_mem, int convfail, N_Vector ypred, } cvls_mem = (CVLsMem) cv_mem->cv_lmem; + /* Immediately return when using matrix-embedded linear solver */ + if (SUNLinSolGetType(cvls_mem->LS) == SUNLINEARSOLVER_MATRIX_EMBEDDED) { + cvls_mem->last_flag = CVLS_SUCCESS; + return(cvls_mem->last_flag); + } + /* Set CVLs N_Vector pointers to current solution and rhs */ cvls_mem->ycur = ypred; cvls_mem->fcur = fpred; @@ -1698,6 +1719,10 @@ int cvLsSolve(CVodeMem cv_mem, N_Vector b, N_Vector weight, /* Set initial guess x = 0 to LS */ N_VConst(ZERO, cvls_mem->x); + /* Set zero initial guess flag */ + retval = SUNLinSolSetZeroGuess(cvls_mem->LS, SUNTRUE); + if (retval != SUNLS_SUCCESS) return(-1); + /* If a user-provided jtsetup routine is supplied, call that here */ if (cvls_mem->jtsetup) { cvls_mem->last_flag = cvls_mem->jtsetup(cv_mem->cv_tn, ynow, fnow, diff --git a/deps/AMICI/ThirdParty/sundials/src/cvodes/cvodes_nls.c b/deps/AMICI/ThirdParty/sundials/src/cvodes/cvodes_nls.c index f7131b068..0d1ccca1c 100644 --- a/deps/AMICI/ThirdParty/sundials/src/cvodes/cvodes_nls.c +++ b/deps/AMICI/ThirdParty/sundials/src/cvodes/cvodes_nls.c @@ -109,6 +109,42 @@ int CVodeSetNonlinearSolver(void *cvode_mem, SUNNonlinearSolver NLS) /* Reset the acnrmcur flag to SUNFALSE */ cv_mem->cv_acnrmcur = SUNFALSE; + /* Set the nonlinear system RHS function */ + if (!(cv_mem->cv_f)) { + cvProcessError(cv_mem, CV_ILL_INPUT, "CVODES", "CVodeSetNonlinearSolver", + "The ODE RHS function is NULL"); + return(CV_ILL_INPUT); + } + cv_mem->nls_f = cv_mem->cv_f; + + return(CV_SUCCESS); +} + + +/*--------------------------------------------------------------- + CVodeSetNlsRhsFn: + + This routine sets an alternative user-supplied ODE right-hand + side function to use in the evaluation of nonlinear system + functions. + ---------------------------------------------------------------*/ +int CVodeSetNlsRhsFn(void *cvode_mem, CVRhsFn f) +{ + CVodeMem cv_mem; + + if (cvode_mem==NULL) { + cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeSetNlsRhsFn", + MSGCV_NO_MEM); + return(CV_MEM_NULL); + } + + cv_mem = (CVodeMem) cvode_mem; + + if (f) + cv_mem->nls_f = f; + else + cv_mem->nls_f = cv_mem->cv_f; + return(CV_SUCCESS); } @@ -281,7 +317,7 @@ static int cvNlsConvTest(SUNNonlinearSolver NLS, N_Vector ycor, N_Vector delta, dcon = del * SUNMIN(ONE, cv_mem->cv_crate) / tol; if (dcon <= ONE) { - cv_mem->cv_acnrm = (m==0) ? del : N_VWrmsNorm(ycor, cv_mem->cv_ewt); + cv_mem->cv_acnrm = (m==0) ? del : N_VWrmsNorm(ycor, ewt); cv_mem->cv_acnrmcur = SUNTRUE; return(CV_SUCCESS); /* Nonlinear system was solved successfully */ } @@ -303,8 +339,7 @@ static int cvNlsResidual(N_Vector ycor, N_Vector res, void* cvode_mem) int retval; if (cvode_mem == NULL) { - cvProcessError(NULL, CV_MEM_NULL, "CVODE", - "cvNlsResidual", MSGCV_NO_MEM); + cvProcessError(NULL, CV_MEM_NULL, "CVODE", "cvNlsResidual", MSGCV_NO_MEM); return(CV_MEM_NULL); } cv_mem = (CVodeMem) cvode_mem; @@ -313,8 +348,8 @@ static int cvNlsResidual(N_Vector ycor, N_Vector res, void* cvode_mem) N_VLinearSum(ONE, cv_mem->cv_zn[0], ONE, ycor, cv_mem->cv_y); /* evaluate the rhs function */ - retval = cv_mem->cv_f(cv_mem->cv_tn, cv_mem->cv_y, cv_mem->cv_ftemp, - cv_mem->cv_user_data); + retval = cv_mem->nls_f(cv_mem->cv_tn, cv_mem->cv_y, cv_mem->cv_ftemp, + cv_mem->cv_user_data); cv_mem->cv_nfe++; if (retval < 0) return(CV_RHSFUNC_FAIL); if (retval > 0) return(RHSFUNC_RECVR); @@ -342,8 +377,8 @@ static int cvNlsFPFunction(N_Vector ycor, N_Vector res, void* cvode_mem) N_VLinearSum(ONE, cv_mem->cv_zn[0], ONE, ycor, cv_mem->cv_y); /* evaluate the rhs function */ - retval = cv_mem->cv_f(cv_mem->cv_tn, cv_mem->cv_y, res, - cv_mem->cv_user_data); + retval = cv_mem->nls_f(cv_mem->cv_tn, cv_mem->cv_y, res, + cv_mem->cv_user_data); cv_mem->cv_nfe++; if (retval < 0) return(CV_RHSFUNC_FAIL); if (retval > 0) return(RHSFUNC_RECVR); diff --git a/deps/AMICI/ThirdParty/sundials/src/cvodes/cvodes_nls_sim.c b/deps/AMICI/ThirdParty/sundials/src/cvodes/cvodes_nls_sim.c index f084418b7..706124be9 100644 --- a/deps/AMICI/ThirdParty/sundials/src/cvodes/cvodes_nls_sim.c +++ b/deps/AMICI/ThirdParty/sundials/src/cvodes/cvodes_nls_sim.c @@ -187,6 +187,15 @@ int CVodeSetNonlinearSolverSensSim(void *cvode_mem, SUNNonlinearSolver NLS) /* Reset the acnrmcur flag to SUNFALSE */ cv_mem->cv_acnrmcur = SUNFALSE; + /* Set the nonlinear system RHS function */ + if (!(cv_mem->cv_f)) { + cvProcessError(cv_mem, CV_ILL_INPUT, "CVODES", + "CVodeSetNonlinearSolverSensSim", + "The ODE RHS function is NULL"); + return(CV_ILL_INPUT); + } + cv_mem->nls_f = cv_mem->cv_f; + return(CV_SUCCESS); } @@ -451,8 +460,8 @@ static int cvNlsResidualSensSim(N_Vector ycorSim, N_Vector resSim, void* cvode_m N_VLinearSum(ONE, cv_mem->cv_zn[0], ONE, ycor, cv_mem->cv_y); /* evaluate the rhs function */ - retval = cv_mem->cv_f(cv_mem->cv_tn, cv_mem->cv_y, cv_mem->cv_ftemp, - cv_mem->cv_user_data); + retval = cv_mem->nls_f(cv_mem->cv_tn, cv_mem->cv_y, cv_mem->cv_ftemp, + cv_mem->cv_user_data); cv_mem->cv_nfe++; if (retval < 0) return(CV_RHSFUNC_FAIL); if (retval > 0) return(RHSFUNC_RECVR); @@ -515,8 +524,8 @@ static int cvNlsFPFunctionSensSim(N_Vector ycorSim, N_Vector resSim, void* cvode N_VLinearSum(ONE, cv_mem->cv_zn[0], ONE, ycor, cv_mem->cv_y); /* evaluate the rhs function */ - retval = cv_mem->cv_f(cv_mem->cv_tn, cv_mem->cv_y, res, - cv_mem->cv_user_data); + retval = cv_mem->nls_f(cv_mem->cv_tn, cv_mem->cv_y, res, + cv_mem->cv_user_data); cv_mem->cv_nfe++; if (retval < 0) return(CV_RHSFUNC_FAIL); if (retval > 0) return(RHSFUNC_RECVR); diff --git a/deps/AMICI/ThirdParty/sundials/src/idas/README.md b/deps/AMICI/ThirdParty/sundials/src/idas/README.md index 6908b1d12..a597e5e1d 100644 --- a/deps/AMICI/ThirdParty/sundials/src/idas/README.md +++ b/deps/AMICI/ThirdParty/sundials/src/idas/README.md @@ -1,5 +1,5 @@ # IDAS -### Version 4.7.0 (Jan 2021) +### Version 4.8.0 (Sep 2021) **Radu Serban, Cosmin Petra, Alan C. Hindmarsh, Cody J. Balos, David J. Gardner, and Carol S. Woodward, Center for Applied Scientific Computing, LLNL** @@ -44,11 +44,11 @@ the "SUNDIALS Release History" appendix of the IDAS User Guide. ## References * R. Serban, C. Petra, A. C. Hindmarsh, C. J. Balos, D. J. Gardner, - D. R. Reynolds and C. S. Woodward, "User Documentation for IDAS v4.7.0," - LLNL technical report UCRL-SM-234051, Jan 2021. + D. R. Reynolds and C. S. Woodward, "User Documentation for IDAS v4.8.0," + LLNL technical report UCRL-SM-234051, Sep 2021. -* R. Serban and A.C. Hindmarsh, "Example Programs for IDAS v4.7.0," - LLNL technical report LLNL-TR-437091, Jan 2021. +* R. Serban and A.C. Hindmarsh, "Example Programs for IDAS v4.8.0," + LLNL technical report LLNL-TR-437091, Sep 2021. * A. C. Hindmarsh, P. N. Brown, K. E. Grant, S. L. Lee, R. Serban, D. E. Shumaker, and C. S. Woodward, "SUNDIALS, Suite of Nonlinear and diff --git a/deps/AMICI/ThirdParty/sundials/src/idas/idas.c b/deps/AMICI/ThirdParty/sundials/src/idas/idas.c index 96f9de0fc..634a71581 100644 --- a/deps/AMICI/ThirdParty/sundials/src/idas/idas.c +++ b/deps/AMICI/ThirdParty/sundials/src/idas/idas.c @@ -616,6 +616,16 @@ int IDAInit(void *ida_mem, IDAResFn res, return(IDA_MEM_FAIL); } + /* Input checks complete at this point and history array allocated */ + + /* Copy the input parameters into IDA memory block */ + IDA_mem->ida_res = res; + IDA_mem->ida_tn = t0; + + /* Initialize the phi array */ + N_VScale(ONE, yy0, IDA_mem->ida_phi[0]); + N_VScale(ONE, yp0, IDA_mem->ida_phi[1]); + /* create a Newton nonlinear solver object by default */ NLS = SUNNonlinSol_Newton(yy0); diff --git a/deps/AMICI/ThirdParty/sundials/src/idas/idas_impl.h b/deps/AMICI/ThirdParty/sundials/src/idas/idas_impl.h index 9129c4e70..b4e39a96a 100644 --- a/deps/AMICI/ThirdParty/sundials/src/idas/idas_impl.h +++ b/deps/AMICI/ThirdParty/sundials/src/idas/idas_impl.h @@ -415,6 +415,9 @@ typedef struct IDAMemRec { booleantype simMallocDone; booleantype stgMallocDone; + IDAResFn nls_res; /* F(t,y(t),y'(t))=0; used in the nonlinear + solver */ + /*------------------ Linear Solver Data ------------------*/ diff --git a/deps/AMICI/ThirdParty/sundials/src/idas/idas_ls.c b/deps/AMICI/ThirdParty/sundials/src/idas/idas_ls.c index c3e66ee5d..41354095e 100644 --- a/deps/AMICI/ThirdParty/sundials/src/idas/idas_ls.c +++ b/deps/AMICI/ThirdParty/sundials/src/idas/idas_ls.c @@ -130,7 +130,8 @@ int IDASetLinearSolver(void *ida_mem, SUNLinearSolver LS, SUNMatrix A) /* Set flags based on LS type */ iterative = (LSType != SUNLINEARSOLVER_DIRECT); - matrixbased = (LSType != SUNLINEARSOLVER_ITERATIVE); + matrixbased = ((LSType != SUNLINEARSOLVER_ITERATIVE) && + (LSType != SUNLINEARSOLVER_MATRIX_EMBEDDED)); /* Test if vector is compatible with LS interface */ if (IDA_mem->ida_tempv1->ops->nvconst == NULL || @@ -140,6 +141,13 @@ int IDASetLinearSolver(void *ida_mem, SUNLinearSolver LS, SUNMatrix A) return(IDALS_ILL_INPUT); } + /* Ensure that A is NULL when LS is matrix-embedded */ + if ((LSType == SUNLINEARSOLVER_MATRIX_EMBEDDED) && (A != NULL)) { + IDAProcessError(IDA_mem, IDALS_ILL_INPUT, "IDASLS", "IDASetLinearSolver", + "Incompatible inputs: matrix-embedded LS requires NULL matrix"); + return(IDALS_ILL_INPUT); + } + /* Check for compatible LS type, matrix and "atimes" support */ if (iterative) { @@ -149,19 +157,22 @@ int IDASetLinearSolver(void *ida_mem, SUNLinearSolver LS, SUNMatrix A) return(IDALS_ILL_INPUT); } - if (LS->ops->resid == NULL || LS->ops->numiters == NULL) { - IDAProcessError(IDA_mem, IDALS_ILL_INPUT, "IDASLS", "IDASetLinearSolver", - "Iterative LS object requires 'resid' and 'numiters' routines"); - return(IDALS_ILL_INPUT); + if (LSType != SUNLINEARSOLVER_MATRIX_EMBEDDED) { + if (LS->ops->resid == NULL || LS->ops->numiters == NULL) { + IDAProcessError(IDA_mem, IDALS_ILL_INPUT, "IDASLS", "IDASetLinearSolver", + "Iterative LS object requires 'resid' and 'numiters' routines"); + return(IDALS_ILL_INPUT); + } } - if (!matrixbased && LS->ops->setatimes == NULL) { + if (!matrixbased && (LSType != SUNLINEARSOLVER_MATRIX_EMBEDDED) && + (LS->ops->setatimes == NULL)) { IDAProcessError(IDA_mem, IDALS_ILL_INPUT, "IDASLS", "IDASetLinearSolver", "Incompatible inputs: iterative LS must support ATimes routine"); return(IDALS_ILL_INPUT); } - if (matrixbased && A == NULL) { + if (matrixbased && (A == NULL)) { IDAProcessError(IDA_mem, IDALS_ILL_INPUT, "IDASLS", "IDASetLinearSolver", "Incompatible inputs: matrix-iterative LS requires non-NULL matrix"); return(IDALS_ILL_INPUT); @@ -1026,7 +1037,7 @@ int idaLsDenseDQJac(realtype tt, realtype c_j, N_Vector yy, y_data[j] += inc; yp_data[j] += c_j*inc; - retval = idals_mem->jt_res(tt, yy, yp, rtemp, IDA_mem->ida_user_data); + retval = IDA_mem->ida_res(tt, yy, yp, rtemp, IDA_mem->ida_user_data); idals_mem->nreDQ++; if (retval != 0) break; @@ -1220,7 +1231,7 @@ int idaLsDQJtimes(realtype tt, N_Vector yy, N_Vector yp, N_Vector rr, N_VLinearSum(c_j*sig, v, ONE, yp, yp_tmp); /* Call res for Jv = F(t, y_tmp, yp_tmp), and return if it failed. */ - retval = IDA_mem->ida_res(tt, y_tmp, yp_tmp, Jv, IDA_mem->ida_user_data); + retval = idals_mem->jt_res(tt, y_tmp, yp_tmp, Jv, IDA_mem->ida_user_data); idals_mem->nreDQ++; if (retval == 0) break; if (retval < 0) return(-1); @@ -1316,6 +1327,12 @@ int idaLsInitialize(IDAMem IDA_mem) if ( (idals_mem->J == NULL) && (idals_mem->pset == NULL) ) IDA_mem->ida_lsetup = NULL; + /* When using a matrix-embedded linear solver disable lsetup call */ + if (SUNLinSolGetType(idals_mem->LS) == SUNLINEARSOLVER_MATRIX_EMBEDDED) { + IDA_mem->ida_lsetup = NULL; + idals_mem->scalesol = SUNFALSE; + } + /* Call LS initialize routine */ idals_mem->last_flag = SUNLinSolInitialize(idals_mem->LS); return(idals_mem->last_flag); @@ -1343,6 +1360,12 @@ int idaLsSetup(IDAMem IDA_mem, N_Vector y, N_Vector yp, N_Vector r, } idals_mem = (IDALsMem) IDA_mem->ida_lmem; + /* Immediately return when using matrix-embedded linear solver */ + if (SUNLinSolGetType(idals_mem->LS) == SUNLINEARSOLVER_MATRIX_EMBEDDED) { + idals_mem->last_flag = IDALS_SUCCESS; + return(idals_mem->last_flag); + } + /* Set IDALs N_Vector pointers to inputs */ idals_mem->ycur = y; idals_mem->ypcur = yp; @@ -1465,6 +1488,10 @@ int idaLsSolve(IDAMem IDA_mem, N_Vector b, N_Vector weight, /* Set initial guess x = 0 to LS */ N_VConst(ZERO, idals_mem->x); + /* Set zero initial guess flag */ + retval = SUNLinSolSetZeroGuess(idals_mem->LS, SUNTRUE); + if (retval != SUNLS_SUCCESS) return(-1); + /* If a user-provided jtsetup routine is supplied, call that here */ if (idals_mem->jtsetup) { idals_mem->last_flag = idals_mem->jtsetup(IDA_mem->ida_tn, ycur, ypcur, rescur, @@ -1488,7 +1515,8 @@ int idaLsSolve(IDAMem IDA_mem, N_Vector b, N_Vector weight, nli_inc = SUNLinSolNumIters(idals_mem->LS); /* Copy x (or preconditioned residual vector if no iterations required) to b */ - if (nli_inc == 0) N_VScale(ONE, SUNLinSolResid(idals_mem->LS), b); + if ((nli_inc == 0) && (SUNLinSolGetType(idals_mem->LS) != SUNLINEARSOLVER_MATRIX_EMBEDDED)) + N_VScale(ONE, SUNLinSolResid(idals_mem->LS), b); else N_VScale(ONE, idals_mem->x, b); /* Increment nli counter */ diff --git a/deps/AMICI/ThirdParty/sundials/src/idas/idas_nls.c b/deps/AMICI/ThirdParty/sundials/src/idas/idas_nls.c index 6c72167ed..a48372703 100644 --- a/deps/AMICI/ThirdParty/sundials/src/idas/idas_nls.c +++ b/deps/AMICI/ThirdParty/sundials/src/idas/idas_nls.c @@ -114,6 +114,42 @@ int IDASetNonlinearSolver(void *ida_mem, SUNNonlinearSolver NLS) return(IDA_ILL_INPUT); } + /* Set the nonlinear system RES function */ + if (!(IDA_mem->ida_res)) { + IDAProcessError(IDA_mem, IDA_ILL_INPUT, "IDAS", + "IDASetNonlinearSolver", + "The DAE residual function is NULL"); + return(IDA_ILL_INPUT); + } + IDA_mem->nls_res = IDA_mem->ida_res; + + return(IDA_SUCCESS); +} + + +/*--------------------------------------------------------------- + IDASetNlsResFn: + + This routine sets an alternative user-supplied DAE residual + function to use in the evaluation of nonlinear system functions. + ---------------------------------------------------------------*/ +int IDASetNlsResFn(void *ida_mem, IDAResFn res) +{ + IDAMem IDA_mem; + + if (ida_mem==NULL) { + IDAProcessError(NULL, IDA_MEM_NULL, "IDAS", "IDASetNlsResFn", + MSG_NO_MEM); + return(IDA_MEM_NULL); + } + + IDA_mem = (IDAMem) ida_mem; + + if (res) + IDA_mem->nls_res = res; + else + IDA_mem->nls_res = IDA_mem->ida_res; + return(IDA_SUCCESS); } @@ -151,6 +187,7 @@ int IDAGetNonlinearSystemData(void *ida_mem, realtype *tcur, N_Vector *yypred, } + /* ----------------------------------------------------------------------------- * Private functions * ---------------------------------------------------------------------------*/ @@ -268,7 +305,7 @@ static int idaNlsResidual(N_Vector ycor, N_Vector res, void* ida_mem) N_VLinearSum(ONE, IDA_mem->ida_yppredict, IDA_mem->ida_cj, ycor, IDA_mem->ida_yp); /* evaluate residual */ - retval = IDA_mem->ida_res(IDA_mem->ida_tn, IDA_mem->ida_yy, IDA_mem->ida_yp, + retval = IDA_mem->nls_res(IDA_mem->ida_tn, IDA_mem->ida_yy, IDA_mem->ida_yp, res, IDA_mem->ida_user_data); /* increment the number of residual evaluations */ diff --git a/deps/AMICI/ThirdParty/sundials/src/idas/idas_nls_sim.c b/deps/AMICI/ThirdParty/sundials/src/idas/idas_nls_sim.c index 61cee6ace..60b30d435 100644 --- a/deps/AMICI/ThirdParty/sundials/src/idas/idas_nls_sim.c +++ b/deps/AMICI/ThirdParty/sundials/src/idas/idas_nls_sim.c @@ -174,6 +174,15 @@ int IDASetNonlinearSolverSensSim(void *ida_mem, SUNNonlinearSolver NLS) NV_VEC_SW(IDA_mem->ewtSim, is+1) = IDA_mem->ida_ewtS[is]; } + /* Set the nonlinear system RES function */ + if (!(IDA_mem->ida_res)) { + IDAProcessError(IDA_mem, IDA_ILL_INPUT, "IDAS", + "IDASetNonlinearSolverSensSim", + "The DAE residual function is NULL"); + return(IDA_ILL_INPUT); + } + IDA_mem->nls_res = IDA_mem->ida_res; + return(IDA_SUCCESS); } @@ -357,7 +366,7 @@ static int idaNlsResidualSensSim(N_Vector ycorSim, N_Vector resSim, void* ida_me N_VLinearSum(ONE, IDA_mem->ida_yppredict, IDA_mem->ida_cj, ycor, IDA_mem->ida_yp); /* evaluate residual */ - retval = IDA_mem->ida_res(IDA_mem->ida_tn, IDA_mem->ida_yy, IDA_mem->ida_yp, + retval = IDA_mem->nls_res(IDA_mem->ida_tn, IDA_mem->ida_yy, IDA_mem->ida_yp, res, IDA_mem->ida_user_data); /* increment the number of residual evaluations */ diff --git a/deps/AMICI/ThirdParty/sundials/src/kinsol/CMakeLists.txt b/deps/AMICI/ThirdParty/sundials/src/kinsol/CMakeLists.txt deleted file mode 100644 index e6bb1a907..000000000 --- a/deps/AMICI/ThirdParty/sundials/src/kinsol/CMakeLists.txt +++ /dev/null @@ -1,81 +0,0 @@ -# --------------------------------------------------------------- -# Programmer(s): Daniel R. Reynolds @ SMU -# Radu Serban, Cody J. Balos @ LLNL -# --------------------------------------------------------------- -# SUNDIALS Copyright Start -# Copyright (c) 2002-2021, Lawrence Livermore National Security -# and Southern Methodist University. -# All rights reserved. -# -# See the top-level LICENSE and NOTICE files for details. -# -# SPDX-License-Identifier: BSD-3-Clause -# SUNDIALS Copyright End -# --------------------------------------------------------------- -# CMakeLists.txt file for the KINSOL library -# --------------------------------------------------------------- - -install(CODE "MESSAGE(\"\nInstall KINSOL\n\")") - -# Add variable kinsol_SOURCES with the sources for the KINSOL library -set(kinsol_SOURCES - kinsol.c - kinsol_bbdpre.c - kinsol_direct.c - kinsol_io.c - kinsol_ls.c - kinsol_spils.c - ) - -# Add variable kinsol_HEADERS with the exported KINSOL header files -set(kinsol_HEADERS - kinsol.h - kinsol_bbdpre.h - kinsol_direct.h - kinsol_ls.h - kinsol_spils.h - ) - -# Add prefix with complete path to the KINSOL header files -add_prefix(${SUNDIALS_SOURCE_DIR}/include/kinsol/ kinsol_HEADERS) - -# Create the library -sundials_add_library(sundials_kinsol - SOURCES - ${kinsol_SOURCES} - HEADERS - ${kinsol_HEADERS} - INCLUDE_SUBDIR - kinsol - OBJECT_LIBRARIES - sundials_generic_obj - sundials_nvecserial_obj - sundials_sunmatrixband_obj - sundials_sunmatrixdense_obj - sundials_sunmatrixsparse_obj - sundials_sunlinsolband_obj - sundials_sunlinsoldense_obj - sundials_sunlinsolspbcgs_obj - sundials_sunlinsolspfgmr_obj - sundials_sunlinsolspgmr_obj - sundials_sunlinsolsptfqmr_obj - sundials_sunlinsolpcg_obj - OUTPUT_NAME - sundials_kinsol - VERSION - ${kinsollib_VERSION} - SOVERSION - ${kinsollib_SOVERSION} -) - -# Finished KINSOL -message(STATUS "Added KINSOL module") - -# Add F2003 module if the interface is enabled -if(BUILD_FORTRAN_MODULE_INTERFACE) - add_subdirectory(fmod) -endif() - -if(BUILD_FORTRAN77_INTERFACE) - add_subdirectory(fcmix) -endif() diff --git a/deps/AMICI/ThirdParty/sundials/src/kinsol/LICENSE b/deps/AMICI/ThirdParty/sundials/src/kinsol/LICENSE deleted file mode 100644 index 4ba2c4848..000000000 --- a/deps/AMICI/ThirdParty/sundials/src/kinsol/LICENSE +++ /dev/null @@ -1,29 +0,0 @@ -BSD 3-Clause License - -Copyright (c) 2002-2021, Lawrence Livermore National Security and Southern Methodist University. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* 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. - -* Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/deps/AMICI/ThirdParty/sundials/src/kinsol/NOTICE b/deps/AMICI/ThirdParty/sundials/src/kinsol/NOTICE deleted file mode 100644 index 329b142ee..000000000 --- a/deps/AMICI/ThirdParty/sundials/src/kinsol/NOTICE +++ /dev/null @@ -1,21 +0,0 @@ -This work was produced under the auspices of the U.S. Department of -Energy by Lawrence Livermore National Laboratory under Contract -DE-AC52-07NA27344. - -This work was prepared as an account of work sponsored by an agency of -the United States Government. Neither the United States Government nor -Lawrence Livermore National Security, LLC, nor any of their employees -makes any warranty, expressed or implied, or assumes any legal liability -or responsibility for the accuracy, completeness, or usefulness of any -information, apparatus, product, or process disclosed, or represents that -its use would not infringe privately owned rights. - -Reference herein to any specific commercial product, process, or service -by trade name, trademark, manufacturer, or otherwise does not necessarily -constitute or imply its endorsement, recommendation, or favoring by the -United States Government or Lawrence Livermore National Security, LLC. - -The views and opinions of authors expressed herein do not necessarily -state or reflect those of the United States Government or Lawrence -Livermore National Security, LLC, and shall not be used for advertising -or product endorsement purposes. \ No newline at end of file diff --git a/deps/AMICI/ThirdParty/sundials/src/kinsol/README.md b/deps/AMICI/ThirdParty/sundials/src/kinsol/README.md deleted file mode 100644 index 2f73f75ff..000000000 --- a/deps/AMICI/ThirdParty/sundials/src/kinsol/README.md +++ /dev/null @@ -1,69 +0,0 @@ -# KINSOL -### Version 5.7.0 (Jan 2021) - -**Alan C. Hindmarsh, Radu Serban, Cody J. Balos, David J. Gardner, - and Carol S. Woodward, Center for Applied Scientific Computing, LLNL** - -**Daniel R. Reynolds, Department of Mathematics, Southern Methodist University** - - -KINSOL is a package for the solution for nonlinear algebraic systems -``` -F(u) = 0. -``` -Nonlinear solver methods available include Newton-Krylov, Picard, and -fixed-point. Both Picard and fixed point can be accelerated with Anderson -acceleration. - -KINSOL is part of the SUNDIALS Suite of Nonlinear and Differential/Algebraic -equation Solvers which consists of ARKode, CVODE, CVODES, IDA, IDAS and KINSOL. -It is written in ANSI standard C, but is based on the previous Fortran package -NKSOL, written by Peter Brown and Youcef Saad. KINSOL can be used in a variety -of computing environments including serial, shared memory, distributed memory, -and accelerator-based (e.g., GPU) systems. This flexibility is obtained from a -modular design that leverages the shared vector, matrix, and linear solver APIs -used across SUNDIALS packages. - -For use with Fortran applications, a set of Fortran/C interface routines, called -FKINSOL, is also supplied. These are written in C, but assume that the user -calling program and all user-supplied routines are in Fortran. - -## Documentation - -See the [KINSOL User Guide](/doc/kinsol/kin_guide.pdf) and -[KINSOL Examples](/doc/kinsol/kin_examples.pdf) document for more information -about IDA usage and the provided example programs respectively. - -## Installation - -For installation instructions see the [INSTALL_GUIDE](/INSTALL_GUIDE.pdf) -or the "Installation Procedure" chapter in the KINSOL User Guide. - -## Release History - -Information on recent changes to KINSOL can be found in the "Introduction" -chapter of the KINSOL User Guide and a complete release history is available in -the "SUNDIALS Release History" appendix of the KINSOL User Guide. - -## References - -* A. C. Hindmarsh, R. Serban, C. J. Balos, D. J. Gardner, - D. R. Reynolds and C. S. Woodward, - "User Documentation for KINSOL v5.7.0," LLNL technical report - UCRL-SM-208116, Jan 2021. - -* A. M. Collier and R. Serban, "Example Programs for KINSOL v5.7.0," - LLNL technical report UCRL-SM-208114, Jan 2021. - -* A. C. Hindmarsh, P. N. Brown, K. E. Grant, S. L. Lee, R. Serban, - D. E. Shumaker, and C. S. Woodward, "SUNDIALS, Suite of Nonlinear and - Differential/Algebraic Equation Solvers," ACM Trans. Math. Softw., - 31(3), pp. 363-396, 2005. - -* Peter N. Brown and Youcef Saad, "Hybrid Krylov Methods for - Nonlinear Systems of Equations," SIAM J. Sci. Stat. Comput., - Vol 11, no 3, pp. 450-481, May 1990. - -* A. G. Taylor and A. C. Hindmarsh, "User Documentation for KINSOL, - A Nonlinear Solver for Sequential and Parallel Computers," LLNL - technical report UCRL-ID-131185, July 1998. diff --git a/deps/AMICI/ThirdParty/sundials/src/kinsol/kinsol.c b/deps/AMICI/ThirdParty/sundials/src/kinsol/kinsol.c deleted file mode 100644 index 5d6e99423..000000000 --- a/deps/AMICI/ThirdParty/sundials/src/kinsol/kinsol.c +++ /dev/null @@ -1,2702 +0,0 @@ -/* - * ----------------------------------------------------------------- - * Programmer(s): Allan Taylor, Alan Hindmarsh, Radu Serban, Carol Woodward, - * John Loffeld, and Aaron Collier @ LLNL - * ----------------------------------------------------------------- - * SUNDIALS Copyright Start - * Copyright (c) 2002-2021, Lawrence Livermore National Security - * and Southern Methodist University. - * All rights reserved. - * - * See the top-level LICENSE and NOTICE files for details. - * - * SPDX-License-Identifier: BSD-3-Clause - * SUNDIALS Copyright End - * ----------------------------------------------------------------- - * This is the implementation file for the main KINSol solver. - * It is independent of the KINSol linear solver in use. - * ----------------------------------------------------------------- - * - * EXPORTED FUNCTIONS - * ------------------ - * Creation and allocation functions - * KINCreate - * KINInit - * Main solver function - * KINSol - * Deallocation function - * KINFree - * - * PRIVATE FUNCTIONS - * ----------------- - * KINCheckNvector - * Memory allocation/deallocation - * KINAllocVectors - * KINFreeVectors - * Initial setup - * KINSolInit - * Step functions - * KINLinSolDrv - * KINFullNewton - * KINLineSearch - * KINConstraint - * KINFP - * KINPicardAA - * Stopping tests - * KINStop - * KINForcingTerm - * Norm functions - * KINScFNorm - * KINScSNorm - * KINSOL Verbose output functions - * KINPrintInfo - * KINInfoHandler - * KINSOL Error Handling functions - * KINProcessError - * KINErrHandler - * ----------------------------------------------------------------- - */ - -/* - * ================================================================= - * IMPORTED HEADER FILES - * ================================================================= - */ - -#include -#include -#include -#include - -#include - -#include "kinsol_impl.h" -#include - -/* - * ================================================================= - * KINSOL PRIVATE CONSTANTS - * ================================================================= - */ - -#define HALF RCONST(0.5) -#define ZERO RCONST(0.0) -#define ONE RCONST(1.0) -#define ONEPT5 RCONST(1.5) -#define TWO RCONST(2.0) -#define THREE RCONST(3.0) -#define FIVE RCONST(5.0) -#define TWELVE RCONST(12.0) -#define POINT1 RCONST(0.1) -#define POINT01 RCONST(0.01) -#define POINT99 RCONST(0.99) -#define THOUSAND RCONST(1000.0) -#define ONETHIRD RCONST(0.3333333333333333) -#define TWOTHIRDS RCONST(0.6666666666666667) -#define POINT9 RCONST(0.9) -#define POINT0001 RCONST(0.0001) - -/* - * ================================================================= - * KINSOL ROUTINE-SPECIFIC CONSTANTS - * ================================================================= - */ - -/* - * Control constants for lower-level functions used by KINSol - * ---------------------------------------------------------- - * - * KINStop return value requesting more iterations - * RETRY_ITERATION - * CONTINUE_ITERATIONS - * - * KINFullNewton, KINLineSearch, KINFP, and KINPicardAA return values: - * KIN_SUCCESS - * KIN_SYSFUNC_FAIL - * STEP_TOO_SMALL - * - * KINConstraint return values: - * KIN_SUCCESS - * CONSTR_VIOLATED - */ - -#define RETRY_ITERATION -998 -#define CONTINUE_ITERATIONS -999 -#define STEP_TOO_SMALL -997 -#define CONSTR_VIOLATED -996 - -/* - * Algorithmic constants - * --------------------- - * - * MAX_RECVR max. no. of attempts to correct a recoverable func error - */ - -#define MAX_RECVR 5 - -/* - * Keys for KINPrintInfo - * --------------------- - */ - -#define PRNT_RETVAL 1 -#define PRNT_NNI 2 -#define PRNT_TOL 3 -#define PRNT_FMAX 4 -#define PRNT_PNORM 5 -#define PRNT_PNORM1 6 -#define PRNT_FNORM 7 -#define PRNT_LAM 8 -#define PRNT_ALPHA 9 -#define PRNT_BETA 10 -#define PRNT_ALPHABETA 11 -#define PRNT_ADJ 12 - -/* - * ================================================================= - * PRIVATE FUNCTION PROTOTYPES - * ================================================================= - */ - -static booleantype KINCheckNvector(N_Vector tmpl); -static booleantype KINAllocVectors(KINMem kin_mem, N_Vector tmpl); -static int KINSolInit(KINMem kin_mem); -static int KINConstraint(KINMem kin_mem ); -static void KINForcingTerm(KINMem kin_mem, realtype fnormp); -static void KINFreeVectors(KINMem kin_mem); - -static int KINFullNewton(KINMem kin_mem, realtype *fnormp, - realtype *f1normp, booleantype *maxStepTaken); -static int KINLineSearch(KINMem kin_mem, realtype *fnormp, - realtype *f1normp, booleantype *maxStepTaken); -static int KINPicardAA(KINMem kin_mem, long int *iter, realtype *R, - realtype *gamma, realtype *fmax); -static int KINFP(KINMem kin_mem); - -static int KINLinSolDrv(KINMem kinmem); -static int KINPicardFcnEval(KINMem kin_mem, N_Vector gval, N_Vector uval, - N_Vector fval1); -static realtype KINScFNorm(KINMem kin_mem, N_Vector v, N_Vector scale); -static realtype KINScSNorm(KINMem kin_mem, N_Vector v, N_Vector u); -static int KINStop(KINMem kin_mem, booleantype maxStepTaken, - int sflag); -static int AndersonAcc(KINMem kin_mem, N_Vector gval, N_Vector fv, N_Vector x, - N_Vector x_old, long int iter, realtype *R, realtype *gamma); - -/* - * ================================================================= - * EXPORTED FUNCTIONS IMPLEMENTATION - * ================================================================= - */ - -/* - * ----------------------------------------------------------------- - * Creation and allocation functions - * ----------------------------------------------------------------- - */ - -/* - * Function : KINCreate - * - * KINCreate creates an internal memory block for a problem to - * be solved by KINSOL. If successful, KINCreate returns a pointer - * to the problem memory. This pointer should be passed to - * KINInit. If an initialization error occurs, KINCreate prints - * an error message to standard error and returns NULL. - */ - -void *KINCreate(void) -{ - KINMem kin_mem; - realtype uround; - - kin_mem = NULL; - kin_mem = (KINMem) malloc(sizeof(struct KINMemRec)); - if (kin_mem == NULL) { - KINProcessError(kin_mem, 0, "KINSOL", "KINCreate", MSG_MEM_FAIL); - return(NULL); - } - - /* Zero out kin_mem */ - memset(kin_mem, 0, sizeof(struct KINMemRec)); - - /* set uround (unit roundoff) */ - - kin_mem->kin_uround = uround = UNIT_ROUNDOFF; - - /* set default values for solver optional inputs */ - - kin_mem->kin_func = NULL; - kin_mem->kin_user_data = NULL; - kin_mem->kin_uu = NULL; - kin_mem->kin_unew = NULL; - kin_mem->kin_fval = NULL; - kin_mem->kin_gval = NULL; - kin_mem->kin_uscale = NULL; - kin_mem->kin_fscale = NULL; - kin_mem->kin_pp = NULL; - kin_mem->kin_constraints = NULL; - kin_mem->kin_vtemp1 = NULL; - kin_mem->kin_vtemp2 = NULL; - kin_mem->kin_fold_aa = NULL; - kin_mem->kin_gold_aa = NULL; - kin_mem->kin_df_aa = NULL; - kin_mem->kin_dg_aa = NULL; - kin_mem->kin_q_aa = NULL; - kin_mem->kin_gamma_aa = NULL; - kin_mem->kin_R_aa = NULL; - kin_mem->kin_ipt_map = NULL; - kin_mem->kin_cv = NULL; - kin_mem->kin_Xv = NULL; - kin_mem->kin_lmem = NULL; - kin_mem->kin_m_aa = 0; - kin_mem->kin_aamem_aa = 0; - kin_mem->kin_setstop_aa = 0; - kin_mem->kin_beta_aa = ONE; - kin_mem->kin_damping_aa = SUNFALSE; - kin_mem->kin_constraintsSet = SUNFALSE; - kin_mem->kin_ehfun = KINErrHandler; - kin_mem->kin_eh_data = kin_mem; - kin_mem->kin_errfp = stderr; - kin_mem->kin_ihfun = KINInfoHandler; - kin_mem->kin_ih_data = kin_mem; - kin_mem->kin_infofp = stdout; - kin_mem->kin_printfl = PRINTFL_DEFAULT; - kin_mem->kin_mxiter = MXITER_DEFAULT; - kin_mem->kin_noInitSetup = SUNFALSE; - kin_mem->kin_msbset = MSBSET_DEFAULT; - kin_mem->kin_noResMon = SUNFALSE; - kin_mem->kin_msbset_sub = MSBSET_SUB_DEFAULT; - kin_mem->kin_update_fnorm_sub = SUNFALSE; - kin_mem->kin_mxnbcf = MXNBCF_DEFAULT; - kin_mem->kin_sthrsh = TWO; - kin_mem->kin_noMinEps = SUNFALSE; - kin_mem->kin_mxnstepin = ZERO; - kin_mem->kin_sqrt_relfunc = SUNRsqrt(uround); - kin_mem->kin_scsteptol = SUNRpowerR(uround,TWOTHIRDS); - kin_mem->kin_fnormtol = SUNRpowerR(uround,ONETHIRD); - kin_mem->kin_etaflag = KIN_ETACHOICE1; - kin_mem->kin_eta = POINT1; /* default for KIN_ETACONSTANT */ - kin_mem->kin_eta_alpha = TWO; /* default for KIN_ETACHOICE2 */ - kin_mem->kin_eta_gamma = POINT9; /* default for KIN_ETACHOICE2 */ - kin_mem->kin_MallocDone = SUNFALSE; - kin_mem->kin_eval_omega = SUNTRUE; - kin_mem->kin_omega = ZERO; /* default to using min/max */ - kin_mem->kin_omega_min = OMEGA_MIN; - kin_mem->kin_omega_max = OMEGA_MAX; - - /* initialize lrw and liw */ - - kin_mem->kin_lrw = 17; - kin_mem->kin_liw = 22; - - /* NOTE: needed since KINInit could be called after KINSetConstraints */ - - kin_mem->kin_lrw1 = 0; - kin_mem->kin_liw1 = 0; - - return((void *) kin_mem); -} - -/* - * Function : KINInit - * - * KINInit allocates memory for a problem or execution of KINSol. - * If memory is successfully allocated, KIN_SUCCESS is returned. - * Otherwise, an error message is printed and an error flag - * returned. - */ - -int KINInit(void *kinmem, KINSysFn func, N_Vector tmpl) -{ - sunindextype liw1, lrw1; - KINMem kin_mem; - booleantype allocOK, nvectorOK; - - /* check kinmem */ - - if (kinmem == NULL) { - KINProcessError(NULL, KIN_MEM_NULL, "KINSOL", "KINInit", MSG_NO_MEM); - return(KIN_MEM_NULL); - } - kin_mem = (KINMem) kinmem; - - if (func == NULL) { - KINProcessError(kin_mem, KIN_ILL_INPUT, "KINSOL", "KINInit", MSG_FUNC_NULL); - return(KIN_ILL_INPUT); - } - - /* check if all required vector operations are implemented */ - - nvectorOK = KINCheckNvector(tmpl); - if (!nvectorOK) { - KINProcessError(kin_mem, KIN_ILL_INPUT, "KINSOL", "KINInit", MSG_BAD_NVECTOR); - return(KIN_ILL_INPUT); - } - - /* set space requirements for one N_Vector */ - - if (tmpl->ops->nvspace != NULL) { - N_VSpace(tmpl, &lrw1, &liw1); - kin_mem->kin_lrw1 = lrw1; - kin_mem->kin_liw1 = liw1; - } - else { - kin_mem->kin_lrw1 = 0; - kin_mem->kin_liw1 = 0; - } - - /* allocate necessary vectors */ - - allocOK = KINAllocVectors(kin_mem, tmpl); - if (!allocOK) { - KINProcessError(kin_mem, KIN_MEM_FAIL, "KINSOL", "KINInit", MSG_MEM_FAIL); - free(kin_mem); kin_mem = NULL; - return(KIN_MEM_FAIL); - } - - /* copy the input parameter into KINSol state */ - - kin_mem->kin_func = func; - - /* set the linear solver addresses to NULL */ - - kin_mem->kin_linit = NULL; - kin_mem->kin_lsetup = NULL; - kin_mem->kin_lsolve = NULL; - kin_mem->kin_lfree = NULL; - kin_mem->kin_lmem = NULL; - - /* problem memory has been successfully allocated */ - - kin_mem->kin_MallocDone = SUNTRUE; - - return(KIN_SUCCESS); -} - -/* - * ----------------------------------------------------------------- - * Main solver function - * ----------------------------------------------------------------- - */ - -/* - * Function : KINSol - * - * KINSol (main KINSOL driver routine) manages the computational - * process of computing an approximate solution of the nonlinear - * system F(uu) = 0. The KINSol routine calls the following - * subroutines: - * - * KINSolInit checks if initial guess satisfies user-supplied - * constraints and initializes linear solver - * - * KINLinSolDrv interfaces with linear solver to find a - * solution of the system J(uu)*x = b (calculate - * Newton step) - * - * KINFullNewton/KINLineSearch implement the global strategy - * - * KINForcingTerm computes the forcing term (eta) - * - * KINStop determines if an approximate solution has been found - */ - -int KINSol(void *kinmem, N_Vector u, int strategy_in, - N_Vector u_scale, N_Vector f_scale) -{ - realtype fnormp, f1normp, epsmin, fmax=ZERO; - KINMem kin_mem; - int ret, sflag; - booleantype maxStepTaken; - - /* intialize to avoid compiler warning messages */ - - maxStepTaken = SUNFALSE; - f1normp = fnormp = -ONE; - - /* initialize epsmin to avoid compiler warning message */ - - epsmin = ZERO; - - /* check for kinmem non-NULL */ - - if (kinmem == NULL) { - KINProcessError(NULL, KIN_MEM_NULL, "KINSOL", "KINSol", MSG_NO_MEM); - return(KIN_MEM_NULL); - } - kin_mem = (KINMem) kinmem; - - if(kin_mem->kin_MallocDone == SUNFALSE) { - KINProcessError(NULL, KIN_NO_MALLOC, "KINSOL", "KINSol", MSG_NO_MALLOC); - return(KIN_NO_MALLOC); - } - - /* load input arguments */ - - kin_mem->kin_uu = u; - kin_mem->kin_uscale = u_scale; - kin_mem->kin_fscale = f_scale; - kin_mem->kin_globalstrategy = strategy_in; - - /* CSW: - Call fixed point solver if requested. Note that this should probably - be forked off to a FPSOL solver instead of kinsol in the future. */ - if ( kin_mem->kin_globalstrategy == KIN_FP ) { - if (kin_mem->kin_uu == NULL) { - KINProcessError(kin_mem, KIN_ILL_INPUT, "KINSOL", "KINSol", MSG_UU_NULL); - return(KIN_ILL_INPUT); - } - - if (kin_mem->kin_constraintsSet != SUNFALSE) { - KINProcessError(kin_mem, KIN_ILL_INPUT, "KINSOL", "KINSol", MSG_CONSTRAINTS_NOTOK); - return(KIN_ILL_INPUT); - } - - if (kin_mem->kin_printfl > 0) - KINPrintInfo(kin_mem, PRNT_TOL, "KINSOL", "KINSol", INFO_TOL, kin_mem->kin_scsteptol, kin_mem->kin_fnormtol); - - kin_mem->kin_nfe = kin_mem->kin_nnilset = kin_mem->kin_nnilset_sub = kin_mem->kin_nni = kin_mem->kin_nbcf = kin_mem->kin_nbktrk = 0; - ret = KINFP(kin_mem); - - switch(ret) { - case KIN_SYSFUNC_FAIL: - KINProcessError(kin_mem, KIN_SYSFUNC_FAIL, "KINSOL", "KINSol", MSG_SYSFUNC_FAILED); - break; - case KIN_MAXITER_REACHED: - KINProcessError(kin_mem, KIN_MAXITER_REACHED, "KINSOL", "KINSol", MSG_MAXITER_REACHED); - break; - } - - return(ret); - } - - /* initialize solver */ - ret = KINSolInit(kin_mem); - if (ret != KIN_SUCCESS) return(ret); - - kin_mem->kin_ncscmx = 0; - - /* Note: The following logic allows the choice of whether or not - to force a call to the linear solver setup upon a given call to - KINSol */ - - if (kin_mem->kin_noInitSetup) kin_mem->kin_sthrsh = ONE; - else kin_mem->kin_sthrsh = TWO; - - /* if eps is to be bounded from below, set the bound */ - - if (kin_mem->kin_inexact_ls && !(kin_mem->kin_noMinEps)) - epsmin = POINT01 * kin_mem->kin_fnormtol; - - - /* if omega is zero at this point, make sure it will be evaluated - at each iteration based on the provided min/max bounds and the - current function norm. */ - if (kin_mem->kin_omega == ZERO) kin_mem->kin_eval_omega = SUNTRUE; - else kin_mem->kin_eval_omega = SUNFALSE; - - - /* CSW: - Call fixed point solver for Picard method if requested. - Note that this should probably be forked off to a part of an - FPSOL solver instead of kinsol in the future. */ - if ( kin_mem->kin_globalstrategy == KIN_PICARD ) { - - if (kin_mem->kin_gval == NULL) { - kin_mem->kin_gval = N_VClone(kin_mem->kin_unew); - if (kin_mem->kin_gval == NULL) { - KINProcessError(kin_mem, KIN_MEM_FAIL, "KINSOL", "KINSol", MSG_MEM_FAIL); - return(KIN_MEM_FAIL); - } - kin_mem->kin_liw += kin_mem->kin_liw1; - kin_mem->kin_lrw += kin_mem->kin_lrw1; - } - ret = KINPicardAA(kin_mem, &(kin_mem->kin_nni), kin_mem->kin_R_aa, kin_mem->kin_gamma_aa, &fmax); - - return(ret); - } - - - for(;;){ - - kin_mem->kin_retry_nni = SUNFALSE; - - kin_mem->kin_nni++; - - /* calculate the epsilon (stopping criteria for iterative linear solver) - for this iteration based on eta from the routine KINForcingTerm */ - - if (kin_mem->kin_inexact_ls) { - kin_mem->kin_eps = (kin_mem->kin_eta + kin_mem->kin_uround) * kin_mem->kin_fnorm; - if(!(kin_mem->kin_noMinEps)) kin_mem->kin_eps = SUNMAX(epsmin, kin_mem->kin_eps); - } - - repeat_nni: - - /* call the appropriate routine to calculate an acceptable step pp */ - - sflag = 0; - - if (kin_mem->kin_globalstrategy == KIN_NONE) { - - /* Full Newton Step*/ - - /* call KINLinSolDrv to calculate the (approximate) Newton step, pp */ - ret = KINLinSolDrv(kin_mem); - if (ret != KIN_SUCCESS) break; - - sflag = KINFullNewton(kin_mem, &fnormp, &f1normp, &maxStepTaken); - - /* if sysfunc failed unrecoverably, stop */ - if ((sflag == KIN_SYSFUNC_FAIL) || (sflag == KIN_REPTD_SYSFUNC_ERR)) { - ret = sflag; - break; - } - - } else if (kin_mem->kin_globalstrategy == KIN_LINESEARCH) { - - /* Line Search */ - - /* call KINLinSolDrv to calculate the (approximate) Newton step, pp */ - ret = KINLinSolDrv(kin_mem); - if (ret != KIN_SUCCESS) break; - - sflag = KINLineSearch(kin_mem, &fnormp, &f1normp, &maxStepTaken); - - /* if sysfunc failed unrecoverably, stop */ - if ((sflag == KIN_SYSFUNC_FAIL) || (sflag == KIN_REPTD_SYSFUNC_ERR)) { - ret = sflag; - break; - } - - /* if too many beta condition failures, then stop iteration */ - if (kin_mem->kin_nbcf > kin_mem->kin_mxnbcf) { - ret = KIN_LINESEARCH_BCFAIL; - break; - } - - } - - if ( (kin_mem->kin_globalstrategy != KIN_PICARD) && - (kin_mem->kin_globalstrategy != KIN_FP) ) { - - /* evaluate eta by calling the forcing term routine */ - if (kin_mem->kin_callForcingTerm) KINForcingTerm(kin_mem, fnormp); - - kin_mem->kin_fnorm = fnormp; - - /* call KINStop to check if tolerances where met by this iteration */ - ret = KINStop(kin_mem, maxStepTaken, sflag); - - if (ret == RETRY_ITERATION) { - kin_mem->kin_retry_nni = SUNTRUE; - goto repeat_nni; - } - } - - /* update uu after the iteration */ - N_VScale(ONE, kin_mem->kin_unew, kin_mem->kin_uu); - - kin_mem->kin_f1norm = f1normp; - - /* print the current nni, fnorm, and nfe values if printfl > 0 */ - - if (kin_mem->kin_printfl > 0) - KINPrintInfo(kin_mem, PRNT_NNI, "KINSOL", "KINSol", INFO_NNI, kin_mem->kin_nni, kin_mem->kin_nfe, kin_mem->kin_fnorm); - - if (ret != CONTINUE_ITERATIONS) break; - - fflush(kin_mem->kin_errfp); - - } /* end of loop; return */ - - - - if (kin_mem->kin_printfl > 0) - KINPrintInfo(kin_mem, PRNT_RETVAL, "KINSOL", "KINSol", INFO_RETVAL, ret); - - switch(ret) { - case KIN_SYSFUNC_FAIL: - KINProcessError(kin_mem, KIN_SYSFUNC_FAIL, "KINSOL", "KINSol", MSG_SYSFUNC_FAILED); - break; - case KIN_REPTD_SYSFUNC_ERR: - KINProcessError(kin_mem, KIN_REPTD_SYSFUNC_ERR, "KINSOL", "KINSol", MSG_SYSFUNC_REPTD); - break; - case KIN_LSETUP_FAIL: - KINProcessError(kin_mem, KIN_LSETUP_FAIL, "KINSOL", "KINSol", MSG_LSETUP_FAILED); - break; - case KIN_LSOLVE_FAIL: - KINProcessError(kin_mem, KIN_LSOLVE_FAIL, "KINSOL", "KINSol", MSG_LSOLVE_FAILED); - break; - case KIN_LINSOLV_NO_RECOVERY: - KINProcessError(kin_mem, KIN_LINSOLV_NO_RECOVERY, "KINSOL", "KINSol", MSG_LINSOLV_NO_RECOVERY); - break; - case KIN_LINESEARCH_NONCONV: - KINProcessError(kin_mem, KIN_LINESEARCH_NONCONV, "KINSOL", "KINSol", MSG_LINESEARCH_NONCONV); - break; - case KIN_LINESEARCH_BCFAIL: - KINProcessError(kin_mem, KIN_LINESEARCH_BCFAIL, "KINSOL", "KINSol", MSG_LINESEARCH_BCFAIL); - break; - case KIN_MAXITER_REACHED: - KINProcessError(kin_mem, KIN_MAXITER_REACHED, "KINSOL", "KINSol", MSG_MAXITER_REACHED); - break; - case KIN_MXNEWT_5X_EXCEEDED: - KINProcessError(kin_mem, KIN_MXNEWT_5X_EXCEEDED, "KINSOL", "KINSol", MSG_MXNEWT_5X_EXCEEDED); - break; - } - - return(ret); -} - -/* - * ----------------------------------------------------------------- - * Deallocation function - * ----------------------------------------------------------------- - */ - -/* - * Function : KINFree - * - * This routine frees the problem memory allocated by KINInit. - * Such memory includes all the vectors allocated by - * KINAllocVectors, and the memory lmem for the linear solver - * (deallocated by a call to lfree). - */ - -void KINFree(void **kinmem) -{ - KINMem kin_mem; - - if (*kinmem == NULL) return; - - kin_mem = (KINMem) (*kinmem); - KINFreeVectors(kin_mem); - - /* call lfree if non-NULL */ - - if (kin_mem->kin_lfree != NULL) kin_mem->kin_lfree(kin_mem); - - free(*kinmem); - *kinmem = NULL; -} - -/* - * ================================================================= - * PRIVATE FUNCTIONS - * ================================================================= - */ - -/* - * Function : KINCheckNvector - * - * This routine checks if all required vector operations are - * implemented (excluding those required by KINConstraint). If all - * necessary operations are present, then KINCheckNvector returns - * SUNTRUE. Otherwise, SUNFALSE is returned. - */ - -static booleantype KINCheckNvector(N_Vector tmpl) -{ - if ((tmpl->ops->nvclone == NULL) || - (tmpl->ops->nvdestroy == NULL) || - (tmpl->ops->nvlinearsum == NULL) || - (tmpl->ops->nvprod == NULL) || - (tmpl->ops->nvdiv == NULL) || - (tmpl->ops->nvscale == NULL) || - (tmpl->ops->nvabs == NULL) || - (tmpl->ops->nvinv == NULL) || - (tmpl->ops->nvmaxnorm == NULL) || - (tmpl->ops->nvmin == NULL) || - (tmpl->ops->nvwl2norm == NULL)) return(SUNFALSE); - else return(SUNTRUE); -} - -/* - * ----------------------------------------------------------------- - * Memory allocation/deallocation - * ----------------------------------------------------------------- - */ - -/* - * Function : KINAllocVectors - * - * This routine allocates the KINSol vectors. If all memory - * allocations are successful, KINAllocVectors returns SUNTRUE. - * Otherwise all allocated memory is freed and KINAllocVectors - * returns SUNFALSE. - */ - -static booleantype KINAllocVectors(KINMem kin_mem, N_Vector tmpl) -{ - /* allocate unew, fval, pp, vtemp1 and vtemp2. */ - /* allocate df, dg, q, for Anderson Acceleration, Broyden and EN */ - - if (kin_mem->kin_unew == NULL) { - kin_mem->kin_unew = N_VClone(tmpl); - if (kin_mem->kin_unew == NULL) return(SUNFALSE); - kin_mem->kin_liw += kin_mem->kin_liw1; - kin_mem->kin_lrw += kin_mem->kin_lrw1; - } - - if (kin_mem->kin_fval == NULL) { - kin_mem->kin_fval = N_VClone(tmpl); - if (kin_mem->kin_fval == NULL) { - N_VDestroy(kin_mem->kin_unew); - kin_mem->kin_liw -= kin_mem->kin_liw1; - kin_mem->kin_lrw -= kin_mem->kin_lrw1; - return(SUNFALSE); - } - kin_mem->kin_liw += kin_mem->kin_liw1; - kin_mem->kin_lrw += kin_mem->kin_lrw1; - } - - if (kin_mem->kin_pp == NULL) { - kin_mem->kin_pp = N_VClone(tmpl); - if (kin_mem->kin_pp == NULL) { - N_VDestroy(kin_mem->kin_unew); - N_VDestroy(kin_mem->kin_fval); - kin_mem->kin_liw -= 2*kin_mem->kin_liw1; - kin_mem->kin_lrw -= 2*kin_mem->kin_lrw1; - return(SUNFALSE); - } - kin_mem->kin_liw += kin_mem->kin_liw1; - kin_mem->kin_lrw += kin_mem->kin_lrw1; - } - - if (kin_mem->kin_vtemp1 == NULL) { - kin_mem->kin_vtemp1 = N_VClone(tmpl); - if (kin_mem->kin_vtemp1 == NULL) { - N_VDestroy(kin_mem->kin_unew); - N_VDestroy(kin_mem->kin_fval); - N_VDestroy(kin_mem->kin_pp); - kin_mem->kin_liw -= 3*kin_mem->kin_liw1; - kin_mem->kin_lrw -= 3*kin_mem->kin_lrw1; - return(SUNFALSE); - } - kin_mem->kin_liw += kin_mem->kin_liw1; - kin_mem->kin_lrw += kin_mem->kin_lrw1; - } - - if (kin_mem->kin_vtemp2 == NULL) { - kin_mem->kin_vtemp2 = N_VClone(tmpl); - if (kin_mem->kin_vtemp2 == NULL) { - N_VDestroy(kin_mem->kin_unew); - N_VDestroy(kin_mem->kin_fval); - N_VDestroy(kin_mem->kin_pp); - N_VDestroy(kin_mem->kin_vtemp1); - kin_mem->kin_liw -= 4*kin_mem->kin_liw1; - kin_mem->kin_lrw -= 4*kin_mem->kin_lrw1; - return(SUNFALSE); - } - kin_mem->kin_liw += kin_mem->kin_liw1; - kin_mem->kin_lrw += kin_mem->kin_lrw1; - } - - /* Vectors for Anderson acceleration */ - - if (kin_mem->kin_m_aa) { - - if (kin_mem->kin_R_aa == NULL) { - kin_mem->kin_R_aa = (realtype *) malloc((kin_mem->kin_m_aa*kin_mem->kin_m_aa) * sizeof(realtype)); - if (kin_mem->kin_R_aa == NULL) { - KINProcessError(kin_mem, 0, "KINSOL", "KINAllocVectors", MSG_MEM_FAIL); - N_VDestroy(kin_mem->kin_unew); - N_VDestroy(kin_mem->kin_fval); - N_VDestroy(kin_mem->kin_pp); - N_VDestroy(kin_mem->kin_vtemp1); - N_VDestroy(kin_mem->kin_vtemp2); - kin_mem->kin_liw -= 5*kin_mem->kin_liw1; - kin_mem->kin_lrw -= 5*kin_mem->kin_lrw1; - return(KIN_MEM_FAIL); - } - } - - if (kin_mem->kin_gamma_aa == NULL) { - kin_mem->kin_gamma_aa = (realtype *) malloc(kin_mem->kin_m_aa * sizeof(realtype)); - if (kin_mem->kin_gamma_aa == NULL) { - KINProcessError(kin_mem, 0, "KINSOL", "KINAllocVectors", MSG_MEM_FAIL); - N_VDestroy(kin_mem->kin_unew); - N_VDestroy(kin_mem->kin_fval); - N_VDestroy(kin_mem->kin_pp); - N_VDestroy(kin_mem->kin_vtemp1); - N_VDestroy(kin_mem->kin_vtemp2); - free(kin_mem->kin_R_aa); - kin_mem->kin_liw -= 5*kin_mem->kin_liw1; - kin_mem->kin_lrw -= 5*kin_mem->kin_lrw1; - return(KIN_MEM_FAIL); - } - } - - if (kin_mem->kin_ipt_map == NULL) { - kin_mem->kin_ipt_map = (long int *) malloc(kin_mem->kin_m_aa * sizeof(long int)); - if (kin_mem->kin_ipt_map == NULL) { - KINProcessError(kin_mem, 0, "KINSOL", "KINAllocVectors", MSG_MEM_FAIL); - N_VDestroy(kin_mem->kin_unew); - N_VDestroy(kin_mem->kin_fval); - N_VDestroy(kin_mem->kin_pp); - N_VDestroy(kin_mem->kin_vtemp1); - N_VDestroy(kin_mem->kin_vtemp2); - free(kin_mem->kin_R_aa); - free(kin_mem->kin_gamma_aa); - kin_mem->kin_liw -= 5*kin_mem->kin_liw1; - kin_mem->kin_lrw -= 5*kin_mem->kin_lrw1; - return(KIN_MEM_FAIL); - } - } - - if (kin_mem->kin_cv == NULL) { - kin_mem->kin_cv = (realtype *) malloc(2 * (kin_mem->kin_m_aa+1) * sizeof(realtype)); - if (kin_mem->kin_cv == NULL) { - KINProcessError(kin_mem, 0, "KINSOL", "KINAllocVectors", MSG_MEM_FAIL); - N_VDestroy(kin_mem->kin_unew); - N_VDestroy(kin_mem->kin_fval); - N_VDestroy(kin_mem->kin_pp); - N_VDestroy(kin_mem->kin_vtemp1); - N_VDestroy(kin_mem->kin_vtemp2); - free(kin_mem->kin_R_aa); - free(kin_mem->kin_gamma_aa); - free(kin_mem->kin_ipt_map); - kin_mem->kin_liw -= 5*kin_mem->kin_liw1; - kin_mem->kin_lrw -= 5*kin_mem->kin_lrw1; - return(KIN_MEM_FAIL); - } - } - - if (kin_mem->kin_Xv == NULL) { - kin_mem->kin_Xv = (N_Vector *) malloc(2 * (kin_mem->kin_m_aa+1) * sizeof(N_Vector)); - if (kin_mem->kin_Xv == NULL) { - KINProcessError(kin_mem, 0, "KINSOL", "KINAllocVectors", MSG_MEM_FAIL); - N_VDestroy(kin_mem->kin_unew); - N_VDestroy(kin_mem->kin_fval); - N_VDestroy(kin_mem->kin_pp); - N_VDestroy(kin_mem->kin_vtemp1); - N_VDestroy(kin_mem->kin_vtemp2); - free(kin_mem->kin_R_aa); - free(kin_mem->kin_gamma_aa); - free(kin_mem->kin_ipt_map); - free(kin_mem->kin_cv); - kin_mem->kin_liw -= 5*kin_mem->kin_liw1; - kin_mem->kin_lrw -= 5*kin_mem->kin_lrw1; - return(KIN_MEM_FAIL); - } - } - - if (kin_mem->kin_fold_aa == NULL) { - kin_mem->kin_fold_aa = N_VClone(tmpl); - if (kin_mem->kin_fold_aa == NULL) { - N_VDestroy(kin_mem->kin_unew); - N_VDestroy(kin_mem->kin_fval); - N_VDestroy(kin_mem->kin_pp); - N_VDestroy(kin_mem->kin_vtemp1); - N_VDestroy(kin_mem->kin_vtemp2); - free(kin_mem->kin_R_aa); - free(kin_mem->kin_gamma_aa); - free(kin_mem->kin_ipt_map); - free(kin_mem->kin_cv); - free(kin_mem->kin_Xv); - kin_mem->kin_liw -= 5*kin_mem->kin_liw1; - kin_mem->kin_lrw -= 5*kin_mem->kin_lrw1; - return(SUNFALSE); - } - kin_mem->kin_liw += kin_mem->kin_liw1; - kin_mem->kin_lrw += kin_mem->kin_lrw1; - } - - if (kin_mem->kin_gold_aa == NULL) { - kin_mem->kin_gold_aa = N_VClone(tmpl); - if (kin_mem->kin_gold_aa == NULL) { - N_VDestroy(kin_mem->kin_unew); - N_VDestroy(kin_mem->kin_fval); - N_VDestroy(kin_mem->kin_pp); - N_VDestroy(kin_mem->kin_vtemp1); - N_VDestroy(kin_mem->kin_vtemp2); - free(kin_mem->kin_R_aa); - free(kin_mem->kin_gamma_aa); - free(kin_mem->kin_ipt_map); - free(kin_mem->kin_cv); - free(kin_mem->kin_Xv); - N_VDestroy(kin_mem->kin_fold_aa); - kin_mem->kin_liw -= 6*kin_mem->kin_liw1; - kin_mem->kin_lrw -= 6*kin_mem->kin_lrw1; - return(SUNFALSE); - } - kin_mem->kin_liw += kin_mem->kin_liw1; - kin_mem->kin_lrw += kin_mem->kin_lrw1; - } - - if (kin_mem->kin_df_aa == NULL) { - kin_mem->kin_df_aa = N_VCloneVectorArray((int) kin_mem->kin_m_aa,tmpl); - if (kin_mem->kin_df_aa == NULL) { - N_VDestroy(kin_mem->kin_unew); - N_VDestroy(kin_mem->kin_fval); - N_VDestroy(kin_mem->kin_pp); - N_VDestroy(kin_mem->kin_vtemp1); - N_VDestroy(kin_mem->kin_vtemp2); - free(kin_mem->kin_R_aa); - free(kin_mem->kin_gamma_aa); - free(kin_mem->kin_ipt_map); - free(kin_mem->kin_cv); - free(kin_mem->kin_Xv); - N_VDestroy(kin_mem->kin_fold_aa); - N_VDestroy(kin_mem->kin_gold_aa); - kin_mem->kin_liw -= 7*kin_mem->kin_liw1; - kin_mem->kin_lrw -= 7*kin_mem->kin_lrw1; - return(SUNFALSE); - } - kin_mem->kin_liw += kin_mem->kin_m_aa * kin_mem->kin_liw1; - kin_mem->kin_lrw += kin_mem->kin_m_aa * kin_mem->kin_lrw1; - } - - if (kin_mem->kin_dg_aa == NULL) { - kin_mem->kin_dg_aa = N_VCloneVectorArray((int) kin_mem->kin_m_aa,tmpl); - if (kin_mem->kin_dg_aa == NULL) { - N_VDestroy(kin_mem->kin_unew); - N_VDestroy(kin_mem->kin_fval); - N_VDestroy(kin_mem->kin_pp); - N_VDestroy(kin_mem->kin_vtemp1); - N_VDestroy(kin_mem->kin_vtemp2); - free(kin_mem->kin_R_aa); - free(kin_mem->kin_gamma_aa); - free(kin_mem->kin_ipt_map); - free(kin_mem->kin_cv); - free(kin_mem->kin_Xv); - N_VDestroy(kin_mem->kin_fold_aa); - N_VDestroy(kin_mem->kin_gold_aa); - N_VDestroyVectorArray(kin_mem->kin_df_aa, (int) kin_mem->kin_m_aa); - kin_mem->kin_liw -= (7 + kin_mem->kin_m_aa) * kin_mem->kin_liw1; - kin_mem->kin_lrw -= (7 + kin_mem->kin_m_aa) * kin_mem->kin_lrw1; - return(SUNFALSE); - } - kin_mem->kin_liw += kin_mem->kin_m_aa * kin_mem->kin_liw1; - kin_mem->kin_lrw += kin_mem->kin_m_aa * kin_mem->kin_lrw1; - } - - if (kin_mem->kin_q_aa == NULL) { - kin_mem->kin_q_aa = N_VCloneVectorArray((int) kin_mem->kin_m_aa,tmpl); - if (kin_mem->kin_q_aa == NULL) { - N_VDestroy(kin_mem->kin_unew); - N_VDestroy(kin_mem->kin_fval); - N_VDestroy(kin_mem->kin_pp); - N_VDestroy(kin_mem->kin_vtemp1); - N_VDestroy(kin_mem->kin_vtemp2); - free(kin_mem->kin_R_aa); - free(kin_mem->kin_gamma_aa); - free(kin_mem->kin_ipt_map); - free(kin_mem->kin_cv); - free(kin_mem->kin_Xv); - N_VDestroy(kin_mem->kin_fold_aa); - N_VDestroy(kin_mem->kin_gold_aa); - N_VDestroyVectorArray(kin_mem->kin_df_aa, (int) kin_mem->kin_m_aa); - N_VDestroyVectorArray(kin_mem->kin_dg_aa, (int) kin_mem->kin_m_aa); - kin_mem->kin_liw -= (7 + 2 * kin_mem->kin_m_aa) * kin_mem->kin_liw1; - kin_mem->kin_lrw -= (7 + 2 * kin_mem->kin_m_aa) * kin_mem->kin_lrw1; - return(SUNFALSE); - } - kin_mem->kin_liw += kin_mem->kin_m_aa * kin_mem->kin_liw1; - kin_mem->kin_lrw += kin_mem->kin_m_aa * kin_mem->kin_lrw1; - } - } - - return(SUNTRUE); -} - -/* - * KINFreeVectors - * - * This routine frees the KINSol vectors allocated by - * KINAllocVectors. - */ - -static void KINFreeVectors(KINMem kin_mem) -{ - if (kin_mem->kin_unew != NULL) { - N_VDestroy(kin_mem->kin_unew); - kin_mem->kin_unew = NULL; - kin_mem->kin_lrw -= kin_mem->kin_lrw1; - kin_mem->kin_liw -= kin_mem->kin_liw1; - } - - if (kin_mem->kin_fval != NULL) { - N_VDestroy(kin_mem->kin_fval); - kin_mem->kin_fval = NULL; - kin_mem->kin_lrw -= kin_mem->kin_lrw1; - kin_mem->kin_liw -= kin_mem->kin_liw1; - } - - if (kin_mem->kin_pp != NULL) { - N_VDestroy(kin_mem->kin_pp); - kin_mem->kin_pp = NULL; - kin_mem->kin_lrw -= kin_mem->kin_lrw1; - kin_mem->kin_liw -= kin_mem->kin_liw1; - } - - if (kin_mem->kin_vtemp1 != NULL) { - N_VDestroy(kin_mem->kin_vtemp1); - kin_mem->kin_vtemp1 = NULL; - kin_mem->kin_lrw -= kin_mem->kin_lrw1; - kin_mem->kin_liw -= kin_mem->kin_liw1; - } - - if (kin_mem->kin_vtemp2 != NULL) { - N_VDestroy(kin_mem->kin_vtemp2); - kin_mem->kin_vtemp2 = NULL; - kin_mem->kin_lrw -= kin_mem->kin_lrw1; - kin_mem->kin_liw -= kin_mem->kin_liw1; - } - - if (kin_mem->kin_gval != NULL) { - N_VDestroy(kin_mem->kin_gval); - kin_mem->kin_gval = NULL; - kin_mem->kin_lrw -= kin_mem->kin_lrw1; - kin_mem->kin_liw -= kin_mem->kin_liw1; - } - - if (kin_mem->kin_R_aa != NULL) { - free(kin_mem->kin_R_aa); - kin_mem->kin_R_aa = NULL; - } - - if (kin_mem->kin_gamma_aa != NULL) { - free(kin_mem->kin_gamma_aa); - kin_mem->kin_gamma_aa = NULL; - } - - if (kin_mem->kin_ipt_map != NULL) { - free(kin_mem->kin_ipt_map); - kin_mem->kin_ipt_map = NULL; - } - - if (kin_mem->kin_cv != NULL) { - free(kin_mem->kin_cv); - kin_mem->kin_cv = NULL; - } - - if (kin_mem->kin_Xv != NULL) { - free(kin_mem->kin_Xv); - kin_mem->kin_Xv = NULL; - } - - if (kin_mem->kin_fold_aa != NULL) { - N_VDestroy(kin_mem->kin_fold_aa); - kin_mem->kin_fold_aa = NULL; - kin_mem->kin_lrw -= kin_mem->kin_lrw1; - kin_mem->kin_liw -= kin_mem->kin_liw1; - } - - if (kin_mem->kin_gold_aa != NULL) { - N_VDestroy(kin_mem->kin_gold_aa); - kin_mem->kin_gold_aa = NULL; - kin_mem->kin_lrw -= kin_mem->kin_lrw1; - kin_mem->kin_liw -= kin_mem->kin_liw1; - } - - if (kin_mem->kin_df_aa != NULL) { - N_VDestroyVectorArray(kin_mem->kin_df_aa, (int) kin_mem->kin_m_aa); - kin_mem->kin_df_aa = NULL; - kin_mem->kin_lrw -= kin_mem->kin_m_aa * kin_mem->kin_lrw1; - kin_mem->kin_liw -= kin_mem->kin_m_aa * kin_mem->kin_liw1; - } - - if (kin_mem->kin_dg_aa != NULL) { - N_VDestroyVectorArray(kin_mem->kin_dg_aa, (int) kin_mem->kin_m_aa); - kin_mem->kin_dg_aa = NULL; - kin_mem->kin_lrw -= kin_mem->kin_m_aa * kin_mem->kin_lrw1; - kin_mem->kin_liw -= kin_mem->kin_m_aa * kin_mem->kin_liw1; - } - - if (kin_mem->kin_q_aa != NULL) { - N_VDestroyVectorArray(kin_mem->kin_q_aa, (int) kin_mem->kin_m_aa); - kin_mem->kin_q_aa = NULL; - kin_mem->kin_lrw -= kin_mem->kin_m_aa * kin_mem->kin_lrw1; - kin_mem->kin_liw -= kin_mem->kin_m_aa * kin_mem->kin_liw1; - } - - if (kin_mem->kin_constraints != NULL) { - N_VDestroy(kin_mem->kin_constraints); - kin_mem->kin_constraints = NULL; - kin_mem->kin_lrw -= kin_mem->kin_lrw1; - kin_mem->kin_liw -= kin_mem->kin_liw1; - } - - return; -} - -/* - * ----------------------------------------------------------------- - * Initial setup - * ----------------------------------------------------------------- - */ - -/* - * KINSolInit - * - * KINSolInit initializes the problem for the specific input - * received in this call to KINSol (which calls KINSolInit). All - * problem specification inputs are checked for errors. If any error - * occurs during initialization, it is reported to the file whose - * file pointer is errfp. - * - * The possible return values for KINSolInit are: - * KIN_SUCCESS : indicates a normal initialization - * - * KIN_ILL_INPUT : indicates that an input error has been found - * - * KIN_INITIAL_GUESS_OK : indicates that the guess uu - * satisfied the system func(uu) = 0 - * within the tolerances specified - */ - -static int KINSolInit(KINMem kin_mem) -{ - int retval; - realtype fmax; - - /* check for illegal input parameters */ - - if (kin_mem->kin_uu == NULL) { - KINProcessError(kin_mem, KIN_ILL_INPUT, "KINSOL", "KINSolInit", MSG_UU_NULL); - return(KIN_ILL_INPUT); - } - - /* check for valid strategy */ - - if ( (kin_mem->kin_globalstrategy != KIN_NONE) && - (kin_mem->kin_globalstrategy != KIN_LINESEARCH) && - (kin_mem->kin_globalstrategy != KIN_PICARD) && - (kin_mem->kin_globalstrategy != KIN_FP) ) { - KINProcessError(kin_mem, KIN_ILL_INPUT, "KINSOL", "KINSolInit", MSG_BAD_GLSTRAT); - return(KIN_ILL_INPUT); - } - - if (kin_mem->kin_uscale == NULL) { - KINProcessError(kin_mem, KIN_ILL_INPUT, "KINSOL", "KINSolInit", MSG_BAD_USCALE); - return(KIN_ILL_INPUT); - } - - if (N_VMin(kin_mem->kin_uscale) <= ZERO){ - KINProcessError(kin_mem, KIN_ILL_INPUT, "KINSOL", "KINSolInit", MSG_USCALE_NONPOSITIVE); - return(KIN_ILL_INPUT); - } - - if (kin_mem->kin_fscale == NULL) { - KINProcessError(kin_mem, KIN_ILL_INPUT, "KINSOL", "KINSolInit", MSG_BAD_FSCALE); - return(KIN_ILL_INPUT); - } - - if (N_VMin(kin_mem->kin_fscale) <= ZERO){ - KINProcessError(kin_mem, KIN_ILL_INPUT, "KINSOL", "KINSolInit", MSG_FSCALE_NONPOSITIVE); - return(KIN_ILL_INPUT); - } - - if ( (kin_mem->kin_constraints != NULL) && - ( (kin_mem->kin_globalstrategy == KIN_PICARD) || - (kin_mem->kin_globalstrategy == KIN_FP) ) ) { - KINProcessError(kin_mem, KIN_ILL_INPUT, "KINSOL", "KINSolInit", MSG_CONSTRAINTS_NOTOK); - return(KIN_ILL_INPUT); - } - - - /* set the constraints flag */ - - if (kin_mem->kin_constraints == NULL) - kin_mem->kin_constraintsSet = SUNFALSE; - else { - kin_mem->kin_constraintsSet = SUNTRUE; - if ((kin_mem->kin_constraints->ops->nvconstrmask == NULL) || - (kin_mem->kin_constraints->ops->nvminquotient == NULL)) { - KINProcessError(kin_mem, KIN_ILL_INPUT, "KINSOL", "KINSolInit", MSG_BAD_NVECTOR); - return(KIN_ILL_INPUT); - } - } - - /* check the initial guess uu against the constraints */ - - if (kin_mem->kin_constraintsSet) { - if (!N_VConstrMask(kin_mem->kin_constraints, kin_mem->kin_uu, kin_mem->kin_vtemp1)) { - KINProcessError(kin_mem, KIN_ILL_INPUT, "KINSOL", "KINSolInit", MSG_INITIAL_CNSTRNT); - return(KIN_ILL_INPUT); - } - } - - /* all error checking is complete at this point */ - - if (kin_mem->kin_printfl > 0) - KINPrintInfo(kin_mem, PRNT_TOL, "KINSOL", "KINSolInit", INFO_TOL, kin_mem->kin_scsteptol, kin_mem->kin_fnormtol); - - /* calculate the default value for mxnewtstep (maximum Newton step) */ - - if (kin_mem->kin_mxnstepin == ZERO) kin_mem->kin_mxnewtstep = THOUSAND * N_VWL2Norm(kin_mem->kin_uu, kin_mem->kin_uscale); - else kin_mem->kin_mxnewtstep = kin_mem->kin_mxnstepin; - - if (kin_mem->kin_mxnewtstep < ONE) kin_mem->kin_mxnewtstep = ONE; - - /* additional set-up for inexact linear solvers */ - - if (kin_mem->kin_inexact_ls) { - - /* set up the coefficients for the eta calculation */ - - kin_mem->kin_callForcingTerm = (kin_mem->kin_etaflag != KIN_ETACONSTANT); - - /* this value is always used for choice #1 */ - - if (kin_mem->kin_etaflag == KIN_ETACHOICE1) kin_mem->kin_eta_alpha = (ONE + SUNRsqrt(FIVE)) * HALF; - - /* initial value for eta set to 0.5 for other than the - KIN_ETACONSTANT option */ - - if (kin_mem->kin_etaflag != KIN_ETACONSTANT) kin_mem->kin_eta = HALF; - - /* disable residual monitoring if using an inexact linear solver */ - - kin_mem->kin_noResMon = SUNTRUE; - - } else { - - kin_mem->kin_callForcingTerm = SUNFALSE; - - } - - /* initialize counters */ - - kin_mem->kin_nfe = kin_mem->kin_nnilset = kin_mem->kin_nnilset_sub = kin_mem->kin_nni = kin_mem->kin_nbcf = kin_mem->kin_nbktrk = 0; - - /* see if the initial guess uu satisfies the nonlinear system */ - retval = kin_mem->kin_func(kin_mem->kin_uu, kin_mem->kin_fval, kin_mem->kin_user_data); kin_mem->kin_nfe++; - - if (retval < 0) { - KINProcessError(kin_mem, KIN_SYSFUNC_FAIL, "KINSOL", "KINSolInit", - MSG_SYSFUNC_FAILED); - return(KIN_SYSFUNC_FAIL); - } else if (retval > 0) { - KINProcessError(kin_mem, KIN_FIRST_SYSFUNC_ERR, "KINSOL", "KINSolInit", - MSG_SYSFUNC_FIRST); - return(KIN_FIRST_SYSFUNC_ERR); - } - - fmax = KINScFNorm(kin_mem, kin_mem->kin_fval, kin_mem->kin_fscale); - if (fmax <= (POINT01 * kin_mem->kin_fnormtol)) { - kin_mem->kin_fnorm = N_VWL2Norm(kin_mem->kin_fval, kin_mem->kin_fscale); - return(KIN_INITIAL_GUESS_OK); - } - - if (kin_mem->kin_printfl > 1) - KINPrintInfo(kin_mem, PRNT_FMAX, "KINSOL", "KINSolInit", INFO_FMAX, fmax); - - /* initialize the linear solver if linit != NULL */ - - if (kin_mem->kin_linit != NULL) { - retval = kin_mem->kin_linit(kin_mem); - if (retval != 0) { - KINProcessError(kin_mem, KIN_LINIT_FAIL, "KINSOL", "KINSolInit", MSG_LINIT_FAIL); - return(KIN_LINIT_FAIL); - } - } - - /* initialize the L2 (Euclidean) norms of f for the linear iteration steps */ - - kin_mem->kin_fnorm = N_VWL2Norm(kin_mem->kin_fval, kin_mem->kin_fscale); - kin_mem->kin_f1norm = HALF * kin_mem->kin_fnorm * kin_mem->kin_fnorm; - kin_mem->kin_fnorm_sub = kin_mem->kin_fnorm; - - if (kin_mem->kin_printfl > 0) - KINPrintInfo(kin_mem, PRNT_NNI, "KINSOL", "KINSolInit", - INFO_NNI, kin_mem->kin_nni, kin_mem->kin_nfe, kin_mem->kin_fnorm); - - /* problem has now been successfully initialized */ - - return(KIN_SUCCESS); -} - -/* - * ----------------------------------------------------------------- - * Step functions - * ----------------------------------------------------------------- - */ - -/* - * KINLinSolDrv - * - * This routine handles the process of solving for the approximate - * solution of the Newton equations in the Newton iteration. - * Subsequent routines handle the nonlinear aspects of its - * application. - */ - -static int KINLinSolDrv(KINMem kin_mem) -{ - N_Vector x, b; - int retval; - - if ((kin_mem->kin_nni - kin_mem->kin_nnilset) >= kin_mem->kin_msbset) { - kin_mem->kin_sthrsh = TWO; - kin_mem->kin_update_fnorm_sub = SUNTRUE; - } - - for(;;){ - - kin_mem->kin_jacCurrent = SUNFALSE; - - if ((kin_mem->kin_sthrsh > ONEPT5) && (kin_mem->kin_lsetup != NULL)) { - retval = kin_mem->kin_lsetup(kin_mem); - kin_mem->kin_jacCurrent = SUNTRUE; - kin_mem->kin_nnilset = kin_mem->kin_nni; - kin_mem->kin_nnilset_sub = kin_mem->kin_nni; - if (retval != 0) return(KIN_LSETUP_FAIL); - } - - /* rename vectors for readability */ - - b = kin_mem->kin_unew; - x = kin_mem->kin_pp; - - /* load b with the current value of -fval */ - - N_VScale(-ONE, kin_mem->kin_fval, b); - - /* call the generic 'lsolve' routine to solve the system Jx = b */ - - retval = kin_mem->kin_lsolve(kin_mem, x, b, &(kin_mem->kin_sJpnorm), - &(kin_mem->kin_sFdotJp)); - - if (retval == 0) return(KIN_SUCCESS); - else if (retval < 0) return(KIN_LSOLVE_FAIL); - else if ((kin_mem->kin_lsetup == NULL) || (kin_mem->kin_jacCurrent)) return(KIN_LINSOLV_NO_RECOVERY); - - /* loop back only if the linear solver setup is in use - and Jacobian information is not current */ - - kin_mem->kin_sthrsh = TWO; - - } -} - -/* - * KINFullNewton - * - * This routine is the main driver for the Full Newton - * algorithm. Its purpose is to compute unew = uu + pp in the - * direction pp from uu, taking the full Newton step. The - * step may be constrained if the constraint conditions are - * violated, or if the norm of pp is greater than mxnewtstep. - */ - -static int KINFullNewton(KINMem kin_mem, realtype *fnormp, realtype *f1normp, - booleantype *maxStepTaken) -{ - realtype pnorm, ratio; - booleantype fOK; - int ircvr, retval; - - *maxStepTaken = SUNFALSE; - pnorm = N_VWL2Norm(kin_mem->kin_pp, kin_mem->kin_uscale); - ratio = ONE; - if (pnorm > kin_mem->kin_mxnewtstep) { - ratio = kin_mem->kin_mxnewtstep / pnorm; - N_VScale(ratio, kin_mem->kin_pp, kin_mem->kin_pp); - pnorm = kin_mem->kin_mxnewtstep; - } - - if (kin_mem->kin_printfl > 0) - KINPrintInfo(kin_mem, PRNT_PNORM, "KINSOL", "KINFullNewton", INFO_PNORM, pnorm); - - /* If constraints are active, then constrain the step accordingly */ - - kin_mem->kin_stepl = pnorm; - kin_mem->kin_stepmul = ONE; - if (kin_mem->kin_constraintsSet) { - retval = KINConstraint(kin_mem); - if (retval == CONSTR_VIOLATED) { - /* Apply stepmul set in KINConstraint */ - ratio *= kin_mem->kin_stepmul; - N_VScale(kin_mem->kin_stepmul, kin_mem->kin_pp, kin_mem->kin_pp); - pnorm *= kin_mem->kin_stepmul; - kin_mem->kin_stepl = pnorm; - if (kin_mem->kin_printfl > 0) - KINPrintInfo(kin_mem, PRNT_PNORM, "KINSOL", "KINFullNewton", INFO_PNORM, pnorm); - if (pnorm <= kin_mem->kin_scsteptol) { - N_VLinearSum(ONE, kin_mem->kin_uu, ONE, kin_mem->kin_pp, kin_mem->kin_unew); - return(STEP_TOO_SMALL);} - } - } - - /* Attempt (at most MAX_RECVR times) to evaluate function at the new iterate */ - - fOK = SUNFALSE; - - for (ircvr = 1; ircvr <= MAX_RECVR; ircvr++) { - - /* compute the iterate unew = uu + pp */ - N_VLinearSum(ONE, kin_mem->kin_uu, ONE, kin_mem->kin_pp, kin_mem->kin_unew); - - /* evaluate func(unew) and its norm, and return */ - retval = kin_mem->kin_func(kin_mem->kin_unew, kin_mem->kin_fval, kin_mem->kin_user_data); kin_mem->kin_nfe++; - - /* if func was successful, accept pp */ - if (retval == 0) {fOK = SUNTRUE; break;} - - /* if func failed unrecoverably, give up */ - else if (retval < 0) return(KIN_SYSFUNC_FAIL); - - /* func failed recoverably; cut step in half and try again */ - ratio *= HALF; - N_VScale(HALF, kin_mem->kin_pp, kin_mem->kin_pp); - pnorm *= HALF; - kin_mem->kin_stepl = pnorm; - } - - /* If func() failed recoverably MAX_RECVR times, give up */ - - if (!fOK) return(KIN_REPTD_SYSFUNC_ERR); - - /* Evaluate function norms */ - - *fnormp = N_VWL2Norm(kin_mem->kin_fval, kin_mem->kin_fscale); - *f1normp = HALF * (*fnormp) * (*fnormp); - - /* scale sFdotJp and sJpnorm by ratio for later use in KINForcingTerm */ - - kin_mem->kin_sFdotJp *= ratio; - kin_mem->kin_sJpnorm *= ratio; - - if (kin_mem->kin_printfl > 1) - KINPrintInfo(kin_mem, PRNT_FNORM, "KINSOL", "KINFullNewton", INFO_FNORM, *fnormp); - - if (pnorm > (POINT99 * kin_mem->kin_mxnewtstep)) *maxStepTaken = SUNTRUE; - - return(KIN_SUCCESS); -} - -/* - * KINLineSearch - * - * The routine KINLineSearch implements the LineSearch algorithm. - * Its purpose is to find unew = uu + rl * pp in the direction pp - * from uu so that: - * t - * func(unew) <= func(uu) + alpha * g (unew - uu) (alpha = 1.e-4) - * - * and - * t - * func(unew) >= func(uu) + beta * g (unew - uu) (beta = 0.9) - * - * where 0 < rlmin <= rl <= rlmax. - * - * Note: - * mxnewtstep - * rlmax = ---------------- if uu+pp is feasible - * ||uscale*pp||_L2 - * - * rlmax = 1 otherwise - * - * and - * - * scsteptol - * rlmin = -------------------------- - * || pp || - * || -------------------- ||_L-infinity - * || (1/uscale + SUNRabs(uu)) || - * - * - * If the system function fails unrecoverably at any time, KINLineSearch - * returns KIN_SYSFUNC_FAIL which will halt the solver. - * - * We attempt to corect recoverable system function failures only before - * the alpha-condition loop; i.e. when the solution is updated with the - * full Newton step (possibly reduced due to constraint violations). - * Once we find a feasible pp, we assume that any update up to pp is - * feasible. - * - * If the step size is limited due to constraint violations and/or - * recoverable system function failures, we set rlmax=1 to ensure - * that the update remains feasible during the attempts to enforce - * the beta-condition (this is not an issue while enforcing the alpha - * condition, as rl can only decrease from 1 at that stage) - */ - -static int KINLineSearch(KINMem kin_mem, realtype *fnormp, realtype *f1normp, - booleantype *maxStepTaken) -{ - realtype pnorm, ratio, slpi, rlmin, rlength, rl, rlmax, rldiff; - realtype rltmp, rlprev, pt1trl, f1nprv, rllo, rlinc, alpha, beta; - realtype alpha_cond, beta_cond, rl_a, tmp1, rl_b, tmp2, disc; - int ircvr, nbktrk_l, retval; - booleantype firstBacktrack, fOK; - - /* Initializations */ - - nbktrk_l = 0; /* local backtracking counter */ - ratio = ONE; /* step change ratio */ - alpha = POINT0001; - beta = POINT9; - - firstBacktrack = SUNTRUE; - *maxStepTaken = SUNFALSE; - - rlprev = f1nprv = ZERO; - - /* Compute length of Newton step */ - - pnorm = N_VWL2Norm(kin_mem->kin_pp, kin_mem->kin_uscale); - rlmax = kin_mem->kin_mxnewtstep / pnorm; - kin_mem->kin_stepl = pnorm; - - /* If the full Newton step is too large, set it to the maximum allowable value */ - - if(pnorm > kin_mem->kin_mxnewtstep ) { - ratio = kin_mem->kin_mxnewtstep / pnorm; - N_VScale(ratio, kin_mem->kin_pp, kin_mem->kin_pp); - pnorm = kin_mem->kin_mxnewtstep; - rlmax = ONE; - kin_mem->kin_stepl = pnorm; - } - - /* If constraint checking is activated, check and correct violations */ - - kin_mem->kin_stepmul = ONE; - - if(kin_mem->kin_constraintsSet){ - retval = KINConstraint(kin_mem); - if(retval == CONSTR_VIOLATED){ - /* Apply stepmul set in KINConstraint */ - N_VScale(kin_mem->kin_stepmul, kin_mem->kin_pp, kin_mem->kin_pp); - ratio *= kin_mem->kin_stepmul; - pnorm *= kin_mem->kin_stepmul; - rlmax = ONE; - kin_mem->kin_stepl = pnorm; - if (kin_mem->kin_printfl > 0) KINPrintInfo(kin_mem, PRNT_PNORM1, "KINSOL", "KINLineSearch", INFO_PNORM1, pnorm); - if (pnorm <= kin_mem->kin_scsteptol) { - N_VLinearSum(ONE, kin_mem->kin_uu, ONE, kin_mem->kin_pp, kin_mem->kin_unew); - return(STEP_TOO_SMALL);} - } - } - - /* Attempt (at most MAX_RECVR times) to evaluate function at the new iterate */ - - fOK = SUNFALSE; - - for (ircvr = 1; ircvr <= MAX_RECVR; ircvr++) { - - /* compute the iterate unew = uu + pp */ - N_VLinearSum(ONE, kin_mem->kin_uu, ONE, kin_mem->kin_pp, kin_mem->kin_unew); - - /* evaluate func(unew) and its norm, and return */ - retval = kin_mem->kin_func(kin_mem->kin_unew, kin_mem->kin_fval, kin_mem->kin_user_data); kin_mem->kin_nfe++; - - /* if func was successful, accept pp */ - if (retval == 0) {fOK = SUNTRUE; break;} - - /* if func failed unrecoverably, give up */ - else if (retval < 0) return(KIN_SYSFUNC_FAIL); - - /* func failed recoverably; cut step in half and try again */ - N_VScale(HALF, kin_mem->kin_pp, kin_mem->kin_pp); - ratio *= HALF; - pnorm *= HALF; - rlmax = ONE; - kin_mem->kin_stepl = pnorm; - - } - - /* If func() failed recoverably MAX_RECVR times, give up */ - - if (!fOK) return(KIN_REPTD_SYSFUNC_ERR); - - /* Evaluate function norms */ - - *fnormp = N_VWL2Norm(kin_mem->kin_fval, kin_mem->kin_fscale); - *f1normp = HALF * (*fnormp) * (*fnormp) ; - - /* Estimate the line search value rl (lambda) to satisfy both ALPHA and BETA conditions */ - - slpi = kin_mem->kin_sFdotJp * ratio; - rlength = KINScSNorm(kin_mem, kin_mem->kin_pp, kin_mem->kin_uu); - rlmin = (kin_mem->kin_scsteptol) / rlength; - rl = ONE; - - if (kin_mem->kin_printfl > 2) - KINPrintInfo(kin_mem, PRNT_LAM, "KINSOL", "KINLineSearch", INFO_LAM, rlmin, kin_mem->kin_f1norm, pnorm); - - /* Loop until the ALPHA condition is satisfied. Terminate if rl becomes too small */ - - for(;;) { - - /* Evaluate test quantity */ - - alpha_cond = kin_mem->kin_f1norm + (alpha * slpi * rl); - - if (kin_mem->kin_printfl > 2) - KINPrintInfo(kin_mem, PRNT_ALPHA, "KINSOL", "KINLinesearch", - INFO_ALPHA, *fnormp, *f1normp, alpha_cond, rl); - - /* If ALPHA condition is satisfied, break out from loop */ - - if ((*f1normp) <= alpha_cond) break; - - /* Backtracking. Use quadratic fit the first time and cubic fit afterwards. */ - - if (firstBacktrack) { - - rltmp = -slpi / (TWO * ((*f1normp) - kin_mem->kin_f1norm - slpi)); - firstBacktrack = SUNFALSE; - - } else { - - tmp1 = (*f1normp) - kin_mem->kin_f1norm - (rl * slpi); - tmp2 = f1nprv - kin_mem->kin_f1norm - (rlprev * slpi); - rl_a = ((ONE / (rl * rl)) * tmp1) - ((ONE / (rlprev * rlprev)) * tmp2); - rl_b = ((-rlprev / (rl * rl)) * tmp1) + ((rl / (rlprev * rlprev)) * tmp2); - tmp1 = ONE / (rl - rlprev); - rl_a *= tmp1; - rl_b *= tmp1; - disc = (rl_b * rl_b) - (THREE * rl_a * slpi); - - if (SUNRabs(rl_a) < kin_mem->kin_uround) { /* cubic is actually just a quadratic (rl_a ~ 0) */ - rltmp = -slpi / (TWO * rl_b); - } else { /* real cubic */ - rltmp = (-rl_b + SUNRsqrt(disc)) / (THREE * rl_a); - } - } - if (rltmp > (HALF * rl)) rltmp = HALF * rl; - - /* Set new rl (do not allow a reduction by a factor larger than 10) */ - - rlprev = rl; - f1nprv = (*f1normp); - pt1trl = POINT1 * rl; - rl = SUNMAX(pt1trl, rltmp); - nbktrk_l++; - - /* Update unew and re-evaluate function */ - - N_VLinearSum(ONE, kin_mem->kin_uu, rl, kin_mem->kin_pp, kin_mem->kin_unew); - - retval = kin_mem->kin_func(kin_mem->kin_unew, kin_mem->kin_fval, kin_mem->kin_user_data); kin_mem->kin_nfe++; - if (retval != 0) return(KIN_SYSFUNC_FAIL); - - *fnormp = N_VWL2Norm(kin_mem->kin_fval, kin_mem->kin_fscale); - *f1normp = HALF * (*fnormp) * (*fnormp) ; - - /* Check if rl (lambda) is too small */ - - if (rl < rlmin) { - /* unew sufficiently distinct from uu cannot be found. - copy uu into unew (step remains unchanged) and - return STEP_TOO_SMALL */ - N_VScale(ONE, kin_mem->kin_uu, kin_mem->kin_unew); - return(STEP_TOO_SMALL); - } - - } /* end ALPHA condition loop */ - - - /* ALPHA condition is satisfied. Now check the BETA condition */ - - beta_cond = kin_mem->kin_f1norm + (beta * slpi * rl); - - if ((*f1normp) < beta_cond) { - - /* BETA condition not satisfied */ - - if ((rl == ONE) && (pnorm < kin_mem->kin_mxnewtstep)) { - - do { - - rlprev = rl; - f1nprv = *f1normp; - rl = SUNMIN((TWO * rl), rlmax); - nbktrk_l++; - - N_VLinearSum(ONE, kin_mem->kin_uu, rl, kin_mem->kin_pp, kin_mem->kin_unew); - retval = kin_mem->kin_func(kin_mem->kin_unew, kin_mem->kin_fval, kin_mem->kin_user_data); kin_mem->kin_nfe++; - if (retval != 0) return(KIN_SYSFUNC_FAIL); - *fnormp = N_VWL2Norm(kin_mem->kin_fval, kin_mem->kin_fscale); - *f1normp = HALF * (*fnormp) * (*fnormp); - - alpha_cond = kin_mem->kin_f1norm + (alpha * slpi * rl); - beta_cond = kin_mem->kin_f1norm + (beta * slpi * rl); - - if (kin_mem->kin_printfl > 2) - KINPrintInfo(kin_mem, PRNT_BETA, "KINSOL", "KINLineSearch", - INFO_BETA, *f1normp, beta_cond, rl); - - } while (((*f1normp) <= alpha_cond) && - ((*f1normp) < beta_cond) && (rl < rlmax)); - - } /* end if (rl == ONE) block */ - - if ((rl < ONE) || ((rl > ONE) && (*f1normp > alpha_cond))) { - - rllo = SUNMIN(rl, rlprev); - rldiff = SUNRabs(rlprev - rl); - - do { - - rlinc = HALF * rldiff; - rl = rllo + rlinc; - nbktrk_l++; - - N_VLinearSum(ONE, kin_mem->kin_uu, rl, kin_mem->kin_pp, kin_mem->kin_unew); - retval = kin_mem->kin_func(kin_mem->kin_unew, kin_mem->kin_fval, kin_mem->kin_user_data); kin_mem->kin_nfe++; - if (retval != 0) return(KIN_SYSFUNC_FAIL); - *fnormp = N_VWL2Norm(kin_mem->kin_fval, kin_mem->kin_fscale); - *f1normp = HALF * (*fnormp) * (*fnormp); - - alpha_cond = kin_mem->kin_f1norm + (alpha * slpi * rl); - beta_cond = kin_mem->kin_f1norm + (beta * slpi * rl); - - if (kin_mem->kin_printfl > 2) - KINPrintInfo(kin_mem, PRNT_ALPHABETA, "KINSOL", "KINLineSearch", - INFO_ALPHABETA, *f1normp, alpha_cond, beta_cond, rl); - - if ((*f1normp) > alpha_cond) rldiff = rlinc; - else if (*f1normp < beta_cond) { - rllo = rl; - rldiff = rldiff - rlinc; - } - - } while ((*f1normp > alpha_cond) || - ((*f1normp < beta_cond) && (rldiff >= rlmin))); - - if ( (*f1normp < beta_cond) || ((rldiff < rlmin) && (*f1normp > alpha_cond)) ) { - - /* beta condition could not be satisfied or rldiff too small - and alpha_cond not satisfied, so set unew to last u value - that satisfied the alpha condition and continue */ - - N_VLinearSum(ONE, kin_mem->kin_uu, rllo, kin_mem->kin_pp, kin_mem->kin_unew); - retval = kin_mem->kin_func(kin_mem->kin_unew, kin_mem->kin_fval, kin_mem->kin_user_data); kin_mem->kin_nfe++; - if (retval != 0) return(KIN_SYSFUNC_FAIL); - *fnormp = N_VWL2Norm(kin_mem->kin_fval, kin_mem->kin_fscale); - *f1normp = HALF * (*fnormp) * (*fnormp); - - /* increment beta-condition failures counter */ - - kin_mem->kin_nbcf++; - - } - - } /* end of if (rl < ONE) block */ - - } /* end of if (f1normp < beta_cond) block */ - - /* Update number of backtracking operations */ - - kin_mem->kin_nbktrk += nbktrk_l; - - if (kin_mem->kin_printfl > 1) - KINPrintInfo(kin_mem, PRNT_ADJ, "KINSOL", "KINLineSearch", INFO_ADJ, nbktrk_l); - - /* scale sFdotJp and sJpnorm by rl * ratio for later use in KINForcingTerm */ - - kin_mem->kin_sFdotJp = kin_mem->kin_sFdotJp * rl * ratio; - kin_mem->kin_sJpnorm = kin_mem->kin_sJpnorm * rl * ratio; - - if ((rl * pnorm) > (POINT99 * kin_mem->kin_mxnewtstep)) *maxStepTaken = SUNTRUE; - - return(KIN_SUCCESS); -} - -/* - * Function : KINConstraint - * - * This routine checks if the proposed solution vector uu + pp - * violates any constraints. If a constraint is violated, then the - * scalar stepmul is determined such that uu + stepmul * pp does - * not violate any constraints. - * - * Note: This routine is called by the functions - * KINLineSearch and KINFullNewton. - */ - -static int KINConstraint(KINMem kin_mem) -{ - N_VLinearSum(ONE, kin_mem->kin_uu, ONE, kin_mem->kin_pp, kin_mem->kin_vtemp1); - - /* if vtemp1[i] violates constraint[i] then vtemp2[i] = 1 - else vtemp2[i] = 0 (vtemp2 is the mask vector) */ - - if(N_VConstrMask(kin_mem->kin_constraints, kin_mem->kin_vtemp1, kin_mem->kin_vtemp2)) return(KIN_SUCCESS); - - /* vtemp1[i] = SUNRabs(pp[i]) */ - - N_VAbs(kin_mem->kin_pp, kin_mem->kin_vtemp1); - - /* consider vtemp1[i] only if vtemp2[i] = 1 (constraint violated) */ - - N_VProd(kin_mem->kin_vtemp2, kin_mem->kin_vtemp1, kin_mem->kin_vtemp1); - - N_VAbs(kin_mem->kin_uu, kin_mem->kin_vtemp2); - kin_mem->kin_stepmul = POINT9 * N_VMinQuotient(kin_mem->kin_vtemp2, kin_mem->kin_vtemp1); - - return(CONSTR_VIOLATED); -} - -/* - * ----------------------------------------------------------------- - * Stopping tests - * ----------------------------------------------------------------- - */ - -/* - * KINStop - * - * This routine checks the current iterate unew to see if the - * system func(unew) = 0 is satisfied by a variety of tests. - * - * strategy is one of KIN_NONE or KIN_LINESEARCH - * sflag is one of KIN_SUCCESS, STEP_TOO_SMALL - */ - -static int KINStop(KINMem kin_mem, booleantype maxStepTaken, int sflag) -{ - realtype fmax, rlength, omexp; - N_Vector delta; - - /* Check for too small a step */ - - if (sflag == STEP_TOO_SMALL) { - - if ((kin_mem->kin_lsetup != NULL) && !(kin_mem->kin_jacCurrent)) { - /* If the Jacobian is out of date, update it and retry */ - kin_mem->kin_sthrsh = TWO; - return(RETRY_ITERATION); - } else { - /* Give up */ - if (kin_mem->kin_globalstrategy == KIN_NONE) return(KIN_STEP_LT_STPTOL); - else return(KIN_LINESEARCH_NONCONV); - } - - } - - /* Check tolerance on scaled function norm at the current iterate */ - - fmax = KINScFNorm(kin_mem, kin_mem->kin_fval, kin_mem->kin_fscale); - - if (kin_mem->kin_printfl > 1) - KINPrintInfo(kin_mem, PRNT_FMAX, "KINSOL", "KINStop", INFO_FMAX, fmax); - - if (fmax <= kin_mem->kin_fnormtol) return(KIN_SUCCESS); - - /* Check if the scaled distance between the last two steps is too small */ - /* NOTE: pp used as work space to store this distance */ - - delta = kin_mem->kin_pp; - N_VLinearSum(ONE, kin_mem->kin_unew, -ONE, kin_mem->kin_uu, delta); - rlength = KINScSNorm(kin_mem, delta, kin_mem->kin_unew); - - if (rlength <= kin_mem->kin_scsteptol) { - - if ((kin_mem->kin_lsetup != NULL) && !(kin_mem->kin_jacCurrent)) { - /* If the Jacobian is out of date, update it and retry */ - kin_mem->kin_sthrsh = TWO; - return(CONTINUE_ITERATIONS); - } else { - /* give up */ - return(KIN_STEP_LT_STPTOL); - } - - } - - /* Check if the maximum number of iterations is reached */ - - if (kin_mem->kin_nni >= kin_mem->kin_mxiter) return(KIN_MAXITER_REACHED); - - /* Check for consecutive number of steps taken of size mxnewtstep - and if not maxStepTaken, then set ncscmx to 0 */ - - if (maxStepTaken) kin_mem->kin_ncscmx++; - else kin_mem->kin_ncscmx = 0; - - if (kin_mem->kin_ncscmx == 5) return(KIN_MXNEWT_5X_EXCEEDED); - - /* Proceed according to the type of linear solver used */ - - if (kin_mem->kin_inexact_ls) { - - /* We're doing inexact Newton. - Load threshold for reevaluating the Jacobian. */ - - kin_mem->kin_sthrsh = rlength; - - } else if (!(kin_mem->kin_noResMon)) { - - /* We're doing modified Newton and the user did not disable residual monitoring. - Check if it is time to monitor residual. */ - - if ((kin_mem->kin_nni - kin_mem->kin_nnilset_sub) >= kin_mem->kin_msbset_sub) { - - /* Residual monitoring needed */ - - kin_mem->kin_nnilset_sub = kin_mem->kin_nni; - - /* If indicated, estimate new OMEGA value */ - if (kin_mem->kin_eval_omega) { - omexp = SUNMAX(ZERO,((kin_mem->kin_fnorm)/(kin_mem->kin_fnormtol))-ONE); - kin_mem->kin_omega = (omexp > TWELVE)? kin_mem->kin_omega_max : SUNMIN(kin_mem->kin_omega_min * SUNRexp(omexp), kin_mem->kin_omega_max); - } - /* Check if making satisfactory progress */ - - if (kin_mem->kin_fnorm > kin_mem->kin_omega * kin_mem->kin_fnorm_sub) { - /* Insufficient progress */ - if ((kin_mem->kin_lsetup != NULL) && !(kin_mem->kin_jacCurrent)) { - /* If the Jacobian is out of date, update it and retry */ - kin_mem->kin_sthrsh = TWO; - return(CONTINUE_ITERATIONS); - } else { - /* Otherwise, we cannot do anything, so just return. */ - } - } else { - /* Sufficient progress */ - kin_mem->kin_fnorm_sub = kin_mem->kin_fnorm; - kin_mem->kin_sthrsh = ONE; - } - - } else { - - /* Residual monitoring not needed */ - - /* Reset sthrsh */ - if (kin_mem->kin_retry_nni || kin_mem->kin_update_fnorm_sub) kin_mem->kin_fnorm_sub = kin_mem->kin_fnorm; - if (kin_mem->kin_update_fnorm_sub) kin_mem->kin_update_fnorm_sub = SUNFALSE; - kin_mem->kin_sthrsh = ONE; - - } - - } - - /* if made it to here, then the iteration process is not finished - so return CONTINUE_ITERATIONS flag */ - - return(CONTINUE_ITERATIONS); -} - -/* - * KINForcingTerm - * - * This routine computes eta, the scaling factor in the linear - * convergence stopping tolerance eps when choice #1 or choice #2 - * forcing terms are used. Eta is computed here for all but the - * first iterative step, which is set to the default in routine - * KINSolInit. - * - * This routine was written by Homer Walker of Utah State - * University with subsequent modifications by Allan Taylor @ LLNL. - * - * It is based on the concepts of the paper 'Choosing the forcing - * terms in an inexact Newton method', SIAM J Sci Comput, 17 - * (1996), pp 16 - 32, or Utah State University Research Report - * 6/94/75 of the same title. - */ - -static void KINForcingTerm(KINMem kin_mem, realtype fnormp) -{ - realtype eta_max, eta_min, eta_safe, linmodel_norm; - - eta_max = POINT9; - eta_min = POINT0001; - eta_safe = HALF; - - /* choice #1 forcing term */ - - if (kin_mem->kin_etaflag == KIN_ETACHOICE1) { - - /* compute the norm of f + Jp , scaled L2 norm */ - - linmodel_norm = SUNRsqrt((kin_mem->kin_fnorm * kin_mem->kin_fnorm) + - (TWO * kin_mem->kin_sFdotJp) + - (kin_mem->kin_sJpnorm * kin_mem->kin_sJpnorm)); - - /* form the safeguarded for choice #1 */ - - eta_safe = SUNRpowerR(kin_mem->kin_eta, kin_mem->kin_eta_alpha); - kin_mem->kin_eta = SUNRabs(fnormp - linmodel_norm) / kin_mem->kin_fnorm; - } - - /* choice #2 forcing term */ - - if (kin_mem->kin_etaflag == KIN_ETACHOICE2) { - eta_safe = kin_mem->kin_eta_gamma * - SUNRpowerR(kin_mem->kin_eta, kin_mem->kin_eta_alpha); - - kin_mem->kin_eta = kin_mem->kin_eta_gamma * - SUNRpowerR((fnormp / kin_mem->kin_fnorm), kin_mem->kin_eta_alpha); - } - - /* apply safeguards */ - - if(eta_safe < POINT1) eta_safe = ZERO; - kin_mem->kin_eta = SUNMAX(kin_mem->kin_eta, eta_safe); - kin_mem->kin_eta = SUNMAX(kin_mem->kin_eta, eta_min); - kin_mem->kin_eta = SUNMIN(kin_mem->kin_eta, eta_max); - - return; -} - - -/* - * ----------------------------------------------------------------- - * Norm functions - * ----------------------------------------------------------------- - */ - -/* - * Function : KINScFNorm - * - * This routine computes the max norm for scaled vectors. The - * scaling vector is scale, and the vector of which the norm is to - * be determined is vv. The returned value, fnormval, is the - * resulting scaled vector norm. This routine uses N_Vector - * functions from the vector module. - */ - -static realtype KINScFNorm(KINMem kin_mem, N_Vector v, N_Vector scale) -{ - N_VProd(scale, v, kin_mem->kin_vtemp1); - return(N_VMaxNorm(kin_mem->kin_vtemp1)); -} - -/* - * Function : KINScSNorm - * - * This routine computes the max norm of the scaled steplength, ss. - * Here ucur is the current step and usc is the u scale factor. - */ - -static realtype KINScSNorm(KINMem kin_mem, N_Vector v, N_Vector u) -{ - realtype length; - - N_VInv(kin_mem->kin_uscale, kin_mem->kin_vtemp1); - N_VAbs(u, kin_mem->kin_vtemp2); - N_VLinearSum(ONE, kin_mem->kin_vtemp1, ONE, kin_mem->kin_vtemp2, kin_mem->kin_vtemp1); - N_VDiv(v, kin_mem->kin_vtemp1, kin_mem->kin_vtemp1); - - length = N_VMaxNorm(kin_mem->kin_vtemp1); - - return(length); -} - -/* - * ================================================================= - * KINSOL Verbose output functions - * ================================================================= - */ - -/* - * KINPrintInfo - * - * KINPrintInfo is a high level error handling function - * Based on the value info_code, it composes the info message and - * passes it to the info handler function. - */ - -void KINPrintInfo(KINMem kin_mem, - int info_code, const char *module, const char *fname, - const char *msgfmt, ...) -{ - va_list ap; - char msg[256], msg1[40]; - char retstr[30]; - int ret; - - /* Initialize argument processing - (msgfrmt is the last required argument) */ - - va_start(ap, msgfmt); - - if (info_code == PRNT_RETVAL) { - - /* If info_code = PRNT_RETVAL, decode the numeric value */ - - ret = va_arg(ap, int); - - switch(ret) { - case KIN_SUCCESS: - sprintf(retstr, "KIN_SUCCESS"); - break; - case KIN_SYSFUNC_FAIL: - sprintf(retstr, "KIN_SYSFUNC_FAIL"); - break; - case KIN_REPTD_SYSFUNC_ERR: - sprintf(retstr, "KIN_REPTD_SYSFUNC_ERR"); - break; - case KIN_STEP_LT_STPTOL: - sprintf(retstr, "KIN_STEP_LT_STPTOL"); - break; - case KIN_LINESEARCH_NONCONV: - sprintf(retstr, "KIN_LINESEARCH_NONCONV"); - break; - case KIN_LINESEARCH_BCFAIL: - sprintf(retstr, "KIN_LINESEARCH_BCFAIL"); - break; - case KIN_MAXITER_REACHED: - sprintf(retstr, "KIN_MAXITER_REACHED"); - break; - case KIN_MXNEWT_5X_EXCEEDED: - sprintf(retstr, "KIN_MXNEWT_5X_EXCEEDED"); - break; - case KIN_LINSOLV_NO_RECOVERY: - sprintf(retstr, "KIN_LINSOLV_NO_RECOVERY"); - break; - case KIN_LSETUP_FAIL: - sprintf(retstr, "KIN_PRECONDSET_FAILURE"); - break; - case KIN_LSOLVE_FAIL: - sprintf(retstr, "KIN_PRECONDSOLVE_FAILURE"); - break; - } - - /* Compose the message */ - - sprintf(msg1, msgfmt, ret); - sprintf(msg,"%s (%s)",msg1,retstr); - - - } else { - - /* Compose the message */ - - vsprintf(msg, msgfmt, ap); - - } - - /* call the info message handler */ - - kin_mem->kin_ihfun(module, fname, msg, kin_mem->kin_ih_data); - - /* finalize argument processing */ - - va_end(ap); - - return; -} - - -/* - * KINInfoHandler - * - * This is the default KINSOL info handling function. - * It sends the info message to the stream pointed to by kin_infofp - */ - -void KINInfoHandler(const char *module, const char *function, - char *msg, void *data) -{ - KINMem kin_mem; - - /* data points to kin_mem here */ - - kin_mem = (KINMem) data; - -#ifndef NO_FPRINTF_OUTPUT - if (kin_mem->kin_infofp != NULL) { - fprintf(kin_mem->kin_infofp,"\n[%s] %s\n",module, function); - fprintf(kin_mem->kin_infofp," %s\n",msg); - } -#endif - -} - -/* - * ================================================================= - * KINSOL Error Handling functions - * ================================================================= - */ - -/* - * KINProcessError - * - * KINProcessError is a high level error handling function. - * - If cv_mem==NULL it prints the error message to stderr. - * - Otherwise, it sets up and calls the error handling function - * pointed to by cv_ehfun. - */ - -void KINProcessError(KINMem kin_mem, - int error_code, const char *module, const char *fname, - const char *msgfmt, ...) -{ - va_list ap; - char msg[256]; - - /* Initialize the argument pointer variable - (msgfmt is the last required argument to KINProcessError) */ - - va_start(ap, msgfmt); - - /* Compose the message */ - - vsprintf(msg, msgfmt, ap); - - if (kin_mem == NULL) { /* We write to stderr */ -#ifndef NO_FPRINTF_OUTPUT - fprintf(stderr, "\n[%s ERROR] %s\n ", module, fname); - fprintf(stderr, "%s\n\n", msg); -#endif - - } else { /* We can call ehfun */ - kin_mem->kin_ehfun(error_code, module, fname, msg, kin_mem->kin_eh_data); - } - - /* Finalize argument processing */ - va_end(ap); - - return; -} - -/* - * KINErrHandler - * - * This is the default error handling function. - * It sends the error message to the stream pointed to by kin_errfp - */ - -void KINErrHandler(int error_code, const char *module, - const char *function, char *msg, void *data) -{ - KINMem kin_mem; - char err_type[10]; - - /* data points to kin_mem here */ - - kin_mem = (KINMem) data; - - if (error_code == KIN_WARNING) - sprintf(err_type,"WARNING"); - else - sprintf(err_type,"ERROR"); - -#ifndef NO_FPRINTF_OUTPUT - if (kin_mem->kin_errfp != NULL) { - fprintf(kin_mem->kin_errfp,"\n[%s %s] %s\n",module,err_type,function); - fprintf(kin_mem->kin_errfp," %s\n\n",msg); - } -#endif - - return; -} - - -/* - * ======================================================================= - * Picard and fixed point solvers - * ======================================================================= - */ - -/* - * KINPicardAA - * - * This routine is the main driver for the Picard iteration with - * acclerated fixed point. - */ - -static int KINPicardAA(KINMem kin_mem, long int *iterp, realtype *R, - realtype *gamma, realtype *fmaxptr) -{ - int retval, ret; - long int iter; - realtype fmax, epsmin, fnormp; - N_Vector delta, gval; - - delta = kin_mem->kin_vtemp1; - gval = kin_mem->kin_gval; - ret = CONTINUE_ITERATIONS; - fmax = kin_mem->kin_fnormtol + ONE; - iter = 0; - epsmin = ZERO; - fnormp = -ONE; - - N_VConst(ZERO, gval); - - /* if eps is to be bounded from below, set the bound */ - if (kin_mem->kin_inexact_ls && !(kin_mem->kin_noMinEps)) epsmin = POINT01 * kin_mem->kin_fnormtol; - - while (ret == CONTINUE_ITERATIONS) { - - iter++; - - /* Update the forcing term for the inexact linear solves */ - if (kin_mem->kin_inexact_ls) { - kin_mem->kin_eps = (kin_mem->kin_eta + kin_mem->kin_uround) * kin_mem->kin_fnorm; - if(!(kin_mem->kin_noMinEps)) kin_mem->kin_eps = SUNMAX(epsmin, kin_mem->kin_eps); - } - - /* evaluate g = uu - L^{-1}func(uu) and return if failed. - For Picard, assume that the fval vector has been filled - with an eval of the nonlinear residual prior to this call. */ - retval = KINPicardFcnEval(kin_mem, gval, kin_mem->kin_uu, kin_mem->kin_fval); - - if (retval < 0) { - ret = KIN_SYSFUNC_FAIL; - break; - } - - if (kin_mem->kin_m_aa == 0) { - N_VScale(ONE, gval, kin_mem->kin_unew); - } - else { /* use Anderson, if desired */ - N_VScale(ONE, kin_mem->kin_uu, kin_mem->kin_unew); - AndersonAcc(kin_mem, gval, delta, kin_mem->kin_unew, kin_mem->kin_uu, iter-1, R, gamma); - } - - /* Fill the Newton residual based on the new solution iterate */ - retval = kin_mem->kin_func(kin_mem->kin_unew, kin_mem->kin_fval, kin_mem->kin_user_data); kin_mem->kin_nfe++; - - if (retval < 0) { - ret = KIN_SYSFUNC_FAIL; - break; - } - - /* Evaluate function norms */ - fnormp = N_VWL2Norm(kin_mem->kin_fval, kin_mem->kin_fscale); - fmax = KINScFNorm(kin_mem, kin_mem->kin_fval, kin_mem->kin_fscale); /* measure || F(x) ||_max */ - kin_mem->kin_fnorm = fmax; - *fmaxptr = fmax; - - if (kin_mem->kin_printfl > 1) - KINPrintInfo(kin_mem, PRNT_FMAX, "KINSOL", "KINPicardAA", INFO_FMAX, fmax); - - /* print the current iter, fnorm, and nfe values if printfl > 0 */ - if (kin_mem->kin_printfl > 0) - KINPrintInfo(kin_mem, PRNT_NNI, "KINSOL", "KINPicardAA", INFO_NNI, iter, kin_mem->kin_nfe, kin_mem->kin_fnorm); - - /* Check if the maximum number of iterations is reached */ - if (iter >= kin_mem->kin_mxiter) { - ret = KIN_MAXITER_REACHED; - } - if (fmax <= kin_mem->kin_fnormtol) { - ret = KIN_SUCCESS; - } - - /* Update with new iterate. */ - N_VScale(ONE, kin_mem->kin_unew, kin_mem->kin_uu); - - if (ret == CONTINUE_ITERATIONS) { - /* evaluate eta by calling the forcing term routine */ - if (kin_mem->kin_callForcingTerm) KINForcingTerm(kin_mem, fnormp); - } - - fflush(kin_mem->kin_errfp); - - } /* end of loop; return */ - - *iterp = iter; - - if (kin_mem->kin_printfl > 0) - KINPrintInfo(kin_mem, PRNT_RETVAL, "KINSOL", "KINPicardAA", INFO_RETVAL, ret); - - return(ret); -} - -/* - * KINPicardFcnEval - * - * This routine evaluates the Picard fixed point function - * using the linear solver, gval = u - L^{-1}F(u). - * The function assumes the user has defined L either through - * a user-supplied matvec if using a SPILS solver or through - * a supplied matrix if using a dense solver. This assumption is - * tested by a check on the strategy and the requisite functionality - * within the linear solve routines. - * - * This routine fills gval = uu - L^{-1}F(uu) given uu and fval = F(uu). - */ - -static int KINPicardFcnEval(KINMem kin_mem, N_Vector gval, N_Vector uval, N_Vector fval1) -{ - int retval; - - if ((kin_mem->kin_nni - kin_mem->kin_nnilset) >= kin_mem->kin_msbset) { - kin_mem->kin_sthrsh = TWO; - kin_mem->kin_update_fnorm_sub = SUNTRUE; - } - - for(;;){ - - kin_mem->kin_jacCurrent = SUNFALSE; - - if ((kin_mem->kin_sthrsh > ONEPT5) && (kin_mem->kin_lsetup != NULL)) { - retval = kin_mem->kin_lsetup(kin_mem); - kin_mem->kin_jacCurrent = SUNTRUE; - kin_mem->kin_nnilset = kin_mem->kin_nni; - kin_mem->kin_nnilset_sub = kin_mem->kin_nni; - if (retval != 0) return(KIN_LSETUP_FAIL); - } - - /* call the generic 'lsolve' routine to solve the system Lx = -fval - Note that we are using gval to hold x. */ - N_VScale(-ONE, fval1, fval1); - retval = kin_mem->kin_lsolve(kin_mem, gval, fval1, &(kin_mem->kin_sJpnorm), - &(kin_mem->kin_sFdotJp)); - - if (retval == 0) { - /* Update gval = uval + gval since gval = -L^{-1}F(uu) */ - N_VLinearSum(ONE, uval, ONE, gval, gval); - return(KIN_SUCCESS); - } - else if (retval < 0) return(KIN_LSOLVE_FAIL); - else if ((kin_mem->kin_lsetup == NULL) || (kin_mem->kin_jacCurrent)) return(KIN_LINSOLV_NO_RECOVERY); - - /* loop back only if the linear solver setup is in use - and matrix information is not current */ - - kin_mem->kin_sthrsh = TWO; - } - -} - - -/* - * KINFP - * - * This routine is the main driver for the fixed point iteration with - * Anderson Acceleration. - */ - -static int KINFP(KINMem kin_mem) -{ - int retval; /* return value from user func */ - int ret; /* iteration status */ - realtype fmax; /* max norm of residual func */ - N_Vector delta; /* temporary workspace vector */ - - delta = kin_mem->kin_vtemp1; - ret = CONTINUE_ITERATIONS; - fmax = kin_mem->kin_fnormtol + ONE; - - /* initialize iteration count */ - kin_mem->kin_nni = 0; - - while (ret == CONTINUE_ITERATIONS) { - - /* update iteration count */ - kin_mem->kin_nni++; - - /* evaluate func(uu) and return if failed */ - retval = kin_mem->kin_func(kin_mem->kin_uu, kin_mem->kin_fval, - kin_mem->kin_user_data); - kin_mem->kin_nfe++; - - if (retval < 0) { - ret = KIN_SYSFUNC_FAIL; - break; - } - - /* compute new solution */ - if (kin_mem->kin_m_aa == 0) { - /* standard fixed point */ - N_VScale(ONE, kin_mem->kin_fval, kin_mem->kin_unew); - } else { - /* apply Anderson acceleration */ - AndersonAcc(kin_mem, kin_mem->kin_fval, delta, kin_mem->kin_unew, - kin_mem->kin_uu, kin_mem->kin_nni - 1, kin_mem->kin_R_aa, - kin_mem->kin_gamma_aa); - } - - /* compute change between iterations */ - N_VLinearSum(ONE, kin_mem->kin_unew, -ONE, kin_mem->kin_uu, delta); - - fmax = KINScFNorm(kin_mem, delta, kin_mem->kin_fscale); /* measure || g(x)-x || */ - - if (kin_mem->kin_printfl > 1) - KINPrintInfo(kin_mem, PRNT_FMAX, "KINSOL", "KINFP", INFO_FMAX, fmax); - - kin_mem->kin_fnorm = fmax; - - /* print the current iter, fnorm, and nfe values if printfl > 0 */ - if (kin_mem->kin_printfl > 0) - KINPrintInfo(kin_mem, PRNT_NNI, "KINSOL", "KINFP", INFO_NNI, - kin_mem->kin_nni, kin_mem->kin_nfe, kin_mem->kin_fnorm); - - /* Check if the maximum number of iterations is reached */ - if (kin_mem->kin_nni >= kin_mem->kin_mxiter) { - ret = KIN_MAXITER_REACHED; - } - if (fmax <= kin_mem->kin_fnormtol) { - ret = KIN_SUCCESS; - } - - if (ret == CONTINUE_ITERATIONS) { - /* Only update solution if taking a next iteration. */ - /* CSW Should put in a conditional to send back the newest iterate or - the one consistent with the fval */ - N_VScale(ONE, kin_mem->kin_unew, kin_mem->kin_uu); - } - - fflush(kin_mem->kin_errfp); - - } /* end of loop; return */ - - if (kin_mem->kin_printfl > 0) - KINPrintInfo(kin_mem, PRNT_RETVAL, "KINSOL", "KINFP", INFO_RETVAL, ret); - - return(ret); -} - - -/* ----------------------------------------------------------------- - * Stopping tests - * ----------------------------------------------------------------- - */ - - -/* - * ======================================================================== - * Anderson Acceleration - * ======================================================================== - */ - -static int AndersonAcc(KINMem kin_mem, N_Vector gval, N_Vector fv, - N_Vector x, N_Vector xold, - long int iter, realtype *R, realtype *gamma) -{ - int retval; - long int i_pt, i, j, lAA; - long int *ipt_map; - realtype alfa; - realtype onembeta; - realtype a, b, temp, c, s; - - /* local shortcuts for fused vector operation */ - int nvec=0; - realtype* cv=kin_mem->kin_cv; - N_Vector* Xv=kin_mem->kin_Xv; - - ipt_map = kin_mem->kin_ipt_map; - i_pt = iter-1 - ((iter-1) / kin_mem->kin_m_aa) * kin_mem->kin_m_aa; - N_VLinearSum(ONE, gval, -ONE, xold, fv); - if (iter > 0) { - /* compute dg_new = gval - gval_old */ - N_VLinearSum(ONE, gval, -ONE, kin_mem->kin_gold_aa, kin_mem->kin_dg_aa[i_pt]); - /* compute df_new = fval - fval_old */ - N_VLinearSum(ONE, fv, -ONE, kin_mem->kin_fold_aa, kin_mem->kin_df_aa[i_pt]); - } - - N_VScale(ONE, gval, kin_mem->kin_gold_aa); - N_VScale(ONE, fv, kin_mem->kin_fold_aa); - - /* on first iteration, just do basic fixed-point update */ - if (iter == 0) { - N_VScale(ONE, gval, x); - return(0); - } - - /* update data structures based on current iteration index */ - - if (iter == 1) { - - /* second iteration */ - R[0] = SUNRsqrt(N_VDotProd(kin_mem->kin_df_aa[i_pt], kin_mem->kin_df_aa[i_pt])); - alfa = ONE/R[0]; - N_VScale(alfa, kin_mem->kin_df_aa[i_pt], kin_mem->kin_q_aa[i_pt]); - ipt_map[0] = 0; - - } else if (iter <= kin_mem->kin_m_aa) { - - /* another iteration before we've reached maa */ - N_VScale(ONE, kin_mem->kin_df_aa[i_pt], kin_mem->kin_vtemp2); - for (j=0; j < (iter-1); j++) { - ipt_map[j] = j; - R[(iter-1)*kin_mem->kin_m_aa+j] = N_VDotProd(kin_mem->kin_q_aa[j], kin_mem->kin_vtemp2); - N_VLinearSum(ONE,kin_mem->kin_vtemp2, -R[(iter-1)*kin_mem->kin_m_aa+j], kin_mem->kin_q_aa[j], kin_mem->kin_vtemp2); - } - R[(iter-1)*kin_mem->kin_m_aa+iter-1] = SUNRsqrt(N_VDotProd(kin_mem->kin_vtemp2, kin_mem->kin_vtemp2)); - N_VScale((1/R[(iter-1)*kin_mem->kin_m_aa+iter-1]), kin_mem->kin_vtemp2, kin_mem->kin_q_aa[i_pt]); - ipt_map[iter-1] = iter-1; - - } else { - - /* we've filled the acceleration subspace, so start recycling */ - - /* Delete left-most column vector from QR factorization */ - for (i=0; i < kin_mem->kin_m_aa-1; i++) { - a = R[(i+1)*kin_mem->kin_m_aa + i]; - b = R[(i+1)*kin_mem->kin_m_aa + i+1]; - temp = SUNRsqrt(a*a + b*b); - c = a / temp; - s = b / temp; - R[(i+1)*kin_mem->kin_m_aa + i] = temp; - R[(i+1)*kin_mem->kin_m_aa + i+1] = ZERO; - /* OK to re-use temp */ - if (i < kin_mem->kin_m_aa-1) { - for (j = i+2; j < kin_mem->kin_m_aa; j++) { - a = R[j*kin_mem->kin_m_aa + i]; - b = R[j*kin_mem->kin_m_aa + i+1]; - temp = c * a + s * b; - R[j*kin_mem->kin_m_aa + i+1] = -s*a + c*b; - R[j*kin_mem->kin_m_aa + i] = temp; - } - } - N_VLinearSum(c, kin_mem->kin_q_aa[i], s, kin_mem->kin_q_aa[i+1], kin_mem->kin_vtemp2); - N_VLinearSum(-s, kin_mem->kin_q_aa[i], c, kin_mem->kin_q_aa[i+1], kin_mem->kin_q_aa[i+1]); - N_VScale(ONE, kin_mem->kin_vtemp2, kin_mem->kin_q_aa[i]); - } - - /* Shift R to the left by one. */ - for (i = 1; i < kin_mem->kin_m_aa; i++) { - for (j = 0; j < kin_mem->kin_m_aa-1; j++) { - R[(i-1)*kin_mem->kin_m_aa + j] = R[i*kin_mem->kin_m_aa + j]; - } - } - - /* Add the new df vector */ - N_VScale(ONE, kin_mem->kin_df_aa[i_pt], kin_mem->kin_vtemp2); - for (j=0; j < (kin_mem->kin_m_aa-1); j++) { - R[(kin_mem->kin_m_aa-1)*kin_mem->kin_m_aa+j] = N_VDotProd(kin_mem->kin_q_aa[j], kin_mem->kin_vtemp2); - N_VLinearSum(ONE, kin_mem->kin_vtemp2, -R[(kin_mem->kin_m_aa-1)*kin_mem->kin_m_aa+j], kin_mem->kin_q_aa[j],kin_mem->kin_vtemp2); - } - R[(kin_mem->kin_m_aa-1)*kin_mem->kin_m_aa+kin_mem->kin_m_aa-1] = SUNRsqrt(N_VDotProd(kin_mem->kin_vtemp2, kin_mem->kin_vtemp2)); - N_VScale((1/R[(kin_mem->kin_m_aa-1)*kin_mem->kin_m_aa+kin_mem->kin_m_aa-1]), kin_mem->kin_vtemp2, kin_mem->kin_q_aa[kin_mem->kin_m_aa-1]); - - /* Update the iteration map */ - j = 0; - for (i=i_pt+1; i < kin_mem->kin_m_aa; i++) - ipt_map[j++] = i; - for (i=0; i < (i_pt+1); i++) - ipt_map[j++] = i; - } - - /* Solve least squares problem and update solution */ - lAA = iter; - if (kin_mem->kin_m_aa < iter) lAA = kin_mem->kin_m_aa; - - retval = N_VDotProdMulti((int) lAA, fv, kin_mem->kin_q_aa, gamma); - if (retval != KIN_SUCCESS) return(KIN_VECTOROP_ERR); - - /* set arrays for fused vector operation */ - cv[0] = ONE; - Xv[0] = gval; - nvec = 1; - - for (i=lAA-1; i > -1; i--) { - for (j=i+1; j < lAA; j++) { - gamma[i] = gamma[i]-R[j*kin_mem->kin_m_aa+i]*gamma[j]; - } - gamma[i] = gamma[i]/R[i*kin_mem->kin_m_aa+i]; - - cv[nvec] = -gamma[i]; - Xv[nvec] = kin_mem->kin_dg_aa[ipt_map[i]]; - nvec += 1; - } - - /* if enabled, apply damping */ - if (kin_mem->kin_damping_aa) { - onembeta = (ONE - kin_mem->kin_beta_aa); - cv[nvec] = -onembeta; - Xv[nvec] = fv; - nvec += 1; - for (i = lAA - 1; i > -1; i--) { - cv[nvec] = onembeta * gamma[i]; - Xv[nvec] = kin_mem->kin_df_aa[ipt_map[i]]; - nvec += 1; - } - } - - /* update solution */ - retval = N_VLinearCombination(nvec, cv, Xv, x); - if (retval != KIN_SUCCESS) return(KIN_VECTOROP_ERR); - - return 0; -} diff --git a/deps/AMICI/ThirdParty/sundials/src/kinsol/kinsol_bbdpre.c b/deps/AMICI/ThirdParty/sundials/src/kinsol/kinsol_bbdpre.c deleted file mode 100644 index 4bdff3079..000000000 --- a/deps/AMICI/ThirdParty/sundials/src/kinsol/kinsol_bbdpre.c +++ /dev/null @@ -1,582 +0,0 @@ -/* ----------------------------------------------------------------- - * Programmer(s): David J. Gardner @ LLNL - * Allan Taylor, Alan Hindmarsh, Radu Serban, and - * Aaron Collier @ LLNL - * ----------------------------------------------------------------- - * SUNDIALS Copyright Start - * Copyright (c) 2002-2021, Lawrence Livermore National Security - * and Southern Methodist University. - * All rights reserved. - * - * See the top-level LICENSE and NOTICE files for details. - * - * SPDX-License-Identifier: BSD-3-Clause - * SUNDIALS Copyright End - * ----------------------------------------------------------------- - * This file contains implementations of routines for a - * band-block-diagonal preconditioner, i.e. a block-diagonal - * matrix with banded blocks, for use with KINSol and the - * KINLS linear solver interface. - * - * Note: With only one process, a banded matrix results - * rather than a b-b-d matrix with banded blocks. Diagonal - * blocking occurs at the process level. - * -----------------------------------------------------------------*/ - -#include -#include - -#include "kinsol_impl.h" -#include "kinsol_ls_impl.h" -#include "kinsol_bbdpre_impl.h" - -#include -#include - -#define ZERO RCONST(0.0) -#define ONE RCONST(1.0) - -/* Prototypes of functions KINBBDPrecSetup and KINBBDPrecSolve */ -static int KINBBDPrecSetup(N_Vector uu, N_Vector uscale, - N_Vector fval, N_Vector fscale, - void *pdata); - -static int KINBBDPrecSolve(N_Vector uu, N_Vector uscale, - N_Vector fval, N_Vector fscale, - N_Vector vv, void *pdata); - -/* Prototype for KINBBDPrecFree */ -static int KINBBDPrecFree(KINMem kin_mem); - -/* Prototype for difference quotient jacobian calculation routine */ -static int KBBDDQJac(KBBDPrecData pdata, - N_Vector uu, N_Vector uscale, - N_Vector gu, N_Vector gtemp, N_Vector utemp); - -/*------------------------------------------------------------------ - user-callable functions - ------------------------------------------------------------------*/ - -/*------------------------------------------------------------------ - KINBBDPrecInit - ------------------------------------------------------------------*/ -int KINBBDPrecInit(void *kinmem, sunindextype Nlocal, - sunindextype mudq, sunindextype mldq, - sunindextype mukeep, sunindextype mlkeep, - realtype dq_rel_uu, - KINBBDLocalFn gloc, KINBBDCommFn gcomm) -{ - KINMem kin_mem; - KINLsMem kinls_mem; - KBBDPrecData pdata; - sunindextype muk, mlk, storage_mu, lrw1, liw1; - long int lrw, liw; - int flag; - - if (kinmem == NULL) { - KINProcessError(NULL, KINLS_MEM_NULL, "KINBBDPRE", - "KINBBDPrecInit", MSGBBD_MEM_NULL); - return(KINLS_MEM_NULL); - } - kin_mem = (KINMem) kinmem; - - /* Test if the LS linear solver interface has been created */ - if (kin_mem->kin_lmem == NULL) { - KINProcessError(kin_mem, KINLS_LMEM_NULL, "KINBBDPRE", - "KINBBDPrecInit", MSGBBD_LMEM_NULL); - return(KINLS_LMEM_NULL); - } - kinls_mem = (KINLsMem) kin_mem->kin_lmem; - - /* Test compatibility of NVECTOR package with the BBD preconditioner */ - /* Note: Do NOT need to check for N_VScale since has already been checked for in KINSOL */ - if (kin_mem->kin_vtemp1->ops->nvgetarraypointer == NULL) { - KINProcessError(kin_mem, KINLS_ILL_INPUT, "KINBBDPRE", - "KINBBDPrecInit", MSGBBD_BAD_NVECTOR); - return(KINLS_ILL_INPUT); - } - - /* Allocate data memory */ - pdata = NULL; - pdata = (KBBDPrecData) malloc(sizeof *pdata); - if (pdata == NULL) { - KINProcessError(kin_mem, KINLS_MEM_FAIL, - "KINBBDPRE", "KINBBDPrecInit", MSGBBD_MEM_FAIL); - return(KINLS_MEM_FAIL); - } - - /* Set pointers to gloc and gcomm; load half-bandwidths */ - pdata->kin_mem = kinmem; - pdata->gloc = gloc; - pdata->gcomm = gcomm; - pdata->mudq = SUNMIN(Nlocal-1, SUNMAX(0, mudq)); - pdata->mldq = SUNMIN(Nlocal-1, SUNMAX(0, mldq)); - muk = SUNMIN(Nlocal-1, SUNMAX(0, mukeep)); - mlk = SUNMIN(Nlocal-1, SUNMAX(0, mlkeep)); - pdata->mukeep = muk; - pdata->mlkeep = mlk; - - /* Set extended upper half-bandwidth for PP (required for pivoting) */ - storage_mu = SUNMIN(Nlocal-1, muk+mlk); - - /* Allocate memory for preconditioner matrix */ - pdata->PP = NULL; - pdata->PP = SUNBandMatrixStorage(Nlocal, muk, mlk, storage_mu); - if (pdata->PP == NULL) { - free(pdata); pdata = NULL; - KINProcessError(kin_mem, KINLS_MEM_FAIL, "KINBBDPRE", - "KINBBDPrecInit", MSGBBD_MEM_FAIL); - return(KINLS_MEM_FAIL); - } - - /* Allocate memory for temporary N_Vectors */ - pdata->zlocal = NULL; - pdata->zlocal = N_VNew_Serial(Nlocal); - if (pdata->zlocal == NULL) { - SUNMatDestroy(pdata->PP); - free(pdata); pdata = NULL; - KINProcessError(kin_mem, KINLS_MEM_FAIL, "KINBBDPRE", - "KINBBDPrecInit", MSGBBD_MEM_FAIL); - return(KINLS_MEM_FAIL); - } - - pdata->rlocal = NULL; - pdata->rlocal = N_VNewEmpty_Serial(Nlocal); /* empty vector */ - if (pdata->rlocal == NULL) { - N_VDestroy(pdata->zlocal); - SUNMatDestroy(pdata->PP); - free(pdata); pdata = NULL; - KINProcessError(kin_mem, KINLS_MEM_FAIL, "KINBBDPRE", - "KINBBDPrecInit", MSGBBD_MEM_FAIL); - return(KINLS_MEM_FAIL); - } - - pdata->tempv1 = NULL; - pdata->tempv1 = N_VClone(kin_mem->kin_vtemp1); - if (pdata->tempv1 == NULL) { - N_VDestroy(pdata->zlocal); - N_VDestroy(pdata->rlocal); - SUNMatDestroy(pdata->PP); - free(pdata); pdata = NULL; - KINProcessError(kin_mem, KINLS_MEM_FAIL, "KINBBDPRE", - "KINBBDPrecInit", MSGBBD_MEM_FAIL); - return(KINLS_MEM_FAIL); - } - - pdata->tempv2 = NULL; - pdata->tempv2 = N_VClone(kin_mem->kin_vtemp1); - if (pdata->tempv2 == NULL) { - N_VDestroy(pdata->zlocal); - N_VDestroy(pdata->rlocal); - N_VDestroy(pdata->tempv1); - SUNMatDestroy(pdata->PP); - free(pdata); pdata = NULL; - KINProcessError(kin_mem, KINLS_MEM_FAIL, "KINBBDPRE", - "KINBBDPrecInit", MSGBBD_MEM_FAIL); - return(KINLS_MEM_FAIL); - } - - pdata->tempv3 = NULL; - pdata->tempv3 = N_VClone(kin_mem->kin_vtemp1); - if (pdata->tempv3 == NULL) { - N_VDestroy(pdata->zlocal); - N_VDestroy(pdata->rlocal); - N_VDestroy(pdata->tempv1); - N_VDestroy(pdata->tempv2); - SUNMatDestroy(pdata->PP); - free(pdata); pdata = NULL; - KINProcessError(kin_mem, KINLS_MEM_FAIL, "KINBBDPRE", - "KINBBDPrecInit", MSGBBD_MEM_FAIL); - return(KINLS_MEM_FAIL); - } - - /* Allocate memory for banded linear solver */ - pdata->LS = NULL; - pdata->LS = SUNLinSol_Band(pdata->zlocal, pdata->PP); - if (pdata->LS == NULL) { - N_VDestroy(pdata->zlocal); - N_VDestroy(pdata->rlocal); - N_VDestroy(pdata->tempv1); - N_VDestroy(pdata->tempv2); - N_VDestroy(pdata->tempv3); - SUNMatDestroy(pdata->PP); - free(pdata); pdata = NULL; - KINProcessError(kin_mem, KINLS_MEM_FAIL, "KINBBDPRE", - "KINBBDPrecInit", MSGBBD_MEM_FAIL); - return(KINLS_MEM_FAIL); - } - - /* initialize band linear solver object */ - flag = SUNLinSolInitialize(pdata->LS); - if (flag != SUNLS_SUCCESS) { - N_VDestroy(pdata->zlocal); - N_VDestroy(pdata->rlocal); - N_VDestroy(pdata->tempv1); - N_VDestroy(pdata->tempv2); - N_VDestroy(pdata->tempv3); - SUNMatDestroy(pdata->PP); - SUNLinSolFree(pdata->LS); - free(pdata); pdata = NULL; - KINProcessError(kin_mem, KINLS_SUNLS_FAIL, "KINBBDPRE", - "KINBBDPrecInit", MSGBBD_SUNLS_FAIL); - return(KINLS_SUNLS_FAIL); - } - - /* Set rel_uu based on input value dq_rel_uu (0 implies default) */ - pdata->rel_uu = (dq_rel_uu > ZERO) ? dq_rel_uu : SUNRsqrt(kin_mem->kin_uround); - - /* Store Nlocal to be used in KINBBDPrecSetup */ - pdata->n_local = Nlocal; - - /* Set work space sizes and initialize nge */ - pdata->rpwsize = 0; - pdata->ipwsize = 0; - if (kin_mem->kin_vtemp1->ops->nvspace) { - N_VSpace(kin_mem->kin_vtemp1, &lrw1, &liw1); - pdata->rpwsize += 3*lrw1; - pdata->ipwsize += 3*liw1; - } - if (pdata->zlocal->ops->nvspace) { - N_VSpace(pdata->zlocal, &lrw1, &liw1); - pdata->rpwsize += lrw1; - pdata->ipwsize += liw1; - } - if (pdata->rlocal->ops->nvspace) { - N_VSpace(pdata->rlocal, &lrw1, &liw1); - pdata->rpwsize += lrw1; - pdata->ipwsize += liw1; - } - if (pdata->PP->ops->space) { - flag = SUNMatSpace(pdata->PP, &lrw, &liw); - pdata->rpwsize += lrw; - pdata->ipwsize += liw; - } - if (pdata->LS->ops->space) { - flag = SUNLinSolSpace(pdata->LS, &lrw, &liw); - pdata->rpwsize += lrw; - pdata->ipwsize += liw; - } - pdata->nge = 0; - - /* make sure pdata is free from any previous allocations */ - if (kinls_mem->pfree != NULL) - kinls_mem->pfree(kin_mem); - - /* Point to the new pdata field in the LS memory */ - kinls_mem->pdata = pdata; - - /* Attach the pfree function */ - kinls_mem->pfree = KINBBDPrecFree; - - /* Attach preconditioner solve and setup functions */ - flag = KINSetPreconditioner(kinmem, KINBBDPrecSetup, - KINBBDPrecSolve); - - return(flag); -} - - -/*------------------------------------------------------------------ - KINBBDPrecGetWorkSpace - ------------------------------------------------------------------*/ -int KINBBDPrecGetWorkSpace(void *kinmem, - long int *lenrwBBDP, - long int *leniwBBDP) -{ - KINMem kin_mem; - KINLsMem kinls_mem; - KBBDPrecData pdata; - - if (kinmem == NULL) { - KINProcessError(NULL, KINLS_MEM_NULL, "KINBBDPRE", - "KINBBDPrecGetWorkSpace", MSGBBD_MEM_NULL); - return(KINLS_MEM_NULL); - } - kin_mem = (KINMem) kinmem; - - if (kin_mem->kin_lmem == NULL) { - KINProcessError(kin_mem, KINLS_LMEM_NULL, "KINBBDPRE", - "KINBBDPrecGetWorkSpace", MSGBBD_LMEM_NULL); - return(KINLS_LMEM_NULL); - } - kinls_mem = (KINLsMem) kin_mem->kin_lmem; - - if (kinls_mem->pdata == NULL) { - KINProcessError(kin_mem, KINLS_PMEM_NULL, "KINBBDPRE", - "KINBBDPrecGetWorkSpace", MSGBBD_PMEM_NULL); - return(KINLS_PMEM_NULL); - } - pdata = (KBBDPrecData) kinls_mem->pdata; - - *lenrwBBDP = pdata->rpwsize; - *leniwBBDP = pdata->ipwsize; - - return(KINLS_SUCCESS); -} - -/*------------------------------------------------------------------ - KINBBDPrecGetNumGfnEvals - -------------------------------------------------------------------*/ -int KINBBDPrecGetNumGfnEvals(void *kinmem, - long int *ngevalsBBDP) -{ - KINMem kin_mem; - KINLsMem kinls_mem; - KBBDPrecData pdata; - - if (kinmem == NULL) { - KINProcessError(NULL, KINLS_MEM_NULL, "KINBBDPRE", - "KINBBDPrecGetNumGfnEvals", MSGBBD_MEM_NULL); - return(KINLS_MEM_NULL); - } - kin_mem = (KINMem) kinmem; - - if (kin_mem->kin_lmem == NULL) { - KINProcessError(kin_mem, KINLS_LMEM_NULL, "KINBBDPRE", - "KINBBDPrecGetNumGfnEvals", MSGBBD_LMEM_NULL); - return(KINLS_LMEM_NULL); - } - kinls_mem = (KINLsMem) kin_mem->kin_lmem; - - if (kinls_mem->pdata == NULL) { - KINProcessError(kin_mem, KINLS_PMEM_NULL, "KINBBDPRE", - "KINBBDPrecGetNumGfnEvals", MSGBBD_PMEM_NULL); - return(KINLS_PMEM_NULL); - } - pdata = (KBBDPrecData) kinls_mem->pdata; - - *ngevalsBBDP = pdata->nge; - - return(KINLS_SUCCESS); -} - - -/*------------------------------------------------------------------ - KINBBDPrecSetup - - KINBBDPrecSetup generates and factors a banded block of the - preconditioner matrix on each processor, via calls to the - user-supplied gloc and gcomm functions. It uses difference - quotient approximations to the Jacobian elements. - - KINBBDPrecSetup calculates a new Jacobian, stored in banded - matrix PP and does an LU factorization of P in place in PP. - - The parameters of KINBBDPrecSetup are as follows: - - uu is the current value of the dependent variable vector, - namely the solutin to func(uu)=0 - - uscale is the dependent variable scaling vector (i.e. uu) - - fval is the vector f(u) - - fscale is the function scaling vector - - bbd_data is the pointer to BBD data set by KINBBDInit. - - Note: The value to be returned by the KINBBDPrecSetup function - is a flag indicating whether it was successful. This value is: - 0 if successful, - > 0 for a recoverable error - step will be retried. - ------------------------------------------------------------------*/ -static int KINBBDPrecSetup(N_Vector uu, N_Vector uscale, - N_Vector fval, N_Vector fscale, - void *bbd_data) -{ - KBBDPrecData pdata; - KINMem kin_mem; - int retval; - - pdata = (KBBDPrecData) bbd_data; - - kin_mem = (KINMem) pdata->kin_mem; - - /* Call KBBDDQJac for a new Jacobian calculation and store in PP */ - retval = SUNMatZero(pdata->PP); - if (retval != 0) { - KINProcessError(kin_mem, -1, "KINBBDPRE", "KINBBDPrecSetup", - MSGBBD_SUNMAT_FAIL); - return(-1); - } - - retval = KBBDDQJac(pdata, uu, uscale, - pdata->tempv1, pdata->tempv2, pdata->tempv3); - if (retval != 0) { - KINProcessError(kin_mem, -1, "KINBBDPRE", "KINBBDPrecSetup", - MSGBBD_FUNC_FAILED); - return(-1); - } - - /* Do LU factorization of P and return error flag */ - retval = SUNLinSolSetup_Band(pdata->LS, pdata->PP); - return(retval); -} - -/*------------------------------------------------------------------ - INBBDPrecSolve - - KINBBDPrecSolve solves a linear system P z = r, with the - banded blocked preconditioner matrix P generated and factored - by KINBBDPrecSetup. Here, r comes in as vv and z is - returned in vv as well. - - The parameters for KINBBDPrecSolve are as follows: - - uu an N_Vector giving the current iterate for the system - - uscale an N_Vector giving the diagonal entries of the - uu scaling matrix - - fval an N_Vector giving the current function value - - fscale an N_Vector giving the diagonal entries of the - function scaling matrix - - vv vector initially set to the right-hand side vector r, but - which upon return contains a solution of the linear system - P*z = r - - bbd_data is the pointer to BBD data set by KINBBDInit. - - Note: The value returned by the KINBBDPrecSolve function is a - flag returned from the lienar solver object. - ------------------------------------------------------------------*/ - -static int KINBBDPrecSolve(N_Vector uu, N_Vector uscale, - N_Vector fval, N_Vector fscale, - N_Vector vv, void *bbd_data) -{ - KBBDPrecData pdata; - realtype *vd; - realtype *zd; - int i, retval; - - pdata = (KBBDPrecData) bbd_data; - - /* Get data pointers */ - vd = N_VGetArrayPointer(vv); - zd = N_VGetArrayPointer(pdata->zlocal); - - /* Attach local data array for vv to rlocal */ - N_VSetArrayPointer(vd, pdata->rlocal); - - /* Call banded solver object to do the work */ - retval = SUNLinSolSolve(pdata->LS, pdata->PP, pdata->zlocal, - pdata->rlocal, ZERO); - - /* Copy result into vv */ - for (i=0; in_local; i++) - vd[i] = zd[i]; - - return(retval); -} - - -/*------------------------------------------------------------------ - KINBBDPrecFree - ------------------------------------------------------------------*/ -static int KINBBDPrecFree(KINMem kin_mem) -{ - KINLsMem kinls_mem; - KBBDPrecData pdata; - - if (kin_mem->kin_lmem == NULL) return(0); - kinls_mem = (KINLsMem) kin_mem->kin_lmem; - - if (kinls_mem->pdata == NULL) return(0); - pdata = (KBBDPrecData) kinls_mem->pdata; - - SUNLinSolFree(pdata->LS); - N_VDestroy(pdata->zlocal); - N_VDestroy(pdata->rlocal); - N_VDestroy(pdata->tempv1); - N_VDestroy(pdata->tempv2); - N_VDestroy(pdata->tempv3); - SUNMatDestroy(pdata->PP); - - free(pdata); - pdata = NULL; - - return(0); -} - - -/*------------------------------------------------------------------ - KBBDDQJac - - This routine generates a banded difference quotient - approximation to the Jacobian of f(u). It assumes that a band - matrix of type SUNMatrix is stored column-wise, and that elements - within each column are contiguous. All matrix elements are - generated as difference quotients, by way of calls to the user - routine gloc. By virtue of the band structure, the number of - these calls is bandwidth + 1, where bandwidth = ml + mu + 1. - This routine also assumes that the local elements of a vector - are stored contiguously. - ------------------------------------------------------------------*/ -static int KBBDDQJac(KBBDPrecData pdata, - N_Vector uu, N_Vector uscale, - N_Vector gu, N_Vector gtemp, N_Vector utemp) -{ - KINMem kin_mem; - realtype inc, inc_inv; - int retval; - sunindextype group, i, j, width, ngroups, i1, i2; - realtype *udata, *uscdata, *gudata, *gtempdata, *utempdata, *col_j; - - kin_mem = (KINMem) pdata->kin_mem; - - /* load utemp with uu = predicted solution vector */ - N_VScale(ONE, uu, utemp); - - /* set pointers to the data for all vectors */ - udata = N_VGetArrayPointer(uu); - uscdata = N_VGetArrayPointer(uscale); - gudata = N_VGetArrayPointer(gu); - gtempdata = N_VGetArrayPointer(gtemp); - utempdata = N_VGetArrayPointer(utemp); - - /* Call gcomm and gloc to get base value of g(uu) */ - if (pdata->gcomm != NULL) { - retval = pdata->gcomm(pdata->n_local, uu, kin_mem->kin_user_data); - if (retval != 0) return(retval); - } - - retval = pdata->gloc(pdata->n_local, uu, gu, kin_mem->kin_user_data); - pdata->nge++; - if (retval != 0) return(retval); - - /* Set bandwidth and number of column groups for band differencing */ - width = pdata->mldq + pdata->mudq + 1; - ngroups = SUNMIN(width, pdata->n_local); - - /* Loop over groups */ - for(group = 1; group <= ngroups; group++) { - - /* increment all u_j in group */ - for(j = group - 1; j < pdata->n_local; j += width) { - inc = pdata->rel_uu * SUNMAX(SUNRabs(udata[j]), (ONE / uscdata[j])); - utempdata[j] += inc; - } - - /* Evaluate g with incremented u */ - retval = pdata->gloc(pdata->n_local, utemp, gtemp, kin_mem->kin_user_data); - pdata->nge++; - if (retval != 0) return(retval); - - /* restore utemp, then form and load difference quotients */ - for (j = group - 1; j < pdata->n_local; j += width) { - utempdata[j] = udata[j]; - col_j = SUNBandMatrix_Column(pdata->PP,j); - inc = pdata->rel_uu * SUNMAX(SUNRabs(udata[j]) , (ONE / uscdata[j])); - inc_inv = ONE / inc; - i1 = SUNMAX(0, (j - pdata->mukeep)); - i2 = SUNMIN((j + pdata->mlkeep), (pdata->n_local - 1)); - for (i = i1; i <= i2; i++) - SM_COLUMN_ELEMENT_B(col_j, i, j) = inc_inv * (gtempdata[i] - gudata[i]); - } - } - - return(0); -} diff --git a/deps/AMICI/ThirdParty/sundials/src/kinsol/kinsol_bbdpre_impl.h b/deps/AMICI/ThirdParty/sundials/src/kinsol/kinsol_bbdpre_impl.h deleted file mode 100644 index 6541a32be..000000000 --- a/deps/AMICI/ThirdParty/sundials/src/kinsol/kinsol_bbdpre_impl.h +++ /dev/null @@ -1,82 +0,0 @@ -/* ----------------------------------------------------------------- - * Programmer(s): David J. Gardner @ LLNL - * Allan Taylor, Alan Hindmarsh, Radu Serban, and - * Aaron Collier @ LLNL - * ----------------------------------------------------------------- - * SUNDIALS Copyright Start - * Copyright (c) 2002-2021, Lawrence Livermore National Security - * and Southern Methodist University. - * All rights reserved. - * - * See the top-level LICENSE and NOTICE files for details. - * - * SPDX-License-Identifier: BSD-3-Clause - * SUNDIALS Copyright End - * ----------------------------------------------------------------- - * KINBBDPRE module header file (private version) - * -----------------------------------------------------------------*/ - -#ifndef _KINBBDPRE_IMPL_H -#define _KINBBDPRE_IMPL_H - -#include -#include -#include - -#ifdef __cplusplus /* wrapper to enable C++ usage */ -extern "C" { -#endif - -/*------------------------------------------------------------------ - Definition of KBBDData - ------------------------------------------------------------------*/ - -typedef struct KBBDPrecDataRec { - - /* passed by user to KINBBDPrecAlloc, used by pset/psolve functions */ - sunindextype mudq, mldq, mukeep, mlkeep; - realtype rel_uu; /* relative error for the Jacobian DQ routine */ - KINBBDLocalFn gloc; - KINBBDCommFn gcomm; - - /* set by KINBBDPrecSetup and used by KINBBDPrecSetup and - KINBBDPrecSolve functions */ - sunindextype n_local; - SUNMatrix PP; - SUNLinearSolver LS; - N_Vector rlocal; - N_Vector zlocal; - N_Vector tempv1; - N_Vector tempv2; - N_Vector tempv3; - - /* available for optional output */ - long int rpwsize; - long int ipwsize; - long int nge; - - /* pointer to KINSol memory */ - void *kin_mem; - -} *KBBDPrecData; - -/* - *----------------------------------------------------------------- - * KINBBDPRE error messages - *----------------------------------------------------------------- - */ - -#define MSGBBD_MEM_NULL "KINSOL Memory is NULL." -#define MSGBBD_LMEM_NULL "Linear solver memory is NULL. One of the SPILS linear solvers must be attached." -#define MSGBBD_MEM_FAIL "A memory request failed." -#define MSGBBD_BAD_NVECTOR "A required vector operation is not implemented." -#define MSGBBD_SUNMAT_FAIL "An error arose from a SUNBandMatrix routine." -#define MSGBBD_SUNLS_FAIL "An error arose from a SUNBandLinearSolver routine." -#define MSGBBD_PMEM_NULL "BBD peconditioner memory is NULL. IDABBDPrecInit must be called." -#define MSGBBD_FUNC_FAILED "The gloc or gcomm routine failed in an unrecoverable manner." - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/deps/AMICI/ThirdParty/sundials/src/kinsol/kinsol_direct.c b/deps/AMICI/ThirdParty/sundials/src/kinsol/kinsol_direct.c deleted file mode 100644 index d5b0e3805..000000000 --- a/deps/AMICI/ThirdParty/sundials/src/kinsol/kinsol_direct.c +++ /dev/null @@ -1,55 +0,0 @@ -/*----------------------------------------------------------------- - * Programmer(s): Daniel R. Reynolds @ SMU - * Radu Serban @ LLNL - *----------------------------------------------------------------- - * SUNDIALS Copyright Start - * Copyright (c) 2002-2021, Lawrence Livermore National Security - * and Southern Methodist University. - * All rights reserved. - * - * See the top-level LICENSE and NOTICE files for details. - * - * SPDX-License-Identifier: BSD-3-Clause - * SUNDIALS Copyright End - *----------------------------------------------------------------- - * Implementation file for the deprecated direct linear solver interface in - * KINSOL; these routines now just wrap the updated KINSOL generic - * linear solver interface in kinsol_ls.h. - *-----------------------------------------------------------------*/ - -#include -#include - -#ifdef __cplusplus /* wrapper to enable C++ usage */ -extern "C" { -#endif - -/*================================================================= - Exported Functions (wrappers for equivalent routines in kinsol_ls.h) - =================================================================*/ - -int KINDlsSetLinearSolver(void *kinmem, SUNLinearSolver LS, SUNMatrix A) -{ return(KINSetLinearSolver(kinmem, LS, A)); } - -int KINDlsSetJacFn(void *kinmem, KINDlsJacFn jac) -{ return(KINSetJacFn(kinmem, jac)); } - -int KINDlsGetWorkSpace(void *kinmem, long int *lenrw, long int *leniw) -{ return(KINGetLinWorkSpace(kinmem, lenrw, leniw)); } - -int KINDlsGetNumJacEvals(void *kinmem, long int *njevals) -{ return(KINGetNumJacEvals(kinmem, njevals)); } - -int KINDlsGetNumFuncEvals(void *kinmem, long int *nfevals) -{ return(KINGetNumLinFuncEvals(kinmem, nfevals)); } - -int KINDlsGetLastFlag(void *kinmem, long int *flag) -{ return(KINGetLastLinFlag(kinmem, flag)); } - -char *KINDlsGetReturnFlagName(long int flag) -{ return(KINGetLinReturnFlagName(flag)); } - -#ifdef __cplusplus -} -#endif - diff --git a/deps/AMICI/ThirdParty/sundials/src/kinsol/kinsol_impl.h b/deps/AMICI/ThirdParty/sundials/src/kinsol/kinsol_impl.h deleted file mode 100644 index ad1ccb656..000000000 --- a/deps/AMICI/ThirdParty/sundials/src/kinsol/kinsol_impl.h +++ /dev/null @@ -1,489 +0,0 @@ -/* - * ----------------------------------------------------------------- - * Programmer(s): Allan Taylor, Alan Hindmarsh, Radu Serban, and - * Aaron Collier @ LLNL - * ----------------------------------------------------------------- - * SUNDIALS Copyright Start - * Copyright (c) 2002-2021, Lawrence Livermore National Security - * and Southern Methodist University. - * All rights reserved. - * - * See the top-level LICENSE and NOTICE files for details. - * - * SPDX-License-Identifier: BSD-3-Clause - * SUNDIALS Copyright End - * ----------------------------------------------------------------- - * KINSOL solver module header file (private version) - * ----------------------------------------------------------------- - */ - -#ifndef _KINSOL_IMPL_H -#define _KINSOL_IMPL_H - -#include - -#include - -#ifdef __cplusplus /* wrapper to enable C++ usage */ -extern "C" { -#endif - -/* - * ================================================================= - * M A I N S O L V E R M E M O R Y B L O C K - * ================================================================= - */ - -/* KINSOL default constants */ - -#define PRINTFL_DEFAULT 0 -#define MXITER_DEFAULT 200 -#define MXNBCF_DEFAULT 10 -#define MSBSET_DEFAULT 10 -#define MSBSET_SUB_DEFAULT 5 - -#define OMEGA_MIN RCONST(0.00001) -#define OMEGA_MAX RCONST(0.9) - -/* - * ----------------------------------------------------------------- - * Types : struct KINMemRec and struct *KINMem - * ----------------------------------------------------------------- - * A variable declaration of type struct *KINMem denotes a - * pointer to a data structure of type struct KINMemRec. The - * KINMemRec structure contains numerous fields that must be - * accessible by KINSOL solver module routines. - * ----------------------------------------------------------------- - */ - -typedef struct KINMemRec { - - realtype kin_uround; /* machine epsilon (or unit roundoff error) - (defined in sundials_types.h) */ - - /* problem specification data */ - - KINSysFn kin_func; /* nonlinear system function implementation */ - void *kin_user_data; /* work space available to func routine */ - realtype kin_fnormtol; /* stopping tolerance on L2-norm of function - value */ - realtype kin_scsteptol; /* scaled step length tolerance */ - int kin_globalstrategy; /* choices are KIN_NONE, KIN_LINESEARCH - KIN_PICARD and KIN_FP */ - int kin_printfl; /* level of verbosity of output */ - long int kin_mxiter; /* maximum number of nonlinear iterations */ - long int kin_msbset; /* maximum number of nonlinear iterations that - may be performed between calls to the - linear solver setup routine (lsetup) */ - long int kin_msbset_sub; /* subinterval length for residual monitoring */ - long int kin_mxnbcf; /* maximum number of beta condition failures */ - int kin_etaflag; /* choices are KIN_ETACONSTANT, KIN_ETACHOICE1 - and KIN_ETACHOICE2 */ - booleantype kin_noMinEps; /* flag controlling whether or not the value - of eps is bounded below */ - booleantype kin_constraintsSet; /* flag indicating if constraints are being - used */ - booleantype kin_jacCurrent; /* flag indicating if the Jacobian info. - used by the linear solver is current */ - booleantype kin_callForcingTerm; /* flag set if using either KIN_ETACHOICE1 - or KIN_ETACHOICE2 */ - booleantype kin_noResMon; /* flag indicating if the nonlinear - residual monitoring scheme should be - used */ - booleantype kin_retry_nni; /* flag indicating if nonlinear iteration - should be retried (set by residual - monitoring algorithm) */ - booleantype kin_update_fnorm_sub; /* flag indicating if the fnorm associated - with the subinterval needs to be - updated (set by residual monitoring - algorithm) */ - - realtype kin_mxnewtstep; /* maximum allowable scaled step length */ - realtype kin_mxnstepin; /* input (or preset) value for mxnewtstep */ - realtype kin_sqrt_relfunc; /* relative error bound for func(u) */ - realtype kin_stepl; /* scaled length of current step */ - realtype kin_stepmul; /* step scaling factor */ - realtype kin_eps; /* current value of eps */ - realtype kin_eta; /* current value of eta */ - realtype kin_eta_gamma; /* gamma value used in eta calculation - (choice #2) */ - realtype kin_eta_alpha; /* alpha value used in eta calculation - (choice #2) */ - booleantype kin_noInitSetup; /* flag controlling whether or not the KINSol - routine makes an initial call to the - linear solver setup routine (lsetup) */ - realtype kin_sthrsh; /* threshold value for calling the linear - solver setup routine */ - - /* counters */ - - long int kin_nni; /* number of nonlinear iterations */ - long int kin_nfe; /* number of calls made to func routine */ - long int kin_nnilset; /* value of nni counter when the linear solver - setup was last called */ - long int kin_nnilset_sub; /* value of nni counter when the linear solver - setup was last called (subinterval) */ - long int kin_nbcf; /* number of times the beta-condition could not - be met in KINLineSearch */ - long int kin_nbktrk; /* number of backtracks performed by - KINLineSearch */ - long int kin_ncscmx; /* number of consecutive steps of size - mxnewtstep taken */ - - /* vectors */ - - N_Vector kin_uu; /* solution vector/current iterate (initially - contains initial guess, but holds approximate - solution upon completion if no errors occurred) */ - N_Vector kin_unew; /* next iterate (unew = uu+pp) */ - N_Vector kin_fval; /* vector containing result of nonlinear system - function evaluated at a given iterate - (fval = func(uu)) */ - N_Vector kin_gval; /* vector containing result of the fixed point - function evaluated at a given iterate; - used in KIN_PICARD strategy only. - (gval = uu - L^{-1}fval(uu)) */ - N_Vector kin_uscale; /* iterate scaling vector */ - N_Vector kin_fscale; /* fval scaling vector */ - N_Vector kin_pp; /* incremental change vector (pp = unew-uu) */ - N_Vector kin_constraints; /* constraints vector */ - N_Vector kin_vtemp1; /* scratch vector #1 */ - N_Vector kin_vtemp2; /* scratch vector #2 */ - - /* space requirements for AA, Broyden and NLEN */ - N_Vector kin_fold_aa; /* vector needed for AA, Broyden, and NLEN */ - N_Vector kin_gold_aa; /* vector needed for AA, Broyden, and NLEN */ - N_Vector *kin_df_aa; /* vector array needed for AA, Broyden, and NLEN */ - N_Vector *kin_dg_aa; /* vector array needed for AA, Broyden and NLEN */ - N_Vector *kin_q_aa; /* vector array needed for AA */ - realtype kin_beta_aa; /* beta damping parameter for AA */ - realtype *kin_gamma_aa; /* array of size maa used in AA */ - realtype *kin_R_aa; /* array of size maa*maa used in AA */ - long int *kin_ipt_map; /* array of size maa used in AA */ - long int kin_m_aa; /* parameter for AA, Broyden or NLEN */ - booleantype kin_aamem_aa; /* sets additional memory needed for Anderson Acc */ - booleantype kin_setstop_aa; /* determines whether user will set stopping criterion */ - booleantype kin_damping_aa; /* flag to apply damping in AA */ - realtype *kin_cv; /* scalar array for fused vector operations */ - N_Vector *kin_Xv; /* vector array for fused vector operations */ - - /* space requirements for vector storage */ - - sunindextype kin_lrw1; /* number of realtype-sized memory blocks needed - for a single N_Vector */ - sunindextype kin_liw1; /* number of int-sized memory blocks needed for - a single N_Vecotr */ - long int kin_lrw; /* total number of realtype-sized memory blocks - needed for all KINSOL work vectors */ - long int kin_liw; /* total number of int-sized memory blocks needed - for all KINSOL work vectors */ - - /* linear solver data */ - - /* function prototypes (pointers) */ - - int (*kin_linit)(struct KINMemRec *kin_mem); - - int (*kin_lsetup)(struct KINMemRec *kin_mem); - - int (*kin_lsolve)(struct KINMemRec *kin_mem, N_Vector xx, N_Vector bb, - realtype *sJpnorm, realtype *sFdotJp); - - int (*kin_lfree)(struct KINMemRec *kin_mem); - - booleantype kin_inexact_ls; /* flag set by the linear solver module - (in linit) indicating whether this is an - iterative linear solver (SUNTRUE), or a direct - linear solver (SUNFALSE) */ - - void *kin_lmem; /* pointer to linear solver memory block */ - - realtype kin_fnorm; /* value of L2-norm of fscale*fval */ - realtype kin_f1norm; /* f1norm = 0.5*(fnorm)^2 */ - realtype kin_sFdotJp; /* value of scaled F(u) vector (fscale*fval) - dotted with scaled J(u)*pp vector (set by lsolve) */ - realtype kin_sJpnorm; /* value of L2-norm of fscale*(J(u)*pp) - (set by lsolve) */ - - realtype kin_fnorm_sub; /* value of L2-norm of fscale*fval (subinterval) */ - booleantype kin_eval_omega; /* flag indicating that omega must be evaluated. */ - realtype kin_omega; /* constant value for real scalar used in test to - determine if reduction of norm of nonlinear - residual is sufficient. Unless a valid constant - value is specified by the user, omega is estimated - from omega_min and omega_max at each iteration. */ - realtype kin_omega_min; /* lower bound on omega */ - realtype kin_omega_max; /* upper bound on omega */ - - /* - * ----------------------------------------------------------------- - * Note: The KINLineSearch subroutine scales the values of the - * variables sFdotJp and sJpnorm by a factor rl (lambda) that is - * chosen by the line search algorithm such that the sclaed Newton - * step satisfies the following conditions: - * - * F(u_k+1) <= F(u_k) + alpha*(F(u_k)^T * J(u_k))*p*rl - * - * F(u_k+1) >= F(u_k) + beta*(F(u_k)^T * J(u_k))*p*rl - * - * where alpha = 1.0e-4, beta = 0.9, u_k+1 = u_k + rl*p, - * 0 < rl <= 1, J denotes the system Jacobian, and F represents - * the nonliner system function. - * ----------------------------------------------------------------- - */ - - booleantype kin_MallocDone; /* flag indicating if KINMalloc has been - called yet */ - - /* message files */ - /*------------------------------------------- - Error handler function and error ouput file - -------------------------------------------*/ - - KINErrHandlerFn kin_ehfun; /* Error messages are handled by ehfun */ - void *kin_eh_data; /* dats pointer passed to ehfun */ - FILE *kin_errfp; /* KINSOL error messages are sent to errfp */ - - KINInfoHandlerFn kin_ihfun; /* Info messages are handled by ihfun */ - void *kin_ih_data; /* dats pointer passed to ihfun */ - FILE *kin_infofp; /* where KINSol info messages are sent */ - -} *KINMem; - -/* - * ================================================================= - * I N T E R F A C E T O L I N E A R S O L V E R - * ================================================================= - */ - -/* - * ----------------------------------------------------------------- - * Function : int (*kin_linit)(KINMem kin_mem) - * ----------------------------------------------------------------- - * kin_linit initializes solver-specific data structures (including - * variables used as counters or for storing statistical information), - * but system memory allocation should be done by the subroutine - * that actually initializes the environment for liner solver - * package. If the linear system is to be preconditioned, then the - * variable setupNonNull (type booleantype) should be set to SUNTRUE - * (predefined constant) and the kin_lsetup routine should be - * appropriately defined. - * - * kinmem pointer to an internal memory block allocated during - * prior calls to KINCreate and KINMalloc - * - * If the necessary variables have been successfully initialized, - * then the kin_linit function should return 0 (zero). Otherwise, - * the subroutine should indicate a failure has occurred by - * returning a non-zero integer value. - * ----------------------------------------------------------------- - */ - -/* - * ----------------------------------------------------------------- - * Function : int (*kin_lsetup)(KINMem kin_mem) - * ----------------------------------------------------------------- - * kin_lsetup interfaces with the user-supplied pset subroutine (the - * preconditioner setup routine), and updates relevant variable - * values (see KINSpgmrSetup/KINSpbcgSetup). Simply stated, the - * kin_lsetup routine prepares the linear solver for a subsequent - * call to the user-supplied kin_lsolve function. - * - * kinmem pointer to an internal memory block allocated during - * prior calls to KINCreate and KINMalloc - * - * If successful, the kin_lsetup routine should return 0 (zero). - * Otherwise it should return a non-zero value. - * ----------------------------------------------------------------- - */ - -/* - * ----------------------------------------------------------------- - * Function : int (*kin_lsolve)(KINMem kin_mem, N_Vector xx, - * N_Vector bb, realtype *sJpnorm, realtype *sFdotJp) - * ----------------------------------------------------------------- - * kin_lsolve interfaces with the subroutine implementing the - * numerical method to be used to solve the linear system J*xx = bb, - * and must increment the relevant counter variable values in - * addition to computing certain values used by the global strategy - * and forcing term routines (see KINInexactNewton, KINLineSearch, - * KINForcingTerm, and KINSpgmrSolve/KINSpbcgSolve). - * - * kinmem pointer to an internal memory block allocated during - * prior calls to KINCreate and KINMalloc - * - * xx vector (type N_Vector) set to initial guess by kin_lsolve - * routine prior to calling the linear solver, but which upon - * return contains an approximate solution of the linear - * system J*xx = bb, where J denotes the system Jacobian - * - * bb vector (type N_Vector) set to -func(u) (negative of the - * value of the system function evaluated at the current - * iterate) by KINLinSolDrv before kin_lsolve is called - * - * sJpnorm holds the value of the L2-norm (Euclidean norm) of - * fscale*(J(u)*pp) upon return - * - * sFdotJp holds the value of the scaled F(u) (fscale*F) dotted - * with the scaled J(u)*pp vector upon return - * - * If successful, the kin_lsolve routine should return 0 (zero). - * Otherwise it should return a positive value if a re-evaluation - * of the lsetup function could recover, or a negative value if - * no such recovery is possible. - * ----------------------------------------------------------------- - */ - -/* - * ----------------------------------------------------------------- - * Function : int (*kin_lfree)(KINMem kin_mem) - * ----------------------------------------------------------------- - * kin_lfree is called by KINFree and should free (deallocate) all - * system memory resources allocated for the linear solver module - * (see KINSpgmrFree/KINSpbcgFree). It should return 0 upon - * success, nonzero on failure. - * - * kinmem pointer to an internal memory block allocated during - * prior calls to KINCreate and KINMalloc - * ----------------------------------------------------------------- - */ - -/* - * ================================================================= - * K I N S O L I N T E R N A L F U N C T I O N S - * ================================================================= - */ - - -/* High level error handler */ - -void KINProcessError(KINMem kin_mem, - int error_code, const char *module, const char *fname, - const char *msgfmt, ...); - -/* Prototype of internal errHandler function */ - -void KINErrHandler(int error_code, const char *module, const char *function, - char *msg, void *user_data); - - -/* High level info handler */ - -void KINPrintInfo(KINMem kin_mem, - int info_code, const char *module, const char *fname, - const char *msgfmt, ...); - -/* Prototype of internal infoHandler function */ - -void KINInfoHandler(const char *module, const char *function, - char *msg, void *user_data); - -/* - * ================================================================= - * K I N S O L E R R O R M E S S A G E S - * ================================================================= - */ - -#define MSG_MEM_FAIL "A memory request failed." -#define MSG_NO_MEM "kinsol_mem = NULL illegal." -#define MSG_BAD_NVECTOR "A required vector operation is not implemented." -#define MSG_FUNC_NULL "func = NULL illegal." -#define MSG_NO_MALLOC "Attempt to call before KINMalloc illegal." - -#define MSG_BAD_PRINTFL "Illegal value for printfl." -#define MSG_BAD_MXITER "Illegal value for mxiter." -#define MSG_BAD_MSBSET "Illegal msbset < 0." -#define MSG_BAD_MSBSETSUB "Illegal msbsetsub < 0." -#define MSG_BAD_ETACHOICE "Illegal value for etachoice." -#define MSG_BAD_ETACONST "eta out of range." -#define MSG_BAD_GAMMA "gamma out of range." -#define MSG_BAD_ALPHA "alpha out of range." -#define MSG_BAD_MXNEWTSTEP "Illegal mxnewtstep < 0." -#define MSG_BAD_RELFUNC "relfunc < 0 illegal." -#define MSG_BAD_FNORMTOL "fnormtol < 0 illegal." -#define MSG_BAD_SCSTEPTOL "scsteptol < 0 illegal." -#define MSG_BAD_MXNBCF "mxbcf < 0 illegal." -#define MSG_BAD_CONSTRAINTS "Illegal values in constraints vector." -#define MSG_BAD_OMEGA "scalars < 0 illegal." -#define MSG_BAD_MAA "maa < 0 illegal." -#define MSG_ZERO_MAA "maa = 0 illegal." - -#define MSG_LSOLV_NO_MEM "The linear solver memory pointer is NULL." -#define MSG_UU_NULL "uu = NULL illegal." -#define MSG_BAD_GLSTRAT "Illegal value for global strategy." -#define MSG_BAD_USCALE "uscale = NULL illegal." -#define MSG_USCALE_NONPOSITIVE "uscale has nonpositive elements." -#define MSG_BAD_FSCALE "fscale = NULL illegal." -#define MSG_FSCALE_NONPOSITIVE "fscale has nonpositive elements." -#define MSG_CONSTRAINTS_NOTOK "Constraints not allowed with fixed point or Picard iterations" -#define MSG_INITIAL_CNSTRNT "Initial guess does NOT meet constraints." -#define MSG_LINIT_FAIL "The linear solver's init routine failed." - -#define MSG_SYSFUNC_FAILED "The system function failed in an unrecoverable manner." -#define MSG_SYSFUNC_FIRST "The system function failed at the first call." -#define MSG_LSETUP_FAILED "The linear solver's setup function failed in an unrecoverable manner." -#define MSG_LSOLVE_FAILED "The linear solver's solve function failed in an unrecoverable manner." -#define MSG_LINSOLV_NO_RECOVERY "The linear solver's solve function failed recoverably, but the Jacobian data is already current." -#define MSG_LINESEARCH_NONCONV "The line search algorithm was unable to find an iterate sufficiently distinct from the current iterate." -#define MSG_LINESEARCH_BCFAIL "The line search algorithm was unable to satisfy the beta-condition for nbcfails iterations." -#define MSG_MAXITER_REACHED "The maximum number of iterations was reached before convergence." -#define MSG_MXNEWT_5X_EXCEEDED "Five consecutive steps have been taken that satisfy a scaled step length test." -#define MSG_SYSFUNC_REPTD "Unable to correct repeated recoverable system function errors." -#define MSG_NOL_FAIL "Unable to find user's Linear Jacobian, which is required for the KIN_PICARD Strategy" - -/* - * ================================================================= - * K I N S O L I N F O M E S S A G E S - * ================================================================= - */ - -#define INFO_RETVAL "Return value: %d" -#define INFO_ADJ "no. of lambda adjustments = %ld" - -#if defined(SUNDIALS_EXTENDED_PRECISION) - -#define INFO_NNI "nni = %4ld nfe = %6ld fnorm = %26.16Lg" -#define INFO_TOL "scsteptol = %12.3Lg fnormtol = %12.3Lg" -#define INFO_FMAX "scaled f norm (for stopping) = %12.3Lg" -#define INFO_PNORM "pnorm = %12.4Le" -#define INFO_PNORM1 "(ivio=1) pnorm = %12.4Le" -#define INFO_FNORM "fnorm(L2) = %20.8Le" -#define INFO_LAM "min_lam = %11.4Le f1norm = %11.4Le pnorm = %11.4Le" -#define INFO_ALPHA "fnorm = %15.8Le f1norm = %15.8Le alpha_cond = %15.8Le lam = %15.8Le" -#define INFO_BETA "f1norm = %15.8Le beta_cond = %15.8Le lam = %15.8Le" -#define INFO_ALPHABETA "f1norm = %15.8Le alpha_cond = %15.8Le beta_cond = %15.8Le lam = %15.8Le" - -#elif defined(SUNDIALS_DOUBLE_PRECISION) - -#define INFO_NNI "nni = %4ld nfe = %6ld fnorm = %26.16lg" -#define INFO_TOL "scsteptol = %12.3lg fnormtol = %12.3lg" -#define INFO_FMAX "scaled f norm (for stopping) = %12.3lg" -#define INFO_PNORM "pnorm = %12.4le" -#define INFO_PNORM1 "(ivio=1) pnorm = %12.4le" -#define INFO_FNORM "fnorm(L2) = %20.8le" -#define INFO_LAM "min_lam = %11.4le f1norm = %11.4le pnorm = %11.4le" -#define INFO_ALPHA "fnorm = %15.8le f1norm = %15.8le alpha_cond = %15.8le lam = %15.8le" -#define INFO_BETA "f1norm = %15.8le beta_cond = %15.8le lam = %15.8le" -#define INFO_ALPHABETA "f1norm = %15.8le alpha_cond = %15.8le beta_cond = %15.8le lam = %15.8le" - -#else - -#define INFO_NNI "nni = %4ld nfe = %6ld fnorm = %26.16g" -#define INFO_TOL "scsteptol = %12.3g fnormtol = %12.3g" -#define INFO_FMAX "scaled f norm (for stopping) = %12.3g" -#define INFO_PNORM "pnorm = %12.4e" -#define INFO_PNORM1 "(ivio=1) pnorm = %12.4e" -#define INFO_FNORM "fnorm(L2) = %20.8e" -#define INFO_LAM "min_lam = %11.4e f1norm = %11.4e pnorm = %11.4e" -#define INFO_ALPHA "fnorm = %15.8e f1norm = %15.8e alpha_cond = %15.8e lam = %15.8e" -#define INFO_BETA "f1norm = %15.8e beta_cond = %15.8e lam = %15.8e" -#define INFO_ALPHABETA "f1norm = %15.8e alpha_cond = %15.8e beta_cond = %15.8e lam = %15.8e" - -#endif - - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/deps/AMICI/ThirdParty/sundials/src/kinsol/kinsol_io.c b/deps/AMICI/ThirdParty/sundials/src/kinsol/kinsol_io.c deleted file mode 100644 index 5f11b0dda..000000000 --- a/deps/AMICI/ThirdParty/sundials/src/kinsol/kinsol_io.c +++ /dev/null @@ -1,1073 +0,0 @@ -/* ----------------------------------------------------------------- - * Programmer(s): Allan Taylor, Alan Hindmarsh, Radu Serban, and - * Aaron Collier @ LLNL - * ----------------------------------------------------------------- - * SUNDIALS Copyright Start - * Copyright (c) 2002-2021, Lawrence Livermore National Security - * and Southern Methodist University. - * All rights reserved. - * - * See the top-level LICENSE and NOTICE files for details. - * - * SPDX-License-Identifier: BSD-3-Clause - * SUNDIALS Copyright End - * ----------------------------------------------------------------- - * This is the implementation file for the optional input and output - * functions for the KINSOL solver. - * ----------------------------------------------------------------- - */ - -#include -#include - -#include "kinsol_impl.h" -#include -#include - -#define ZERO RCONST(0.0) -#define POINT1 RCONST(0.1) -#define ONETHIRD RCONST(0.3333333333333333) -#define HALF RCONST(0.5) -#define TWOTHIRDS RCONST(0.6666666666666667) -#define POINT9 RCONST(0.9) -#define ONE RCONST(1.0) -#define TWO RCONST(2.0) -#define TWOPT5 RCONST(2.5) - -/* - * ================================================================= - * KINSOL optional input functions - * ================================================================= - */ - -/* - * ----------------------------------------------------------------- - * KINSetErrHandlerFn - * ----------------------------------------------------------------- - */ - -int KINSetErrHandlerFn(void *kinmem, KINErrHandlerFn ehfun, void *eh_data) -{ - KINMem kin_mem; - - if (kinmem == NULL) { - KINProcessError(NULL, KIN_MEM_NULL, "KINSOL", "KINSetErrHandlerFn", MSG_NO_MEM); - return(KIN_MEM_NULL); - } - - kin_mem = (KINMem) kinmem; - - kin_mem->kin_ehfun = ehfun; - kin_mem->kin_eh_data = eh_data; - - return(KIN_SUCCESS); -} - -/* - * ----------------------------------------------------------------- - * Function : KINSetErrFile - * ----------------------------------------------------------------- - */ - -int KINSetErrFile(void *kinmem, FILE *errfp) -{ - KINMem kin_mem; - - if (kinmem == NULL) { - KINProcessError(NULL, KIN_MEM_NULL, "KINSOL", "KINSetErrFile", MSG_NO_MEM); - return(KIN_MEM_NULL); - } - - kin_mem = (KINMem) kinmem; - kin_mem->kin_errfp = errfp; - - return(KIN_SUCCESS); -} - -#define errfp (kin_mem->kin_errfp) - -/* - * ----------------------------------------------------------------- - * Function : KINSetPrintLevel - * ----------------------------------------------------------------- - */ - -int KINSetPrintLevel(void *kinmem, int printfl) -{ - KINMem kin_mem; - - if (kinmem == NULL) { - KINProcessError(NULL, KIN_MEM_NULL, "KINSOL", "KINSetPrintLevel", MSG_NO_MEM); - return(KIN_MEM_NULL); - } - - kin_mem = (KINMem) kinmem; - - if ((printfl < 0) || (printfl > 3)) { - KINProcessError(NULL, KIN_ILL_INPUT, "KINSOL", "KINSetPrintLevel", MSG_BAD_PRINTFL); - return(KIN_ILL_INPUT); - } - - kin_mem->kin_printfl = printfl; - - return(KIN_SUCCESS); -} - -/* - * ----------------------------------------------------------------- - * KINSetInfoHandlerFn - * ----------------------------------------------------------------- - */ - -int KINSetInfoHandlerFn(void *kinmem, KINInfoHandlerFn ihfun, void *ih_data) -{ - KINMem kin_mem; - - if (kinmem == NULL) { - KINProcessError(NULL, KIN_MEM_NULL, "KINSOL", "KINSetInfoHandlerFn", MSG_NO_MEM); - return(KIN_MEM_NULL); - } - - kin_mem = (KINMem) kinmem; - - kin_mem->kin_ihfun = ihfun; - kin_mem->kin_ih_data = ih_data; - - return(KIN_SUCCESS); -} - - -/* - * ----------------------------------------------------------------- - * Function : KINSetInfoFile - * ----------------------------------------------------------------- - */ - -int KINSetInfoFile(void *kinmem, FILE *infofp) -{ - KINMem kin_mem; - - if (kinmem == NULL) { - KINProcessError(NULL, KIN_MEM_NULL, "KINSOL", "KINSetInfoFile", MSG_NO_MEM); - return(KIN_MEM_NULL); - } - - kin_mem = (KINMem) kinmem; - kin_mem->kin_infofp = infofp; - - return(KIN_SUCCESS); -} - -/* - * ----------------------------------------------------------------- - * Function : KINSetUserData - * ----------------------------------------------------------------- - */ - -int KINSetUserData(void *kinmem, void *user_data) -{ - KINMem kin_mem; - - if (kinmem == NULL) { - KINProcessError(NULL, KIN_MEM_NULL, "KINSOL", "KINSetUserData", MSG_NO_MEM); - return(KIN_MEM_NULL); - } - - kin_mem = (KINMem) kinmem; - kin_mem->kin_user_data = user_data; - - return(KIN_SUCCESS); -} - -/* - * ----------------------------------------------------------------- - * Function : KINSetMAA - * ----------------------------------------------------------------- - */ - -int KINSetMAA(void *kinmem, long int maa) -{ - KINMem kin_mem; - - if (kinmem == NULL) { - KINProcessError(NULL, KIN_MEM_NULL, "KINSOL", "KINSetMAA", MSG_NO_MEM); - return(KIN_MEM_NULL); - } - - kin_mem = (KINMem) kinmem; - - if (maa < 0) { - KINProcessError(NULL, KIN_ILL_INPUT, "KINSOL", "KINSetMAA", MSG_BAD_MAA); - return(KIN_ILL_INPUT); - } - - if (maa > kin_mem->kin_mxiter) maa = kin_mem->kin_mxiter; - - kin_mem->kin_m_aa = maa; - kin_mem->kin_aamem_aa = (maa == 0) ? SUNFALSE : SUNTRUE; - - return(KIN_SUCCESS); -} - -/* - * ----------------------------------------------------------------- - * Function : KINSetDampingAA - * ----------------------------------------------------------------- - */ - -int KINSetDampingAA(void *kinmem, realtype beta) -{ - KINMem kin_mem; - - if (kinmem == NULL) { - KINProcessError(NULL, KIN_MEM_NULL, "KINSOL", "KINSetMAA", MSG_NO_MEM); - return(KIN_MEM_NULL); - } - - kin_mem = (KINMem) kinmem; - - /* check for illegal input value */ - if (beta <= ZERO) { - KINProcessError(NULL, KIN_ILL_INPUT, "KINSOL", "KINSetDampingAA", - "beta <= 0 illegal"); - return(KIN_ILL_INPUT); - } - - if (beta < ONE) { - /* enable damping */ - kin_mem->kin_beta_aa = beta; - kin_mem->kin_damping_aa = SUNTRUE; - } else { - /* disable damping */ - kin_mem->kin_beta_aa = ONE; - kin_mem->kin_damping_aa = SUNFALSE; - } - - return(KIN_SUCCESS); -} - -/* - * ----------------------------------------------------------------- - * Function : KINSetAAStopCrit - * ----------------------------------------------------------------- - */ - -/* CSW: This function is currently not supported. - -int KINSetAAStopCrit(void *kinmem, booleantype setstop) -{ - KINMem kin_mem; - - if (kinmem == NULL) { - KINProcessError(NULL, KIN_MEM_NULL, "KINSOL", "KINSetAAStopCrit", MSG_NO_MEM); - return(KIN_MEM_NULL); - } - - kin_mem = (KINMem) kinmem; - kin_mem->kin_setstop_aa = setstop; - - return(KIN_SUCCESS); -} -*/ - -/* - * ----------------------------------------------------------------- - * Function : KINSetNumMaxIters - * ----------------------------------------------------------------- - */ - -int KINSetNumMaxIters(void *kinmem, long int mxiter) -{ - KINMem kin_mem; - - if (kinmem == NULL) { - KINProcessError(NULL, KIN_MEM_NULL, "KINSOL", "KINSetNumMaxIters", MSG_NO_MEM); - return(KIN_MEM_NULL); - } - - kin_mem = (KINMem) kinmem; - - if (mxiter < 0) { - KINProcessError(NULL, KIN_ILL_INPUT, "KINSOL", "KINSetNumMaxIters", MSG_BAD_MXITER); - return(KIN_ILL_INPUT); - } - - if (mxiter == 0) - kin_mem->kin_mxiter = MXITER_DEFAULT; - else - kin_mem->kin_mxiter = mxiter; - - return(KIN_SUCCESS); -} - -/* - * ----------------------------------------------------------------- - * Function : KINSetNoInitSetup - * ----------------------------------------------------------------- - */ - -int KINSetNoInitSetup(void *kinmem, booleantype noInitSetup) -{ - KINMem kin_mem; - - if (kinmem == NULL) { - KINProcessError(NULL, KIN_MEM_NULL, "KINSOL", "KINSetNoInitSetup", MSG_NO_MEM); - return(KIN_MEM_NULL); - } - - kin_mem = (KINMem) kinmem; - kin_mem->kin_noInitSetup = noInitSetup; - - return(KIN_SUCCESS); -} - -/* - * ----------------------------------------------------------------- - * Function : KINSetNoResMon - * ----------------------------------------------------------------- - */ - -int KINSetNoResMon(void *kinmem, booleantype noResMon) -{ - KINMem kin_mem; - - if (kinmem == NULL) { - KINProcessError(NULL, KIN_MEM_NULL, "KINSOL", "KINSetNoResMon", MSG_NO_MEM); - return(KIN_MEM_NULL); - } - - kin_mem = (KINMem) kinmem; - kin_mem->kin_noResMon = noResMon; - - return(KIN_SUCCESS); -} - -/* - * ----------------------------------------------------------------- - * Function : KINSetMaxSetupCalls - * ----------------------------------------------------------------- - */ - -int KINSetMaxSetupCalls(void *kinmem, long int msbset) -{ - KINMem kin_mem; - - if (kinmem == NULL) { - KINProcessError(NULL, KIN_MEM_NULL, "KINSOL", "KINSetMaxSetupCalls", MSG_NO_MEM); - return(KIN_MEM_NULL); - } - - kin_mem = (KINMem) kinmem; - - if (msbset < 0) { - KINProcessError(NULL, KIN_ILL_INPUT, "KINSOL", "KINSetMaxSetupCalls", MSG_BAD_MSBSET); - return(KIN_ILL_INPUT); - } - - if (msbset == 0) - kin_mem->kin_msbset = MSBSET_DEFAULT; - else - kin_mem->kin_msbset = msbset; - - return(KIN_SUCCESS); -} - -/* - * ----------------------------------------------------------------- - * Function : KINSetMaxSubSetupCalls - * ----------------------------------------------------------------- - */ - -int KINSetMaxSubSetupCalls(void *kinmem, long int msbsetsub) -{ - KINMem kin_mem; - - if (kinmem == NULL) { - KINProcessError(NULL, KIN_MEM_NULL, "KINSOL", "KINSetMaxSubSetupCalls", MSG_NO_MEM); - return(KIN_MEM_NULL); - } - - kin_mem = (KINMem) kinmem; - - if (msbsetsub < 0) { - KINProcessError(NULL, KIN_ILL_INPUT, "KINSOL", "KINSetMaxSubSetupCalls", MSG_BAD_MSBSETSUB); - return(KIN_ILL_INPUT); - } - - if (msbsetsub == 0) - kin_mem->kin_msbset_sub = MSBSET_SUB_DEFAULT; - else - kin_mem->kin_msbset_sub = msbsetsub; - - return(KIN_SUCCESS); -} - -/* - * ----------------------------------------------------------------- - * Function : KINSetEtaForm - * ----------------------------------------------------------------- - */ - -int KINSetEtaForm(void *kinmem, int etachoice) -{ - KINMem kin_mem; - - if (kinmem == NULL) { - KINProcessError(NULL, KIN_MEM_NULL, "KINSOL", "KINSetEtaForm", MSG_NO_MEM); - return(KIN_MEM_NULL); - } - - kin_mem = (KINMem) kinmem; - - if ((etachoice != KIN_ETACONSTANT) && - (etachoice != KIN_ETACHOICE1) && - (etachoice != KIN_ETACHOICE2)) { - KINProcessError(NULL, KIN_ILL_INPUT, "KINSOL", "KINSetEtaForm", MSG_BAD_ETACHOICE); - return(KIN_ILL_INPUT); - } - - kin_mem->kin_etaflag = etachoice; - - return(KIN_SUCCESS); -} - -/* - * ----------------------------------------------------------------- - * Function : KINSetEtaConstValue - * ----------------------------------------------------------------- - */ - -int KINSetEtaConstValue(void *kinmem, realtype eta) -{ - KINMem kin_mem; - - if (kinmem == NULL) { - KINProcessError(NULL, KIN_MEM_NULL, "KINSOL", "KINSetEtaConstValue", MSG_NO_MEM); - return(KIN_MEM_NULL); - } - - kin_mem = (KINMem) kinmem; - - if ((eta < ZERO) || (eta > ONE)) { - KINProcessError(NULL, KIN_ILL_INPUT, "KINSOL", "KINSetEtaConstValue", MSG_BAD_ETACONST); - return(KIN_ILL_INPUT); - } - - if (eta == ZERO) - kin_mem->kin_eta = POINT1; - else - kin_mem->kin_eta = eta; - - return(KIN_SUCCESS); -} - -/* - * ----------------------------------------------------------------- - * Function : KINSetEtaParams - * ----------------------------------------------------------------- - */ - -int KINSetEtaParams(void *kinmem, realtype egamma, realtype ealpha) -{ - KINMem kin_mem; - - if (kinmem == NULL) { - KINProcessError(NULL, KIN_MEM_NULL, "KINSOL", "KINSetEtaParams", MSG_NO_MEM); - return(KIN_MEM_NULL); - } - - kin_mem = (KINMem) kinmem; - - if ((ealpha <= ONE) || (ealpha > TWO)) - if (ealpha != ZERO) { - KINProcessError(NULL, KIN_ILL_INPUT, "KINSOL", "KINSetEtaParams", MSG_BAD_ALPHA); - return(KIN_ILL_INPUT); - } - - if (ealpha == ZERO) - kin_mem->kin_eta_alpha = TWO; - else - kin_mem->kin_eta_alpha = ealpha; - - if ((egamma <= ZERO) || (egamma > ONE)) - if (egamma != ZERO) { - KINProcessError(NULL, KIN_ILL_INPUT, "KINSOL", "KINSetEtaParams", MSG_BAD_GAMMA); - return(KIN_ILL_INPUT); - } - - if (egamma == ZERO) - kin_mem->kin_eta_gamma = POINT9; - else - kin_mem->kin_eta_gamma = egamma; - - return(KIN_SUCCESS); -} - -/* - * ----------------------------------------------------------------- - * Function : KINSetResMonParams - * ----------------------------------------------------------------- - */ - -int KINSetResMonParams(void *kinmem, realtype omegamin, realtype omegamax) -{ - KINMem kin_mem; - - if (kinmem == NULL) { - KINProcessError(NULL, KIN_MEM_NULL, "KINSOL", "KINSetResMonParams", MSG_NO_MEM); - return(KIN_MEM_NULL); - } - - kin_mem = (KINMem) kinmem; - - /* check omegamin */ - - if (omegamin < ZERO) { - KINProcessError(NULL, KIN_ILL_INPUT, "KINSOL", "KINSetResMonParams", MSG_BAD_OMEGA); - return(KIN_ILL_INPUT); - } - - if (omegamin == ZERO) - kin_mem->kin_omega_min = OMEGA_MIN; - else - kin_mem->kin_omega_min = omegamin; - - /* check omegamax */ - - if (omegamax < ZERO) { - KINProcessError(NULL, KIN_ILL_INPUT, "KINSOL", "KINSetResMonParams", MSG_BAD_OMEGA); - return(KIN_ILL_INPUT); - } - - if (omegamax == ZERO) { - - if (kin_mem->kin_omega_min > OMEGA_MAX) { - KINProcessError(NULL, KIN_ILL_INPUT, "KINSOL", "KINSetResMonParams", MSG_BAD_OMEGA); - return(KIN_ILL_INPUT); - } - else kin_mem->kin_omega_max = OMEGA_MAX; - - } else { - - if (kin_mem->kin_omega_min > omegamax) { - KINProcessError(NULL, KIN_ILL_INPUT, "KINSOL", "KINSetResMonParams", MSG_BAD_OMEGA); - return(KIN_ILL_INPUT); - } - else kin_mem->kin_omega_max = omegamax; - - } - - return(KIN_SUCCESS); -} - -/* - * ----------------------------------------------------------------- - * Function : KINSetResMonConstValue - * ----------------------------------------------------------------- - */ - -int KINSetResMonConstValue(void *kinmem, realtype omegaconst) -{ - KINMem kin_mem; - - if (kinmem == NULL) { - KINProcessError(NULL, KIN_MEM_NULL, "KINSOL", "KINSetResMonConstValue", MSG_NO_MEM); - return(KIN_MEM_NULL); - } - - kin_mem = (KINMem) kinmem; - - /* check omegaconst */ - - if (omegaconst < ZERO) { - KINProcessError(NULL, KIN_ILL_INPUT, "KINSOL", "KINSetResMonConstValue", MSG_BAD_OMEGA); - return(KIN_ILL_INPUT); - } - - /* Load omega value. A value of 0 will force using omega_min and omega_max */ - kin_mem->kin_omega = omegaconst; - - return(KIN_SUCCESS); -} - -/* - * ----------------------------------------------------------------- - * Function : KINSetNoMinEps - * ----------------------------------------------------------------- - */ - -int KINSetNoMinEps(void *kinmem, booleantype noMinEps) -{ - KINMem kin_mem; - - if (kinmem == NULL) { - KINProcessError(NULL, KIN_MEM_NULL, "KINSOL", "KINSetNoMinEps", MSG_NO_MEM); - return(KIN_MEM_NULL); - } - - kin_mem = (KINMem) kinmem; - kin_mem->kin_noMinEps = noMinEps; - - return(KIN_SUCCESS); -} - -/* - * ----------------------------------------------------------------- - * Function : KINSetMaxNewtonStep - * ----------------------------------------------------------------- - */ - -int KINSetMaxNewtonStep(void *kinmem, realtype mxnewtstep) -{ - KINMem kin_mem; - - if (kinmem == NULL) { - KINProcessError(NULL, KIN_MEM_NULL, "KINSOL", "KINSetMaxNewtonStep", MSG_NO_MEM); - return(KIN_MEM_NULL); - } - - kin_mem = (KINMem) kinmem; - - if (mxnewtstep < ZERO) { - KINProcessError(NULL, KIN_ILL_INPUT, "KINSOL", "KINSetMaxNewtonStep", MSG_BAD_MXNEWTSTEP); - return(KIN_ILL_INPUT); - } - - /* Note: passing a value of 0.0 will use the default - value (computed in KINSolInit) */ - - kin_mem->kin_mxnstepin = mxnewtstep; - - return(KIN_SUCCESS); -} - -/* - * ----------------------------------------------------------------- - * Function : KINSetMaxBetaFails - * ----------------------------------------------------------------- - */ - -int KINSetMaxBetaFails(void *kinmem, long int mxnbcf) -{ - KINMem kin_mem; - - if (kinmem == NULL) { - KINProcessError(NULL, KIN_MEM_NULL, "KINSOL", "KINSetMaxBetaFails", MSG_NO_MEM); - return(KIN_MEM_NULL); - } - - kin_mem = (KINMem) kinmem; - - if (mxnbcf < 0) { - KINProcessError(NULL, KIN_ILL_INPUT, "KINSOL", "KINSetMaxBetaFails", MSG_BAD_MXNBCF); - return(KIN_ILL_INPUT); - } - - if (mxnbcf == 0) - kin_mem->kin_mxnbcf = MXNBCF_DEFAULT; - else - kin_mem->kin_mxnbcf = mxnbcf; - - return(KIN_SUCCESS); - -} - -/* - * ----------------------------------------------------------------- - * Function : KINSetRelErrFunc - * ----------------------------------------------------------------- - */ - -int KINSetRelErrFunc(void *kinmem, realtype relfunc) -{ - KINMem kin_mem; - realtype uround; - - if (kinmem == NULL) { - KINProcessError(NULL, KIN_MEM_NULL, "KINSOL", "KINSetRelErrFunc", MSG_NO_MEM); - return(KIN_MEM_NULL); - } - - kin_mem = (KINMem) kinmem; - - if (relfunc < ZERO) { - KINProcessError(NULL, KIN_ILL_INPUT, "KINSOL", "KINSetRelErrFunc", MSG_BAD_RELFUNC); - return(KIN_ILL_INPUT); - } - - if (relfunc == ZERO) { - uround = kin_mem->kin_uround; - kin_mem->kin_sqrt_relfunc = SUNRsqrt(uround); - } else { - kin_mem->kin_sqrt_relfunc = SUNRsqrt(relfunc); - } - - return(KIN_SUCCESS); -} - -/* - * ----------------------------------------------------------------- - * Function : KINSetFuncNormTol - * ----------------------------------------------------------------- - */ - -int KINSetFuncNormTol(void *kinmem, realtype fnormtol) -{ - KINMem kin_mem; - realtype uround; - - if (kinmem == NULL) { - KINProcessError(NULL, KIN_MEM_NULL, "KINSOL", "KINSetFuncNormTol", MSG_NO_MEM); - return(KIN_MEM_NULL); - } - - kin_mem = (KINMem) kinmem; - - if (fnormtol < ZERO) { - KINProcessError(NULL, KIN_ILL_INPUT, "KINSOL", "KINSetFuncNormTol", MSG_BAD_FNORMTOL); - return(KIN_ILL_INPUT); - } - - if (fnormtol == ZERO) { - uround = kin_mem->kin_uround; - kin_mem->kin_fnormtol = SUNRpowerR(uround,ONETHIRD); - } else { - kin_mem->kin_fnormtol = fnormtol; - } - - return(KIN_SUCCESS); -} - -/* - * ----------------------------------------------------------------- - * Function : KINSetScaledStepTol - * ----------------------------------------------------------------- - */ - -int KINSetScaledStepTol(void *kinmem, realtype scsteptol) -{ - KINMem kin_mem; - realtype uround; - - if (kinmem == NULL) { - KINProcessError(NULL, KIN_MEM_NULL, "KINSOL", "KINSetScaledStepTol", MSG_NO_MEM); - return(KIN_MEM_NULL); - } - - kin_mem = (KINMem) kinmem; - - if (scsteptol < ZERO) { - KINProcessError(NULL, KIN_ILL_INPUT, "KINSOL", "KINSetScaledStepTol", MSG_BAD_SCSTEPTOL); - return(KIN_ILL_INPUT); - } - - if (scsteptol == ZERO) { - uround = kin_mem->kin_uround; - kin_mem->kin_scsteptol = SUNRpowerR(uround,TWOTHIRDS); - } else { - kin_mem->kin_scsteptol = scsteptol; - } - - return(KIN_SUCCESS); -} - -/* - * ----------------------------------------------------------------- - * Function : KINSetConstraints - * ----------------------------------------------------------------- - */ - -int KINSetConstraints(void *kinmem, N_Vector constraints) -{ - KINMem kin_mem; - realtype temptest; - - if (kinmem == NULL) { - KINProcessError(NULL, KIN_MEM_NULL, "KINSOL", "KINSetConstraints", MSG_NO_MEM); - return(KIN_MEM_NULL); - } - - kin_mem = (KINMem) kinmem; - - if (constraints == NULL) { - if (kin_mem->kin_constraintsSet) { - N_VDestroy(kin_mem->kin_constraints); - kin_mem->kin_lrw -= kin_mem->kin_lrw1; - kin_mem->kin_liw -= kin_mem->kin_liw1; - } - kin_mem->kin_constraintsSet = SUNFALSE; - return(KIN_SUCCESS); - } - - /* Check the constraints vector */ - - temptest = N_VMaxNorm(constraints); - if (temptest > TWOPT5){ - KINProcessError(NULL, KIN_ILL_INPUT, "KINSOL", "KINSetConstraints", MSG_BAD_CONSTRAINTS); - return(KIN_ILL_INPUT); - } - - if (!kin_mem->kin_constraintsSet) { - kin_mem->kin_constraints = N_VClone(constraints); - kin_mem->kin_lrw += kin_mem->kin_lrw1; - kin_mem->kin_liw += kin_mem->kin_liw1; - kin_mem->kin_constraintsSet = SUNTRUE; - } - - /* Load the constraint vector */ - - N_VScale(ONE, constraints, kin_mem->kin_constraints); - - return(KIN_SUCCESS); -} - -/* - * ----------------------------------------------------------------- - * Function : KINSetSysFunc - * ----------------------------------------------------------------- - */ - -int KINSetSysFunc(void *kinmem, KINSysFn func) -{ - KINMem kin_mem; - - if (kinmem == NULL) { - KINProcessError(NULL, KIN_MEM_NULL, "KINSOL", "KINSetSysFunc", MSG_NO_MEM); - return(KIN_MEM_NULL); - } - - kin_mem = (KINMem) kinmem; - - if (func == NULL) { - KINProcessError(NULL, KIN_ILL_INPUT, "KINSOL", "KINSetSysFunc", MSG_FUNC_NULL); - return(KIN_ILL_INPUT); - } - - kin_mem->kin_func = func; - - return(KIN_SUCCESS); -} - - -/* - * ================================================================= - * KINSOL optional output functions - * ================================================================= - */ - -/* - * ----------------------------------------------------------------- - * Function : KINGetWorkSpace - * ----------------------------------------------------------------- - */ - -int KINGetWorkSpace(void *kinmem, long int *lenrw, long int *leniw) -{ - KINMem kin_mem; - - if (kinmem == NULL) { - KINProcessError(NULL, KIN_MEM_NULL, "KINSOL", "KINGetWorkSpace", MSG_NO_MEM); - return(KIN_MEM_NULL); - } - - kin_mem = (KINMem) kinmem; - - *lenrw = kin_mem->kin_lrw; - *leniw = kin_mem->kin_liw; - - return(KIN_SUCCESS); -} - -/* - * ----------------------------------------------------------------- - * Function : KINGetNumNonlinSolvIters - * ----------------------------------------------------------------- - */ - -int KINGetNumNonlinSolvIters(void *kinmem, long int *nniters) -{ - KINMem kin_mem; - - if (kinmem == NULL) { - KINProcessError(NULL, KIN_MEM_NULL, "KINSOL", "KINGetNumNonlinSolvIters", MSG_NO_MEM); - return(KIN_MEM_NULL); - } - - kin_mem = (KINMem) kinmem; - *nniters = kin_mem->kin_nni; - - return(KIN_SUCCESS); -} - -/* - * ----------------------------------------------------------------- - * Function : KINGetNumFuncEvals - * ----------------------------------------------------------------- - */ - -int KINGetNumFuncEvals(void *kinmem, long int *nfevals) -{ - KINMem kin_mem; - - if (kinmem == NULL) { - KINProcessError(NULL, KIN_MEM_NULL, "KINSOL", "KINGetNumFuncEvals", MSG_NO_MEM); - return(KIN_MEM_NULL); - } - - kin_mem = (KINMem) kinmem; - *nfevals = kin_mem->kin_nfe; - - return(KIN_SUCCESS); -} - -/* - * ----------------------------------------------------------------- - * Function : KINGetNumBetaCondFails - * ----------------------------------------------------------------- - */ - -int KINGetNumBetaCondFails(void *kinmem, long int *nbcfails) -{ - KINMem kin_mem; - - if (kinmem == NULL) { - KINProcessError(NULL, KIN_MEM_NULL, "KINSOL", "KINGetNumBetaCondFails", MSG_NO_MEM); - return(KIN_MEM_NULL); - } - - kin_mem = (KINMem) kinmem; - *nbcfails = kin_mem->kin_nbcf; - - return(KIN_SUCCESS); -} - -/* - * ----------------------------------------------------------------- - * Function : KINGetNumBacktrackOps - * ----------------------------------------------------------------- - */ - -int KINGetNumBacktrackOps(void *kinmem, long int *nbacktr) -{ - KINMem kin_mem; - - if (kinmem == NULL) { - KINProcessError(NULL, KIN_MEM_NULL, "KINSOL", "KINGetNumBacktrackOps", MSG_NO_MEM); - return(KIN_MEM_NULL); - } - - kin_mem = (KINMem) kinmem; - *nbacktr = kin_mem->kin_nbktrk; - - return(KIN_SUCCESS); -} - -/* - * ----------------------------------------------------------------- - * Function : KINGetFuncNorm - * ----------------------------------------------------------------- - */ - -int KINGetFuncNorm(void *kinmem, realtype *funcnorm) -{ - KINMem kin_mem; - - if (kinmem == NULL) { - KINProcessError(NULL, KIN_MEM_NULL, "KINSOL", "KINGetFuncNorm", MSG_NO_MEM); - return(KIN_MEM_NULL); - } - - kin_mem = (KINMem) kinmem; - *funcnorm = kin_mem->kin_fnorm; - - return(KIN_SUCCESS); -} - -/* - * ----------------------------------------------------------------- - * Function : KINGetStepLength - * ----------------------------------------------------------------- - */ - -int KINGetStepLength(void *kinmem, realtype *steplength) -{ - KINMem kin_mem; - - if (kinmem == NULL) { - KINProcessError(NULL, KIN_MEM_NULL, "KINSOL", "KINGetStepLength", MSG_NO_MEM); - return(KIN_MEM_NULL); - } - - kin_mem = (KINMem) kinmem; - *steplength = kin_mem->kin_stepl; - - return(KIN_SUCCESS); -} - -/* - * ----------------------------------------------------------------- - * Function : KINGetReturnFlagName - * ----------------------------------------------------------------- - */ - -char *KINGetReturnFlagName(long int flag) -{ - char *name; - - name = (char *)malloc(24*sizeof(char)); - - switch(flag) { - case KIN_SUCCESS: - sprintf(name, "KIN_SUCCESS"); - break; - case KIN_INITIAL_GUESS_OK: - sprintf(name, "KIN_INITIAL_GUESS_OK"); - break; - case KIN_STEP_LT_STPTOL: - sprintf(name, "KIN_STEP_LT_STPTOL"); - break; - case KIN_WARNING: - sprintf(name, "KIN_WARNING"); - break; - case KIN_MEM_NULL: - sprintf(name, "KIN_MEM_NULL"); - break; - case KIN_ILL_INPUT: - sprintf(name, "KIN_ILL_INPUT"); - break; - case KIN_NO_MALLOC: - sprintf(name, "KIN_NO_MALLOC"); - break; - case KIN_MEM_FAIL: - sprintf(name, "KIN_MEM_FAIL"); - break; - case KIN_LINESEARCH_NONCONV: - sprintf(name, "KIN_LINESEARCH_NONCONV"); - break; - case KIN_MAXITER_REACHED: - sprintf(name, "KIN_MAXITER_REACHED"); - break; - case KIN_MXNEWT_5X_EXCEEDED: - sprintf(name, "KIN_MXNEWT_5X_EXCEEDED"); - break; - case KIN_LINESEARCH_BCFAIL: - sprintf(name, "KIN_LINESEARCH_BCFAIL"); - break; - case KIN_LINSOLV_NO_RECOVERY: - sprintf(name, "KIN_LINSOLV_NO_RECOVERY"); - break; - case KIN_LINIT_FAIL: - sprintf(name, "KIN_LINIT_FAIL"); - break; - case KIN_LSETUP_FAIL: - sprintf(name, "KIN_LSETUP_FAIL"); - break; - case KIN_LSOLVE_FAIL: - sprintf(name, "KIN_LSOLVE_FAIL"); - break; - default: - sprintf(name, "NONE"); - } - - return(name); -} diff --git a/deps/AMICI/ThirdParty/sundials/src/kinsol/kinsol_ls.c b/deps/AMICI/ThirdParty/sundials/src/kinsol/kinsol_ls.c deleted file mode 100644 index 28128614b..000000000 --- a/deps/AMICI/ThirdParty/sundials/src/kinsol/kinsol_ls.c +++ /dev/null @@ -1,1365 +0,0 @@ -/*----------------------------------------------------------------- - * Programmer(s): Daniel R. Reynolds @ SMU - * David J. Gardner, Radu Serban and Aaron Collier @ LLNL - *----------------------------------------------------------------- - * SUNDIALS Copyright Start - * Copyright (c) 2002-2021, Lawrence Livermore National Security - * and Southern Methodist University. - * All rights reserved. - * - * See the top-level LICENSE and NOTICE files for details. - * - * SPDX-License-Identifier: BSD-3-Clause - * SUNDIALS Copyright End - *----------------------------------------------------------------- - * Implementation file for KINSOL's linear solver interface. - *-----------------------------------------------------------------*/ - -#include -#include -#include -#include - -#include "kinsol_impl.h" -#include "kinsol_ls_impl.h" - -#include -#include -#include -#include - -/* constants */ -#define MIN_INC_MULT RCONST(1000.0) -#define ZERO RCONST(0.0) -#define ONE RCONST(1.0) -#define TWO RCONST(2.0) - - -/*================================================================== - KINLS Exported functions -- Required - ==================================================================*/ - -/*--------------------------------------------------------------- - KINSetLinearSolver specifies the linear solver - ---------------------------------------------------------------*/ -int KINSetLinearSolver(void *kinmem, SUNLinearSolver LS, SUNMatrix A) -{ - KINMem kin_mem; - KINLsMem kinls_mem; - int retval, LSType; - booleantype iterative; /* is the solver iterative? */ - booleantype matrixbased; /* is a matrix structure used? */ - - /* Return immediately if either kinmem or LS inputs are NULL */ - if (kinmem == NULL) { - KINProcessError(NULL, KINLS_MEM_NULL, "KINLS", - "KINSetLinearSolver", MSG_LS_KINMEM_NULL); - return(KINLS_MEM_NULL); - } - if (LS == NULL) { - KINProcessError(NULL, KINLS_ILL_INPUT, "KINLS", - "KINSetLinearSolver", - "LS must be non-NULL"); - return(KINLS_ILL_INPUT); - } - kin_mem = (KINMem) kinmem; - - /* Test if solver is compatible with LS interface */ - if ( (LS->ops->gettype == NULL) || (LS->ops->solve == NULL) ) { - KINProcessError(kin_mem, KINLS_ILL_INPUT, "KINLS", - "KINSetLinearSolver", - "LS object is missing a required operation"); - return(KINLS_ILL_INPUT); - } - - /* Retrieve the LS type */ - LSType = SUNLinSolGetType(LS); - - /* Set flags based on LS type */ - iterative = (LSType != SUNLINEARSOLVER_DIRECT); - matrixbased = (LSType != SUNLINEARSOLVER_ITERATIVE); - - /* check for required vector operations for KINLS interface */ - if ( (kin_mem->kin_vtemp1->ops->nvconst == NULL) || - (kin_mem->kin_vtemp1->ops->nvdotprod == NULL) ) { - KINProcessError(kin_mem, KINLS_ILL_INPUT, "KINLS", - "KINSetLinearSolver", MSG_LS_BAD_NVECTOR); - return(KINLS_ILL_INPUT); - } - - /* Check for compatible LS type, matrix and "atimes" support */ - if (iterative) { - - if ((LS->ops->setscalingvectors == NULL) && - (kin_mem->kin_vtemp1->ops->nvgetlength == NULL)) { - KINProcessError(kin_mem, KINLS_ILL_INPUT, "KINLS", - "KINSetLinearSolver", MSG_LS_BAD_NVECTOR); - return(KINLS_ILL_INPUT); - } - - if (!matrixbased && (LS->ops->setatimes == NULL)) { - KINProcessError(kin_mem, KINLS_ILL_INPUT, "KINLS", "KINSetLinearSolver", - "Incompatible inputs: iterative LS must support ATimes routine"); - return(KINLS_ILL_INPUT); - } - - if (matrixbased && A == NULL) { - KINProcessError(kin_mem, KINLS_ILL_INPUT, "KINLS", "KINSetLinearSolver", - "Incompatible inputs: matrix-iterative LS requires non-NULL matrix"); - return(KINLS_ILL_INPUT); - } - - } else if (A == NULL) { - - KINProcessError(kin_mem, KINLS_ILL_INPUT, "KINLS", "KINSetLinearSolver", - "Incompatible inputs: direct LS requires non-NULL matrix"); - return(KINLS_ILL_INPUT); - } - - /* free any existing system solver attached to KIN */ - if (kin_mem->kin_lfree) kin_mem->kin_lfree(kin_mem); - - /* Determine if this is an iterative linear solver */ - kin_mem->kin_inexact_ls = iterative; - - /* Set four main system linear solver function fields in kin_mem */ - kin_mem->kin_linit = kinLsInitialize; - kin_mem->kin_lsetup = kinLsSetup; - kin_mem->kin_lsolve = kinLsSolve; - kin_mem->kin_lfree = kinLsFree; - - /* Get memory for KINLsMemRec */ - kinls_mem = NULL; - kinls_mem = (KINLsMem) malloc(sizeof(struct KINLsMemRec)); - if (kinls_mem == NULL) { - KINProcessError(kin_mem, KINLS_MEM_FAIL, "KINLS", - "KINSetLinearSolver", MSG_LS_MEM_FAIL); - return(KINLS_MEM_FAIL); - } - memset(kinls_mem, 0, sizeof(struct KINLsMemRec)); - - /* set SUNLinearSolver pointer */ - kinls_mem->LS = LS; - - /* Set defaults for Jacobian-related fields */ - if (A != NULL) { - kinls_mem->jacDQ = SUNTRUE; - kinls_mem->jac = kinLsDQJac; - kinls_mem->J_data = kin_mem; - } else { - kinls_mem->jacDQ = SUNFALSE; - kinls_mem->jac = NULL; - kinls_mem->J_data = NULL; - } - kinls_mem->jtimesDQ = SUNTRUE; - kinls_mem->jtimes = kinLsDQJtimes; - kinls_mem->jt_func = kin_mem->kin_func; - kinls_mem->jt_data = kin_mem; - - /* Set defaults for preconditioner-related fields */ - kinls_mem->pset = NULL; - kinls_mem->psolve = NULL; - kinls_mem->pfree = NULL; - kinls_mem->pdata = kin_mem->kin_user_data; - - /* Initialize counters */ - kinLsInitializeCounters(kinls_mem); - - /* Set default values for the rest of the LS parameters */ - kinls_mem->last_flag = KINLS_SUCCESS; - - /* If LS supports ATimes, attach KINLs routine */ - if (LS->ops->setatimes) { - retval = SUNLinSolSetATimes(LS, kin_mem, kinLsATimes); - if (retval != SUNLS_SUCCESS) { - KINProcessError(kin_mem, KINLS_SUNLS_FAIL, "KINLS", - "KINSetLinearSolver", - "Error in calling SUNLinSolSetATimes"); - free(kinls_mem); kinls_mem = NULL; - return(KINLS_SUNLS_FAIL); - } - } - - /* If LS supports preconditioning, initialize pset/psol to NULL */ - if (LS->ops->setpreconditioner) { - retval = SUNLinSolSetPreconditioner(LS, kin_mem, NULL, NULL); - if (retval != SUNLS_SUCCESS) { - KINProcessError(kin_mem, KINLS_SUNLS_FAIL, "KINLS", - "KINSetLinearSolver", - "Error in calling SUNLinSolSetPreconditioner"); - free(kinls_mem); kinls_mem = NULL; - return(KINLS_SUNLS_FAIL); - } - } - - /* initialize tolerance scaling factor */ - kinls_mem->tol_fac = -ONE; - - /* set SUNMatrix pointer (can be NULL) */ - kinls_mem->J = A; - - /* Attach linear solver memory to integrator memory */ - kin_mem->kin_lmem = kinls_mem; - - return(KINLS_SUCCESS); -} - - -/*================================================================== - Optional input/output routines - ==================================================================*/ - -/*------------------------------------------------------------------ - KINSetJacFn specifies the Jacobian function - ------------------------------------------------------------------*/ -int KINSetJacFn(void *kinmem, KINLsJacFn jac) -{ - KINMem kin_mem; - KINLsMem kinls_mem; - int retval; - - /* access KINLsMem structure */ - retval = kinLs_AccessLMem(kinmem, "KINSetJacFn", - &kin_mem, &kinls_mem); - if (retval != KIN_SUCCESS) return(retval); - - /* return with failure if jac cannot be used */ - if ((jac != NULL) && (kinls_mem->J == NULL)) { - KINProcessError(kin_mem, KINLS_ILL_INPUT, "KINLS", "KINSetJacFn", - "Jacobian routine cannot be supplied for NULL SUNMatrix"); - return(KINLS_ILL_INPUT); - } - - if (jac != NULL) { - kinls_mem->jacDQ = SUNFALSE; - kinls_mem->jac = jac; - kinls_mem->J_data = kin_mem->kin_user_data; - } else { - kinls_mem->jacDQ = SUNTRUE; - kinls_mem->jac = kinLsDQJac; - kinls_mem->J_data = kin_mem; - } - - return(KINLS_SUCCESS); -} - - -/*------------------------------------------------------------------ - KINSetPreconditioner sets the preconditioner setup and solve - functions - ------------------------------------------------------------------*/ -int KINSetPreconditioner(void *kinmem, - KINLsPrecSetupFn psetup, - KINLsPrecSolveFn psolve) -{ - KINMem kin_mem; - KINLsMem kinls_mem; - PSetupFn kinls_psetup; - PSolveFn kinls_psolve; - int retval; - - /* access KINLsMem structure */ - retval = kinLs_AccessLMem(kinmem, "KINSetPreconditioner", - &kin_mem, &kinls_mem); - if (retval != KIN_SUCCESS) return(retval); - - /* store function pointers for user-supplied routines in KINLS interface */ - kinls_mem->pset = psetup; - kinls_mem->psolve = psolve; - - /* issue error if LS object does not support user-supplied preconditioning */ - if (kinls_mem->LS->ops->setpreconditioner == NULL) { - KINProcessError(kin_mem, KINLS_ILL_INPUT, "KINLS", "KINSetPreconditioner", - "SUNLinearSolver object does not support user-supplied preconditioning"); - return(KINLS_ILL_INPUT); - } - - /* notify iterative linear solver to call KINLs interface routines */ - kinls_psetup = (psetup == NULL) ? NULL : kinLsPSetup; - kinls_psolve = (psolve == NULL) ? NULL : kinLsPSolve; - retval = SUNLinSolSetPreconditioner(kinls_mem->LS, kin_mem, - kinls_psetup, kinls_psolve); - if (retval != SUNLS_SUCCESS) { - KINProcessError(kin_mem, KINLS_SUNLS_FAIL, "KINLS", "KINSetPreconditioner", - "Error in calling SUNLinSolSetPreconditioner"); - return(KINLS_SUNLS_FAIL); - } - - return(KINLS_SUCCESS); -} - -/*------------------------------------------------------------------ - KINSetJacTimesVecFn sets the matrix-vector product function - ------------------------------------------------------------------*/ -int KINSetJacTimesVecFn(void *kinmem, KINLsJacTimesVecFn jtv) -{ - int retval; - KINMem kin_mem; - KINLsMem kinls_mem; - - /* access KINLsMem structure */ - retval = kinLs_AccessLMem(kinmem, "KINSetJacTimesVecFn", - &kin_mem, &kinls_mem); - if (retval != KIN_SUCCESS) return(retval); - - /* issue error if LS object does not support user-supplied ATimes */ - if (kinls_mem->LS->ops->setatimes == NULL) { - KINProcessError(kin_mem, KINLS_ILL_INPUT, "KINLS", "KINSetJacTimesVecFn", - "SUNLinearSolver object does not support user-supplied ATimes routine"); - return(KINLS_ILL_INPUT); - } - - /* store function pointers for user-supplied routine in KINLs - interface (NULL jtimes implies use of DQ default) */ - if (jtv != NULL) { - kinls_mem->jtimesDQ = SUNFALSE; - kinls_mem->jtimes = jtv; - kinls_mem->jt_data = kin_mem->kin_user_data; - } else { - kinls_mem->jtimesDQ = SUNTRUE; - kinls_mem->jtimes = kinLsDQJtimes; - kinls_mem->jt_func = kin_mem->kin_func; - kinls_mem->jt_data = kin_mem; - } - - return(KINLS_SUCCESS); -} - - -/* KINSetJacTimesVecSysFn specifies an alternative user-supplied system function - to use in the internal finite difference Jacobian-vector product */ -int KINSetJacTimesVecSysFn(void *kinmem, KINSysFn jtimesSysFn) -{ - int retval; - KINMem kin_mem = NULL; - KINLsMem kinls_mem = NULL; - - /* access KINLsMem structure */ - retval = kinLs_AccessLMem(kin_mem, "KINSetJacTimesVecSysFn", - &kin_mem, &kinls_mem); - if (retval != KIN_SUCCESS) return(retval); - - /* check if using internal finite difference approximation */ - if (!(kinls_mem->jtimesDQ)) { - KINProcessError(kin_mem, KINLS_ILL_INPUT, "KINLS", "KINSetJacTimesVecSysFn", - "Internal finite-difference Jacobian-vector product is disabled."); - return(KINLS_ILL_INPUT); - } - - /* store function pointers for system function (NULL implies use kin_func) */ - if (jtimesSysFn != NULL) - kinls_mem->jt_func = jtimesSysFn; - else - kinls_mem->jt_func = kin_mem->kin_func; - - return(KINLS_SUCCESS); -} - - -/*------------------------------------------------------------------ - KINGetLinWorkSpace returns the integer and real workspace size - ------------------------------------------------------------------*/ -int KINGetLinWorkSpace(void *kinmem, long int *lenrwLS, long int *leniwLS) -{ - KINMem kin_mem; - KINLsMem kinls_mem; - sunindextype lrw1, liw1; - long int lrw, liw; - int retval; - - /* access KINLsMem structure */ - retval = kinLs_AccessLMem(kinmem, "KINGetLinWorkSpace", - &kin_mem, &kinls_mem); - if (retval != KIN_SUCCESS) return(retval); - - /* start with fixed sizes plus vector/matrix pointers */ - *lenrwLS = 1; - *leniwLS = 21; - - /* add N_Vector sizes */ - if (kin_mem->kin_vtemp1->ops->nvspace) { - N_VSpace(kin_mem->kin_vtemp1, &lrw1, &liw1); - *lenrwLS += lrw1; - *leniwLS += liw1; - } - - /* add LS sizes */ - if (kinls_mem->LS->ops->space) { - retval = SUNLinSolSpace(kinls_mem->LS, &lrw, &liw); - if (retval == 0) { - *lenrwLS += lrw; - *leniwLS += liw; - } - } - - return(KINLS_SUCCESS); -} - -/*------------------------------------------------------------------ - KINGetNumJacEvals returns the number of Jacobian evaluations - ------------------------------------------------------------------*/ -int KINGetNumJacEvals(void *kinmem, long int *njevals) -{ - KINMem kin_mem; - KINLsMem kinls_mem; - int retval; - - /* access KINLsMem structure; set output value and return */ - retval = kinLs_AccessLMem(kinmem, "KINGetNumJacEvals", - &kin_mem, &kinls_mem); - if (retval != KIN_SUCCESS) return(retval); - *njevals = kinls_mem->nje; - return(KINLS_SUCCESS); -} - -/*------------------------------------------------------------------ - KINGetNumPrecEvals returns the total number of preconditioner - evaluations - ------------------------------------------------------------------*/ -int KINGetNumPrecEvals(void *kinmem, long int *npevals) -{ - KINMem kin_mem; - KINLsMem kinls_mem; - int retval; - - /* access KINLsMem structure; set output value and return */ - retval = kinLs_AccessLMem(kinmem, "KINGetNumPrecEvals", - &kin_mem, &kinls_mem); - if (retval != KIN_SUCCESS) return(retval); - *npevals = kinls_mem->npe; - return(KINLS_SUCCESS); -} - -/*------------------------------------------------------------------ - KINGetNumPrecSolves returns the total number of times the - preconditioner was applied - ------------------------------------------------------------------*/ -int KINGetNumPrecSolves(void *kinmem, long int *npsolves) -{ - KINMem kin_mem; - KINLsMem kinls_mem; - int retval; - - /* access KINLsMem structure; set output value and return */ - retval = kinLs_AccessLMem(kinmem, "KINGetNumPrecSolves", - &kin_mem, &kinls_mem); - if (retval != KIN_SUCCESS) return(retval); - *npsolves = kinls_mem->nps; - return(KINLS_SUCCESS); -} - -/*------------------------------------------------------------------ - KINGetNumLinIters returns the total number of linear - iterations - ------------------------------------------------------------------*/ -int KINGetNumLinIters(void *kinmem, long int *nliters) -{ - KINMem kin_mem; - KINLsMem kinls_mem; - int retval; - - /* access KINLsMem structure; set output value and return */ - retval = kinLs_AccessLMem(kinmem, "KINGetNumLinIters", - &kin_mem, &kinls_mem); - if (retval != KIN_SUCCESS) return(retval); - *nliters = kinls_mem->nli; - return(KINLS_SUCCESS); -} - -/*------------------------------------------------------------------ - KINGetNumLinConvFails returns the total numbe of convergence - failures - ------------------------------------------------------------------*/ -int KINGetNumLinConvFails(void *kinmem, long int *nlcfails) -{ - KINMem kin_mem; - KINLsMem kinls_mem; - int retval; - - /* access KINLsMem structure; set output value and return */ - retval = kinLs_AccessLMem(kinmem, "KINGetNumLinConvFails", - &kin_mem, &kinls_mem); - if (retval != KIN_SUCCESS) return(retval); - *nlcfails = kinls_mem->ncfl; - return(KINLS_SUCCESS); -} - -/*------------------------------------------------------------------ - KINGetNumJtimesEvals returns the number of times the matrix - vector product was computed - ------------------------------------------------------------------*/ -int KINGetNumJtimesEvals(void *kinmem, long int *njvevals) -{ - KINMem kin_mem; - KINLsMem kinls_mem; - int retval; - - /* access KINLsMem structure; set output value and return */ - retval = kinLs_AccessLMem(kinmem, "KINGetNumJtimesEvals", - &kin_mem, &kinls_mem); - if (retval != KIN_SUCCESS) return(retval); - *njvevals = kinls_mem->njtimes; - return(KINLS_SUCCESS); -} - -/*------------------------------------------------------------------ - KINGetNumLinFuncEvals returns the number of calls to the user's - F routine by the linear solver module - ------------------------------------------------------------------*/ -int KINGetNumLinFuncEvals(void *kinmem, long int *nfevals) -{ - KINMem kin_mem; - KINLsMem kinls_mem; - int retval; - - /* access KINLsMem structure; set output value and return */ - retval = kinLs_AccessLMem(kinmem, "KINGetNumLinFuncEvals", - &kin_mem, &kinls_mem); - if (retval != KIN_SUCCESS) return(retval); - *nfevals = kinls_mem->nfeDQ; - return(KINLS_SUCCESS); -} - -/*------------------------------------------------------------------ - KINGetLastLinFlag returns the last flag set in the KINLS - function - ------------------------------------------------------------------*/ -int KINGetLastLinFlag(void *kinmem, long int *flag) -{ - KINMem kin_mem; - KINLsMem kinls_mem; - int retval; - - /* access KINLsMem structure; set output value and return */ - retval = kinLs_AccessLMem(kinmem, "KINGetLastLinFlag", - &kin_mem, &kinls_mem); - if (retval != KIN_SUCCESS) return(retval); - *flag = kinls_mem->last_flag; - return(KINLS_SUCCESS); -} - -/*------------------------------------------------------------------ - KINGetLinReturnFlagName - ------------------------------------------------------------------*/ -char *KINGetLinReturnFlagName(long int flag) -{ - char *name; - - name = (char *)malloc(30*sizeof(char)); - - switch(flag) { - case KINLS_SUCCESS: - sprintf(name, "KINLS_SUCCESS"); - break; - case KINLS_MEM_NULL: - sprintf(name, "KINLS_MEM_NULL"); - break; - case KINLS_LMEM_NULL: - sprintf(name, "KINLS_LMEM_NULL"); - break; - case KINLS_ILL_INPUT: - sprintf(name, "KINLS_ILL_INPUT"); - break; - case KINLS_MEM_FAIL: - sprintf(name, "KINLS_MEM_FAIL"); - break; - case KINLS_PMEM_NULL: - sprintf(name, "KINLS_PMEM_NULL"); - break; - case KINLS_JACFUNC_ERR: - sprintf(name,"KINLS_JACFUNC_ERR"); - break; - case KINLS_SUNMAT_FAIL: - sprintf(name,"KINLS_SUNMAT_FAIL"); - break; - case KINLS_SUNLS_FAIL: - sprintf(name,"KINLS_SUNLS_FAIL"); - break; - default: - sprintf(name, "NONE"); - } - - return(name); -} - - -/*================================================================== - KINLS Private functions - ==================================================================*/ - -/*------------------------------------------------------------------ - kinLsATimes - - This routine coordinates the generation of the matrix-vector - product z = J*v by calling either kinLsDQJtimes, which uses - a difference quotient approximation for J*v, or by calling the - user-supplied routine KINLsJacTimesVecFn if it is non-null. - ------------------------------------------------------------------*/ -int kinLsATimes(void *kinmem, N_Vector v, N_Vector z) -{ - KINMem kin_mem; - KINLsMem kinls_mem; - int retval; - - /* access KINLsMem structure */ - retval = kinLs_AccessLMem(kinmem, "kinLsATimes", - &kin_mem, &kinls_mem); - if (retval != KIN_SUCCESS) return(retval); - - /* call Jacobian-times-vector product routine - (either user-supplied or internal DQ) */ - retval = kinls_mem->jtimes(v, z, kin_mem->kin_uu, - &(kinls_mem->new_uu), - kinls_mem->jt_data); - kinls_mem->njtimes++; - return(retval); -} - - -/*--------------------------------------------------------------- - kinLsPSetup: - - This routine interfaces between the generic iterative linear - solvers and the user's psetup routine. It passes to psetup all - required state information from kin_mem. Its return value - is the same as that returned by psetup. Note that the generic - iterative linear solvers guarantee that kinLsPSetup will only - be called in the case that the user's psetup routine is non-NULL. - ---------------------------------------------------------------*/ -int kinLsPSetup(void *kinmem) -{ - KINMem kin_mem; - KINLsMem kinls_mem; - int retval; - - /* access KINLsMem structure */ - retval = kinLs_AccessLMem(kinmem, "kinLsPSetup", - &kin_mem, &kinls_mem); - if (retval != KIN_SUCCESS) return(retval); - - /* Call user pset routine to update preconditioner */ - retval = kinls_mem->pset(kin_mem->kin_uu, kin_mem->kin_uscale, - kin_mem->kin_fval, kin_mem->kin_fscale, - kinls_mem->pdata); - kinls_mem->npe++; - return(retval); -} - - -/*------------------------------------------------------------------ - kinLsPSolve - - This routine interfaces between the generic iterative linear - solvers and the user's psolve routine. It passes to psolve all - required state information from kinsol_mem. Its return value is - the same as that returned by psolve. Note that the generic - SUNLinSol solver guarantees that kinLsPSolve will not be called - in the case in which preconditioning is not done. This is the only - case in which the user's psolve routine is allowed to be NULL. - ------------------------------------------------------------------*/ -int kinLsPSolve(void *kinmem, N_Vector r, N_Vector z, realtype tol, int lr) -{ - KINMem kin_mem; - KINLsMem kinls_mem; - int retval; - - /* access KINLsMem structure */ - retval = kinLs_AccessLMem(kinmem, "kinLsPSolve", - &kin_mem, &kinls_mem); - if (retval != KIN_SUCCESS) return(retval); - - /* copy the rhs into z before the psolve call */ - /* Note: z returns with the solution */ - N_VScale(ONE, r, z); - - /* note: user-supplied preconditioning with KINSOL does not - support either the 'tol' or 'lr' inputs */ - retval = kinls_mem->psolve(kin_mem->kin_uu, kin_mem->kin_uscale, - kin_mem->kin_fval, kin_mem->kin_fscale, - z, kinls_mem->pdata); - kinls_mem->nps++; - return(retval); -} - - -/*------------------------------------------------------------------ - kinLsDQJac - - This routine is a wrapper for the Dense and Band implementations - of the difference quotient Jacobian approximation routines. - ------------------------------------------------------------------*/ -int kinLsDQJac(N_Vector u, N_Vector fu, SUNMatrix Jac, - void *kinmem, N_Vector tmp1, N_Vector tmp2) -{ - KINMem kin_mem; - int retval; - - /* access KINMem structure */ - if (kinmem == NULL) { - KINProcessError(NULL, KINLS_MEM_NULL, "KINLS", - "kinLsDQJac", MSG_LS_KINMEM_NULL); - return(KINLS_MEM_NULL); - } - kin_mem = (KINMem) kinmem; - - /* verify that Jac is non-NULL */ - if (Jac == NULL) { - KINProcessError(kin_mem, KINLS_LMEM_NULL, "KINLS", - "kinLsDQJac", MSG_LS_LMEM_NULL); - return(KINLS_LMEM_NULL); - } - - /* Call the matrix-structure-specific DQ approximation routine */ - if (SUNMatGetID(Jac) == SUNMATRIX_DENSE) { - retval = kinLsDenseDQJac(u, fu, Jac, kin_mem, tmp1, tmp2); - } else if (SUNMatGetID(Jac) == SUNMATRIX_BAND) { - retval = kinLsBandDQJac(u, fu, Jac, kin_mem, tmp1, tmp2); - } else { - KINProcessError(kin_mem, KIN_ILL_INPUT, "KINLS", "kinLsDQJac", - "unrecognized matrix type for kinLsDQJac"); - retval = KIN_ILL_INPUT; - } - return(retval); -} - - -/*------------------------------------------------------------------ - kinLsDenseDQJac - - This routine generates a dense difference quotient approximation - to the Jacobian of F(u). It assumes a dense SUNMatrix input - stored column-wise, and that elements within each column are - contiguous. The address of the jth column of J is obtained via - the function SUNDenseMatrix_Column() and this pointer is - associated with an N_Vector using the N_VGetArrayPointer and - N_VSetArrayPointer functions. Finally, the actual computation of - the jth column of the Jacobian is done with a call to N_VLinearSum. - - The increment used in the finite-difference approximation - J_ij = ( F_i(u+sigma_j * e_j) - F_i(u) ) / sigma_j - is - sigma_j = max{|u_j|, |1/uscale_j|} * sqrt(uround) - - Note: uscale_j = 1/typ(u_j) - - NOTE: Any type of failure of the system function here leads to an - unrecoverable failure of the Jacobian function and thus of - the linear solver setup function, stopping KINSOL. - ------------------------------------------------------------------*/ -int kinLsDenseDQJac(N_Vector u, N_Vector fu, SUNMatrix Jac, - KINMem kin_mem, N_Vector tmp1, N_Vector tmp2) -{ - realtype inc, inc_inv, ujsaved, ujscale, sign; - realtype *tmp2_data, *u_data, *uscale_data; - N_Vector ftemp, jthCol; - sunindextype j, N; - KINLsMem kinls_mem; - int retval = 0; - - /* access LsMem interface structure */ - kinls_mem = (KINLsMem) kin_mem->kin_lmem; - - /* access matrix dimension */ - N = SUNDenseMatrix_Columns(Jac); - - /* Save pointer to the array in tmp2 */ - tmp2_data = N_VGetArrayPointer(tmp2); - - /* Rename work vectors for readibility */ - ftemp = tmp1; - jthCol = tmp2; - - /* Obtain pointers to the data for u and uscale */ - u_data = N_VGetArrayPointer(u); - uscale_data = N_VGetArrayPointer(kin_mem->kin_uscale); - - /* This is the only for loop for 0..N-1 in KINSOL */ - - for (j = 0; j < N; j++) { - - /* Generate the jth col of J(u) */ - - /* Set data address of jthCol, and save u_j values and scaling */ - N_VSetArrayPointer(SUNDenseMatrix_Column(Jac,j), jthCol); - ujsaved = u_data[j]; - ujscale = ONE/uscale_data[j]; - - /* Compute increment */ - sign = (ujsaved >= ZERO) ? ONE : -ONE; - inc = kin_mem->kin_sqrt_relfunc*SUNMAX(SUNRabs(ujsaved), ujscale)*sign; - - /* Increment u_j, call F(u), and return if error occurs */ - u_data[j] += inc; - - retval = kin_mem->kin_func(u, ftemp, kin_mem->kin_user_data); - kinls_mem->nfeDQ++; - if (retval != 0) break; - - /* reset u_j */ - u_data[j] = ujsaved; - - /* Construct difference quotient in jthCol */ - inc_inv = ONE/inc; - N_VLinearSum(inc_inv, ftemp, -inc_inv, fu, jthCol); - } - - /* Restore original array pointer in tmp2 */ - N_VSetArrayPointer(tmp2_data, tmp2); - - return(retval); -} - - -/*------------------------------------------------------------------ - kinLsBandDQJac - - This routine generates a banded difference quotient approximation - to the Jacobian of F(u). It assumes a SUNBandMatrix input stored - column-wise, and that elements within each column are contiguous. - This makes it possible to get the address of a column of J via the - function SUNBandMatrix_Column() and to write a simple for loop to - set each of the elements of a column in succession. - - NOTE: Any type of failure of the system function her leads to an - unrecoverable failure of the Jacobian function and thus of - the linear solver setup function, stopping KINSOL. - ------------------------------------------------------------------*/ -int kinLsBandDQJac(N_Vector u, N_Vector fu, SUNMatrix Jac, - KINMem kin_mem, N_Vector tmp1, N_Vector tmp2) -{ - realtype inc, inc_inv; - N_Vector futemp, utemp; - sunindextype group, i, j, width, ngroups, i1, i2; - sunindextype N, mupper, mlower; - realtype *col_j, *fu_data, *futemp_data, *u_data, *utemp_data, *uscale_data; - KINLsMem kinls_mem; - int retval = 0; - - /* access LsMem interface structure */ - kinls_mem = (KINLsMem) kin_mem->kin_lmem; - - /* access matrix dimensions */ - N = SUNBandMatrix_Columns(Jac); - mupper = SUNBandMatrix_UpperBandwidth(Jac); - mlower = SUNBandMatrix_LowerBandwidth(Jac); - - /* Rename work vectors for use as temporary values of u and fu */ - futemp = tmp1; - utemp = tmp2; - - /* Obtain pointers to the data for ewt, fy, futemp, y, ytemp */ - fu_data = N_VGetArrayPointer(fu); - futemp_data = N_VGetArrayPointer(futemp); - u_data = N_VGetArrayPointer(u); - uscale_data = N_VGetArrayPointer(kin_mem->kin_uscale); - utemp_data = N_VGetArrayPointer(utemp); - - /* Load utemp with u */ - N_VScale(ONE, u, utemp); - - /* Set bandwidth and number of column groups for band differencing */ - width = mlower + mupper + 1; - ngroups = SUNMIN(width, N); - - for (group=1; group <= ngroups; group++) { - - /* Increment all utemp components in group */ - for(j=group-1; j < N; j+=width) { - inc = kin_mem->kin_sqrt_relfunc*SUNMAX(SUNRabs(u_data[j]), - ONE/SUNRabs(uscale_data[j])); - utemp_data[j] += inc; - } - - /* Evaluate f with incremented u */ - retval = kin_mem->kin_func(utemp, futemp, kin_mem->kin_user_data); - if (retval != 0) return(retval); - - /* Restore utemp components, then form and load difference quotients */ - for (j=group-1; j < N; j+=width) { - utemp_data[j] = u_data[j]; - col_j = SUNBandMatrix_Column(Jac, j); - inc = kin_mem->kin_sqrt_relfunc*SUNMAX(SUNRabs(u_data[j]), - ONE/SUNRabs(uscale_data[j])); - inc_inv = ONE/inc; - i1 = SUNMAX(0, j-mupper); - i2 = SUNMIN(j+mlower, N-1); - for (i=i1; i <= i2; i++) - SM_COLUMN_ELEMENT_B(col_j,i,j) = inc_inv * (futemp_data[i] - fu_data[i]); - } - } - - /* Increment counter nfeDQ */ - kinls_mem->nfeDQ += ngroups; - - return(0); -} - - -/*------------------------------------------------------------------ - kinLsDQJtimes - - This routine generates the matrix-vector product z = J*v using a - difference quotient approximation. The approximation is - J*v = [func(uu + sigma*v) - func(uu)]/sigma. Here sigma is based - on the dot products (uscale*uu, uscale*v) and - (uscale*v, uscale*v), the L1Norm(uscale*v), and on sqrt_relfunc - (the square root of the relative error in the function). Note - that v in the argument list has already been both preconditioned - and unscaled. - - NOTE: Unlike the DQ Jacobian functions for direct linear solvers - (which are called from within the lsetup function), this - function is called from within the lsolve function and thus - a recovery may still be possible even if the system function - fails (recoverably). - ------------------------------------------------------------------*/ -int kinLsDQJtimes(N_Vector v, N_Vector Jv, N_Vector u, - booleantype *new_u, void *kinmem) -{ - realtype sigma, sigma_inv, sutsv, sq1norm, sign, vtv; - KINMem kin_mem; - KINLsMem kinls_mem; - int retval; - - /* access KINLsMem structure */ - retval = kinLs_AccessLMem(kinmem, "kinLsDQJtimes", - &kin_mem, &kinls_mem); - if (retval != KIN_SUCCESS) return(retval); - - /* ensure that NVector supplies requisite routines */ - if ( (v->ops->nvprod == NULL) || (v->ops->nvdotprod == NULL) || - (v->ops->nvl1norm == NULL) || (v->ops->nvlinearsum == NULL) ){ - KINProcessError(kin_mem, KINLS_ILL_INPUT, "KINLS", - "kinLsDQJtimes", MSG_LS_BAD_NVECTOR); - return(KINLS_ILL_INPUT); - } - - /* scale the vector v and put Du*v into vtemp1 */ - N_VProd(v, kin_mem->kin_uscale, kin_mem->kin_vtemp1); - - /* scale u and put into Jv (used as a temporary storage) */ - N_VProd(u, kin_mem->kin_uscale, Jv); - - /* compute dot product (Du*u).(Du*v) */ - sutsv = N_VDotProd(Jv, kin_mem->kin_vtemp1); - - /* compute dot product (Du*v).(Du*v) */ - vtv = N_VDotProd(kin_mem->kin_vtemp1, kin_mem->kin_vtemp1); - - /* compute differencing factor -- this is from p. 469, Brown and Saad paper */ - sq1norm = N_VL1Norm(kin_mem->kin_vtemp1); - sign = (sutsv >= ZERO) ? ONE : -ONE ; - sigma = sign*(kin_mem->kin_sqrt_relfunc)*SUNMAX(SUNRabs(sutsv),sq1norm)/vtv; - sigma_inv = ONE/sigma; - - /* compute the u-prime at which to evaluate the function func */ - N_VLinearSum(ONE, u, sigma, v, kin_mem->kin_vtemp1); - - /* call the system function to calculate func(u+sigma*v) */ - retval = kinls_mem->jt_func(kin_mem->kin_vtemp1, kin_mem->kin_vtemp2, - kin_mem->kin_user_data); - kinls_mem->nfeDQ++; - if (retval != 0) return(retval); - - /* finish the computation of the difference quotient */ - N_VLinearSum(sigma_inv, kin_mem->kin_vtemp2, -sigma_inv, kin_mem->kin_fval, Jv); - - return(0); -} - - -/*------------------------------------------------------------------ - kinLsInitialize performs remaining initializations specific - to the iterative linear solver interface (and solver itself) - ------------------------------------------------------------------*/ -int kinLsInitialize(KINMem kin_mem) -{ - KINLsMem kinls_mem; - int retval; - - /* Access KINLsMem structure */ - if (kin_mem->kin_lmem == NULL) { - KINProcessError(kin_mem, KINLS_LMEM_NULL, "KINLS", - "kinLsInitialize", MSG_LS_LMEM_NULL); - return(KINLS_LMEM_NULL); - } - kinls_mem = (KINLsMem) kin_mem->kin_lmem; - - /* Test for valid combinations of matrix & Jacobian routines: */ - if (kinls_mem->J == NULL) { - - /* If SUNMatrix A is NULL: ensure 'jac' function pointer is NULL */ - kinls_mem->jacDQ = SUNFALSE; - kinls_mem->jac = NULL; - kinls_mem->J_data = NULL; - - } else if (kinls_mem->jacDQ) { - - /* If J is non-NULL, and 'jac' is not user-supplied: - - if A is dense or band, ensure that our DQ approx. is used - - otherwise => error */ - retval = 0; - if (kinls_mem->J->ops->getid) { - - if ( (SUNMatGetID(kinls_mem->J) == SUNMATRIX_DENSE) || - (SUNMatGetID(kinls_mem->J) == SUNMATRIX_BAND) ) { - kinls_mem->jac = kinLsDQJac; - kinls_mem->J_data = kin_mem; - } else { - retval++; - } - - } else { - retval++; - } - if (retval) { - KINProcessError(kin_mem, KINLS_ILL_INPUT, "KINLS", "kinLsInitialize", - "No Jacobian constructor available for SUNMatrix type"); - kinls_mem->last_flag = KINLS_ILL_INPUT; - return(KINLS_ILL_INPUT); - } - - /* check for required vector operations for kinLsDQJac routine */ - if ( (kin_mem->kin_vtemp1->ops->nvlinearsum == NULL) || - (kin_mem->kin_vtemp1->ops->nvscale == NULL) || - (kin_mem->kin_vtemp1->ops->nvgetarraypointer == NULL) || - (kin_mem->kin_vtemp1->ops->nvsetarraypointer == NULL) ) { - KINProcessError(kin_mem, KINLS_ILL_INPUT, "KINLS", - "kinLsInitialize", MSG_LS_BAD_NVECTOR); - return(KINLS_ILL_INPUT); - } - - } else { - - /* If J is non-NULL, and 'jac' is user-supplied, - reset J_data pointer (just in case) */ - kinls_mem->J_data = kin_mem->kin_user_data; - } - - /* Prohibit Picard iteration with DQ Jacobian approximation or difference-quotient J*v */ - if ( (kin_mem->kin_globalstrategy == KIN_PICARD) && - kinls_mem->jacDQ && kinls_mem->jtimesDQ ) { - KINProcessError(kin_mem, KINLS_ILL_INPUT, "KINLS", - "kinLsInitialize", MSG_NOL_FAIL); - return(KINLS_ILL_INPUT); - } - - - /** error-checking is complete, begin initializtions **/ - - /* Initialize counters */ - kinLsInitializeCounters(kinls_mem); - - /* Set Jacobian-related fields, based on jtimesDQ */ - if (kinls_mem->jtimesDQ) { - kinls_mem->jtimes = kinLsDQJtimes; - kinls_mem->jt_data = kin_mem; - } else { - kinls_mem->jt_data = kin_mem->kin_user_data; - } - - /* if J is NULL and: NOT preconditioning or do NOT need to setup the - preconditioner, then set the lsetup function to NULL */ - if (kinls_mem->J == NULL) - if ((kinls_mem->psolve == NULL) || (kinls_mem->pset == NULL)) - kin_mem->kin_lsetup = NULL; - - /* Set scaling vectors assuming RIGHT preconditioning */ - /* NOTE: retval is non-zero only if LS == NULL */ - if (kinls_mem->LS->ops->setscalingvectors) { - retval = SUNLinSolSetScalingVectors(kinls_mem->LS, - kin_mem->kin_fscale, - kin_mem->kin_fscale); - if (retval != SUNLS_SUCCESS) { - KINProcessError(kin_mem, KINLS_SUNLS_FAIL, "KINLS", "kinLsInitialize", - "Error in calling SUNLinSolSetScalingVectors"); - return(KINLS_SUNLS_FAIL); - } - } - - /* If the linear solver is iterative or matrix-iterative, and if left/right - scaling are not supported, we must update linear solver tolerances in an - attempt to account for the fscale vector. We make the following assumptions: - 1. fscale_i = fs_mean, for i=0,...,n-1 (i.e. the weights are homogeneous) - 2. the linear solver uses a basic 2-norm to measure convergence - Hence (using the notation from sunlinsol_spgmr.h, with S = diag(fscale)), - || bbar - Abar xbar ||_2 < tol - <=> || S b - S A x ||_2 < tol - <=> || S (b - A x) ||_2 < tol - <=> \sum_{i=0}^{n-1} (fscale_i (b - A x)_i)^2 < tol^2 - <=> fs_mean^2 \sum_{i=0}^{n-1} (b - A x_i)^2 < tol^2 - <=> \sum_{i=0}^{n-1} (b - A x_i)^2 < tol^2 / fs_mean^2 - <=> || b - A x ||_2 < tol / fs_mean - <=> || b - A x ||_2 < tol * tol_fac - So we compute tol_fac = sqrt(N) / ||fscale||_L2 for scaling desired tolerances */ - if (kinls_mem->iterative && kinls_mem->LS->ops->setscalingvectors == NULL) { - N_VConst(ONE, kin_mem->kin_vtemp1); - kinls_mem->tol_fac = SUNRsqrt(N_VGetLength(kin_mem->kin_vtemp1)) - / N_VWL2Norm(kin_mem->kin_fscale, kin_mem->kin_vtemp1); - } else { - kinls_mem->tol_fac = ONE; - } - - /* Call LS initialize routine, and return result */ - kinls_mem->last_flag = SUNLinSolInitialize(kinls_mem->LS); - return(kinls_mem->last_flag); -} - - -/*------------------------------------------------------------------ - kinLsSetup call the LS setup routine - ------------------------------------------------------------------*/ -int kinLsSetup(KINMem kin_mem) -{ - KINLsMem kinls_mem; - int retval; - - /* Access KINLsMem structure */ - if (kin_mem->kin_lmem == NULL) { - KINProcessError(kin_mem, KINLS_LMEM_NULL, "KINLS", - "kinLsSetup", MSG_LS_LMEM_NULL); - return(KINLS_LMEM_NULL); - } - kinls_mem = (KINLsMem) kin_mem->kin_lmem; - - /* recompute if J if it is non-NULL */ - if (kinls_mem->J) { - - /* Increment nje counter. */ - kinls_mem->nje++; - - /* Clear the linear system matrix if necessary */ - if (SUNLinSolGetType(kinls_mem->LS) == SUNLINEARSOLVER_DIRECT) { - retval = SUNMatZero(kinls_mem->J); - if (retval != 0) { - KINProcessError(kin_mem, KINLS_SUNMAT_FAIL, "KINLS", - "kinLsSetup", MSG_LS_MATZERO_FAILED); - kinls_mem->last_flag = KINLS_SUNMAT_FAIL; - return(kinls_mem->last_flag); - } - } - - /* Call Jacobian routine */ - retval = kinls_mem->jac(kin_mem->kin_uu, kin_mem->kin_fval, - kinls_mem->J, kinls_mem->J_data, - kin_mem->kin_vtemp1, kin_mem->kin_vtemp2); - if (retval != 0) { - KINProcessError(kin_mem, KINLS_JACFUNC_ERR, "KINLS", - "kinLsSetup", MSG_LS_JACFUNC_FAILED); - kinls_mem->last_flag = KINLS_JACFUNC_ERR; - return(kinls_mem->last_flag); - } - - } - - /* Call LS setup routine -- the LS will call kinLsPSetup (if applicable) */ - kinls_mem->last_flag = SUNLinSolSetup(kinls_mem->LS, kinls_mem->J); - - /* save nni value from most recent lsetup call */ - kin_mem->kin_nnilset = kin_mem->kin_nni; - - return(kinls_mem->last_flag); -} - - -/*------------------------------------------------------------------ - kinLsSolve interfaces between KINSOL and the generic - SUNLinearSolver object - ------------------------------------------------------------------*/ -int kinLsSolve(KINMem kin_mem, N_Vector xx, N_Vector bb, - realtype *sJpnorm, realtype *sFdotJp) -{ - KINLsMem kinls_mem; - int nli_inc, retval; - realtype res_norm, tol; - - /* Access KINLsMem structure */ - if (kin_mem->kin_lmem == NULL) { - KINProcessError(kin_mem, KINLS_LMEM_NULL, "KINLS", - "kinLsSolve", MSG_LS_LMEM_NULL); - return(KINLS_LMEM_NULL); - } - kinls_mem = (KINLsMem) kin_mem->kin_lmem; - - /* Set linear solver tolerance as input value times scaling factor - (to account for possible lack of support for left/right scaling - vectors in SUNLinSol object) */ - tol = kin_mem->kin_eps * kinls_mem->tol_fac; - - /* Set initial guess x = 0 to LS */ - N_VConst(ZERO, xx); - - /* set flag required for user-supplied J*v routine */ - kinls_mem->new_uu = SUNTRUE; - - /* Call solver */ - retval = SUNLinSolSolve(kinls_mem->LS, kinls_mem->J, xx, bb, tol); - - /* Retrieve solver statistics */ - res_norm = ZERO; - if (kinls_mem->LS->ops->resnorm) - res_norm = SUNLinSolResNorm(kinls_mem->LS); - nli_inc = 0; - if (kinls_mem->LS->ops->numiters) - nli_inc = SUNLinSolNumIters(kinls_mem->LS); - - if (kinls_mem->iterative && kin_mem->kin_printfl > 2) - KINPrintInfo(kin_mem, PRNT_NLI, "KINLS", "kinLsSolve", - INFO_NLI, nli_inc); - - /* Increment counters nli and ncfl */ - kinls_mem->nli += nli_inc; - if (retval != SUNLS_SUCCESS) kinls_mem->ncfl++; - - /* Interpret solver return value */ - kinls_mem->last_flag = retval; - - if ( (retval != 0) && (retval != SUNLS_RES_REDUCED) ) { - - switch(retval) { - case SUNLS_ATIMES_FAIL_REC: - case SUNLS_PSOLVE_FAIL_REC: - return(1); - break; - case SUNLS_MEM_NULL: - case SUNLS_ILL_INPUT: - case SUNLS_MEM_FAIL: - case SUNLS_GS_FAIL: - case SUNLS_CONV_FAIL: - case SUNLS_QRFACT_FAIL: - case SUNLS_LUFACT_FAIL: - case SUNLS_QRSOL_FAIL: - break; - case SUNLS_PACKAGE_FAIL_REC: - KINProcessError(kin_mem, SUNLS_PACKAGE_FAIL_REC, "KINLS", - "kinLsSolve", - "Failure in SUNLinSol external package"); - break; - case SUNLS_PACKAGE_FAIL_UNREC: - KINProcessError(kin_mem, SUNLS_PACKAGE_FAIL_UNREC, "KINLS", - "kinLsSolve", - "Failure in SUNLinSol external package"); - break; - case SUNLS_ATIMES_FAIL_UNREC: - KINProcessError(kin_mem, SUNLS_ATIMES_FAIL_UNREC, "KINLS", - "kinLsSolve", MSG_LS_JTIMES_FAILED); - break; - case SUNLS_PSOLVE_FAIL_UNREC: - KINProcessError(kin_mem, SUNLS_PSOLVE_FAIL_UNREC, "KINLS", - "kinLsSolve", MSG_LS_PSOLVE_FAILED); - break; - } - return(retval); - } - - /* SUNLinSolSolve returned SUNLS_SUCCESS or SUNLS_RES_REDUCED */ - - /* Compute auxiliary values for use in the linesearch and in KINForcingTerm. - These will be subsequently corrected if the step is reduced by constraints - or the linesearch. */ - if (kin_mem->kin_globalstrategy != KIN_FP) { - - /* sJpnorm is the norm of the scaled product (scaled by fscale) of the - current Jacobian matrix J and the step vector p (= solution vector xx) */ - if (kin_mem->kin_inexact_ls && kin_mem->kin_etaflag == KIN_ETACHOICE1) { - retval = kinLsATimes(kin_mem, xx, bb); - if (retval > 0) { - kinls_mem->last_flag = SUNLS_ATIMES_FAIL_REC; - return(1); - } - else if (retval < 0) { - kinls_mem->last_flag = SUNLS_ATIMES_FAIL_UNREC; - return(-1); - } - *sJpnorm = N_VWL2Norm(bb, kin_mem->kin_fscale); - } - - /* sFdotJp is the dot product of the scaled f vector and the scaled - vector J*p, where the scaling uses fscale */ - if ((kin_mem->kin_inexact_ls && kin_mem->kin_etaflag == KIN_ETACHOICE1) || - kin_mem->kin_globalstrategy == KIN_LINESEARCH) { - N_VProd(bb, kin_mem->kin_fscale, bb); - N_VProd(bb, kin_mem->kin_fscale, bb); - *sFdotJp = N_VDotProd(kin_mem->kin_fval, bb); - } - } - - if (kin_mem->kin_inexact_ls && kin_mem->kin_printfl > 2) - KINPrintInfo(kin_mem, PRNT_EPS, "KINLS", "kinLsSolve", - INFO_EPS, res_norm, kin_mem->kin_eps); - - return(0); -} - - -/*------------------------------------------------------------------ - kinLsFree frees memory associated with the KINLs system - solver interface - ------------------------------------------------------------------*/ -int kinLsFree(KINMem kin_mem) -{ - KINLsMem kinls_mem; - - /* Return immediately if kin_mem or kin_mem->kin_lmem are NULL */ - if (kin_mem == NULL) return (KINLS_SUCCESS); - if (kin_mem->kin_lmem == NULL) return(KINLS_SUCCESS); - kinls_mem = (KINLsMem) kin_mem->kin_lmem; - - /* Nullify SUNMatrix pointer */ - kinls_mem->J = NULL; - - /* Free preconditioner memory (if applicable) */ - if (kinls_mem->pfree) kinls_mem->pfree(kin_mem); - - /* free KINLs interface structure */ - free(kin_mem->kin_lmem); - - return(KINLS_SUCCESS); -} - - -/*------------------------------------------------------------------ - kinLsInitializeCounters resets counters for the LS interface - ------------------------------------------------------------------*/ -int kinLsInitializeCounters(KINLsMem kinls_mem) -{ - kinls_mem->nje = 0; - kinls_mem->nfeDQ = 0; - kinls_mem->npe = 0; - kinls_mem->nli = 0; - kinls_mem->nps = 0; - kinls_mem->ncfl = 0; - kinls_mem->njtimes = 0; - return(0); -} - - -/*--------------------------------------------------------------- - kinLs_AccessLMem - - This routine unpacks the kin_mem and ls_mem structures from - void* pointer. If either is missing it returns KINLS_MEM_NULL - or KINLS_LMEM_NULL. - ---------------------------------------------------------------*/ -int kinLs_AccessLMem(void* kinmem, const char *fname, - KINMem *kin_mem, KINLsMem *kinls_mem) -{ - if (kinmem==NULL) { - KINProcessError(NULL, KINLS_MEM_NULL, "KINLS", - fname, MSG_LS_KINMEM_NULL); - return(KINLS_MEM_NULL); - } - *kin_mem = (KINMem) kinmem; - if ((*kin_mem)->kin_lmem==NULL) { - KINProcessError(*kin_mem, KINLS_LMEM_NULL, "KINLS", - fname, MSG_LS_LMEM_NULL); - return(KINLS_LMEM_NULL); - } - *kinls_mem = (KINLsMem) (*kin_mem)->kin_lmem; - return(KINLS_SUCCESS); -} - - -/*--------------------------------------------------------------- - EOF - ---------------------------------------------------------------*/ diff --git a/deps/AMICI/ThirdParty/sundials/src/kinsol/kinsol_ls_impl.h b/deps/AMICI/ThirdParty/sundials/src/kinsol/kinsol_ls_impl.h deleted file mode 100644 index 1977887e2..000000000 --- a/deps/AMICI/ThirdParty/sundials/src/kinsol/kinsol_ls_impl.h +++ /dev/null @@ -1,185 +0,0 @@ -/*----------------------------------------------------------------- - * Programmer(s): Daniel R. Reynolds @ SMU - * David J. Gardner, Radu Serban and Aaron Collier @ LLNL - *----------------------------------------------------------------- - * SUNDIALS Copyright Start - * Copyright (c) 2002-2021, Lawrence Livermore National Security - * and Southern Methodist University. - * All rights reserved. - * - * See the top-level LICENSE and NOTICE files for details. - * - * SPDX-License-Identifier: BSD-3-Clause - * SUNDIALS Copyright End - *----------------------------------------------------------------- - * Implementation header file for KINSOL's linear solver interface. - *-----------------------------------------------------------------*/ - -#ifndef _KINLS_IMPL_H -#define _KINLS_IMPL_H - -#include -#include "kinsol_impl.h" - -#ifdef __cplusplus /* wrapper to enable C++ usage */ -extern "C" { -#endif - - -/*------------------------------------------------------------------ - keys for KINPrintInfo (do not use 1 -> conflict with PRNT_RETVAL) - ------------------------------------------------------------------*/ -#define PRNT_NLI 101 -#define PRNT_EPS 102 - - -/*------------------------------------------------------------------ - Types : struct KINLsMemRec, struct *KINLsMem - - The type KINLsMem is a pointer to a KINLsMemRec, which is a - structure containing fields that must be accessible by LS module - routines. - ------------------------------------------------------------------*/ -typedef struct KINLsMemRec { - - /* Linear solver type information */ - booleantype iterative; /* is the solver iterative? */ - booleantype matrixbased; /* is a matrix structure used? */ - - /* Jacobian construction & storage */ - booleantype jacDQ; /* SUNTRUE if using internal DQ Jacobian approx. */ - KINLsJacFn jac; /* Jacobian routine to be called */ - void *J_data; /* J_data is passed to jac */ - - /* Linear solver, matrix and vector objects/pointers */ - SUNLinearSolver LS; /* generic iterative linear solver object */ - SUNMatrix J; /* problem Jacobian */ - - /* Solver tolerance adjustment factor (if needed, see kinLsSolve) */ - realtype tol_fac; - - /* Statistics and associated parameters */ - long int nje; /* no. of calls to jac */ - long int nfeDQ; /* no. of calls to F due to DQ Jacobian or J*v - approximations */ - long int npe; /* npe = total number of precond calls */ - long int nli; /* nli = total number of linear iterations */ - long int nps; /* nps = total number of psolve calls */ - long int ncfl; /* ncfl = total number of convergence failures */ - long int njtimes; /* njtimes = total number of calls to jtimes */ - - booleantype new_uu; /* flag indicating if the iterate has been - updated - the Jacobian must be updated or - reevaluated (meant to be used by a - user-supplied jtimes function */ - - int last_flag; /* last error return flag */ - - /* Preconditioner computation - (a) user-provided: - - pdata == user_data - - pfree == NULL (the user dealocates memory) - (b) internal preconditioner module - - pdata == kin_mem - - pfree == set by the prec. module and called in kinLsFree */ - KINLsPrecSetupFn pset; - KINLsPrecSolveFn psolve; - int (*pfree)(KINMem kin_mem); - void *pdata; - - /* Jacobian times vector compuation - (a) jtimes function provided by the user: - - jt_data == user_data - - jtimesDQ == SUNFALSE - (b) internal jtimes - - jt_data == kin_mem - - jtimesDQ == SUNTRUE */ - booleantype jtimesDQ; - KINLsJacTimesVecFn jtimes; - KINSysFn jt_func; - void *jt_data; - -} *KINLsMem; - - -/*------------------------------------------------------------------ - Prototypes of internal functions - ------------------------------------------------------------------*/ - -/* Interface routines called by system SUNLinearSolvers */ -int kinLsATimes(void *kinmem, N_Vector v, N_Vector z); -int kinLsPSetup(void *kinmem); -int kinLsPSolve(void *kinmem, N_Vector r, N_Vector z, - realtype tol, int lr); - -/* Difference quotient approximation for Jacobian times vector */ -int kinLsDQJtimes(N_Vector v, N_Vector Jv, N_Vector u, - booleantype *new_u, void *data); - -/* Difference-quotient Jacobian approximation routines */ -int kinLsDQJac(N_Vector u, N_Vector fu, SUNMatrix Jac, - void *data, N_Vector tmp1, N_Vector tmp2); - -int kinLsDenseDQJac(N_Vector u, N_Vector fu, SUNMatrix Jac, - KINMem kin_mem, N_Vector tmp1, N_Vector tmp2); - -int kinLsBandDQJac(N_Vector u, N_Vector fu, SUNMatrix Jac, - KINMem kin_mem, N_Vector tmp1, N_Vector tmp2); - -/* Generic linit/lsetup/lsolve/lfree interface routines for KINSOL to call */ -int kinLsInitialize(KINMem kin_mem); -int kinLsSetup(KINMem kin_mem); -int kinLsSolve(KINMem kin_mem, N_Vector x, N_Vector b, - realtype *sJpnorm, realtype *sFdotJp); -int kinLsFree(KINMem kin_mem); - -/* Auxilliary functions */ -int kinLsInitializeCounters(KINLsMem kinls_mem); -int kinLs_AccessLMem(void* kinmem, const char *fname, - KINMem* kin_mem, KINLsMem *kinls_mem); - - -/*------------------------------------------------------------------ - Error messages - ------------------------------------------------------------------*/ - -#define MSG_LS_KINMEM_NULL "KINSOL memory is NULL." -#define MSG_LS_MEM_FAIL "A memory request failed." -#define MSG_LS_BAD_NVECTOR "A required vector operation is not implemented." -#define MSG_LS_LMEM_NULL "Linear solver memory is NULL." -#define MSG_LS_NEG_MAXRS "maxrs < 0 illegal." -#define MSG_LS_BAD_SIZES "Illegal bandwidth parameter(s). Must have 0 <= ml, mu <= N-1." - -#define MSG_LS_JACFUNC_FAILED "The Jacobian routine failed in an unrecoverable manner." -#define MSG_LS_PSET_FAILED "The preconditioner setup routine failed in an unrecoverable manner." -#define MSG_LS_PSOLVE_FAILED "The preconditioner solve routine failed in an unrecoverable manner." -#define MSG_LS_JTIMES_FAILED "The Jacobian x vector routine failed in an unrecoverable manner." -#define MSG_LS_MATZERO_FAILED "The SUNMatZero routine failed in an unrecoverable manner." - - -/*------------------------------------------------------------------ - Info messages - ------------------------------------------------------------------*/ - -#define INFO_NLI "nli_inc = %d" - -#if defined(SUNDIALS_EXTENDED_PRECISION) - -#define INFO_EPS "residual norm = %12.3Lg eps = %12.3Lg" - -#elif defined(SUNDIALS_DOUBLE_PRECISION) - -#define INFO_EPS "residual norm = %12.3lg eps = %12.3lg" - -#else - -#define INFO_EPS "residual norm = %12.3g eps = %12.3g" - -#endif - - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/deps/AMICI/ThirdParty/sundials/src/kinsol/kinsol_spils.c b/deps/AMICI/ThirdParty/sundials/src/kinsol/kinsol_spils.c deleted file mode 100644 index 9af333554..000000000 --- a/deps/AMICI/ThirdParty/sundials/src/kinsol/kinsol_spils.c +++ /dev/null @@ -1,73 +0,0 @@ -/*----------------------------------------------------------------- - * Programmer(s): Daniel R. Reynolds @ SMU - * Scott Cohen, Alan Hindmarsh, Radu Serban, - * and Aaron Collier @ LLNL - *----------------------------------------------------------------- - * SUNDIALS Copyright Start - * Copyright (c) 2002-2021, Lawrence Livermore National Security - * and Southern Methodist University. - * All rights reserved. - * - * See the top-level LICENSE and NOTICE files for details. - * - * SPDX-License-Identifier: BSD-3-Clause - * SUNDIALS Copyright End - *----------------------------------------------------------------- - * Header file for the deprecated Scaled Preconditioned Iterative - * Linear Solver interface in KINSOL; these routines now just wrap - * the updated KINSOL generic linear solver interface in kinsol_ls.h. - *-----------------------------------------------------------------*/ - -#include -#include - -#ifdef __cplusplus /* wrapper to enable C++ usage */ -extern "C" { -#endif - -/*================================================================= - Exported Functions (wrappers for equivalent routines in kinsol_ls.h) - =================================================================*/ - -int KINSpilsSetLinearSolver(void *kinmem, SUNLinearSolver LS) -{ return(KINSetLinearSolver(kinmem, LS, NULL)); } - -int KINSpilsSetPreconditioner(void *kinmem, KINSpilsPrecSetupFn psetup, - KINSpilsPrecSolveFn psolve) -{ return(KINSetPreconditioner(kinmem, psetup, psolve)); } - -int KINSpilsSetJacTimesVecFn(void *kinmem, KINSpilsJacTimesVecFn jtv) -{ return(KINSetJacTimesVecFn(kinmem, jtv)); } - -int KINSpilsGetWorkSpace(void *kinmem, long int *lenrwLS, long int *leniwLS) -{ return(KINGetLinWorkSpace(kinmem, lenrwLS, leniwLS)); } - -int KINSpilsGetNumPrecEvals(void *kinmem, long int *npevals) -{ return(KINGetNumPrecEvals(kinmem, npevals)); } - -int KINSpilsGetNumPrecSolves(void *kinmem, long int *npsolves) -{ return(KINGetNumPrecSolves(kinmem, npsolves)); } - -int KINSpilsGetNumLinIters(void *kinmem, long int *nliters) -{ return(KINGetNumLinIters(kinmem, nliters)); } - -int KINSpilsGetNumConvFails(void *kinmem, long int *nlcfails) -{ return(KINGetNumLinConvFails(kinmem, nlcfails)); } - -int KINSpilsGetNumJtimesEvals(void *kinmem, long int *njvevals) -{ return(KINGetNumJtimesEvals(kinmem, njvevals)); } - -int KINSpilsGetNumFuncEvals(void *kinmem, long int *nfevals) -{ return(KINGetNumLinFuncEvals(kinmem, nfevals)); } - -int KINSpilsGetLastFlag(void *kinmem, long int *flag) -{ return(KINGetLastLinFlag(kinmem, flag)); } - -char *KINSpilsGetReturnFlagName(long int flag) -{ return(KINGetLinReturnFlagName(flag)); } - - -#ifdef __cplusplus -} -#endif - diff --git a/deps/AMICI/ThirdParty/sundials/src/sundials/sundials_debug.h b/deps/AMICI/ThirdParty/sundials/src/sundials/sundials_debug.h index dbf6c7b0b..1a380243a 100644 --- a/deps/AMICI/ThirdParty/sundials/src/sundials/sundials_debug.h +++ b/deps/AMICI/ThirdParty/sundials/src/sundials/sundials_debug.h @@ -1,5 +1,4 @@ -/* - * ----------------------------------------------------------------- +/* ----------------------------------------------------------------- * Programmer(s): Cody J. Balos @ LLNL * ----------------------------------------------------------------- * SUNDIALS Copyright Start @@ -14,8 +13,7 @@ * ----------------------------------------------------------------- * This header files defines internal utility functions and macros * for SUNDIALS debugging. - * ----------------------------------------------------------------- - */ + * -----------------------------------------------------------------*/ #ifndef _SUNDIALS_DEBUG_H #define _SUNDIALS_DEBUG_H @@ -27,7 +25,7 @@ extern "C" { #endif /* - * Macro which prints to stderr when in debug mode + * Macro which prints to stderr when in debug mode */ #ifdef SUNDIALS_DEBUG #define SUNDIALS_DEBUG_PRINT(str) fprintf(stderr, str) @@ -35,8 +33,19 @@ extern "C" { #define SUNDIALS_DEBUG_PRINT(str) #endif +/* + * Macro which prints error messages in debug mode + */ +#ifdef SUNDIALS_DEBUG +#define SUNDIALS_DEBUG_ERROR(msg) \ + fprintf(stderr, "ERROR in %s (%s line %d): %s", \ + __func__, __FILE__, __LINE__, msg); +#else +#define SUNDIALS_DEBUG_ERROR(msg) +#endif + #ifdef __cplusplus /* wrapper to enable C++ usage */ } #endif -#endif /* _SUNDIALS_DEBUG_H */ \ No newline at end of file +#endif /* _SUNDIALS_DEBUG_H */ diff --git a/deps/AMICI/ThirdParty/sundials/src/sundials/sundials_linearsolver.c b/deps/AMICI/ThirdParty/sundials/src/sundials/sundials_linearsolver.c index bfe1dca3c..28f5e48a8 100644 --- a/deps/AMICI/ThirdParty/sundials/src/sundials/sundials_linearsolver.c +++ b/deps/AMICI/ThirdParty/sundials/src/sundials/sundials_linearsolver.c @@ -46,6 +46,7 @@ SUNLinearSolver SUNLinSolNewEmpty() ops->setatimes = NULL; ops->setpreconditioner = NULL; ops->setscalingvectors = NULL; + ops->setzeroguess = NULL; ops->initialize = NULL; ops->setup = NULL; ops->solve = NULL; @@ -125,6 +126,14 @@ int SUNLinSolSetScalingVectors(SUNLinearSolver S, return SUNLS_SUCCESS; } +int SUNLinSolSetZeroGuess(SUNLinearSolver S, booleantype onoff) +{ + if (S->ops->setzeroguess) + return ((int) S->ops->setzeroguess(S, onoff)); + else + return SUNLS_SUCCESS; +} + int SUNLinSolInitialize(SUNLinearSolver S) { if (S->ops->initialize) diff --git a/deps/AMICI/ThirdParty/sundials/src/sundials/sundials_math.c b/deps/AMICI/ThirdParty/sundials/src/sundials/sundials_math.c index bcbc2b2aa..e7db5edd8 100644 --- a/deps/AMICI/ThirdParty/sundials/src/sundials/sundials_math.c +++ b/deps/AMICI/ThirdParty/sundials/src/sundials/sundials_math.c @@ -22,24 +22,43 @@ #include -#define ZERO RCONST(0.0) -#define ONE RCONST(1.0) +static booleantype sunIsInf(realtype a) +{ +#if (__STDC_VERSION__ >= 199901L) + return(isinf(a)); +#else + return(a < -BIG_REAL || a > BIG_REAL); +#endif +} + +static booleantype sunIsNaN(realtype a) +{ +#if ( __STDC_VERSION__ >= 199901L) + return(isnan(a)); +#else + /* Most compilers/platforms follow NaN != a, + * but since C89 does not require this, it is + * possible some platforms might not follow it. + */ + return(a != a); +#endif +} realtype SUNRpowerI(realtype base, int exponent) { int i, expt; realtype prod; - prod = ONE; + prod = RCONST(1.0); expt = abs(exponent); for(i = 1; i <= expt; i++) prod *= base; - if (exponent < 0) prod = ONE/prod; + if (exponent < 0) prod = RCONST(1.0)/prod; return(prod); } realtype SUNRpowerR(realtype base, realtype exponent) { - if (base <= ZERO) return(ZERO); + if (base <= RCONST(0.0)) return(RCONST(0.0)); #if defined(SUNDIALS_USE_GENERIC_MATH) return((realtype) pow((double) base, (double) exponent)); @@ -51,3 +70,39 @@ realtype SUNRpowerR(realtype base, realtype exponent) return(powl(base, exponent)); #endif } + +booleantype SUNRCompare(realtype a, realtype b) +{ + return(SUNRCompareTol(a, b, 10*UNIT_ROUNDOFF)); +} + +booleantype SUNRCompareTol(realtype a, realtype b, realtype tol) +{ + realtype diff; + realtype norm; + + /* If a and b are exactly equal. + * This also covers the case where a and b are both inf under IEEE 754. + */ + if (a == b) return(SUNFALSE); + + /* If a or b are NaN */ + if (sunIsNaN(a) || sunIsNaN(b)) return(SUNTRUE); + + /* If one of a or b are Inf (since we handled both being inf above) */ + if (sunIsInf(a) || sunIsInf(b)) return(SUNTRUE); + + diff = SUNRabs(a - b); + norm = SUNMIN(SUNRabs(a + b), BIG_REAL); + + /* When |a + b| is very small (less than 10*UNIT_ROUNDOFF) or zero, we use an + * absolute difference: + * |a - b| >= 10*UNIT_ROUNDOFF + * Otherwise we use a relative difference: + * |a - b| < tol * |a + b| + * The choice to use |a + b| over max(a, b) + * is arbitrary, as is the choice to use + * 10*UNIT_ROUNDOFF. + */ + return(diff >= SUNMAX(10*UNIT_ROUNDOFF, tol*norm)); +} diff --git a/deps/AMICI/ThirdParty/sundials/src/sundials/sundials_matrix.c b/deps/AMICI/ThirdParty/sundials/src/sundials/sundials_matrix.c index 00fc5acca..0ffa67f07 100644 --- a/deps/AMICI/ThirdParty/sundials/src/sundials/sundials_matrix.c +++ b/deps/AMICI/ThirdParty/sundials/src/sundials/sundials_matrix.c @@ -68,7 +68,7 @@ SUNMatrix SUNMatNewEmpty() void SUNMatFreeEmpty(SUNMatrix A) { if (A == NULL) return; - + /* free non-NULL ops structure */ if (A->ops) free(A->ops); A->ops = NULL; @@ -90,15 +90,16 @@ int SUNMatCopyOps(SUNMatrix A, SUNMatrix B) if (A->ops == NULL || B->ops == NULL) return(-1); /* Copy ops from A to B */ - B->ops->getid = A->ops->getid; - B->ops->clone = A->ops->clone; - B->ops->destroy = A->ops->destroy; - B->ops->zero = A->ops->zero; - B->ops->copy = A->ops->copy; - B->ops->scaleadd = A->ops->scaleadd; - B->ops->scaleaddi = A->ops->scaleaddi; - B->ops->matvec = A->ops->matvec; - B->ops->space = A->ops->space; + B->ops->getid = A->ops->getid; + B->ops->clone = A->ops->clone; + B->ops->destroy = A->ops->destroy; + B->ops->zero = A->ops->zero; + B->ops->copy = A->ops->copy; + B->ops->scaleadd = A->ops->scaleadd; + B->ops->scaleaddi = A->ops->scaleaddi; + B->ops->matvecsetup = A->ops->matvecsetup; + B->ops->matvec = A->ops->matvec; + B->ops->space = A->ops->space; return(0); } @@ -161,7 +162,10 @@ int SUNMatScaleAddI(realtype c, SUNMatrix A) int SUNMatMatvecSetup(SUNMatrix A) { - return((int) A->ops->matvecsetup(A)); + if (A->ops->matvecsetup) + return((int) A->ops->matvecsetup(A)); + else + return(0); } int SUNMatMatvec(SUNMatrix A, N_Vector x, N_Vector y) diff --git a/deps/AMICI/ThirdParty/sundials/src/sundials/sundials_nvector.c b/deps/AMICI/ThirdParty/sundials/src/sundials/sundials_nvector.c index 52428e4d1..d204425a7 100644 --- a/deps/AMICI/ThirdParty/sundials/src/sundials/sundials_nvector.c +++ b/deps/AMICI/ThirdParty/sundials/src/sundials/sundials_nvector.c @@ -773,7 +773,7 @@ N_Vector* N_VCloneVectorArray(int count, N_Vector w) if (count <= 0) return(NULL); vs = (N_Vector* ) malloc(count * sizeof(N_Vector)); - if(vs == NULL) return(NULL); + if (vs == NULL) return(NULL); for (j = 0; j < count; j++) { vs[j] = N_VClone(w); @@ -790,9 +790,12 @@ void N_VDestroyVectorArray(N_Vector* vs, int count) { int j; - if (vs==NULL) return; + if (vs == NULL) return; - for (j = 0; j < count; j++) N_VDestroy(vs[j]); + for (j = 0; j < count; j++) { + N_VDestroy(vs[j]); + vs[j] = NULL; + } free(vs); vs = NULL; diff --git a/deps/AMICI/ThirdParty/sundials/src/sunlinsol/CMakeLists.txt b/deps/AMICI/ThirdParty/sundials/src/sunlinsol/CMakeLists.txt index b14060d76..8cce2abfb 100644 --- a/deps/AMICI/ThirdParty/sundials/src/sunlinsol/CMakeLists.txt +++ b/deps/AMICI/ThirdParty/sundials/src/sunlinsol/CMakeLists.txt @@ -14,7 +14,7 @@ # linear solver level CMakeLists.txt for SUNDIALS # ------------------------------------------------------------------------------ -# required modules +# required native linear solvers add_subdirectory(band) add_subdirectory(dense) add_subdirectory(pcg) @@ -23,6 +23,7 @@ add_subdirectory(spfgmr) add_subdirectory(spgmr) add_subdirectory(sptfqmr) +# optional TPL linear solvers if(BUILD_SUNLINSOL_CUSOLVERSP) add_subdirectory(cusolversp) endif() @@ -43,10 +44,14 @@ if(BUILD_SUNLINSOL_MAGMADENSE) add_subdirectory(magmadense) endif() +if(BUILD_SUNLINSOL_ONEMKLDENSE) + add_subdirectory(onemkldense) +endif() + if(BUILD_SUNLINSOL_SUPERLUDIST) add_subdirectory(superludist) endif() if(BUILD_SUNLINSOL_SUPERLUMT) add_subdirectory(superlumt) -endif() \ No newline at end of file +endif() diff --git a/deps/AMICI/ThirdParty/sundials/src/sunlinsol/klu/sunlinsol_klu.c b/deps/AMICI/ThirdParty/sundials/src/sunlinsol/klu/sunlinsol_klu.c index 7f37803da..3fd379f1f 100644 --- a/deps/AMICI/ThirdParty/sundials/src/sunlinsol/klu/sunlinsol_klu.c +++ b/deps/AMICI/ThirdParty/sundials/src/sunlinsol/klu/sunlinsol_klu.c @@ -48,9 +48,11 @@ */ #if defined(SUNDIALS_INT64_T) -#define KLU_INDEXTYPE long int +/* Changed for AMICI */ +#define KLU_INDEXTYPE int64_t #else -#define KLU_INDEXTYPE int +/* Changed for AMICI */ +#define KLU_INDEXTYPE int32_t #endif /* @@ -331,31 +333,31 @@ int SUNLinSolSetup_KLU(SUNLinearSolver S, SUNMatrix A) if ( COMMON(S).rcond < uround_twothirds ) { /* Condition number may be getting large. - Compute more accurate estimate */ + Compute more accurate estimate */ retval = sun_klu_condest((KLU_INDEXTYPE*) SUNSparseMatrix_IndexPointers(A), SUNSparseMatrix_Data(A), SYMBOLIC(S), NUMERIC(S), &COMMON(S)); if (retval == 0) { - LASTFLAG(S) = SUNLS_PACKAGE_FAIL_REC; + LASTFLAG(S) = SUNLS_PACKAGE_FAIL_REC; return(LASTFLAG(S)); } if ( COMMON(S).condest > (ONE/uround_twothirds) ) { - /* More accurate estimate also says condition number is - large, so recompute the numeric factorization */ - sun_klu_free_numeric(&NUMERIC(S), &COMMON(S)); - NUMERIC(S) = sun_klu_factor((KLU_INDEXTYPE*) SUNSparseMatrix_IndexPointers(A), + /* More accurate estimate also says condition number is + large, so recompute the numeric factorization */ + sun_klu_free_numeric(&NUMERIC(S), &COMMON(S)); + NUMERIC(S) = sun_klu_factor((KLU_INDEXTYPE*) SUNSparseMatrix_IndexPointers(A), (KLU_INDEXTYPE*) SUNSparseMatrix_IndexValues(A), SUNSparseMatrix_Data(A), SYMBOLIC(S), &COMMON(S)); - if (NUMERIC(S) == NULL) { - LASTFLAG(S) = SUNLS_PACKAGE_FAIL_UNREC; + if (NUMERIC(S) == NULL) { + LASTFLAG(S) = SUNLS_PACKAGE_FAIL_UNREC; return(LASTFLAG(S)); - } + } } } diff --git a/deps/AMICI/ThirdParty/sundials/src/sunlinsol/pcg/sunlinsol_pcg.c b/deps/AMICI/ThirdParty/sundials/src/sunlinsol/pcg/sunlinsol_pcg.c index b9cc409cd..ec1cd0e92 100644 --- a/deps/AMICI/ThirdParty/sundials/src/sunlinsol/pcg/sunlinsol_pcg.c +++ b/deps/AMICI/ThirdParty/sundials/src/sunlinsol/pcg/sunlinsol_pcg.c @@ -85,6 +85,7 @@ SUNLinearSolver SUNLinSol_PCG(N_Vector y, int pretype, int maxl) S->ops->setatimes = SUNLinSolSetATimes_PCG; S->ops->setpreconditioner = SUNLinSolSetPreconditioner_PCG; S->ops->setscalingvectors = SUNLinSolSetScalingVectors_PCG; + S->ops->setzeroguess = SUNLinSolSetZeroGuess_PCG; S->ops->initialize = SUNLinSolInitialize_PCG; S->ops->setup = SUNLinSolSetup_PCG; S->ops->solve = SUNLinSolSolve_PCG; @@ -107,6 +108,7 @@ SUNLinearSolver SUNLinSol_PCG(N_Vector y, int pretype, int maxl) content->last_flag = 0; content->maxl = maxl; content->pretype = pretype; + content->zeroguess = SUNFALSE; content->numiters = 0; content->resnorm = ZERO; content->r = NULL; @@ -267,6 +269,16 @@ int SUNLinSolSetScalingVectors_PCG(SUNLinearSolver S, N_Vector s, } +int SUNLinSolSetZeroGuess_PCG(SUNLinearSolver S, booleantype onoff) +{ + /* set flag indicating a zero initial guess */ + if (S == NULL) return(SUNLS_MEM_NULL); + PCG_CONTENT(S)->zeroguess = onoff; + LASTFLAG(S) = SUNLS_SUCCESS; + return(LASTFLAG(S)); +} + + int SUNLinSolSetup_PCG(SUNLinearSolver S, SUNMatrix nul) { int ier; @@ -302,6 +314,7 @@ int SUNLinSolSolve_PCG(SUNLinearSolver S, SUNMatrix nul, N_Vector x, realtype alpha, beta, r0_norm, rho, rz, rz_old; N_Vector r, p, z, Ap, w; booleantype UsePrec, UseScaling, converged; + booleantype *zeroguess; int l, l_max, pretype, ier; void *A_data, *P_data; ATimesFn atimes; @@ -322,6 +335,7 @@ int SUNLinSolSolve_PCG(SUNLinearSolver S, SUNMatrix nul, N_Vector x, atimes = PCG_CONTENT(S)->ATimes; psolve = PCG_CONTENT(S)->Psolve; pretype = PCG_CONTENT(S)->pretype; + zeroguess = &(PCG_CONTENT(S)->zeroguess); nli = &(PCG_CONTENT(S)->numiters); res_norm = &(PCG_CONTENT(S)->resnorm); @@ -342,21 +356,25 @@ int SUNLinSolSolve_PCG(SUNLinearSolver S, SUNMatrix nul, N_Vector x, /* Check if Atimes function has been set */ if (atimes == NULL) { + *zeroguess = SUNFALSE; LASTFLAG(S) = SUNLS_ATIMES_NULL; return(LASTFLAG(S)); } /* If preconditioning, check if psolve has been set */ if (UsePrec && psolve == NULL) { + *zeroguess = SUNFALSE; LASTFLAG(S) = SUNLS_PSOLVE_NULL; return(LASTFLAG(S)); } /* Set r to initial residual r_0 = b - A*x_0 */ - if (N_VDotProd(x, x) == ZERO) N_VScale(ONE, b, r); - else { + if (*zeroguess) { + N_VScale(ONE, b, r); + } else { ier = atimes(A_data, x, r); if (ier != 0) { + *zeroguess = SUNFALSE; LASTFLAG(S) = (ier < 0) ? SUNLS_ATIMES_FAIL_UNREC : SUNLS_ATIMES_FAIL_REC; return(LASTFLAG(S)); @@ -380,6 +398,7 @@ int SUNLinSolSolve_PCG(SUNLinearSolver S, SUNMatrix nul, N_Vector x, #endif if (rho <= delta) { + *zeroguess = SUNFALSE; LASTFLAG(S) = SUNLS_SUCCESS; return(LASTFLAG(S)); } @@ -388,6 +407,7 @@ int SUNLinSolSolve_PCG(SUNLinearSolver S, SUNMatrix nul, N_Vector x, if (UsePrec) { ier = psolve(P_data, r, z, delta, PREC_LEFT); /* z = P^{-1}r */ if (ier != 0) { + *zeroguess = SUNFALSE; LASTFLAG(S) = (ier < 0) ? SUNLS_PSOLVE_FAIL_UNREC : SUNLS_PSOLVE_FAIL_REC; return(LASTFLAG(S)); @@ -410,6 +430,7 @@ int SUNLinSolSolve_PCG(SUNLinearSolver S, SUNMatrix nul, N_Vector x, /* Generate Ap = A*p */ ier = atimes(A_data, p, Ap); if (ier != 0) { + *zeroguess = SUNFALSE; LASTFLAG(S) = (ier < 0) ? SUNLS_ATIMES_FAIL_UNREC : SUNLS_ATIMES_FAIL_REC; return(LASTFLAG(S)); @@ -419,7 +440,10 @@ int SUNLinSolSolve_PCG(SUNLinearSolver S, SUNMatrix nul, N_Vector x, alpha = rz / N_VDotProd(Ap, p); /* Update x = x + alpha*p */ - N_VLinearSum(ONE, x, alpha, p, x); + if (l == 0 && *zeroguess) + N_VScale(alpha, p, x); + else + N_VLinearSum(ONE, x, alpha, p, x); /* Update r = r - alpha*Ap */ N_VLinearSum(ONE, r, -alpha, Ap, r); @@ -451,6 +475,7 @@ int SUNLinSolSolve_PCG(SUNLinearSolver S, SUNMatrix nul, N_Vector x, if (UsePrec) { ier = psolve(P_data, r, z, delta, PREC_LEFT); if (ier != 0) { + *zeroguess = SUNFALSE; LASTFLAG(S) = (ier < 0) ? SUNLS_PSOLVE_FAIL_UNREC : SUNLS_PSOLVE_FAIL_REC; return(LASTFLAG(S)); @@ -470,6 +495,7 @@ int SUNLinSolSolve_PCG(SUNLinearSolver S, SUNMatrix nul, N_Vector x, } /* Main loop finished, return with result */ + *zeroguess = SUNFALSE; if (converged == SUNTRUE) { LASTFLAG(S) = SUNLS_SUCCESS; } else if (rho < r0_norm) { diff --git a/deps/AMICI/ThirdParty/sundials/src/sunlinsol/spbcgs/sunlinsol_spbcgs.c b/deps/AMICI/ThirdParty/sundials/src/sunlinsol/spbcgs/sunlinsol_spbcgs.c index 52d1c1847..f27a99ce6 100644 --- a/deps/AMICI/ThirdParty/sundials/src/sunlinsol/spbcgs/sunlinsol_spbcgs.c +++ b/deps/AMICI/ThirdParty/sundials/src/sunlinsol/spbcgs/sunlinsol_spbcgs.c @@ -93,6 +93,7 @@ SUNLinearSolver SUNLinSol_SPBCGS(N_Vector y, int pretype, int maxl) S->ops->setatimes = SUNLinSolSetATimes_SPBCGS; S->ops->setpreconditioner = SUNLinSolSetPreconditioner_SPBCGS; S->ops->setscalingvectors = SUNLinSolSetScalingVectors_SPBCGS; + S->ops->setzeroguess = SUNLinSolSetZeroGuess_SPBCGS; S->ops->initialize = SUNLinSolInitialize_SPBCGS; S->ops->setup = SUNLinSolSetup_SPBCGS; S->ops->solve = SUNLinSolSolve_SPBCGS; @@ -115,6 +116,7 @@ SUNLinearSolver SUNLinSol_SPBCGS(N_Vector y, int pretype, int maxl) content->last_flag = 0; content->maxl = maxl; content->pretype = pretype; + content->zeroguess = SUNFALSE; content->numiters = 0; content->resnorm = ZERO; content->r_star = NULL; @@ -289,6 +291,16 @@ int SUNLinSolSetScalingVectors_SPBCGS(SUNLinearSolver S, N_Vector s1, } +int SUNLinSolSetZeroGuess_SPBCGS(SUNLinearSolver S, booleantype onoff) +{ + /* set flag indicating a zero initial guess */ + if (S == NULL) return(SUNLS_MEM_NULL); + SPBCGS_CONTENT(S)->zeroguess = onoff; + LASTFLAG(S) = SUNLS_SUCCESS; + return(LASTFLAG(S)); +} + + int SUNLinSolSetup_SPBCGS(SUNLinearSolver S, SUNMatrix A) { int ier; @@ -306,7 +318,7 @@ int SUNLinSolSetup_SPBCGS(SUNLinearSolver S, SUNMatrix A) ier = Psetup(PData); if (ier != 0) { LASTFLAG(S) = (ier < 0) ? - SUNLS_PSET_FAIL_UNREC : SUNLS_PSET_FAIL_REC; + SUNLS_PSET_FAIL_UNREC : SUNLS_PSET_FAIL_REC; return(LASTFLAG(S)); } } @@ -324,6 +336,7 @@ int SUNLinSolSolve_SPBCGS(SUNLinearSolver S, SUNMatrix A, N_Vector x, realtype alpha, beta, omega, omega_denom, beta_num, beta_denom, r_norm, rho; N_Vector r_star, r, p, q, u, Ap, vtemp; booleantype preOnLeft, preOnRight, scale_x, scale_b, converged; + booleantype *zeroguess; int l, l_max, ier; void *A_data, *P_data; N_Vector sx, sb; @@ -352,6 +365,7 @@ int SUNLinSolSolve_SPBCGS(SUNLinearSolver S, SUNMatrix A, N_Vector x, P_data = SPBCGS_CONTENT(S)->PData; atimes = SPBCGS_CONTENT(S)->ATimes; psolve = SPBCGS_CONTENT(S)->Psolve; + zeroguess = &(SPBCGS_CONTENT(S)->zeroguess); nli = &(SPBCGS_CONTENT(S)->numiters); res_norm = &(SPBCGS_CONTENT(S)->resnorm); @@ -367,6 +381,13 @@ int SUNLinSolSolve_SPBCGS(SUNLinearSolver S, SUNMatrix A, N_Vector x, scale_x = (sx != NULL); scale_b = (sb != NULL); + /* Check for unsupported use case */ + if (preOnRight && !(*zeroguess)) { + *zeroguess = SUNFALSE; + LASTFLAG(S) = SUNLS_ILL_INPUT; + return(SUNLS_ILL_INPUT); + } + #ifdef SUNDIALS_BUILD_WITH_MONITORING if (SPBCGS_CONTENT(S)->print_level && SPBCGS_CONTENT(S)->info_file) fprintf(SPBCGS_CONTENT(S)->info_file, "SUNLINSOL_SPBCGS:\n"); @@ -374,22 +395,26 @@ int SUNLinSolSolve_SPBCGS(SUNLinearSolver S, SUNMatrix A, N_Vector x, /* Check if Atimes function has been set */ if (atimes == NULL) { + *zeroguess = SUNFALSE; LASTFLAG(S) = SUNLS_ATIMES_NULL; return(LASTFLAG(S)); } /* If preconditioning, check if psolve has been set */ if ((preOnLeft || preOnRight) && psolve == NULL) { + *zeroguess = SUNFALSE; LASTFLAG(S) = SUNLS_PSOLVE_NULL; return(LASTFLAG(S)); } /* Set r_star to initial (unscaled) residual r_0 = b - A*x_0 */ - if (N_VDotProd(x, x) == ZERO) N_VScale(ONE, b, r_star); - else { + if (*zeroguess) { + N_VScale(ONE, b, r_star); + } else { ier = atimes(A_data, x, r_star); if (ier != 0) { + *zeroguess = SUNFALSE; LASTFLAG(S) = (ier < 0) ? SUNLS_ATIMES_FAIL_UNREC : SUNLS_ATIMES_FAIL_REC; return(LASTFLAG(S)); @@ -402,6 +427,7 @@ int SUNLinSolSolve_SPBCGS(SUNLinearSolver S, SUNMatrix A, N_Vector x, if (preOnLeft) { ier = psolve(P_data, r_star, r, delta, PREC_LEFT); if (ier != 0) { + *zeroguess = SUNFALSE; LASTFLAG(S) = (ier < 0) ? SUNLS_PSOLVE_FAIL_UNREC : SUNLS_PSOLVE_FAIL_REC; return(LASTFLAG(S)); @@ -432,6 +458,7 @@ int SUNLinSolSolve_SPBCGS(SUNLinearSolver S, SUNMatrix A, N_Vector x, #endif if (r_norm <= delta) { + *zeroguess = SUNFALSE; LASTFLAG(S) = SUNLS_SUCCESS; return(LASTFLAG(S)); } @@ -441,6 +468,9 @@ int SUNLinSolSolve_SPBCGS(SUNLinearSolver S, SUNMatrix A, N_Vector x, N_VScale(ONE, r_star, r); N_VScale(ONE, r_star, p); + /* Set x = sx x if non-zero guess */ + if (scale_x && !(*zeroguess)) N_VProd(sx, x, x); + /* Begin main iteration loop */ for(l = 0; l < l_max; l++) { @@ -460,6 +490,7 @@ int SUNLinSolSolve_SPBCGS(SUNLinearSolver S, SUNMatrix A, N_Vector x, N_VScale(ONE, vtemp, Ap); ier = psolve(P_data, Ap, vtemp, delta, PREC_RIGHT); if (ier != 0) { + *zeroguess = SUNFALSE; LASTFLAG(S) = (ier < 0) ? SUNLS_PSOLVE_FAIL_UNREC : SUNLS_PSOLVE_FAIL_REC; return(LASTFLAG(S)); @@ -470,6 +501,7 @@ int SUNLinSolSolve_SPBCGS(SUNLinearSolver S, SUNMatrix A, N_Vector x, ier = atimes(A_data, vtemp, Ap ); if (ier != 0) { + *zeroguess = SUNFALSE; LASTFLAG(S) = (ier < 0) ? SUNLS_ATIMES_FAIL_UNREC : SUNLS_ATIMES_FAIL_REC; return(LASTFLAG(S)); @@ -480,6 +512,7 @@ int SUNLinSolSolve_SPBCGS(SUNLinearSolver S, SUNMatrix A, N_Vector x, if (preOnLeft) { ier = psolve(P_data, Ap, vtemp, delta, PREC_LEFT); if (ier != 0) { + *zeroguess = SUNFALSE; LASTFLAG(S) = (ier < 0) ? SUNLS_PSOLVE_FAIL_UNREC : SUNLS_PSOLVE_FAIL_REC; return(LASTFLAG(S)); @@ -514,6 +547,7 @@ int SUNLinSolSolve_SPBCGS(SUNLinearSolver S, SUNMatrix A, N_Vector x, N_VScale(ONE, vtemp, u); ier = psolve(P_data, u, vtemp, delta, PREC_RIGHT); if (ier != 0) { + *zeroguess = SUNFALSE; LASTFLAG(S) = (ier < 0) ? SUNLS_PSOLVE_FAIL_UNREC : SUNLS_PSOLVE_FAIL_REC; return(LASTFLAG(S)); @@ -524,6 +558,7 @@ int SUNLinSolSolve_SPBCGS(SUNLinearSolver S, SUNMatrix A, N_Vector x, ier = atimes(A_data, vtemp, u ); if (ier != 0) { + *zeroguess = SUNFALSE; LASTFLAG(S) = (ier < 0) ? SUNLS_ATIMES_FAIL_UNREC : SUNLS_ATIMES_FAIL_REC; return(LASTFLAG(S)); @@ -534,6 +569,7 @@ int SUNLinSolSolve_SPBCGS(SUNLinearSolver S, SUNMatrix A, N_Vector x, if (preOnLeft) { ier = psolve(P_data, u, vtemp, delta, PREC_LEFT); if (ier != 0) { + *zeroguess = SUNFALSE; LASTFLAG(S) = (ier < 0) ? SUNLS_PSOLVE_FAIL_UNREC : SUNLS_PSOLVE_FAIL_REC; return(LASTFLAG(S)); @@ -554,17 +590,26 @@ int SUNLinSolSolve_SPBCGS(SUNLinearSolver S, SUNMatrix A, N_Vector x, omega = (N_VDotProd(u, q) / omega_denom); /* Update x = x + alpha*p + omega*q */ - cv[0] = ONE; - Xv[0] = x; - - cv[1] = alpha; - Xv[1] = p; - - cv[2] = omega; - Xv[2] = q; + if (l == 0 && *zeroguess) { + N_VLinearSum(alpha, p, omega, q, x); + } else { + cv[0] = ONE; + Xv[0] = x; + + cv[1] = alpha; + Xv[1] = p; + + cv[2] = omega; + Xv[2] = q; + + ier = N_VLinearCombination(3, cv, Xv, x); + if (ier != SUNLS_SUCCESS) { + *zeroguess = SUNFALSE; + LASTFLAG(S) = SUNLS_VECTOROP_ERR; + return(SUNLS_VECTOROP_ERR); + } - ier = N_VLinearCombination(3, cv, Xv, x); - if (ier != SUNLS_SUCCESS) return(SUNLS_VECTOROP_ERR); + } /* Update the residual r = q - omega*u */ @@ -606,7 +651,11 @@ int SUNLinSolSolve_SPBCGS(SUNLinearSolver S, SUNMatrix A, N_Vector x, Xv[2] = r; ier = N_VLinearCombination(3, cv, Xv, p); - if (ier != SUNLS_SUCCESS) return(SUNLS_VECTOROP_ERR); + if (ier != SUNLS_SUCCESS) { + *zeroguess = SUNFALSE; + LASTFLAG(S) = SUNLS_VECTOROP_ERR; + return(SUNLS_VECTOROP_ERR); + } /* udpate beta_denom for next iteration */ beta_denom = beta_num; @@ -622,6 +671,7 @@ int SUNLinSolSolve_SPBCGS(SUNLinearSolver S, SUNMatrix A, N_Vector x, if (preOnRight) { ier = psolve(P_data, x, vtemp, delta, PREC_RIGHT); if (ier != 0) { + *zeroguess = SUNFALSE; LASTFLAG(S) = (ier < 0) ? SUNLS_PSOLVE_FAIL_UNREC : SUNLS_PSOLVE_FAIL_REC; return(LASTFLAG(S)); @@ -629,6 +679,7 @@ int SUNLinSolSolve_SPBCGS(SUNLinearSolver S, SUNMatrix A, N_Vector x, N_VScale(ONE, vtemp, x); } + *zeroguess = SUNFALSE; if (converged == SUNTRUE) LASTFLAG(S) = SUNLS_SUCCESS; else @@ -637,6 +688,7 @@ int SUNLinSolSolve_SPBCGS(SUNLinearSolver S, SUNMatrix A, N_Vector x, } else { + *zeroguess = SUNFALSE; LASTFLAG(S) = SUNLS_CONV_FAIL; return(LASTFLAG(S)); } diff --git a/deps/AMICI/ThirdParty/sundials/src/sunlinsol/spfgmr/sunlinsol_spfgmr.c b/deps/AMICI/ThirdParty/sundials/src/sunlinsol/spfgmr/sunlinsol_spfgmr.c index c26837d21..7fb795713 100644 --- a/deps/AMICI/ThirdParty/sundials/src/sunlinsol/spfgmr/sunlinsol_spfgmr.c +++ b/deps/AMICI/ThirdParty/sundials/src/sunlinsol/spfgmr/sunlinsol_spfgmr.c @@ -97,6 +97,7 @@ SUNLinearSolver SUNLinSol_SPFGMR(N_Vector y, int pretype, int maxl) S->ops->setatimes = SUNLinSolSetATimes_SPFGMR; S->ops->setpreconditioner = SUNLinSolSetPreconditioner_SPFGMR; S->ops->setscalingvectors = SUNLinSolSetScalingVectors_SPFGMR; + S->ops->setzeroguess = SUNLinSolSetZeroGuess_SPFGMR; S->ops->initialize = SUNLinSolInitialize_SPFGMR; S->ops->setup = SUNLinSolSetup_SPFGMR; S->ops->solve = SUNLinSolSolve_SPFGMR; @@ -121,6 +122,7 @@ SUNLinearSolver SUNLinSol_SPFGMR(N_Vector y, int pretype, int maxl) content->pretype = pretype; content->gstype = SUNSPFGMR_GSTYPE_DEFAULT; content->max_restarts = SUNSPFGMR_MAXRS_DEFAULT; + content->zeroguess = SUNFALSE; content->numiters = 0; content->resnorm = ZERO; content->xcor = NULL; @@ -380,6 +382,16 @@ int SUNLinSolSetScalingVectors_SPFGMR(SUNLinearSolver S, N_Vector s1, } +int SUNLinSolSetZeroGuess_SPFGMR(SUNLinearSolver S, booleantype onoff) +{ + /* set flag indicating a zero initial guess */ + if (S == NULL) return(SUNLS_MEM_NULL); + SPFGMR_CONTENT(S)->zeroguess = onoff; + LASTFLAG(S) = SUNLS_SUCCESS; + return(LASTFLAG(S)); +} + + int SUNLinSolSetup_SPFGMR(SUNLinearSolver S, SUNMatrix A) { int ier; @@ -397,7 +409,7 @@ int SUNLinSolSetup_SPFGMR(SUNLinearSolver S, SUNMatrix A) ier = Psetup(PData); if (ier != 0) { LASTFLAG(S) = (ier < 0) ? - SUNLS_PSET_FAIL_UNREC : SUNLS_PSET_FAIL_REC; + SUNLS_PSET_FAIL_UNREC : SUNLS_PSET_FAIL_REC; return(LASTFLAG(S)); } } @@ -415,6 +427,7 @@ int SUNLinSolSolve_SPFGMR(SUNLinearSolver S, SUNMatrix A, N_Vector x, realtype **Hes, *givens, *yg, *res_norm; realtype beta, rotation_product, r_norm, s_product, rho; booleantype preOnRight, scale1, scale2, converged; + booleantype *zeroguess; int i, j, k, l, l_max, krydim, ier, ntries, max_restarts, gstype; int *nli; void *A_data, *P_data; @@ -446,6 +459,7 @@ int SUNLinSolSolve_SPFGMR(SUNLinearSolver S, SUNMatrix A, N_Vector x, P_data = SPFGMR_CONTENT(S)->PData; atimes = SPFGMR_CONTENT(S)->ATimes; psolve = SPFGMR_CONTENT(S)->Psolve; + zeroguess = &(SPFGMR_CONTENT(S)->zeroguess); nli = &(SPFGMR_CONTENT(S)->numiters); res_norm = &(SPFGMR_CONTENT(S)->resnorm); cv = SPFGMR_CONTENT(S)->cv; @@ -469,22 +483,25 @@ int SUNLinSolSolve_SPFGMR(SUNLinearSolver S, SUNMatrix A, N_Vector x, /* Check if Atimes function has been set */ if (atimes == NULL) { + *zeroguess = SUNFALSE; LASTFLAG(S) = SUNLS_ATIMES_NULL; return(LASTFLAG(S)); } /* If preconditioning, check if psolve has been set */ if (preOnRight && psolve == NULL) { + *zeroguess = SUNFALSE; LASTFLAG(S) = SUNLS_PSOLVE_NULL; return(LASTFLAG(S)); } /* Set vtemp and V[0] to initial (unscaled) residual r_0 = b - A*x_0 */ - if (N_VDotProd(x, x) == ZERO) { + if (*zeroguess) { N_VScale(ONE, b, vtemp); } else { ier = atimes(A_data, x, vtemp); if (ier != 0) { + *zeroguess = SUNFALSE; LASTFLAG(S) = (ier < 0) ? SUNLS_ATIMES_FAIL_UNREC : SUNLS_ATIMES_FAIL_REC; return(LASTFLAG(S)); @@ -513,6 +530,7 @@ int SUNLinSolSolve_SPFGMR(SUNLinearSolver S, SUNMatrix A, N_Vector x, #endif if (r_norm <= delta) { + *zeroguess = SUNFALSE; LASTFLAG(S) = SUNLS_SUCCESS; return(LASTFLAG(S)); } @@ -552,6 +570,7 @@ int SUNLinSolSolve_SPFGMR(SUNLinearSolver S, SUNMatrix A, N_Vector x, N_VScale(ONE, vtemp, V[l+1]); ier = psolve(P_data, V[l+1], vtemp, delta, PREC_RIGHT); if (ier != 0) { + *zeroguess = SUNFALSE; LASTFLAG(S) = (ier < 0) ? SUNLS_PSOLVE_FAIL_UNREC : SUNLS_PSOLVE_FAIL_REC; return(LASTFLAG(S)); @@ -562,6 +581,7 @@ int SUNLinSolSolve_SPFGMR(SUNLinearSolver S, SUNMatrix A, N_Vector x, /* Apply A: V[l+1] = A P_inv s2_inv V[l]. */ ier = atimes(A_data, vtemp, V[l+1]); if (ier != 0) { + *zeroguess = SUNFALSE; LASTFLAG(S) = (ier < 0) ? SUNLS_ATIMES_FAIL_UNREC : SUNLS_ATIMES_FAIL_REC; return(LASTFLAG(S)); @@ -573,11 +593,13 @@ int SUNLinSolSolve_SPFGMR(SUNLinearSolver S, SUNMatrix A, N_Vector x, /* Orthogonalize V[l+1] against previous V[i]: V[l+1] = w_tilde. */ if (gstype == CLASSICAL_GS) { if (ClassicalGS(V, Hes, l+1, l_max, &(Hes[l+1][l]), cv, Xv) != 0) { + *zeroguess = SUNFALSE; LASTFLAG(S) = SUNLS_GS_FAIL; return(LASTFLAG(S)); } } else { if (ModifiedGS(V, Hes, l+1, l_max, &(Hes[l+1][l])) != 0) { + *zeroguess = SUNFALSE; LASTFLAG(S) = SUNLS_GS_FAIL; return(LASTFLAG(S)); } @@ -585,6 +607,7 @@ int SUNLinSolSolve_SPFGMR(SUNLinearSolver S, SUNMatrix A, N_Vector x, /* Update the QR factorization of Hes. */ if(QRfact(krydim, Hes, givens, l) != 0 ) { + *zeroguess = SUNFALSE; LASTFLAG(S) = SUNLS_QRFACT_FAIL; return(LASTFLAG(S)); } @@ -615,6 +638,7 @@ int SUNLinSolSolve_SPFGMR(SUNLinearSolver S, SUNMatrix A, N_Vector x, yg[0] = r_norm; for (i=1; i<=krydim; i++) yg[i]=ZERO; if (QRsol(krydim, Hes, givens, yg) != 0) { + *zeroguess = SUNFALSE; LASTFLAG(S) = SUNLS_QRSOL_FAIL; return(LASTFLAG(S)); } @@ -628,14 +652,21 @@ int SUNLinSolSolve_SPFGMR(SUNLinearSolver S, SUNMatrix A, N_Vector x, Xv[k+1] = Z[k]; } ier = N_VLinearCombination(krydim+1, cv, Xv, xcor); - if (ier != SUNLS_SUCCESS) return(SUNLS_VECTOROP_ERR); + if (ier != SUNLS_SUCCESS) { + *zeroguess = SUNFALSE; + LASTFLAG(S) = SUNLS_VECTOROP_ERR; + return(SUNLS_VECTOROP_ERR); + } /* If converged, construct the final solution vector x and return. */ if (converged) { - N_VLinearSum(ONE, x, ONE, xcor, x); { - LASTFLAG(S) = SUNLS_SUCCESS; - return(LASTFLAG(S)); - } + if (*zeroguess) + N_VScale(ONE, xcor, x); + else + N_VLinearSum(ONE, x, ONE, xcor, x); + *zeroguess = SUNFALSE; + LASTFLAG(S) = SUNLS_SUCCESS; + return(LASTFLAG(S)); } /* Not yet converged; if allowed, prepare for restart. */ @@ -661,7 +692,11 @@ int SUNLinSolSolve_SPFGMR(SUNLinearSolver S, SUNMatrix A, N_Vector x, Xv[k] = V[k]; } ier = N_VLinearCombination(krydim+1, cv, Xv, V[0]); - if (ier != SUNLS_SUCCESS) return(SUNLS_VECTOROP_ERR); + if (ier != SUNLS_SUCCESS) { + *zeroguess = SUNFALSE; + LASTFLAG(S) = SUNLS_VECTOROP_ERR; + return(SUNLS_VECTOROP_ERR); + } } @@ -669,12 +704,16 @@ int SUNLinSolSolve_SPFGMR(SUNLinearSolver S, SUNMatrix A, N_Vector x, If the residual norm was reduced below its initial value, compute and return x anyway. Otherwise return failure flag. */ if (rho < beta) { - N_VLinearSum(ONE, x, ONE, xcor, x); { - LASTFLAG(S) = SUNLS_RES_REDUCED; - return(LASTFLAG(S)); - } + if (*zeroguess) + N_VScale(ONE, xcor, x); + else + N_VLinearSum(ONE, x, ONE, xcor, x); + *zeroguess = SUNFALSE; + LASTFLAG(S) = SUNLS_RES_REDUCED; + return(LASTFLAG(S)); } + *zeroguess = SUNFALSE; LASTFLAG(S) = SUNLS_CONV_FAIL; return(LASTFLAG(S)); } diff --git a/deps/AMICI/ThirdParty/sundials/src/sunlinsol/spgmr/sunlinsol_spgmr.c b/deps/AMICI/ThirdParty/sundials/src/sunlinsol/spgmr/sunlinsol_spgmr.c index 5b77e6999..8676679ed 100644 --- a/deps/AMICI/ThirdParty/sundials/src/sunlinsol/spgmr/sunlinsol_spgmr.c +++ b/deps/AMICI/ThirdParty/sundials/src/sunlinsol/spgmr/sunlinsol_spgmr.c @@ -94,6 +94,7 @@ SUNLinearSolver SUNLinSol_SPGMR(N_Vector y, int pretype, int maxl) S->ops->setatimes = SUNLinSolSetATimes_SPGMR; S->ops->setpreconditioner = SUNLinSolSetPreconditioner_SPGMR; S->ops->setscalingvectors = SUNLinSolSetScalingVectors_SPGMR; + S->ops->setzeroguess = SUNLinSolSetZeroGuess_SPGMR; S->ops->initialize = SUNLinSolInitialize_SPGMR; S->ops->setup = SUNLinSolSetup_SPGMR; S->ops->solve = SUNLinSolSolve_SPGMR; @@ -118,6 +119,7 @@ SUNLinearSolver SUNLinSol_SPGMR(N_Vector y, int pretype, int maxl) content->pretype = pretype; content->gstype = SUNSPGMR_GSTYPE_DEFAULT; content->max_restarts = SUNSPGMR_MAXRS_DEFAULT; + content->zeroguess = SUNFALSE; content->numiters = 0; content->resnorm = ZERO; content->xcor = NULL; @@ -367,6 +369,16 @@ int SUNLinSolSetScalingVectors_SPGMR(SUNLinearSolver S, N_Vector s1, } +int SUNLinSolSetZeroGuess_SPGMR(SUNLinearSolver S, booleantype onff) +{ + /* set flag indicating a zero initial guess */ + if (S == NULL) return(SUNLS_MEM_NULL); + SPGMR_CONTENT(S)->zeroguess = onff; + LASTFLAG(S) = SUNLS_SUCCESS; + return(LASTFLAG(S)); +} + + int SUNLinSolSetup_SPGMR(SUNLinearSolver S, SUNMatrix A) { int ier; @@ -402,6 +414,7 @@ int SUNLinSolSolve_SPGMR(SUNLinearSolver S, SUNMatrix A, N_Vector x, realtype **Hes, *givens, *yg, *res_norm; realtype beta, rotation_product, r_norm, s_product, rho; booleantype preOnLeft, preOnRight, scale2, scale1, converged; + booleantype *zeroguess; int i, j, k, l, l_plus_1, l_max, krydim, ier, ntries, max_restarts, gstype; int *nli; void *A_data, *P_data; @@ -433,6 +446,7 @@ int SUNLinSolSolve_SPGMR(SUNLinearSolver S, SUNMatrix A, N_Vector x, P_data = SPGMR_CONTENT(S)->PData; atimes = SPGMR_CONTENT(S)->ATimes; psolve = SPGMR_CONTENT(S)->Psolve; + zeroguess = &(SPGMR_CONTENT(S)->zeroguess); nli = &(SPGMR_CONTENT(S)->numiters); res_norm = &(SPGMR_CONTENT(S)->resnorm); cv = SPGMR_CONTENT(S)->cv; @@ -457,22 +471,25 @@ int SUNLinSolSolve_SPGMR(SUNLinearSolver S, SUNMatrix A, N_Vector x, /* Check if Atimes function has been set */ if (atimes == NULL) { + *zeroguess = SUNFALSE; LASTFLAG(S) = SUNLS_ATIMES_NULL; return(LASTFLAG(S)); } /* If preconditioning, check if psolve has been set */ if ((preOnLeft || preOnRight) && psolve == NULL) { + *zeroguess = SUNFALSE; LASTFLAG(S) = SUNLS_PSOLVE_NULL; return(LASTFLAG(S)); } /* Set vtemp and V[0] to initial (unscaled) residual r_0 = b - A*x_0 */ - if (N_VDotProd(x, x) == ZERO) { + if (*zeroguess) { N_VScale(ONE, b, vtemp); } else { ier = atimes(A_data, x, vtemp); if (ier != 0) { + *zeroguess = SUNFALSE; LASTFLAG(S) = (ier < 0) ? SUNLS_ATIMES_FAIL_UNREC : SUNLS_ATIMES_FAIL_REC; return(LASTFLAG(S)); @@ -485,6 +502,7 @@ int SUNLinSolSolve_SPGMR(SUNLinearSolver S, SUNMatrix A, N_Vector x, if (preOnLeft) { ier = psolve(P_data, V[0], vtemp, delta, PREC_LEFT); if (ier != 0) { + *zeroguess = SUNFALSE; LASTFLAG(S) = (ier < 0) ? SUNLS_PSOLVE_FAIL_UNREC : SUNLS_PSOLVE_FAIL_REC; return(LASTFLAG(S)); @@ -514,6 +532,7 @@ int SUNLinSolSolve_SPGMR(SUNLinearSolver S, SUNMatrix A, N_Vector x, #endif if (r_norm <= delta) { + *zeroguess = SUNFALSE; LASTFLAG(S) = SUNLS_SUCCESS; return(LASTFLAG(S)); } @@ -552,6 +571,7 @@ int SUNLinSolSolve_SPGMR(SUNLinearSolver S, SUNMatrix A, N_Vector x, N_VScale(ONE, vtemp, V[l_plus_1]); ier = psolve(P_data, V[l_plus_1], vtemp, delta, PREC_RIGHT); if (ier != 0) { + *zeroguess = SUNFALSE; LASTFLAG(S) = (ier < 0) ? SUNLS_PSOLVE_FAIL_UNREC : SUNLS_PSOLVE_FAIL_REC; return(LASTFLAG(S)); @@ -561,6 +581,7 @@ int SUNLinSolSolve_SPGMR(SUNLinearSolver S, SUNMatrix A, N_Vector x, /* Apply A: V[l+1] = A P2_inv s2_inv V[l] */ ier = atimes( A_data, vtemp, V[l_plus_1] ); if (ier != 0) { + *zeroguess = SUNFALSE; LASTFLAG(S) = (ier < 0) ? SUNLS_ATIMES_FAIL_UNREC : SUNLS_ATIMES_FAIL_REC; return(LASTFLAG(S)); @@ -570,6 +591,7 @@ int SUNLinSolSolve_SPGMR(SUNLinearSolver S, SUNMatrix A, N_Vector x, if (preOnLeft) { ier = psolve(P_data, V[l_plus_1], vtemp, delta, PREC_LEFT); if (ier != 0) { + *zeroguess = SUNFALSE; LASTFLAG(S) = (ier < 0) ? SUNLS_PSOLVE_FAIL_UNREC : SUNLS_PSOLVE_FAIL_REC; return(LASTFLAG(S)); @@ -589,11 +611,13 @@ int SUNLinSolSolve_SPGMR(SUNLinearSolver S, SUNMatrix A, N_Vector x, if (gstype == CLASSICAL_GS) { if (ClassicalGS(V, Hes, l_plus_1, l_max, &(Hes[l_plus_1][l]), cv, Xv) != 0) { + *zeroguess = SUNFALSE; LASTFLAG(S) = SUNLS_GS_FAIL; return(LASTFLAG(S)); } } else { if (ModifiedGS(V, Hes, l_plus_1, l_max, &(Hes[l_plus_1][l])) != 0) { + *zeroguess = SUNFALSE; LASTFLAG(S) = SUNLS_GS_FAIL; return(LASTFLAG(S)); } @@ -601,6 +625,7 @@ int SUNLinSolSolve_SPGMR(SUNLinearSolver S, SUNMatrix A, N_Vector x, /* Update the QR factorization of Hes */ if(QRfact(krydim, Hes, givens, l) != 0 ) { + *zeroguess = SUNFALSE; LASTFLAG(S) = SUNLS_QRFACT_FAIL; return(LASTFLAG(S)); } @@ -631,6 +656,7 @@ int SUNLinSolSolve_SPGMR(SUNLinearSolver S, SUNMatrix A, N_Vector x, yg[0] = r_norm; for (i=1; i<=krydim; i++) yg[i]=ZERO; if (QRsol(krydim, Hes, givens, yg) != 0) { + *zeroguess = SUNFALSE; LASTFLAG(S) = SUNLS_QRSOL_FAIL; return(LASTFLAG(S)); } @@ -644,7 +670,11 @@ int SUNLinSolSolve_SPGMR(SUNLinearSolver S, SUNMatrix A, N_Vector x, Xv[k+1] = V[k]; } ier = N_VLinearCombination(krydim+1, cv, Xv, xcor); - if (ier != SUNLS_SUCCESS) return(SUNLS_VECTOROP_ERR); + if (ier != SUNLS_SUCCESS) { + *zeroguess = SUNFALSE; + LASTFLAG(S) = SUNLS_VECTOROP_ERR; + return(SUNLS_VECTOROP_ERR); + } /* If converged, construct the final solution vector x and return */ if (converged) { @@ -654,6 +684,7 @@ int SUNLinSolSolve_SPGMR(SUNLinearSolver S, SUNMatrix A, N_Vector x, if (preOnRight) { ier = psolve(P_data, xcor, vtemp, delta, PREC_RIGHT); if (ier != 0) { + *zeroguess = SUNFALSE; LASTFLAG(S) = (ier < 0) ? SUNLS_PSOLVE_FAIL_UNREC : SUNLS_PSOLVE_FAIL_REC; return(LASTFLAG(S)); @@ -663,8 +694,12 @@ int SUNLinSolSolve_SPGMR(SUNLinearSolver S, SUNMatrix A, N_Vector x, } /* Add vtemp to initial x to get final solution x, and return */ - N_VLinearSum(ONE, x, ONE, vtemp, x); + if (*zeroguess) + N_VScale(ONE, vtemp, x); + else + N_VLinearSum(ONE, x, ONE, vtemp, x); + *zeroguess = SUNFALSE; LASTFLAG(S) = SUNLS_SUCCESS; return(LASTFLAG(S)); } @@ -692,7 +727,11 @@ int SUNLinSolSolve_SPGMR(SUNLinearSolver S, SUNMatrix A, N_Vector x, Xv[k] = V[k]; } ier = N_VLinearCombination(krydim+1, cv, Xv, V[0]); - if (ier != SUNLS_SUCCESS) return(SUNLS_VECTOROP_ERR); + if (ier != SUNLS_SUCCESS) { + *zeroguess = SUNFALSE; + LASTFLAG(S) = SUNLS_VECTOROP_ERR; + return(SUNLS_VECTOROP_ERR); + } } @@ -706,21 +745,27 @@ int SUNLinSolSolve_SPGMR(SUNLinearSolver S, SUNMatrix A, N_Vector x, if (preOnRight) { ier = psolve(P_data, xcor, vtemp, delta, PREC_RIGHT); if (ier != 0) { + *zeroguess = SUNFALSE; LASTFLAG(S) = (ier < 0) ? SUNLS_PSOLVE_FAIL_UNREC : SUNLS_PSOLVE_FAIL_REC; return(LASTFLAG(S)); } - } else { + } else { N_VScale(ONE, xcor, vtemp); } /* Add vtemp to initial x to get final solution x, and return */ - N_VLinearSum(ONE, x, ONE, vtemp, x); + if (*zeroguess) + N_VScale(ONE, vtemp, x); + else + N_VLinearSum(ONE, x, ONE, vtemp, x); + *zeroguess = SUNFALSE; LASTFLAG(S) = SUNLS_RES_REDUCED; return(LASTFLAG(S)); } + *zeroguess = SUNFALSE; LASTFLAG(S) = SUNLS_CONV_FAIL; return(LASTFLAG(S)); } diff --git a/deps/AMICI/ThirdParty/sundials/src/sunlinsol/sptfqmr/sunlinsol_sptfqmr.c b/deps/AMICI/ThirdParty/sundials/src/sunlinsol/sptfqmr/sunlinsol_sptfqmr.c index 2d7873d6c..044b4ed01 100644 --- a/deps/AMICI/ThirdParty/sundials/src/sunlinsol/sptfqmr/sunlinsol_sptfqmr.c +++ b/deps/AMICI/ThirdParty/sundials/src/sunlinsol/sptfqmr/sunlinsol_sptfqmr.c @@ -91,6 +91,7 @@ SUNLinearSolver SUNLinSol_SPTFQMR(N_Vector y, int pretype, int maxl) S->ops->setatimes = SUNLinSolSetATimes_SPTFQMR; S->ops->setpreconditioner = SUNLinSolSetPreconditioner_SPTFQMR; S->ops->setscalingvectors = SUNLinSolSetScalingVectors_SPTFQMR; + S->ops->setzeroguess = SUNLinSolSetZeroGuess_SPTFQMR; S->ops->initialize = SUNLinSolInitialize_SPTFQMR; S->ops->setup = SUNLinSolSetup_SPTFQMR; S->ops->solve = SUNLinSolSolve_SPTFQMR; @@ -113,6 +114,7 @@ SUNLinearSolver SUNLinSol_SPTFQMR(N_Vector y, int pretype, int maxl) content->last_flag = 0; content->maxl = maxl; content->pretype = pretype; + content->zeroguess = SUNFALSE; content->numiters = 0; content->resnorm = ZERO; content->r_star = NULL; @@ -304,6 +306,16 @@ int SUNLinSolSetScalingVectors_SPTFQMR(SUNLinearSolver S, } +int SUNLinSolSetZeroGuess_SPTFQMR(SUNLinearSolver S, booleantype onoff) +{ + /* set flag indicating a zero initial guess */ + if (S == NULL) return(SUNLS_MEM_NULL); + SPTFQMR_CONTENT(S)->zeroguess = onoff; + LASTFLAG(S) = SUNLS_SUCCESS; + return(LASTFLAG(S)); +} + + int SUNLinSolSetup_SPTFQMR(SUNLinearSolver S, SUNMatrix A) { int ier; @@ -321,7 +333,7 @@ int SUNLinSolSetup_SPTFQMR(SUNLinearSolver S, SUNMatrix A) ier = Psetup(PData); if (ier != 0) { LASTFLAG(S) = (ier < 0) ? - SUNLS_PSET_FAIL_UNREC : SUNLS_PSET_FAIL_REC; + SUNLS_PSET_FAIL_UNREC : SUNLS_PSET_FAIL_REC; return(LASTFLAG(S)); } } @@ -339,8 +351,8 @@ int SUNLinSolSolve_SPTFQMR(SUNLinearSolver S, SUNMatrix A, N_Vector x, realtype rho[2]; realtype r_init_norm, r_curr_norm; realtype temp_val; - booleantype preOnLeft, preOnRight, scale_x, scale_b, converged; - booleantype b_ok; + booleantype preOnLeft, preOnRight, scale_x, scale_b, converged, b_ok; + booleantype *zeroguess; int n, m, ier, l_max; void *A_data, *P_data; ATimesFn atimes; @@ -372,6 +384,7 @@ int SUNLinSolSolve_SPTFQMR(SUNLinearSolver S, SUNMatrix A, N_Vector x, P_data = SPTFQMR_CONTENT(S)->PData; atimes = SPTFQMR_CONTENT(S)->ATimes; psolve = SPTFQMR_CONTENT(S)->Psolve; + zeroguess = &(SPTFQMR_CONTENT(S)->zeroguess); nli = &(SPTFQMR_CONTENT(S)->numiters); res_norm = &(SPTFQMR_CONTENT(S)->resnorm); @@ -389,6 +402,13 @@ int SUNLinSolSolve_SPTFQMR(SUNLinearSolver S, SUNMatrix A, N_Vector x, scale_x = (sx != NULL); scale_b = (sb != NULL); + /* Check for unsupported use case */ + if (preOnRight && !(*zeroguess)) { + *zeroguess = SUNFALSE; + LASTFLAG(S) = SUNLS_ILL_INPUT; + return(SUNLS_ILL_INPUT); + } + #ifdef SUNDIALS_BUILD_WITH_MONITORING if (SPTFQMR_CONTENT(S)->print_level && SPTFQMR_CONTENT(S)->info_file) fprintf(SPTFQMR_CONTENT(S)->info_file, "SUNLINSOL_SPTFQMR:\n"); @@ -396,22 +416,26 @@ int SUNLinSolSolve_SPTFQMR(SUNLinearSolver S, SUNMatrix A, N_Vector x, /* Check if Atimes function has been set */ if (atimes == NULL) { + *zeroguess = SUNFALSE; LASTFLAG(S) = SUNLS_ATIMES_NULL; return(LASTFLAG(S)); } /* If preconditioning, check if psolve has been set */ if ((preOnLeft || preOnRight) && psolve == NULL) { + *zeroguess = SUNFALSE; LASTFLAG(S) = SUNLS_PSOLVE_NULL; return(LASTFLAG(S)); } /* Set r_star to initial (unscaled) residual r_star = r_0 = b - A*x_0 */ /* NOTE: if x == 0 then just set residual to b and continue */ - if (N_VDotProd(x, x) == ZERO) N_VScale(ONE, b, r_star); - else { + if (*zeroguess) { + N_VScale(ONE, b, r_star); + } else { ier = atimes(A_data, x, r_star); if (ier != 0) { + *zeroguess = SUNFALSE; LASTFLAG(S) = (ier < 0) ? SUNLS_ATIMES_FAIL_UNREC : SUNLS_ATIMES_FAIL_REC; return(LASTFLAG(S)); @@ -423,6 +447,7 @@ int SUNLinSolSolve_SPTFQMR(SUNLinearSolver S, SUNMatrix A, N_Vector x, if (preOnLeft) { ier = psolve(P_data, r_star, vtemp1, delta, PREC_LEFT); if (ier != 0) { + *zeroguess = SUNFALSE; LASTFLAG(S) = (ier < 0) ? SUNLS_PSOLVE_FAIL_UNREC : SUNLS_PSOLVE_FAIL_REC; return(LASTFLAG(S)); @@ -453,6 +478,7 @@ int SUNLinSolSolve_SPTFQMR(SUNLinearSolver S, SUNMatrix A, N_Vector x, #endif if (r_init_norm <= delta) { + *zeroguess = SUNFALSE; LASTFLAG(S) = SUNLS_SUCCESS; return(LASTFLAG(S)); } @@ -464,6 +490,7 @@ int SUNLinSolSolve_SPTFQMR(SUNLinearSolver S, SUNMatrix A, N_Vector x, N_VScale(ONE, vtemp1, v); ier = psolve(P_data, v, vtemp1, delta, PREC_RIGHT); if (ier != 0) { + *zeroguess = SUNFALSE; LASTFLAG(S) = (ier < 0) ? SUNLS_PSOLVE_FAIL_UNREC : SUNLS_PSOLVE_FAIL_REC; return(LASTFLAG(S)); @@ -471,6 +498,7 @@ int SUNLinSolSolve_SPTFQMR(SUNLinearSolver S, SUNMatrix A, N_Vector x, } ier = atimes(A_data, vtemp1, v); if (ier != 0) { + *zeroguess = SUNFALSE; LASTFLAG(S) = (ier < 0) ? SUNLS_ATIMES_FAIL_UNREC : SUNLS_ATIMES_FAIL_REC; return(LASTFLAG(S)); @@ -478,6 +506,7 @@ int SUNLinSolSolve_SPTFQMR(SUNLinearSolver S, SUNMatrix A, N_Vector x, if (preOnLeft) { ier = psolve(P_data, v, vtemp1, delta, PREC_LEFT); if (ier != 0) { + *zeroguess = SUNFALSE; LASTFLAG(S) = (ier < 0) ? SUNLS_PSOLVE_FAIL_UNREC : SUNLS_PSOLVE_FAIL_REC; return(LASTFLAG(S)); @@ -493,6 +522,9 @@ int SUNLinSolSolve_SPTFQMR(SUNLinearSolver S, SUNMatrix A, N_Vector x, N_VScale(ONE, r_star, p); N_VConst(ZERO, d); + /* Set x = sx x if non-zero guess */ + if (scale_x && !(*zeroguess)) N_VProd(sx, x, x); + tau = r_init_norm; v_bar = eta = ZERO; @@ -518,6 +550,7 @@ int SUNLinSolSolve_SPTFQMR(SUNLinearSolver S, SUNMatrix A, N_Vector x, N_VScale(ONE, r[1], vtemp1); ier = psolve(P_data, vtemp1, r[1], delta, PREC_RIGHT); if (ier != 0) { + *zeroguess = SUNFALSE; LASTFLAG(S) = (ier < 0) ? SUNLS_PSOLVE_FAIL_UNREC : SUNLS_PSOLVE_FAIL_REC; return(LASTFLAG(S)); @@ -525,6 +558,7 @@ int SUNLinSolSolve_SPTFQMR(SUNLinearSolver S, SUNMatrix A, N_Vector x, } ier = atimes(A_data, r[1], vtemp1); if (ier != 0) { + *zeroguess = SUNFALSE; LASTFLAG(S) = (ier < 0) ? SUNLS_ATIMES_FAIL_UNREC : SUNLS_ATIMES_FAIL_REC; return(LASTFLAG(S)); @@ -532,6 +566,7 @@ int SUNLinSolSolve_SPTFQMR(SUNLinearSolver S, SUNMatrix A, N_Vector x, if (preOnLeft) { ier = psolve(P_data, vtemp1, r[1], delta, PREC_LEFT); if (ier != 0) { + *zeroguess = SUNFALSE; LASTFLAG(S) = (ier < 0) ? SUNLS_PSOLVE_FAIL_UNREC : SUNLS_PSOLVE_FAIL_REC; return(LASTFLAG(S)); @@ -552,13 +587,13 @@ int SUNLinSolSolve_SPTFQMR(SUNLinearSolver S, SUNMatrix A, N_Vector x, * if the inner loop is executed twice */ if (m == 0) { - temp_val = SUNRsqrt(N_VDotProd(r[1], r[1])); - omega = SUNRsqrt(SUNRsqrt(N_VDotProd(r[0], r[0]))*temp_val); - N_VLinearSum(ONE, u, SUNSQR(v_bar)*eta/alpha, d, d); + temp_val = SUNRsqrt(N_VDotProd(r[1], r[1])); + omega = SUNRsqrt(SUNRsqrt(N_VDotProd(r[0], r[0]))*temp_val); + N_VLinearSum(ONE, u, SUNSQR(v_bar)*eta/alpha, d, d); } else { - omega = temp_val; - N_VLinearSum(ONE, q, SUNSQR(v_bar)*eta/alpha, d, d); + omega = temp_val; + N_VLinearSum(ONE, q, SUNSQR(v_bar)*eta/alpha, d, d); } /* v_bar = omega/tau */ @@ -574,7 +609,10 @@ int SUNLinSolSolve_SPTFQMR(SUNLinearSolver S, SUNMatrix A, N_Vector x, eta = SUNSQR(c)*alpha; /* x = x+eta*d */ - N_VLinearSum(ONE, x, eta, d, x); + if (n == 0 && m == 0 && *zeroguess) + N_VScale(eta, d, x); + else + N_VLinearSum(ONE, x, eta, d, x); /* Check for convergence... */ /* NOTE: just use approximation to norm of residual, if possible */ @@ -591,7 +629,7 @@ int SUNLinSolSolve_SPTFQMR(SUNLinearSolver S, SUNMatrix A, N_Vector x, #endif /* Exit inner loop if iteration has converged based upon approximation - to norm of current residual */ + to norm of current residual */ if (r_curr_norm <= delta) { converged = SUNTRUE; break; @@ -610,60 +648,64 @@ int SUNLinSolSolve_SPTFQMR(SUNLinearSolver S, SUNMatrix A, N_Vector x, * number of psovles if using left preconditioning */ if ((r_curr_norm > delta) || - (r_curr_norm >= r_init_norm && m == 1 && n == l_max)) { - - /* Compute norm of residual ||b-A*x||_2 (preconditioned and scaled) */ - if (scale_x) N_VDiv(x, sx, vtemp1); - else N_VScale(ONE, x, vtemp1); - if (preOnRight) { - ier = psolve(P_data, vtemp1, vtemp2, delta, PREC_RIGHT); - if (ier != 0) { + (r_curr_norm >= r_init_norm && m == 1 && n == l_max)) { + + /* Compute norm of residual ||b-A*x||_2 (preconditioned and scaled) */ + if (scale_x) N_VDiv(x, sx, vtemp1); + else N_VScale(ONE, x, vtemp1); + if (preOnRight) { + ier = psolve(P_data, vtemp1, vtemp2, delta, PREC_RIGHT); + if (ier != 0) { + *zeroguess = SUNFALSE; LASTFLAG(S) = (ier < 0) ? SUNLS_PSOLVE_FAIL_UNREC : SUNLS_PSOLVE_FAIL_UNREC; return(LASTFLAG(S)); } - N_VScale(ONE, vtemp2, vtemp1); - } - ier = atimes(A_data, vtemp1, vtemp2); + N_VScale(ONE, vtemp2, vtemp1); + } + ier = atimes(A_data, vtemp1, vtemp2); if (ier != 0) { + *zeroguess = SUNFALSE; LASTFLAG(S) = (ier < 0) ? SUNLS_ATIMES_FAIL_UNREC : SUNLS_ATIMES_FAIL_REC; return(LASTFLAG(S)); } - if (preOnLeft) { - ier = psolve(P_data, vtemp2, vtemp1, delta, PREC_LEFT); - if (ier != 0) { + if (preOnLeft) { + ier = psolve(P_data, vtemp2, vtemp1, delta, PREC_LEFT); + if (ier != 0) { + *zeroguess = SUNFALSE; LASTFLAG(S) = (ier < 0) ? SUNLS_PSOLVE_FAIL_UNREC : SUNLS_PSOLVE_FAIL_REC; return(LASTFLAG(S)); } - } - else N_VScale(ONE, vtemp2, vtemp1); - if (scale_b) N_VProd(sb, vtemp1, vtemp2); - else N_VScale(ONE, vtemp1, vtemp2); - /* Only precondition and scale b once (result saved for reuse) */ - if (!b_ok) { - b_ok = SUNTRUE; - if (preOnLeft) { - ier = psolve(P_data, b, vtemp3, delta, PREC_LEFT); - if (ier != 0) { + } + else N_VScale(ONE, vtemp2, vtemp1); + if (scale_b) N_VProd(sb, vtemp1, vtemp2); + else N_VScale(ONE, vtemp1, vtemp2); + /* Only precondition and scale b once (result saved for reuse) */ + if (!b_ok) { + b_ok = SUNTRUE; + if (preOnLeft) { + ier = psolve(P_data, b, vtemp3, delta, PREC_LEFT); + if (ier != 0) { + *zeroguess = SUNFALSE; LASTFLAG(S) = (ier < 0) ? SUNLS_PSOLVE_FAIL_UNREC : SUNLS_PSOLVE_FAIL_REC; return(LASTFLAG(S)); } - } - else N_VScale(ONE, b, vtemp3); - if (scale_b) N_VProd(sb, vtemp3, vtemp3); - } - N_VLinearSum(ONE, vtemp3, -ONE, vtemp2, vtemp1); - *res_norm = r_curr_norm = SUNRsqrt(N_VDotProd(vtemp1, vtemp1)); - - /* Exit inner loop if inequality condition is satisfied - (meaning exit if we have converged) */ - if (r_curr_norm <= delta) { - converged = SUNTRUE; - break; - } + } + else N_VScale(ONE, b, vtemp3); + if (scale_b) N_VProd(sb, vtemp3, vtemp3); + } + N_VLinearSum(ONE, vtemp3, -ONE, vtemp2, vtemp1); + *res_norm = r_curr_norm = SUNRsqrt(N_VDotProd(vtemp1, vtemp1)); + + /* Exit inner loop if inequality condition is satisfied + (meaning exit if we have converged) */ + if (r_curr_norm <= delta) { + converged = SUNTRUE; + break; + } } @@ -692,7 +734,11 @@ int SUNLinSolSolve_SPTFQMR(SUNLinearSolver S, SUNMatrix A, N_Vector x, Xv[2] = u; ier = N_VLinearCombination(3, cv, Xv, p); - if (ier != SUNLS_SUCCESS) return(SUNLS_VECTOROP_ERR); + if (ier != SUNLS_SUCCESS) { + *zeroguess = SUNFALSE; + LASTFLAG(S) = SUNLS_VECTOROP_ERR; + return(SUNLS_VECTOROP_ERR); + } /* v = A*p */ if (scale_x) N_VDiv(p, sx, vtemp1); @@ -701,6 +747,7 @@ int SUNLinSolSolve_SPTFQMR(SUNLinearSolver S, SUNMatrix A, N_Vector x, N_VScale(ONE, vtemp1, v); ier = psolve(P_data, v, vtemp1, delta, PREC_RIGHT); if (ier != 0) { + *zeroguess = SUNFALSE; LASTFLAG(S) = (ier < 0) ? SUNLS_PSOLVE_FAIL_UNREC : SUNLS_PSOLVE_FAIL_REC; return(LASTFLAG(S)); @@ -708,6 +755,7 @@ int SUNLinSolSolve_SPTFQMR(SUNLinearSolver S, SUNMatrix A, N_Vector x, } ier = atimes(A_data, vtemp1, v); if (ier != 0) { + *zeroguess = SUNFALSE; LASTFLAG(S) = (ier < 0) ? SUNLS_ATIMES_FAIL_UNREC : SUNLS_ATIMES_FAIL_REC; return(LASTFLAG(S)); @@ -715,6 +763,7 @@ int SUNLinSolSolve_SPTFQMR(SUNLinearSolver S, SUNMatrix A, N_Vector x, if (preOnLeft) { ier = psolve(P_data, v, vtemp1, delta, PREC_LEFT); if (ier != 0) { + *zeroguess = SUNFALSE; LASTFLAG(S) = (ier < 0) ? SUNLS_PSOLVE_FAIL_UNREC : SUNLS_PSOLVE_FAIL_REC; return(LASTFLAG(S)); @@ -738,12 +787,15 @@ int SUNLinSolSolve_SPTFQMR(SUNLinearSolver S, SUNMatrix A, N_Vector x, if (preOnRight) { ier = psolve(P_data, x, vtemp1, delta, PREC_RIGHT); if (ier != 0) { + *zeroguess = SUNFALSE; LASTFLAG(S) = (ier < 0) ? SUNLS_PSOLVE_FAIL_UNREC : SUNLS_PSOLVE_FAIL_UNREC; return(LASTFLAG(S)); } N_VScale(ONE, vtemp1, x); } + + *zeroguess = SUNFALSE; if (converged == SUNTRUE) LASTFLAG(S) = SUNLS_SUCCESS; else @@ -752,6 +804,7 @@ int SUNLinSolSolve_SPTFQMR(SUNLinearSolver S, SUNMatrix A, N_Vector x, } /* Otherwise, return error code */ else { + *zeroguess = SUNFALSE; LASTFLAG(S) = SUNLS_CONV_FAIL; return(LASTFLAG(S)); } diff --git a/deps/AMICI/ThirdParty/sundials/src/sunmatrix/CMakeLists.txt b/deps/AMICI/ThirdParty/sundials/src/sunmatrix/CMakeLists.txt index 7cb9b3fee..2f6813ef4 100644 --- a/deps/AMICI/ThirdParty/sundials/src/sunmatrix/CMakeLists.txt +++ b/deps/AMICI/ThirdParty/sundials/src/sunmatrix/CMakeLists.txt @@ -14,19 +14,24 @@ # matrix level CMakeLists.txt for SUNDIALS # ------------------------------------------------------------------------------ -# required modules +# required native matrices add_subdirectory(band) add_subdirectory(dense) add_subdirectory(sparse) -if(BUILD_SUNMATRIX_SLUNRLOC) - add_subdirectory(slunrloc) -endif() - +# optional TPL matrices if(BUILD_SUNMATRIX_CUSPARSE) add_subdirectory(cusparse) endif() if(BUILD_SUNMATRIX_MAGMADENSE) add_subdirectory(magmadense) -endif() \ No newline at end of file +endif() + +if(BUILD_SUNMATRIX_ONEMKLDENSE) + add_subdirectory(onemkldense) +endif() + +if(BUILD_SUNMATRIX_SLUNRLOC) + add_subdirectory(slunrloc) +endif() diff --git a/deps/AMICI/ThirdParty/sundials/src/sunnonlinsol/fixedpoint/fmod/CMakeLists.txt b/deps/AMICI/ThirdParty/sundials/src/sunnonlinsol/fixedpoint/fmod/CMakeLists.txt deleted file mode 100644 index a5ded42bc..000000000 --- a/deps/AMICI/ThirdParty/sundials/src/sunnonlinsol/fixedpoint/fmod/CMakeLists.txt +++ /dev/null @@ -1,31 +0,0 @@ -# --------------------------------------------------------------- -# Programmer(s): Cody J. Balos @ LLNL -# --------------------------------------------------------------- -# SUNDIALS Copyright Start -# Copyright (c) 2002-2021, Lawrence Livermore National Security -# and Southern Methodist University. -# All rights reserved. -# -# See the top-level LICENSE and NOTICE files for details. -# -# SPDX-License-Identifier: BSD-3-Clause -# SUNDIALS Copyright End -# --------------------------------------------------------------- -# CMakeLists.txt file for the F2003 fixed-point SUNNonlinearSolver -# object library -# --------------------------------------------------------------- - -sundials_add_f2003_library(sundials_fsunnonlinsolfixedpoint_mod - SOURCES - fsunnonlinsol_fixedpoint_mod.f90 fsunnonlinsol_fixedpoint_mod.c - OBJECT_LIBRARIES - sundials_fgeneric_mod_obj - OUTPUT_NAME - sundials_fsunnonlinsolfixedpoint_mod - VERSION - ${sunnonlinsollib_VERSION} - SOVERSION - ${sunnonlinsollib_SOVERSION} -) - -message(STATUS "Added SUNNONLINSOL_FIXEDPOINT F2003 interface") diff --git a/deps/AMICI/ThirdParty/sundials/src/sunnonlinsol/fixedpoint/fmod/fsunnonlinsol_fixedpoint_mod.c b/deps/AMICI/ThirdParty/sundials/src/sunnonlinsol/fixedpoint/fmod/fsunnonlinsol_fixedpoint_mod.c deleted file mode 100644 index 1a01c6da8..000000000 --- a/deps/AMICI/ThirdParty/sundials/src/sunnonlinsol/fixedpoint/fmod/fsunnonlinsol_fixedpoint_mod.c +++ /dev/null @@ -1,443 +0,0 @@ -/* ---------------------------------------------------------------------------- - * This file was automatically generated by SWIG (http://www.swig.org). - * Version 4.0.0 - * - * This file is not intended to be easily readable and contains a number of - * coding conventions designed to improve portability and efficiency. Do not make - * changes to this file unless you know what you are doing--modify the SWIG - * interface file instead. - * ----------------------------------------------------------------------------- */ - -/* --------------------------------------------------------------- - * Programmer(s): Auto-generated by swig. - * --------------------------------------------------------------- - * SUNDIALS Copyright Start - * Copyright (c) 2002-2021, Lawrence Livermore National Security - * and Southern Methodist University. - * All rights reserved. - * - * See the top-level LICENSE and NOTICE files for details. - * - * SPDX-License-Identifier: BSD-3-Clause - * SUNDIALS Copyright End - * -------------------------------------------------------------*/ - -/* ----------------------------------------------------------------------------- - * This section contains generic SWIG labels for method/variable - * declarations/attributes, and other compiler dependent labels. - * ----------------------------------------------------------------------------- */ - -/* template workaround for compilers that cannot correctly implement the C++ standard */ -#ifndef SWIGTEMPLATEDISAMBIGUATOR -# if defined(__SUNPRO_CC) && (__SUNPRO_CC <= 0x560) -# define SWIGTEMPLATEDISAMBIGUATOR template -# elif defined(__HP_aCC) -/* Needed even with `aCC -AA' when `aCC -V' reports HP ANSI C++ B3910B A.03.55 */ -/* If we find a maximum version that requires this, the test would be __HP_aCC <= 35500 for A.03.55 */ -# define SWIGTEMPLATEDISAMBIGUATOR template -# else -# define SWIGTEMPLATEDISAMBIGUATOR -# endif -#endif - -/* inline attribute */ -#ifndef SWIGINLINE -# if defined(__cplusplus) || (defined(__GNUC__) && !defined(__STRICT_ANSI__)) -# define SWIGINLINE inline -# else -# define SWIGINLINE -# endif -#endif - -/* attribute recognised by some compilers to avoid 'unused' warnings */ -#ifndef SWIGUNUSED -# if defined(__GNUC__) -# if !(defined(__cplusplus)) || (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) -# define SWIGUNUSED __attribute__ ((__unused__)) -# else -# define SWIGUNUSED -# endif -# elif defined(__ICC) -# define SWIGUNUSED __attribute__ ((__unused__)) -# else -# define SWIGUNUSED -# endif -#endif - -#ifndef SWIG_MSC_UNSUPPRESS_4505 -# if defined(_MSC_VER) -# pragma warning(disable : 4505) /* unreferenced local function has been removed */ -# endif -#endif - -#ifndef SWIGUNUSEDPARM -# ifdef __cplusplus -# define SWIGUNUSEDPARM(p) -# else -# define SWIGUNUSEDPARM(p) p SWIGUNUSED -# endif -#endif - -/* internal SWIG method */ -#ifndef SWIGINTERN -# define SWIGINTERN static SWIGUNUSED -#endif - -/* internal inline SWIG method */ -#ifndef SWIGINTERNINLINE -# define SWIGINTERNINLINE SWIGINTERN SWIGINLINE -#endif - -/* qualifier for exported *const* global data variables*/ -#ifndef SWIGEXTERN -# ifdef __cplusplus -# define SWIGEXTERN extern -# else -# define SWIGEXTERN -# endif -#endif - -/* exporting methods */ -#if defined(__GNUC__) -# if (__GNUC__ >= 4) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) -# ifndef GCC_HASCLASSVISIBILITY -# define GCC_HASCLASSVISIBILITY -# endif -# endif -#endif - -#ifndef SWIGEXPORT -# if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) -# if defined(STATIC_LINKED) -# define SWIGEXPORT -# else -# define SWIGEXPORT __declspec(dllexport) -# endif -# else -# if defined(__GNUC__) && defined(GCC_HASCLASSVISIBILITY) -# define SWIGEXPORT __attribute__ ((visibility("default"))) -# else -# define SWIGEXPORT -# endif -# endif -#endif - -/* calling conventions for Windows */ -#ifndef SWIGSTDCALL -# if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) -# define SWIGSTDCALL __stdcall -# else -# define SWIGSTDCALL -# endif -#endif - -/* Deal with Microsoft's attempt at deprecating C standard runtime functions */ -#if !defined(SWIG_NO_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) && !defined(_CRT_SECURE_NO_DEPRECATE) -# define _CRT_SECURE_NO_DEPRECATE -#endif - -/* Deal with Microsoft's attempt at deprecating methods in the standard C++ library */ -#if !defined(SWIG_NO_SCL_SECURE_NO_DEPRECATE) && defined(_MSC_VER) && !defined(_SCL_SECURE_NO_DEPRECATE) -# define _SCL_SECURE_NO_DEPRECATE -#endif - -/* Deal with Apple's deprecated 'AssertMacros.h' from Carbon-framework */ -#if defined(__APPLE__) && !defined(__ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES) -# define __ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES 0 -#endif - -/* Intel's compiler complains if a variable which was never initialised is - * cast to void, which is a common idiom which we use to indicate that we - * are aware a variable isn't used. So we just silence that warning. - * See: https://github.com/swig/swig/issues/192 for more discussion. - */ -#ifdef __INTEL_COMPILER -# pragma warning disable 592 -#endif - -/* Errors in SWIG */ -#define SWIG_UnknownError -1 -#define SWIG_IOError -2 -#define SWIG_RuntimeError -3 -#define SWIG_IndexError -4 -#define SWIG_TypeError -5 -#define SWIG_DivisionByZero -6 -#define SWIG_OverflowError -7 -#define SWIG_SyntaxError -8 -#define SWIG_ValueError -9 -#define SWIG_SystemError -10 -#define SWIG_AttributeError -11 -#define SWIG_MemoryError -12 -#define SWIG_NullReferenceError -13 - - - - -#include -#define SWIG_exception_impl(DECL, CODE, MSG, RETURNNULL) \ - { printf("In " DECL ": " MSG); assert(0); RETURNNULL; } - - -#include -#if defined(_MSC_VER) || defined(__BORLANDC__) || defined(_WATCOM) -# ifndef snprintf -# define snprintf _snprintf -# endif -#endif - - -/* Support for the `contract` feature. - * - * Note that RETURNNULL is first because it's inserted via a 'Replaceall' in - * the fortran.cxx file. - */ -#define SWIG_contract_assert(RETURNNULL, EXPR, MSG) \ - if (!(EXPR)) { SWIG_exception_impl("$decl", SWIG_ValueError, MSG, RETURNNULL); } - - -#define SWIGVERSION 0x040000 -#define SWIG_VERSION SWIGVERSION - - -#define SWIG_as_voidptr(a) (void *)((const void *)(a)) -#define SWIG_as_voidptrptr(a) ((void)SWIG_as_voidptr(*a),(void**)(a)) - - -#include "sundials/sundials_nonlinearsolver.h" - - -#include "sunnonlinsol/sunnonlinsol_fixedpoint.h" - -SWIGEXPORT SUNNonlinearSolver _wrap_FSUNNonlinSol_FixedPoint(N_Vector farg1, int const *farg2) { - SUNNonlinearSolver fresult ; - N_Vector arg1 = (N_Vector) 0 ; - int arg2 ; - SUNNonlinearSolver result; - - arg1 = (N_Vector)(farg1); - arg2 = (int)(*farg2); - result = (SUNNonlinearSolver)SUNNonlinSol_FixedPoint(arg1,arg2); - fresult = result; - return fresult; -} - - -SWIGEXPORT SUNNonlinearSolver _wrap_FSUNNonlinSol_FixedPointSens(int const *farg1, N_Vector farg2, int const *farg3) { - SUNNonlinearSolver fresult ; - int arg1 ; - N_Vector arg2 = (N_Vector) 0 ; - int arg3 ; - SUNNonlinearSolver result; - - arg1 = (int)(*farg1); - arg2 = (N_Vector)(farg2); - arg3 = (int)(*farg3); - result = (SUNNonlinearSolver)SUNNonlinSol_FixedPointSens(arg1,arg2,arg3); - fresult = result; - return fresult; -} - - -SWIGEXPORT int _wrap_FSUNNonlinSolGetType_FixedPoint(SUNNonlinearSolver farg1) { - int fresult ; - SUNNonlinearSolver arg1 = (SUNNonlinearSolver) 0 ; - SUNNonlinearSolver_Type result; - - arg1 = (SUNNonlinearSolver)(farg1); - result = (SUNNonlinearSolver_Type)SUNNonlinSolGetType_FixedPoint(arg1); - fresult = (int)(result); - return fresult; -} - - -SWIGEXPORT int _wrap_FSUNNonlinSolInitialize_FixedPoint(SUNNonlinearSolver farg1) { - int fresult ; - SUNNonlinearSolver arg1 = (SUNNonlinearSolver) 0 ; - int result; - - arg1 = (SUNNonlinearSolver)(farg1); - result = (int)SUNNonlinSolInitialize_FixedPoint(arg1); - fresult = (int)(result); - return fresult; -} - - -SWIGEXPORT int _wrap_FSUNNonlinSolSolve_FixedPoint(SUNNonlinearSolver farg1, N_Vector farg2, N_Vector farg3, N_Vector farg4, double const *farg5, int const *farg6, void *farg7) { - int fresult ; - SUNNonlinearSolver arg1 = (SUNNonlinearSolver) 0 ; - N_Vector arg2 = (N_Vector) 0 ; - N_Vector arg3 = (N_Vector) 0 ; - N_Vector arg4 = (N_Vector) 0 ; - realtype arg5 ; - int arg6 ; - void *arg7 = (void *) 0 ; - int result; - - arg1 = (SUNNonlinearSolver)(farg1); - arg2 = (N_Vector)(farg2); - arg3 = (N_Vector)(farg3); - arg4 = (N_Vector)(farg4); - arg5 = (realtype)(*farg5); - arg6 = (int)(*farg6); - arg7 = (void *)(farg7); - result = (int)SUNNonlinSolSolve_FixedPoint(arg1,arg2,arg3,arg4,arg5,arg6,arg7); - fresult = (int)(result); - return fresult; -} - - -SWIGEXPORT int _wrap_FSUNNonlinSolFree_FixedPoint(SUNNonlinearSolver farg1) { - int fresult ; - SUNNonlinearSolver arg1 = (SUNNonlinearSolver) 0 ; - int result; - - arg1 = (SUNNonlinearSolver)(farg1); - result = (int)SUNNonlinSolFree_FixedPoint(arg1); - fresult = (int)(result); - return fresult; -} - - -SWIGEXPORT int _wrap_FSUNNonlinSolSetSysFn_FixedPoint(SUNNonlinearSolver farg1, SUNNonlinSolSysFn farg2) { - int fresult ; - SUNNonlinearSolver arg1 = (SUNNonlinearSolver) 0 ; - SUNNonlinSolSysFn arg2 = (SUNNonlinSolSysFn) 0 ; - int result; - - arg1 = (SUNNonlinearSolver)(farg1); - arg2 = (SUNNonlinSolSysFn)(farg2); - result = (int)SUNNonlinSolSetSysFn_FixedPoint(arg1,arg2); - fresult = (int)(result); - return fresult; -} - - -SWIGEXPORT int _wrap_FSUNNonlinSolSetConvTestFn_FixedPoint(SUNNonlinearSolver farg1, SUNNonlinSolConvTestFn farg2, void *farg3) { - int fresult ; - SUNNonlinearSolver arg1 = (SUNNonlinearSolver) 0 ; - SUNNonlinSolConvTestFn arg2 = (SUNNonlinSolConvTestFn) 0 ; - void *arg3 = (void *) 0 ; - int result; - - arg1 = (SUNNonlinearSolver)(farg1); - arg2 = (SUNNonlinSolConvTestFn)(farg2); - arg3 = (void *)(farg3); - result = (int)SUNNonlinSolSetConvTestFn_FixedPoint(arg1,arg2,arg3); - fresult = (int)(result); - return fresult; -} - - -SWIGEXPORT int _wrap_FSUNNonlinSolSetMaxIters_FixedPoint(SUNNonlinearSolver farg1, int const *farg2) { - int fresult ; - SUNNonlinearSolver arg1 = (SUNNonlinearSolver) 0 ; - int arg2 ; - int result; - - arg1 = (SUNNonlinearSolver)(farg1); - arg2 = (int)(*farg2); - result = (int)SUNNonlinSolSetMaxIters_FixedPoint(arg1,arg2); - fresult = (int)(result); - return fresult; -} - - -SWIGEXPORT int _wrap_FSUNNonlinSolSetDamping_FixedPoint(SUNNonlinearSolver farg1, double const *farg2) { - int fresult ; - SUNNonlinearSolver arg1 = (SUNNonlinearSolver) 0 ; - realtype arg2 ; - int result; - - arg1 = (SUNNonlinearSolver)(farg1); - arg2 = (realtype)(*farg2); - result = (int)SUNNonlinSolSetDamping_FixedPoint(arg1,arg2); - fresult = (int)(result); - return fresult; -} - - -SWIGEXPORT int _wrap_FSUNNonlinSolGetNumIters_FixedPoint(SUNNonlinearSolver farg1, long *farg2) { - int fresult ; - SUNNonlinearSolver arg1 = (SUNNonlinearSolver) 0 ; - long *arg2 = (long *) 0 ; - int result; - - arg1 = (SUNNonlinearSolver)(farg1); - arg2 = (long *)(farg2); - result = (int)SUNNonlinSolGetNumIters_FixedPoint(arg1,arg2); - fresult = (int)(result); - return fresult; -} - - -SWIGEXPORT int _wrap_FSUNNonlinSolGetCurIter_FixedPoint(SUNNonlinearSolver farg1, int *farg2) { - int fresult ; - SUNNonlinearSolver arg1 = (SUNNonlinearSolver) 0 ; - int *arg2 = (int *) 0 ; - int result; - - arg1 = (SUNNonlinearSolver)(farg1); - arg2 = (int *)(farg2); - result = (int)SUNNonlinSolGetCurIter_FixedPoint(arg1,arg2); - fresult = (int)(result); - return fresult; -} - - -SWIGEXPORT int _wrap_FSUNNonlinSolGetNumConvFails_FixedPoint(SUNNonlinearSolver farg1, long *farg2) { - int fresult ; - SUNNonlinearSolver arg1 = (SUNNonlinearSolver) 0 ; - long *arg2 = (long *) 0 ; - int result; - - arg1 = (SUNNonlinearSolver)(farg1); - arg2 = (long *)(farg2); - result = (int)SUNNonlinSolGetNumConvFails_FixedPoint(arg1,arg2); - fresult = (int)(result); - return fresult; -} - - -SWIGEXPORT int _wrap_FSUNNonlinSolGetSysFn_FixedPoint(SUNNonlinearSolver farg1, void *farg2) { - int fresult ; - SUNNonlinearSolver arg1 = (SUNNonlinearSolver) 0 ; - SUNNonlinSolSysFn *arg2 = (SUNNonlinSolSysFn *) 0 ; - int result; - - arg1 = (SUNNonlinearSolver)(farg1); - arg2 = (SUNNonlinSolSysFn *)(farg2); - result = (int)SUNNonlinSolGetSysFn_FixedPoint(arg1,arg2); - fresult = (int)(result); - return fresult; -} - - -SWIGEXPORT int _wrap_FSUNNonlinSolSetInfoFile_FixedPoint(SUNNonlinearSolver farg1, void *farg2) { - int fresult ; - SUNNonlinearSolver arg1 = (SUNNonlinearSolver) 0 ; - FILE *arg2 = (FILE *) 0 ; - int result; - - arg1 = (SUNNonlinearSolver)(farg1); - arg2 = (FILE *)(farg2); - result = (int)SUNNonlinSolSetInfoFile_FixedPoint(arg1,arg2); - fresult = (int)(result); - return fresult; -} - - -SWIGEXPORT int _wrap_FSUNNonlinSolSetPrintLevel_FixedPoint(SUNNonlinearSolver farg1, int const *farg2) { - int fresult ; - SUNNonlinearSolver arg1 = (SUNNonlinearSolver) 0 ; - int arg2 ; - int result; - - arg1 = (SUNNonlinearSolver)(farg1); - arg2 = (int)(*farg2); - result = (int)SUNNonlinSolSetPrintLevel_FixedPoint(arg1,arg2); - fresult = (int)(result); - return fresult; -} - - - diff --git a/deps/AMICI/ThirdParty/sundials/src/sunnonlinsol/fixedpoint/fmod/fsunnonlinsol_fixedpoint_mod.f90 b/deps/AMICI/ThirdParty/sundials/src/sunnonlinsol/fixedpoint/fmod/fsunnonlinsol_fixedpoint_mod.f90 deleted file mode 100644 index 4b5531731..000000000 --- a/deps/AMICI/ThirdParty/sundials/src/sunnonlinsol/fixedpoint/fmod/fsunnonlinsol_fixedpoint_mod.f90 +++ /dev/null @@ -1,473 +0,0 @@ -! This file was automatically generated by SWIG (http://www.swig.org). -! Version 4.0.0 -! -! Do not make changes to this file unless you know what you are doing--modify -! the SWIG interface file instead. - -! --------------------------------------------------------------- -! Programmer(s): Auto-generated by swig. -! --------------------------------------------------------------- -! SUNDIALS Copyright Start -! Copyright (c) 2002-2021, Lawrence Livermore National Security -! and Southern Methodist University. -! All rights reserved. -! -! See the top-level LICENSE and NOTICE files for details. -! -! SPDX-License-Identifier: BSD-3-Clause -! SUNDIALS Copyright End -! --------------------------------------------------------------- - -module fsunnonlinsol_fixedpoint_mod - use, intrinsic :: ISO_C_BINDING - use fsundials_nvector_mod - use fsundials_types_mod - use fsundials_nonlinearsolver_mod - use fsundials_nvector_mod - use fsundials_types_mod - implicit none - private - - ! DECLARATION CONSTRUCTS - public :: FSUNNonlinSol_FixedPoint - public :: FSUNNonlinSol_FixedPointSens - public :: FSUNNonlinSolGetType_FixedPoint - public :: FSUNNonlinSolInitialize_FixedPoint - public :: FSUNNonlinSolSolve_FixedPoint - public :: FSUNNonlinSolFree_FixedPoint - public :: FSUNNonlinSolSetSysFn_FixedPoint - public :: FSUNNonlinSolSetConvTestFn_FixedPoint - public :: FSUNNonlinSolSetMaxIters_FixedPoint - public :: FSUNNonlinSolSetDamping_FixedPoint - public :: FSUNNonlinSolGetNumIters_FixedPoint - public :: FSUNNonlinSolGetCurIter_FixedPoint - public :: FSUNNonlinSolGetNumConvFails_FixedPoint - public :: FSUNNonlinSolGetSysFn_FixedPoint - public :: FSUNNonlinSolSetInfoFile_FixedPoint - public :: FSUNNonlinSolSetPrintLevel_FixedPoint - -! WRAPPER DECLARATIONS -interface -function swigc_FSUNNonlinSol_FixedPoint(farg1, farg2) & -bind(C, name="_wrap_FSUNNonlinSol_FixedPoint") & -result(fresult) -use, intrinsic :: ISO_C_BINDING -type(C_PTR), value :: farg1 -integer(C_INT), intent(in) :: farg2 -type(C_PTR) :: fresult -end function - -function swigc_FSUNNonlinSol_FixedPointSens(farg1, farg2, farg3) & -bind(C, name="_wrap_FSUNNonlinSol_FixedPointSens") & -result(fresult) -use, intrinsic :: ISO_C_BINDING -integer(C_INT), intent(in) :: farg1 -type(C_PTR), value :: farg2 -integer(C_INT), intent(in) :: farg3 -type(C_PTR) :: fresult -end function - -function swigc_FSUNNonlinSolGetType_FixedPoint(farg1) & -bind(C, name="_wrap_FSUNNonlinSolGetType_FixedPoint") & -result(fresult) -use, intrinsic :: ISO_C_BINDING -type(C_PTR), value :: farg1 -integer(C_INT) :: fresult -end function - -function swigc_FSUNNonlinSolInitialize_FixedPoint(farg1) & -bind(C, name="_wrap_FSUNNonlinSolInitialize_FixedPoint") & -result(fresult) -use, intrinsic :: ISO_C_BINDING -type(C_PTR), value :: farg1 -integer(C_INT) :: fresult -end function - -function swigc_FSUNNonlinSolSolve_FixedPoint(farg1, farg2, farg3, farg4, farg5, farg6, farg7) & -bind(C, name="_wrap_FSUNNonlinSolSolve_FixedPoint") & -result(fresult) -use, intrinsic :: ISO_C_BINDING -type(C_PTR), value :: farg1 -type(C_PTR), value :: farg2 -type(C_PTR), value :: farg3 -type(C_PTR), value :: farg4 -real(C_DOUBLE), intent(in) :: farg5 -integer(C_INT), intent(in) :: farg6 -type(C_PTR), value :: farg7 -integer(C_INT) :: fresult -end function - -function swigc_FSUNNonlinSolFree_FixedPoint(farg1) & -bind(C, name="_wrap_FSUNNonlinSolFree_FixedPoint") & -result(fresult) -use, intrinsic :: ISO_C_BINDING -type(C_PTR), value :: farg1 -integer(C_INT) :: fresult -end function - -function swigc_FSUNNonlinSolSetSysFn_FixedPoint(farg1, farg2) & -bind(C, name="_wrap_FSUNNonlinSolSetSysFn_FixedPoint") & -result(fresult) -use, intrinsic :: ISO_C_BINDING -type(C_PTR), value :: farg1 -type(C_FUNPTR), value :: farg2 -integer(C_INT) :: fresult -end function - -function swigc_FSUNNonlinSolSetConvTestFn_FixedPoint(farg1, farg2, farg3) & -bind(C, name="_wrap_FSUNNonlinSolSetConvTestFn_FixedPoint") & -result(fresult) -use, intrinsic :: ISO_C_BINDING -type(C_PTR), value :: farg1 -type(C_FUNPTR), value :: farg2 -type(C_PTR), value :: farg3 -integer(C_INT) :: fresult -end function - -function swigc_FSUNNonlinSolSetMaxIters_FixedPoint(farg1, farg2) & -bind(C, name="_wrap_FSUNNonlinSolSetMaxIters_FixedPoint") & -result(fresult) -use, intrinsic :: ISO_C_BINDING -type(C_PTR), value :: farg1 -integer(C_INT), intent(in) :: farg2 -integer(C_INT) :: fresult -end function - -function swigc_FSUNNonlinSolSetDamping_FixedPoint(farg1, farg2) & -bind(C, name="_wrap_FSUNNonlinSolSetDamping_FixedPoint") & -result(fresult) -use, intrinsic :: ISO_C_BINDING -type(C_PTR), value :: farg1 -real(C_DOUBLE), intent(in) :: farg2 -integer(C_INT) :: fresult -end function - -function swigc_FSUNNonlinSolGetNumIters_FixedPoint(farg1, farg2) & -bind(C, name="_wrap_FSUNNonlinSolGetNumIters_FixedPoint") & -result(fresult) -use, intrinsic :: ISO_C_BINDING -type(C_PTR), value :: farg1 -type(C_PTR), value :: farg2 -integer(C_INT) :: fresult -end function - -function swigc_FSUNNonlinSolGetCurIter_FixedPoint(farg1, farg2) & -bind(C, name="_wrap_FSUNNonlinSolGetCurIter_FixedPoint") & -result(fresult) -use, intrinsic :: ISO_C_BINDING -type(C_PTR), value :: farg1 -type(C_PTR), value :: farg2 -integer(C_INT) :: fresult -end function - -function swigc_FSUNNonlinSolGetNumConvFails_FixedPoint(farg1, farg2) & -bind(C, name="_wrap_FSUNNonlinSolGetNumConvFails_FixedPoint") & -result(fresult) -use, intrinsic :: ISO_C_BINDING -type(C_PTR), value :: farg1 -type(C_PTR), value :: farg2 -integer(C_INT) :: fresult -end function - -function swigc_FSUNNonlinSolGetSysFn_FixedPoint(farg1, farg2) & -bind(C, name="_wrap_FSUNNonlinSolGetSysFn_FixedPoint") & -result(fresult) -use, intrinsic :: ISO_C_BINDING -type(C_PTR), value :: farg1 -type(C_PTR), value :: farg2 -integer(C_INT) :: fresult -end function - -function swigc_FSUNNonlinSolSetInfoFile_FixedPoint(farg1, farg2) & -bind(C, name="_wrap_FSUNNonlinSolSetInfoFile_FixedPoint") & -result(fresult) -use, intrinsic :: ISO_C_BINDING -type(C_PTR), value :: farg1 -type(C_PTR), value :: farg2 -integer(C_INT) :: fresult -end function - -function swigc_FSUNNonlinSolSetPrintLevel_FixedPoint(farg1, farg2) & -bind(C, name="_wrap_FSUNNonlinSolSetPrintLevel_FixedPoint") & -result(fresult) -use, intrinsic :: ISO_C_BINDING -type(C_PTR), value :: farg1 -integer(C_INT), intent(in) :: farg2 -integer(C_INT) :: fresult -end function - -end interface - - -contains - ! MODULE SUBPROGRAMS -function FSUNNonlinSol_FixedPoint(y, m) & -result(swig_result) -use, intrinsic :: ISO_C_BINDING -type(SUNNonlinearSolver), pointer :: swig_result -type(N_Vector), target, intent(inout) :: y -integer(C_INT), intent(in) :: m -type(C_PTR) :: fresult -type(C_PTR) :: farg1 -integer(C_INT) :: farg2 - -farg1 = c_loc(y) -farg2 = m -fresult = swigc_FSUNNonlinSol_FixedPoint(farg1, farg2) -call c_f_pointer(fresult, swig_result) -end function - -function FSUNNonlinSol_FixedPointSens(count, y, m) & -result(swig_result) -use, intrinsic :: ISO_C_BINDING -type(SUNNonlinearSolver), pointer :: swig_result -integer(C_INT), intent(in) :: count -type(N_Vector), target, intent(inout) :: y -integer(C_INT), intent(in) :: m -type(C_PTR) :: fresult -integer(C_INT) :: farg1 -type(C_PTR) :: farg2 -integer(C_INT) :: farg3 - -farg1 = count -farg2 = c_loc(y) -farg3 = m -fresult = swigc_FSUNNonlinSol_FixedPointSens(farg1, farg2, farg3) -call c_f_pointer(fresult, swig_result) -end function - -function FSUNNonlinSolGetType_FixedPoint(nls) & -result(swig_result) -use, intrinsic :: ISO_C_BINDING -integer(SUNNonlinearSolver_Type) :: swig_result -type(SUNNonlinearSolver), target, intent(inout) :: nls -integer(C_INT) :: fresult -type(C_PTR) :: farg1 - -farg1 = c_loc(nls) -fresult = swigc_FSUNNonlinSolGetType_FixedPoint(farg1) -swig_result = fresult -end function - -function FSUNNonlinSolInitialize_FixedPoint(nls) & -result(swig_result) -use, intrinsic :: ISO_C_BINDING -integer(C_INT) :: swig_result -type(SUNNonlinearSolver), target, intent(inout) :: nls -integer(C_INT) :: fresult -type(C_PTR) :: farg1 - -farg1 = c_loc(nls) -fresult = swigc_FSUNNonlinSolInitialize_FixedPoint(farg1) -swig_result = fresult -end function - -function FSUNNonlinSolSolve_FixedPoint(nls, y0, y, w, tol, callsetup, mem) & -result(swig_result) -use, intrinsic :: ISO_C_BINDING -integer(C_INT) :: swig_result -type(SUNNonlinearSolver), target, intent(inout) :: nls -type(N_Vector), target, intent(inout) :: y0 -type(N_Vector), target, intent(inout) :: y -type(N_Vector), target, intent(inout) :: w -real(C_DOUBLE), intent(in) :: tol -integer(C_INT), intent(in) :: callsetup -type(C_PTR) :: mem -integer(C_INT) :: fresult -type(C_PTR) :: farg1 -type(C_PTR) :: farg2 -type(C_PTR) :: farg3 -type(C_PTR) :: farg4 -real(C_DOUBLE) :: farg5 -integer(C_INT) :: farg6 -type(C_PTR) :: farg7 - -farg1 = c_loc(nls) -farg2 = c_loc(y0) -farg3 = c_loc(y) -farg4 = c_loc(w) -farg5 = tol -farg6 = callsetup -farg7 = mem -fresult = swigc_FSUNNonlinSolSolve_FixedPoint(farg1, farg2, farg3, farg4, farg5, farg6, farg7) -swig_result = fresult -end function - -function FSUNNonlinSolFree_FixedPoint(nls) & -result(swig_result) -use, intrinsic :: ISO_C_BINDING -integer(C_INT) :: swig_result -type(SUNNonlinearSolver), target, intent(inout) :: nls -integer(C_INT) :: fresult -type(C_PTR) :: farg1 - -farg1 = c_loc(nls) -fresult = swigc_FSUNNonlinSolFree_FixedPoint(farg1) -swig_result = fresult -end function - -function FSUNNonlinSolSetSysFn_FixedPoint(nls, sysfn) & -result(swig_result) -use, intrinsic :: ISO_C_BINDING -integer(C_INT) :: swig_result -type(SUNNonlinearSolver), target, intent(inout) :: nls -type(C_FUNPTR), intent(in), value :: sysfn -integer(C_INT) :: fresult -type(C_PTR) :: farg1 -type(C_FUNPTR) :: farg2 - -farg1 = c_loc(nls) -farg2 = sysfn -fresult = swigc_FSUNNonlinSolSetSysFn_FixedPoint(farg1, farg2) -swig_result = fresult -end function - -function FSUNNonlinSolSetConvTestFn_FixedPoint(nls, ctestfn, ctest_data) & -result(swig_result) -use, intrinsic :: ISO_C_BINDING -integer(C_INT) :: swig_result -type(SUNNonlinearSolver), target, intent(inout) :: nls -type(C_FUNPTR), intent(in), value :: ctestfn -type(C_PTR) :: ctest_data -integer(C_INT) :: fresult -type(C_PTR) :: farg1 -type(C_FUNPTR) :: farg2 -type(C_PTR) :: farg3 - -farg1 = c_loc(nls) -farg2 = ctestfn -farg3 = ctest_data -fresult = swigc_FSUNNonlinSolSetConvTestFn_FixedPoint(farg1, farg2, farg3) -swig_result = fresult -end function - -function FSUNNonlinSolSetMaxIters_FixedPoint(nls, maxiters) & -result(swig_result) -use, intrinsic :: ISO_C_BINDING -integer(C_INT) :: swig_result -type(SUNNonlinearSolver), target, intent(inout) :: nls -integer(C_INT), intent(in) :: maxiters -integer(C_INT) :: fresult -type(C_PTR) :: farg1 -integer(C_INT) :: farg2 - -farg1 = c_loc(nls) -farg2 = maxiters -fresult = swigc_FSUNNonlinSolSetMaxIters_FixedPoint(farg1, farg2) -swig_result = fresult -end function - -function FSUNNonlinSolSetDamping_FixedPoint(nls, beta) & -result(swig_result) -use, intrinsic :: ISO_C_BINDING -integer(C_INT) :: swig_result -type(SUNNonlinearSolver), target, intent(inout) :: nls -real(C_DOUBLE), intent(in) :: beta -integer(C_INT) :: fresult -type(C_PTR) :: farg1 -real(C_DOUBLE) :: farg2 - -farg1 = c_loc(nls) -farg2 = beta -fresult = swigc_FSUNNonlinSolSetDamping_FixedPoint(farg1, farg2) -swig_result = fresult -end function - -function FSUNNonlinSolGetNumIters_FixedPoint(nls, niters) & -result(swig_result) -use, intrinsic :: ISO_C_BINDING -integer(C_INT) :: swig_result -type(SUNNonlinearSolver), target, intent(inout) :: nls -integer(C_LONG), dimension(*), target, intent(inout) :: niters -integer(C_INT) :: fresult -type(C_PTR) :: farg1 -type(C_PTR) :: farg2 - -farg1 = c_loc(nls) -farg2 = c_loc(niters(1)) -fresult = swigc_FSUNNonlinSolGetNumIters_FixedPoint(farg1, farg2) -swig_result = fresult -end function - -function FSUNNonlinSolGetCurIter_FixedPoint(nls, iter) & -result(swig_result) -use, intrinsic :: ISO_C_BINDING -integer(C_INT) :: swig_result -type(SUNNonlinearSolver), target, intent(inout) :: nls -integer(C_INT), dimension(*), target, intent(inout) :: iter -integer(C_INT) :: fresult -type(C_PTR) :: farg1 -type(C_PTR) :: farg2 - -farg1 = c_loc(nls) -farg2 = c_loc(iter(1)) -fresult = swigc_FSUNNonlinSolGetCurIter_FixedPoint(farg1, farg2) -swig_result = fresult -end function - -function FSUNNonlinSolGetNumConvFails_FixedPoint(nls, nconvfails) & -result(swig_result) -use, intrinsic :: ISO_C_BINDING -integer(C_INT) :: swig_result -type(SUNNonlinearSolver), target, intent(inout) :: nls -integer(C_LONG), dimension(*), target, intent(inout) :: nconvfails -integer(C_INT) :: fresult -type(C_PTR) :: farg1 -type(C_PTR) :: farg2 - -farg1 = c_loc(nls) -farg2 = c_loc(nconvfails(1)) -fresult = swigc_FSUNNonlinSolGetNumConvFails_FixedPoint(farg1, farg2) -swig_result = fresult -end function - -function FSUNNonlinSolGetSysFn_FixedPoint(nls, sysfn) & -result(swig_result) -use, intrinsic :: ISO_C_BINDING -integer(C_INT) :: swig_result -type(SUNNonlinearSolver), target, intent(inout) :: nls -type(C_FUNPTR), target, intent(inout) :: sysfn -integer(C_INT) :: fresult -type(C_PTR) :: farg1 -type(C_PTR) :: farg2 - -farg1 = c_loc(nls) -farg2 = c_loc(sysfn) -fresult = swigc_FSUNNonlinSolGetSysFn_FixedPoint(farg1, farg2) -swig_result = fresult -end function - -function FSUNNonlinSolSetInfoFile_FixedPoint(nls, info_file) & -result(swig_result) -use, intrinsic :: ISO_C_BINDING -integer(C_INT) :: swig_result -type(SUNNonlinearSolver), target, intent(inout) :: nls -type(C_PTR) :: info_file -integer(C_INT) :: fresult -type(C_PTR) :: farg1 -type(C_PTR) :: farg2 - -farg1 = c_loc(nls) -farg2 = info_file -fresult = swigc_FSUNNonlinSolSetInfoFile_FixedPoint(farg1, farg2) -swig_result = fresult -end function - -function FSUNNonlinSolSetPrintLevel_FixedPoint(nls, print_level) & -result(swig_result) -use, intrinsic :: ISO_C_BINDING -integer(C_INT) :: swig_result -type(SUNNonlinearSolver), target, intent(inout) :: nls -integer(C_INT), intent(in) :: print_level -integer(C_INT) :: fresult -type(C_PTR) :: farg1 -integer(C_INT) :: farg2 - -farg1 = c_loc(nls) -farg2 = print_level -fresult = swigc_FSUNNonlinSolSetPrintLevel_FixedPoint(farg1, farg2) -swig_result = fresult -end function - - -end module diff --git a/deps/AMICI/ThirdParty/sundials/src/sunnonlinsol/newton/fmod/CMakeLists.txt b/deps/AMICI/ThirdParty/sundials/src/sunnonlinsol/newton/fmod/CMakeLists.txt deleted file mode 100644 index 16be4d438..000000000 --- a/deps/AMICI/ThirdParty/sundials/src/sunnonlinsol/newton/fmod/CMakeLists.txt +++ /dev/null @@ -1,31 +0,0 @@ -# --------------------------------------------------------------- -# Programmer(s): Cody J. Balos @ LLNL -# --------------------------------------------------------------- -# SUNDIALS Copyright Start -# Copyright (c) 2002-2021, Lawrence Livermore National Security -# and Southern Methodist University. -# All rights reserved. -# -# See the top-level LICENSE and NOTICE files for details. -# -# SPDX-License-Identifier: BSD-3-Clause -# SUNDIALS Copyright End -# --------------------------------------------------------------- -# CMakeLists.txt file for the F2003 Newton SUNNonlinearSolver -# object library -# --------------------------------------------------------------- - -sundials_add_f2003_library(sundials_fsunnonlinsolnewton_mod - SOURCES - fsunnonlinsol_newton_mod.f90 fsunnonlinsol_newton_mod.c - OBJECT_LIBRARIES - sundials_fgeneric_mod_obj - OUTPUT_NAME - sundials_fsunnonlinsolnewton_mod - VERSION - ${sunnonlinsollib_VERSION} - SOVERSION - ${sunnonlinsollib_SOVERSION} -) - -message(STATUS "Added SUNNONLINSOL_NEWTON F2003 interface") diff --git a/deps/AMICI/ThirdParty/sundials/src/sunnonlinsol/newton/fmod/fsunnonlinsol_newton_mod.c b/deps/AMICI/ThirdParty/sundials/src/sunnonlinsol/newton/fmod/fsunnonlinsol_newton_mod.c deleted file mode 100644 index e011e8006..000000000 --- a/deps/AMICI/ThirdParty/sundials/src/sunnonlinsol/newton/fmod/fsunnonlinsol_newton_mod.c +++ /dev/null @@ -1,453 +0,0 @@ -/* ---------------------------------------------------------------------------- - * This file was automatically generated by SWIG (http://www.swig.org). - * Version 4.0.0 - * - * This file is not intended to be easily readable and contains a number of - * coding conventions designed to improve portability and efficiency. Do not make - * changes to this file unless you know what you are doing--modify the SWIG - * interface file instead. - * ----------------------------------------------------------------------------- */ - -/* --------------------------------------------------------------- - * Programmer(s): Auto-generated by swig. - * --------------------------------------------------------------- - * SUNDIALS Copyright Start - * Copyright (c) 2002-2021, Lawrence Livermore National Security - * and Southern Methodist University. - * All rights reserved. - * - * See the top-level LICENSE and NOTICE files for details. - * - * SPDX-License-Identifier: BSD-3-Clause - * SUNDIALS Copyright End - * -------------------------------------------------------------*/ - -/* ----------------------------------------------------------------------------- - * This section contains generic SWIG labels for method/variable - * declarations/attributes, and other compiler dependent labels. - * ----------------------------------------------------------------------------- */ - -/* template workaround for compilers that cannot correctly implement the C++ standard */ -#ifndef SWIGTEMPLATEDISAMBIGUATOR -# if defined(__SUNPRO_CC) && (__SUNPRO_CC <= 0x560) -# define SWIGTEMPLATEDISAMBIGUATOR template -# elif defined(__HP_aCC) -/* Needed even with `aCC -AA' when `aCC -V' reports HP ANSI C++ B3910B A.03.55 */ -/* If we find a maximum version that requires this, the test would be __HP_aCC <= 35500 for A.03.55 */ -# define SWIGTEMPLATEDISAMBIGUATOR template -# else -# define SWIGTEMPLATEDISAMBIGUATOR -# endif -#endif - -/* inline attribute */ -#ifndef SWIGINLINE -# if defined(__cplusplus) || (defined(__GNUC__) && !defined(__STRICT_ANSI__)) -# define SWIGINLINE inline -# else -# define SWIGINLINE -# endif -#endif - -/* attribute recognised by some compilers to avoid 'unused' warnings */ -#ifndef SWIGUNUSED -# if defined(__GNUC__) -# if !(defined(__cplusplus)) || (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) -# define SWIGUNUSED __attribute__ ((__unused__)) -# else -# define SWIGUNUSED -# endif -# elif defined(__ICC) -# define SWIGUNUSED __attribute__ ((__unused__)) -# else -# define SWIGUNUSED -# endif -#endif - -#ifndef SWIG_MSC_UNSUPPRESS_4505 -# if defined(_MSC_VER) -# pragma warning(disable : 4505) /* unreferenced local function has been removed */ -# endif -#endif - -#ifndef SWIGUNUSEDPARM -# ifdef __cplusplus -# define SWIGUNUSEDPARM(p) -# else -# define SWIGUNUSEDPARM(p) p SWIGUNUSED -# endif -#endif - -/* internal SWIG method */ -#ifndef SWIGINTERN -# define SWIGINTERN static SWIGUNUSED -#endif - -/* internal inline SWIG method */ -#ifndef SWIGINTERNINLINE -# define SWIGINTERNINLINE SWIGINTERN SWIGINLINE -#endif - -/* qualifier for exported *const* global data variables*/ -#ifndef SWIGEXTERN -# ifdef __cplusplus -# define SWIGEXTERN extern -# else -# define SWIGEXTERN -# endif -#endif - -/* exporting methods */ -#if defined(__GNUC__) -# if (__GNUC__ >= 4) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) -# ifndef GCC_HASCLASSVISIBILITY -# define GCC_HASCLASSVISIBILITY -# endif -# endif -#endif - -#ifndef SWIGEXPORT -# if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) -# if defined(STATIC_LINKED) -# define SWIGEXPORT -# else -# define SWIGEXPORT __declspec(dllexport) -# endif -# else -# if defined(__GNUC__) && defined(GCC_HASCLASSVISIBILITY) -# define SWIGEXPORT __attribute__ ((visibility("default"))) -# else -# define SWIGEXPORT -# endif -# endif -#endif - -/* calling conventions for Windows */ -#ifndef SWIGSTDCALL -# if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) -# define SWIGSTDCALL __stdcall -# else -# define SWIGSTDCALL -# endif -#endif - -/* Deal with Microsoft's attempt at deprecating C standard runtime functions */ -#if !defined(SWIG_NO_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) && !defined(_CRT_SECURE_NO_DEPRECATE) -# define _CRT_SECURE_NO_DEPRECATE -#endif - -/* Deal with Microsoft's attempt at deprecating methods in the standard C++ library */ -#if !defined(SWIG_NO_SCL_SECURE_NO_DEPRECATE) && defined(_MSC_VER) && !defined(_SCL_SECURE_NO_DEPRECATE) -# define _SCL_SECURE_NO_DEPRECATE -#endif - -/* Deal with Apple's deprecated 'AssertMacros.h' from Carbon-framework */ -#if defined(__APPLE__) && !defined(__ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES) -# define __ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES 0 -#endif - -/* Intel's compiler complains if a variable which was never initialised is - * cast to void, which is a common idiom which we use to indicate that we - * are aware a variable isn't used. So we just silence that warning. - * See: https://github.com/swig/swig/issues/192 for more discussion. - */ -#ifdef __INTEL_COMPILER -# pragma warning disable 592 -#endif - -/* Errors in SWIG */ -#define SWIG_UnknownError -1 -#define SWIG_IOError -2 -#define SWIG_RuntimeError -3 -#define SWIG_IndexError -4 -#define SWIG_TypeError -5 -#define SWIG_DivisionByZero -6 -#define SWIG_OverflowError -7 -#define SWIG_SyntaxError -8 -#define SWIG_ValueError -9 -#define SWIG_SystemError -10 -#define SWIG_AttributeError -11 -#define SWIG_MemoryError -12 -#define SWIG_NullReferenceError -13 - - - - -#include -#define SWIG_exception_impl(DECL, CODE, MSG, RETURNNULL) \ - { printf("In " DECL ": " MSG); assert(0); RETURNNULL; } - - -#include -#if defined(_MSC_VER) || defined(__BORLANDC__) || defined(_WATCOM) -# ifndef snprintf -# define snprintf _snprintf -# endif -#endif - - -/* Support for the `contract` feature. - * - * Note that RETURNNULL is first because it's inserted via a 'Replaceall' in - * the fortran.cxx file. - */ -#define SWIG_contract_assert(RETURNNULL, EXPR, MSG) \ - if (!(EXPR)) { SWIG_exception_impl("$decl", SWIG_ValueError, MSG, RETURNNULL); } - - -#define SWIGVERSION 0x040000 -#define SWIG_VERSION SWIGVERSION - - -#define SWIG_as_voidptr(a) (void *)((const void *)(a)) -#define SWIG_as_voidptrptr(a) ((void)SWIG_as_voidptr(*a),(void**)(a)) - - -#include "sundials/sundials_nonlinearsolver.h" - - -#include "sunnonlinsol/sunnonlinsol_newton.h" - -SWIGEXPORT SUNNonlinearSolver _wrap_FSUNNonlinSol_Newton(N_Vector farg1) { - SUNNonlinearSolver fresult ; - N_Vector arg1 = (N_Vector) 0 ; - SUNNonlinearSolver result; - - arg1 = (N_Vector)(farg1); - result = (SUNNonlinearSolver)SUNNonlinSol_Newton(arg1); - fresult = result; - return fresult; -} - - -SWIGEXPORT SUNNonlinearSolver _wrap_FSUNNonlinSol_NewtonSens(int const *farg1, N_Vector farg2) { - SUNNonlinearSolver fresult ; - int arg1 ; - N_Vector arg2 = (N_Vector) 0 ; - SUNNonlinearSolver result; - - arg1 = (int)(*farg1); - arg2 = (N_Vector)(farg2); - result = (SUNNonlinearSolver)SUNNonlinSol_NewtonSens(arg1,arg2); - fresult = result; - return fresult; -} - - -SWIGEXPORT int _wrap_FSUNNonlinSolGetType_Newton(SUNNonlinearSolver farg1) { - int fresult ; - SUNNonlinearSolver arg1 = (SUNNonlinearSolver) 0 ; - SUNNonlinearSolver_Type result; - - arg1 = (SUNNonlinearSolver)(farg1); - result = (SUNNonlinearSolver_Type)SUNNonlinSolGetType_Newton(arg1); - fresult = (int)(result); - return fresult; -} - - -SWIGEXPORT int _wrap_FSUNNonlinSolInitialize_Newton(SUNNonlinearSolver farg1) { - int fresult ; - SUNNonlinearSolver arg1 = (SUNNonlinearSolver) 0 ; - int result; - - arg1 = (SUNNonlinearSolver)(farg1); - result = (int)SUNNonlinSolInitialize_Newton(arg1); - fresult = (int)(result); - return fresult; -} - - -SWIGEXPORT int _wrap_FSUNNonlinSolSolve_Newton(SUNNonlinearSolver farg1, N_Vector farg2, N_Vector farg3, N_Vector farg4, double const *farg5, int const *farg6, void *farg7) { - int fresult ; - SUNNonlinearSolver arg1 = (SUNNonlinearSolver) 0 ; - N_Vector arg2 = (N_Vector) 0 ; - N_Vector arg3 = (N_Vector) 0 ; - N_Vector arg4 = (N_Vector) 0 ; - realtype arg5 ; - int arg6 ; - void *arg7 = (void *) 0 ; - int result; - - arg1 = (SUNNonlinearSolver)(farg1); - arg2 = (N_Vector)(farg2); - arg3 = (N_Vector)(farg3); - arg4 = (N_Vector)(farg4); - arg5 = (realtype)(*farg5); - arg6 = (int)(*farg6); - arg7 = (void *)(farg7); - result = (int)SUNNonlinSolSolve_Newton(arg1,arg2,arg3,arg4,arg5,arg6,arg7); - fresult = (int)(result); - return fresult; -} - - -SWIGEXPORT int _wrap_FSUNNonlinSolFree_Newton(SUNNonlinearSolver farg1) { - int fresult ; - SUNNonlinearSolver arg1 = (SUNNonlinearSolver) 0 ; - int result; - - arg1 = (SUNNonlinearSolver)(farg1); - result = (int)SUNNonlinSolFree_Newton(arg1); - fresult = (int)(result); - return fresult; -} - - -SWIGEXPORT int _wrap_FSUNNonlinSolSetSysFn_Newton(SUNNonlinearSolver farg1, SUNNonlinSolSysFn farg2) { - int fresult ; - SUNNonlinearSolver arg1 = (SUNNonlinearSolver) 0 ; - SUNNonlinSolSysFn arg2 = (SUNNonlinSolSysFn) 0 ; - int result; - - arg1 = (SUNNonlinearSolver)(farg1); - arg2 = (SUNNonlinSolSysFn)(farg2); - result = (int)SUNNonlinSolSetSysFn_Newton(arg1,arg2); - fresult = (int)(result); - return fresult; -} - - -SWIGEXPORT int _wrap_FSUNNonlinSolSetLSetupFn_Newton(SUNNonlinearSolver farg1, SUNNonlinSolLSetupFn farg2) { - int fresult ; - SUNNonlinearSolver arg1 = (SUNNonlinearSolver) 0 ; - SUNNonlinSolLSetupFn arg2 = (SUNNonlinSolLSetupFn) 0 ; - int result; - - arg1 = (SUNNonlinearSolver)(farg1); - arg2 = (SUNNonlinSolLSetupFn)(farg2); - result = (int)SUNNonlinSolSetLSetupFn_Newton(arg1,arg2); - fresult = (int)(result); - return fresult; -} - - -SWIGEXPORT int _wrap_FSUNNonlinSolSetLSolveFn_Newton(SUNNonlinearSolver farg1, SUNNonlinSolLSolveFn farg2) { - int fresult ; - SUNNonlinearSolver arg1 = (SUNNonlinearSolver) 0 ; - SUNNonlinSolLSolveFn arg2 = (SUNNonlinSolLSolveFn) 0 ; - int result; - - arg1 = (SUNNonlinearSolver)(farg1); - arg2 = (SUNNonlinSolLSolveFn)(farg2); - result = (int)SUNNonlinSolSetLSolveFn_Newton(arg1,arg2); - fresult = (int)(result); - return fresult; -} - - -SWIGEXPORT int _wrap_FSUNNonlinSolSetConvTestFn_Newton(SUNNonlinearSolver farg1, SUNNonlinSolConvTestFn farg2, void *farg3) { - int fresult ; - SUNNonlinearSolver arg1 = (SUNNonlinearSolver) 0 ; - SUNNonlinSolConvTestFn arg2 = (SUNNonlinSolConvTestFn) 0 ; - void *arg3 = (void *) 0 ; - int result; - - arg1 = (SUNNonlinearSolver)(farg1); - arg2 = (SUNNonlinSolConvTestFn)(farg2); - arg3 = (void *)(farg3); - result = (int)SUNNonlinSolSetConvTestFn_Newton(arg1,arg2,arg3); - fresult = (int)(result); - return fresult; -} - - -SWIGEXPORT int _wrap_FSUNNonlinSolSetMaxIters_Newton(SUNNonlinearSolver farg1, int const *farg2) { - int fresult ; - SUNNonlinearSolver arg1 = (SUNNonlinearSolver) 0 ; - int arg2 ; - int result; - - arg1 = (SUNNonlinearSolver)(farg1); - arg2 = (int)(*farg2); - result = (int)SUNNonlinSolSetMaxIters_Newton(arg1,arg2); - fresult = (int)(result); - return fresult; -} - - -SWIGEXPORT int _wrap_FSUNNonlinSolGetNumIters_Newton(SUNNonlinearSolver farg1, long *farg2) { - int fresult ; - SUNNonlinearSolver arg1 = (SUNNonlinearSolver) 0 ; - long *arg2 = (long *) 0 ; - int result; - - arg1 = (SUNNonlinearSolver)(farg1); - arg2 = (long *)(farg2); - result = (int)SUNNonlinSolGetNumIters_Newton(arg1,arg2); - fresult = (int)(result); - return fresult; -} - - -SWIGEXPORT int _wrap_FSUNNonlinSolGetCurIter_Newton(SUNNonlinearSolver farg1, int *farg2) { - int fresult ; - SUNNonlinearSolver arg1 = (SUNNonlinearSolver) 0 ; - int *arg2 = (int *) 0 ; - int result; - - arg1 = (SUNNonlinearSolver)(farg1); - arg2 = (int *)(farg2); - result = (int)SUNNonlinSolGetCurIter_Newton(arg1,arg2); - fresult = (int)(result); - return fresult; -} - - -SWIGEXPORT int _wrap_FSUNNonlinSolGetNumConvFails_Newton(SUNNonlinearSolver farg1, long *farg2) { - int fresult ; - SUNNonlinearSolver arg1 = (SUNNonlinearSolver) 0 ; - long *arg2 = (long *) 0 ; - int result; - - arg1 = (SUNNonlinearSolver)(farg1); - arg2 = (long *)(farg2); - result = (int)SUNNonlinSolGetNumConvFails_Newton(arg1,arg2); - fresult = (int)(result); - return fresult; -} - - -SWIGEXPORT int _wrap_FSUNNonlinSolGetSysFn_Newton(SUNNonlinearSolver farg1, void *farg2) { - int fresult ; - SUNNonlinearSolver arg1 = (SUNNonlinearSolver) 0 ; - SUNNonlinSolSysFn *arg2 = (SUNNonlinSolSysFn *) 0 ; - int result; - - arg1 = (SUNNonlinearSolver)(farg1); - arg2 = (SUNNonlinSolSysFn *)(farg2); - result = (int)SUNNonlinSolGetSysFn_Newton(arg1,arg2); - fresult = (int)(result); - return fresult; -} - - -SWIGEXPORT int _wrap_FSUNNonlinSolSetInfoFile_Newton(SUNNonlinearSolver farg1, void *farg2) { - int fresult ; - SUNNonlinearSolver arg1 = (SUNNonlinearSolver) 0 ; - FILE *arg2 = (FILE *) 0 ; - int result; - - arg1 = (SUNNonlinearSolver)(farg1); - arg2 = (FILE *)(farg2); - result = (int)SUNNonlinSolSetInfoFile_Newton(arg1,arg2); - fresult = (int)(result); - return fresult; -} - - -SWIGEXPORT int _wrap_FSUNNonlinSolSetPrintLevel_Newton(SUNNonlinearSolver farg1, int const *farg2) { - int fresult ; - SUNNonlinearSolver arg1 = (SUNNonlinearSolver) 0 ; - int arg2 ; - int result; - - arg1 = (SUNNonlinearSolver)(farg1); - arg2 = (int)(*farg2); - result = (int)SUNNonlinSolSetPrintLevel_Newton(arg1,arg2); - fresult = (int)(result); - return fresult; -} - - - diff --git a/deps/AMICI/ThirdParty/sundials/src/sunnonlinsol/newton/fmod/fsunnonlinsol_newton_mod.f90 b/deps/AMICI/ThirdParty/sundials/src/sunnonlinsol/newton/fmod/fsunnonlinsol_newton_mod.f90 deleted file mode 100644 index 8d6ab8825..000000000 --- a/deps/AMICI/ThirdParty/sundials/src/sunnonlinsol/newton/fmod/fsunnonlinsol_newton_mod.f90 +++ /dev/null @@ -1,491 +0,0 @@ -! This file was automatically generated by SWIG (http://www.swig.org). -! Version 4.0.0 -! -! Do not make changes to this file unless you know what you are doing--modify -! the SWIG interface file instead. - -! --------------------------------------------------------------- -! Programmer(s): Auto-generated by swig. -! --------------------------------------------------------------- -! SUNDIALS Copyright Start -! Copyright (c) 2002-2021, Lawrence Livermore National Security -! and Southern Methodist University. -! All rights reserved. -! -! See the top-level LICENSE and NOTICE files for details. -! -! SPDX-License-Identifier: BSD-3-Clause -! SUNDIALS Copyright End -! --------------------------------------------------------------- - -module fsunnonlinsol_newton_mod - use, intrinsic :: ISO_C_BINDING - use fsundials_nvector_mod - use fsundials_types_mod - use fsundials_nonlinearsolver_mod - use fsundials_nvector_mod - use fsundials_types_mod - implicit none - private - - ! DECLARATION CONSTRUCTS - public :: FSUNNonlinSol_Newton - public :: FSUNNonlinSol_NewtonSens - public :: FSUNNonlinSolGetType_Newton - public :: FSUNNonlinSolInitialize_Newton - public :: FSUNNonlinSolSolve_Newton - public :: FSUNNonlinSolFree_Newton - public :: FSUNNonlinSolSetSysFn_Newton - public :: FSUNNonlinSolSetLSetupFn_Newton - public :: FSUNNonlinSolSetLSolveFn_Newton - public :: FSUNNonlinSolSetConvTestFn_Newton - public :: FSUNNonlinSolSetMaxIters_Newton - public :: FSUNNonlinSolGetNumIters_Newton - public :: FSUNNonlinSolGetCurIter_Newton - public :: FSUNNonlinSolGetNumConvFails_Newton - public :: FSUNNonlinSolGetSysFn_Newton - public :: FSUNNonlinSolSetInfoFile_Newton - public :: FSUNNonlinSolSetPrintLevel_Newton - -! WRAPPER DECLARATIONS -interface -function swigc_FSUNNonlinSol_Newton(farg1) & -bind(C, name="_wrap_FSUNNonlinSol_Newton") & -result(fresult) -use, intrinsic :: ISO_C_BINDING -type(C_PTR), value :: farg1 -type(C_PTR) :: fresult -end function - -function swigc_FSUNNonlinSol_NewtonSens(farg1, farg2) & -bind(C, name="_wrap_FSUNNonlinSol_NewtonSens") & -result(fresult) -use, intrinsic :: ISO_C_BINDING -integer(C_INT), intent(in) :: farg1 -type(C_PTR), value :: farg2 -type(C_PTR) :: fresult -end function - -function swigc_FSUNNonlinSolGetType_Newton(farg1) & -bind(C, name="_wrap_FSUNNonlinSolGetType_Newton") & -result(fresult) -use, intrinsic :: ISO_C_BINDING -type(C_PTR), value :: farg1 -integer(C_INT) :: fresult -end function - -function swigc_FSUNNonlinSolInitialize_Newton(farg1) & -bind(C, name="_wrap_FSUNNonlinSolInitialize_Newton") & -result(fresult) -use, intrinsic :: ISO_C_BINDING -type(C_PTR), value :: farg1 -integer(C_INT) :: fresult -end function - -function swigc_FSUNNonlinSolSolve_Newton(farg1, farg2, farg3, farg4, farg5, farg6, farg7) & -bind(C, name="_wrap_FSUNNonlinSolSolve_Newton") & -result(fresult) -use, intrinsic :: ISO_C_BINDING -type(C_PTR), value :: farg1 -type(C_PTR), value :: farg2 -type(C_PTR), value :: farg3 -type(C_PTR), value :: farg4 -real(C_DOUBLE), intent(in) :: farg5 -integer(C_INT), intent(in) :: farg6 -type(C_PTR), value :: farg7 -integer(C_INT) :: fresult -end function - -function swigc_FSUNNonlinSolFree_Newton(farg1) & -bind(C, name="_wrap_FSUNNonlinSolFree_Newton") & -result(fresult) -use, intrinsic :: ISO_C_BINDING -type(C_PTR), value :: farg1 -integer(C_INT) :: fresult -end function - -function swigc_FSUNNonlinSolSetSysFn_Newton(farg1, farg2) & -bind(C, name="_wrap_FSUNNonlinSolSetSysFn_Newton") & -result(fresult) -use, intrinsic :: ISO_C_BINDING -type(C_PTR), value :: farg1 -type(C_FUNPTR), value :: farg2 -integer(C_INT) :: fresult -end function - -function swigc_FSUNNonlinSolSetLSetupFn_Newton(farg1, farg2) & -bind(C, name="_wrap_FSUNNonlinSolSetLSetupFn_Newton") & -result(fresult) -use, intrinsic :: ISO_C_BINDING -type(C_PTR), value :: farg1 -type(C_FUNPTR), value :: farg2 -integer(C_INT) :: fresult -end function - -function swigc_FSUNNonlinSolSetLSolveFn_Newton(farg1, farg2) & -bind(C, name="_wrap_FSUNNonlinSolSetLSolveFn_Newton") & -result(fresult) -use, intrinsic :: ISO_C_BINDING -type(C_PTR), value :: farg1 -type(C_FUNPTR), value :: farg2 -integer(C_INT) :: fresult -end function - -function swigc_FSUNNonlinSolSetConvTestFn_Newton(farg1, farg2, farg3) & -bind(C, name="_wrap_FSUNNonlinSolSetConvTestFn_Newton") & -result(fresult) -use, intrinsic :: ISO_C_BINDING -type(C_PTR), value :: farg1 -type(C_FUNPTR), value :: farg2 -type(C_PTR), value :: farg3 -integer(C_INT) :: fresult -end function - -function swigc_FSUNNonlinSolSetMaxIters_Newton(farg1, farg2) & -bind(C, name="_wrap_FSUNNonlinSolSetMaxIters_Newton") & -result(fresult) -use, intrinsic :: ISO_C_BINDING -type(C_PTR), value :: farg1 -integer(C_INT), intent(in) :: farg2 -integer(C_INT) :: fresult -end function - -function swigc_FSUNNonlinSolGetNumIters_Newton(farg1, farg2) & -bind(C, name="_wrap_FSUNNonlinSolGetNumIters_Newton") & -result(fresult) -use, intrinsic :: ISO_C_BINDING -type(C_PTR), value :: farg1 -type(C_PTR), value :: farg2 -integer(C_INT) :: fresult -end function - -function swigc_FSUNNonlinSolGetCurIter_Newton(farg1, farg2) & -bind(C, name="_wrap_FSUNNonlinSolGetCurIter_Newton") & -result(fresult) -use, intrinsic :: ISO_C_BINDING -type(C_PTR), value :: farg1 -type(C_PTR), value :: farg2 -integer(C_INT) :: fresult -end function - -function swigc_FSUNNonlinSolGetNumConvFails_Newton(farg1, farg2) & -bind(C, name="_wrap_FSUNNonlinSolGetNumConvFails_Newton") & -result(fresult) -use, intrinsic :: ISO_C_BINDING -type(C_PTR), value :: farg1 -type(C_PTR), value :: farg2 -integer(C_INT) :: fresult -end function - -function swigc_FSUNNonlinSolGetSysFn_Newton(farg1, farg2) & -bind(C, name="_wrap_FSUNNonlinSolGetSysFn_Newton") & -result(fresult) -use, intrinsic :: ISO_C_BINDING -type(C_PTR), value :: farg1 -type(C_PTR), value :: farg2 -integer(C_INT) :: fresult -end function - -function swigc_FSUNNonlinSolSetInfoFile_Newton(farg1, farg2) & -bind(C, name="_wrap_FSUNNonlinSolSetInfoFile_Newton") & -result(fresult) -use, intrinsic :: ISO_C_BINDING -type(C_PTR), value :: farg1 -type(C_PTR), value :: farg2 -integer(C_INT) :: fresult -end function - -function swigc_FSUNNonlinSolSetPrintLevel_Newton(farg1, farg2) & -bind(C, name="_wrap_FSUNNonlinSolSetPrintLevel_Newton") & -result(fresult) -use, intrinsic :: ISO_C_BINDING -type(C_PTR), value :: farg1 -integer(C_INT), intent(in) :: farg2 -integer(C_INT) :: fresult -end function - -end interface - - -contains - ! MODULE SUBPROGRAMS -function FSUNNonlinSol_Newton(y) & -result(swig_result) -use, intrinsic :: ISO_C_BINDING -type(SUNNonlinearSolver), pointer :: swig_result -type(N_Vector), target, intent(inout) :: y -type(C_PTR) :: fresult -type(C_PTR) :: farg1 - -farg1 = c_loc(y) -fresult = swigc_FSUNNonlinSol_Newton(farg1) -call c_f_pointer(fresult, swig_result) -end function - -function FSUNNonlinSol_NewtonSens(count, y) & -result(swig_result) -use, intrinsic :: ISO_C_BINDING -type(SUNNonlinearSolver), pointer :: swig_result -integer(C_INT), intent(in) :: count -type(N_Vector), target, intent(inout) :: y -type(C_PTR) :: fresult -integer(C_INT) :: farg1 -type(C_PTR) :: farg2 - -farg1 = count -farg2 = c_loc(y) -fresult = swigc_FSUNNonlinSol_NewtonSens(farg1, farg2) -call c_f_pointer(fresult, swig_result) -end function - -function FSUNNonlinSolGetType_Newton(nls) & -result(swig_result) -use, intrinsic :: ISO_C_BINDING -integer(SUNNonlinearSolver_Type) :: swig_result -type(SUNNonlinearSolver), target, intent(inout) :: nls -integer(C_INT) :: fresult -type(C_PTR) :: farg1 - -farg1 = c_loc(nls) -fresult = swigc_FSUNNonlinSolGetType_Newton(farg1) -swig_result = fresult -end function - -function FSUNNonlinSolInitialize_Newton(nls) & -result(swig_result) -use, intrinsic :: ISO_C_BINDING -integer(C_INT) :: swig_result -type(SUNNonlinearSolver), target, intent(inout) :: nls -integer(C_INT) :: fresult -type(C_PTR) :: farg1 - -farg1 = c_loc(nls) -fresult = swigc_FSUNNonlinSolInitialize_Newton(farg1) -swig_result = fresult -end function - -function FSUNNonlinSolSolve_Newton(nls, y0, y, w, tol, calllsetup, mem) & -result(swig_result) -use, intrinsic :: ISO_C_BINDING -integer(C_INT) :: swig_result -type(SUNNonlinearSolver), target, intent(inout) :: nls -type(N_Vector), target, intent(inout) :: y0 -type(N_Vector), target, intent(inout) :: y -type(N_Vector), target, intent(inout) :: w -real(C_DOUBLE), intent(in) :: tol -integer(C_INT), intent(in) :: calllsetup -type(C_PTR) :: mem -integer(C_INT) :: fresult -type(C_PTR) :: farg1 -type(C_PTR) :: farg2 -type(C_PTR) :: farg3 -type(C_PTR) :: farg4 -real(C_DOUBLE) :: farg5 -integer(C_INT) :: farg6 -type(C_PTR) :: farg7 - -farg1 = c_loc(nls) -farg2 = c_loc(y0) -farg3 = c_loc(y) -farg4 = c_loc(w) -farg5 = tol -farg6 = calllsetup -farg7 = mem -fresult = swigc_FSUNNonlinSolSolve_Newton(farg1, farg2, farg3, farg4, farg5, farg6, farg7) -swig_result = fresult -end function - -function FSUNNonlinSolFree_Newton(nls) & -result(swig_result) -use, intrinsic :: ISO_C_BINDING -integer(C_INT) :: swig_result -type(SUNNonlinearSolver), target, intent(inout) :: nls -integer(C_INT) :: fresult -type(C_PTR) :: farg1 - -farg1 = c_loc(nls) -fresult = swigc_FSUNNonlinSolFree_Newton(farg1) -swig_result = fresult -end function - -function FSUNNonlinSolSetSysFn_Newton(nls, sysfn) & -result(swig_result) -use, intrinsic :: ISO_C_BINDING -integer(C_INT) :: swig_result -type(SUNNonlinearSolver), target, intent(inout) :: nls -type(C_FUNPTR), intent(in), value :: sysfn -integer(C_INT) :: fresult -type(C_PTR) :: farg1 -type(C_FUNPTR) :: farg2 - -farg1 = c_loc(nls) -farg2 = sysfn -fresult = swigc_FSUNNonlinSolSetSysFn_Newton(farg1, farg2) -swig_result = fresult -end function - -function FSUNNonlinSolSetLSetupFn_Newton(nls, lsetupfn) & -result(swig_result) -use, intrinsic :: ISO_C_BINDING -integer(C_INT) :: swig_result -type(SUNNonlinearSolver), target, intent(inout) :: nls -type(C_FUNPTR), intent(in), value :: lsetupfn -integer(C_INT) :: fresult -type(C_PTR) :: farg1 -type(C_FUNPTR) :: farg2 - -farg1 = c_loc(nls) -farg2 = lsetupfn -fresult = swigc_FSUNNonlinSolSetLSetupFn_Newton(farg1, farg2) -swig_result = fresult -end function - -function FSUNNonlinSolSetLSolveFn_Newton(nls, lsolvefn) & -result(swig_result) -use, intrinsic :: ISO_C_BINDING -integer(C_INT) :: swig_result -type(SUNNonlinearSolver), target, intent(inout) :: nls -type(C_FUNPTR), intent(in), value :: lsolvefn -integer(C_INT) :: fresult -type(C_PTR) :: farg1 -type(C_FUNPTR) :: farg2 - -farg1 = c_loc(nls) -farg2 = lsolvefn -fresult = swigc_FSUNNonlinSolSetLSolveFn_Newton(farg1, farg2) -swig_result = fresult -end function - -function FSUNNonlinSolSetConvTestFn_Newton(nls, ctestfn, ctest_data) & -result(swig_result) -use, intrinsic :: ISO_C_BINDING -integer(C_INT) :: swig_result -type(SUNNonlinearSolver), target, intent(inout) :: nls -type(C_FUNPTR), intent(in), value :: ctestfn -type(C_PTR) :: ctest_data -integer(C_INT) :: fresult -type(C_PTR) :: farg1 -type(C_FUNPTR) :: farg2 -type(C_PTR) :: farg3 - -farg1 = c_loc(nls) -farg2 = ctestfn -farg3 = ctest_data -fresult = swigc_FSUNNonlinSolSetConvTestFn_Newton(farg1, farg2, farg3) -swig_result = fresult -end function - -function FSUNNonlinSolSetMaxIters_Newton(nls, maxiters) & -result(swig_result) -use, intrinsic :: ISO_C_BINDING -integer(C_INT) :: swig_result -type(SUNNonlinearSolver), target, intent(inout) :: nls -integer(C_INT), intent(in) :: maxiters -integer(C_INT) :: fresult -type(C_PTR) :: farg1 -integer(C_INT) :: farg2 - -farg1 = c_loc(nls) -farg2 = maxiters -fresult = swigc_FSUNNonlinSolSetMaxIters_Newton(farg1, farg2) -swig_result = fresult -end function - -function FSUNNonlinSolGetNumIters_Newton(nls, niters) & -result(swig_result) -use, intrinsic :: ISO_C_BINDING -integer(C_INT) :: swig_result -type(SUNNonlinearSolver), target, intent(inout) :: nls -integer(C_LONG), dimension(*), target, intent(inout) :: niters -integer(C_INT) :: fresult -type(C_PTR) :: farg1 -type(C_PTR) :: farg2 - -farg1 = c_loc(nls) -farg2 = c_loc(niters(1)) -fresult = swigc_FSUNNonlinSolGetNumIters_Newton(farg1, farg2) -swig_result = fresult -end function - -function FSUNNonlinSolGetCurIter_Newton(nls, iter) & -result(swig_result) -use, intrinsic :: ISO_C_BINDING -integer(C_INT) :: swig_result -type(SUNNonlinearSolver), target, intent(inout) :: nls -integer(C_INT), dimension(*), target, intent(inout) :: iter -integer(C_INT) :: fresult -type(C_PTR) :: farg1 -type(C_PTR) :: farg2 - -farg1 = c_loc(nls) -farg2 = c_loc(iter(1)) -fresult = swigc_FSUNNonlinSolGetCurIter_Newton(farg1, farg2) -swig_result = fresult -end function - -function FSUNNonlinSolGetNumConvFails_Newton(nls, nconvfails) & -result(swig_result) -use, intrinsic :: ISO_C_BINDING -integer(C_INT) :: swig_result -type(SUNNonlinearSolver), target, intent(inout) :: nls -integer(C_LONG), dimension(*), target, intent(inout) :: nconvfails -integer(C_INT) :: fresult -type(C_PTR) :: farg1 -type(C_PTR) :: farg2 - -farg1 = c_loc(nls) -farg2 = c_loc(nconvfails(1)) -fresult = swigc_FSUNNonlinSolGetNumConvFails_Newton(farg1, farg2) -swig_result = fresult -end function - -function FSUNNonlinSolGetSysFn_Newton(nls, sysfn) & -result(swig_result) -use, intrinsic :: ISO_C_BINDING -integer(C_INT) :: swig_result -type(SUNNonlinearSolver), target, intent(inout) :: nls -type(C_FUNPTR), target, intent(inout) :: sysfn -integer(C_INT) :: fresult -type(C_PTR) :: farg1 -type(C_PTR) :: farg2 - -farg1 = c_loc(nls) -farg2 = c_loc(sysfn) -fresult = swigc_FSUNNonlinSolGetSysFn_Newton(farg1, farg2) -swig_result = fresult -end function - -function FSUNNonlinSolSetInfoFile_Newton(nls, info_file) & -result(swig_result) -use, intrinsic :: ISO_C_BINDING -integer(C_INT) :: swig_result -type(SUNNonlinearSolver), target, intent(inout) :: nls -type(C_PTR) :: info_file -integer(C_INT) :: fresult -type(C_PTR) :: farg1 -type(C_PTR) :: farg2 - -farg1 = c_loc(nls) -farg2 = info_file -fresult = swigc_FSUNNonlinSolSetInfoFile_Newton(farg1, farg2) -swig_result = fresult -end function - -function FSUNNonlinSolSetPrintLevel_Newton(nls, print_level) & -result(swig_result) -use, intrinsic :: ISO_C_BINDING -integer(C_INT) :: swig_result -type(SUNNonlinearSolver), target, intent(inout) :: nls -integer(C_INT), intent(in) :: print_level -integer(C_INT) :: fresult -type(C_PTR) :: farg1 -integer(C_INT) :: farg2 - -farg1 = c_loc(nls) -farg2 = print_level -fresult = swigc_FSUNNonlinSolSetPrintLevel_Newton(farg1, farg2) -swig_result = fresult -end function - - -end module diff --git a/deps/AMICI/binder/Dockerfile b/deps/AMICI/binder/Dockerfile new file mode 100644 index 000000000..bda8f7e1a --- /dev/null +++ b/deps/AMICI/binder/Dockerfile @@ -0,0 +1,50 @@ +FROM ubuntu:22.04 + +# install dependencies + +RUN apt-get update && apt-get install -y \ + g++ \ + git \ + libatlas-base-dev \ + libboost-serialization-dev \ + libboost-chrono-dev \ + libhdf5-serial-dev \ + python-is-python3 \ + python3 \ + python3-dev \ + python3-pip \ + python3-venv \ + swig \ + wget + +# BEGIN BINDER +ARG NB_USER=jovyan +ARG NB_UID=1000 +ENV USER ${NB_USER} +ENV NB_UID ${NB_UID} +ENV HOME /home/${NB_USER} +ENV PATH="${PATH}:$HOME/.local/bin" +RUN adduser --disabled-password \ + --gecos "Default user" \ + --uid ${NB_UID} \ + ${NB_USER} +USER ${NB_USER} +RUN python3 -m pip install --upgrade build wheel setuptools pip && \ + python3 -m pip install --no-cache-dir notebook jupyterlab jupyterhub jax[cpu] +# END BINDER + +# BEGIN BINDER +# Make sure the contents of our repo are in ${HOME} +USER root +COPY . ${HOME} +RUN chown -R ${NB_UID} ${HOME} +USER ${NB_USER} +# END BINDER + +WORKDIR ${HOME} +RUN . ./.profile && python3 -m build --sdist python/sdist && \ + python3 -m pip install -v $(ls -t python/sdist/dist/amici-*.tar.gz | head -1)[petab,pysb] && \ + python3 -m pip install "git+https://github.com/Benchmarking-Initiative/Benchmark-Models-PEtab.git@master#subdirectory=src/python&egg=benchmark_models_petab" && \ + scripts/buildBNGL.sh + +ENV BNGPATH="${HOME}/ThirdParty/BioNetGen-2.7.0" diff --git a/deps/AMICI/binder/README.md b/deps/AMICI/binder/README.md new file mode 100644 index 000000000..4739de23a --- /dev/null +++ b/deps/AMICI/binder/README.md @@ -0,0 +1,5 @@ +# binder/repo2docker configuration + +Configuration files for [binder](https://mybinder.org/) / repo2docker. + +Doc: https://mybinder.readthedocs.io/en/latest/using/config_files.html diff --git a/deps/AMICI/binder/overview.ipynb b/deps/AMICI/binder/overview.ipynb new file mode 100644 index 000000000..0a7ce8108 --- /dev/null +++ b/deps/AMICI/binder/overview.ipynb @@ -0,0 +1,60 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "f7ebed12-4309-4c92-a54e-da80ccd2d5e7", + "metadata": {}, + "source": [ + "# AMICI example notebooks\n", + "\n", + "* [Getting started](../documentation/GettingStarted.ipynb)\n", + "\n", + " Brief intro to AMICI for first-time users.\n", + "\n", + "* [SBML import, observation model, sensitivity analysis, data export and visualization](../python/examples/example_steadystate/ExampleSteadystate.ipynb)\n", + "\n", + " A more detailed introduction to the AMICI interface, demonstrating sensitivity analysis, various options, finite difference checks, ...\n", + "\n", + "* [PEtab import / simulation](../python/examples/example_petab/petab.ipynb)\n", + "\n", + " How to import and simulate PEtab problems.\n", + "\n", + "* [Experimental conditions](../python/examples/example_presimulation/ExampleExperimentalConditions.ipynb)\n", + "\n", + " How to represent different experimental conditions in AMICI and how to use preequilibration.\n", + "\n", + "* [Steadystate (sensitivities)](../python/examples/example_constant_species/ExampleEquilibrationLogic.ipynb)\n", + "\n", + " Describes and demonstrates the various algorithms for computing steady states and steady-state sensitivities.\n", + "\n", + "* [Simulation errors](../python/examples/example_errors.ipynb)\n", + "\n", + " Demonstrates common simulation failures and gives some hints for interpreting, debugging, and fixing them.\n", + "\n", + "* [Interfacing JAX](../python/examples/example_jax/ExampleJax.ipynb)\n", + "\n", + " Provides guidance on how to combine AMICI with differential programming frameworks such as JAX.\n", + "\n", + "* [Efficient spline interpolation](../python/examples/example_splines/ExampleSplines.ipynb)\n", + "\n", + " Shows how to add annotated spline formulas to existing SBML models in order to speed up AMICI's model import.\n", + "\n", + "* [A real-world application of splines](../python/examples/example_splines_swameye/ExampleSplinesSwameye2003.ipynb)\n", + "\n", + " An illustration of how to apply AMICI's spline functionalities to parameter estimation for a reaction network.\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "", + "name": "" + }, + "language_info": { + "name": "" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/deps/AMICI/cmake/AmiciConfig.cmake b/deps/AMICI/cmake/AmiciConfig.cmake index 7db90266f..0ea3f77a4 100644 --- a/deps/AMICI/cmake/AmiciConfig.cmake +++ b/deps/AMICI/cmake/AmiciConfig.cmake @@ -2,6 +2,61 @@ include(CMakeFindDependencyMacro) +find_package(OpenMP) + +# Current SUNDIALSConfig.cmake doesn't use KLU's FindKLU, but hardcodes paths, +# and is not relocatable. This does not work with Python package installation in +# tmpdirs. +list(APPEND CMAKE_MODULE_PATH + @CMAKE_SOURCE_DIR@/ThirdParty/SuiteSparse/lib/cmake/SuiteSparse/) +if(NOT DEFINED SUITESPARSE_CONFIG_ROOT) + set(SUITESPARSE_CONFIG_ROOT @CMAKE_SOURCE_DIR@/ThirdParty/SuiteSparse/) +endif() +if(NOT DEFINED AMD_ROOT) + set(AMD_ROOT @CMAKE_SOURCE_DIR@/ThirdParty/SuiteSparse/) +endif() +if(NOT DEFINED BTF_ROOT) + set(BTF_ROOT @CMAKE_SOURCE_DIR@/ThirdParty/SuiteSparse/) +endif() +if(NOT DEFINED COLAMD_ROOT) + set(COLAMD_ROOT @CMAKE_SOURCE_DIR@/ThirdParty/SuiteSparse/) +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 + "@CMAKE_SOURCE_DIR@/ThirdParty/sundials/build/@CMAKE_INSTALL_LIBDIR@/cmake/sundials/") + +if(@Boost_CHRONO_FOUND@) + find_package(Boost COMPONENTS chrono REQUIRED) +endif() + +if(@HDF5_FOUND@) + find_package( + HDF5 + COMPONENTS C HL CXX + REQUIRED) +endif() + include("${CMAKE_CURRENT_LIST_DIR}/AmiciTargets.cmake") check_required_components(Amici) diff --git a/deps/AMICI/cmake/clang-tools.cmake b/deps/AMICI/cmake/clang-tools.cmake index 126fcbba3..2a7839380 100644 --- a/deps/AMICI/cmake/clang-tools.cmake +++ b/deps/AMICI/cmake/clang-tools.cmake @@ -1,34 +1,36 @@ -######### Add targets for clang-format and clang-tidy ############ +# Add targets for clang-format and clang-tidy ############ # Find all source files -execute_process(COMMAND sh -c "git ls-tree -r HEAD --name-only src/*.cpp include/*.h | tr '\n' ' '" - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} - OUTPUT_VARIABLE ALL_CXX_SOURCE_FILES - ) -############ clang-tidy ############ +execute_process( + COMMAND + sh -c + "git ls-tree -r HEAD --name-only src/*.cpp include/amici/*.h | tr '\n' ' '" + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + OUTPUT_VARIABLE ALL_CXX_SOURCE_FILES) +# ########### clang-tidy ############ # Try to find clang-format and add target if successful find_program(CLANG_FORMAT "clang-format") if(CLANG_FORMAT) - add_custom_target( - clang-format - COMMAND bash -c "/usr/bin/clang-format -i ${ALL_CXX_SOURCE_FILES}" - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} - ) + add_custom_target( + clang-format + COMMAND bash -c "${CLANG_FORMAT} -i ${ALL_CXX_SOURCE_FILES}" + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) else() - message(STATUS "clang-format was not found") + message(STATUS "clang-format was not found") endif() -############ clang-tidy ############ +# ########### clang-tidy ############ # Try to find clang-tidy and add target if successful find_program(CLANG_TIDY "clang-tidy") if(CLANG_TIDY) - add_custom_target( - clang-tidy - COMMAND sh -c "/usr/bin/clang-tidy ${ALL_CXX_SOURCE_FILES} -- -std=c++11 -I${CMAKE_SOURCE_DIR}" - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} - ) + add_custom_target( + clang-tidy + COMMAND + sh -c + "${CLANG_TIDY} ${ALL_CXX_SOURCE_FILES} -- -std=c++17 -I${CMAKE_SOURCE_DIR}" + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) else() - message(STATUS "clang-tidy was not found") + message(STATUS "clang-tidy was not found") endif() diff --git a/deps/AMICI/cmake/cmakelang-tools.cmake b/deps/AMICI/cmake/cmakelang-tools.cmake new file mode 100644 index 000000000..ad489500b --- /dev/null +++ b/deps/AMICI/cmake/cmakelang-tools.cmake @@ -0,0 +1,41 @@ +# --- Add targets for cmake-format https://cmake-format.readthedocs.io/ --- + +# Find all CMakeFiles files +set(ALL_CMAKE_FILES + CMakeLists.txt + python/CMakeLists.txt + swig/CMakeLists.txt + tests/cpp/CMakeLists.txt + tests/cpp/unittests/CMakeLists.txt + ${CMAKE_MODULE_PATH}/cmakelang-tools.cmake + ${CMAKE_MODULE_PATH}/clang-tools.cmake + ${CMAKE_MODULE_PATH}/version.cmake) +list(JOIN ALL_CMAKE_FILES " " ALL_CMAKE_FILES) + +# --- cmake-format --- + +# Try to find cmake-format and add target if successful +find_program(CMAKE_FORMAT "cmake-format") +if(CMAKE_FORMAT) + add_custom_target( + cmake-format + COMMAND bash -c "${CMAKE_FORMAT} -i ${ALL_CMAKE_FILES}" + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + COMMENT "Running cmake-format") +else() + message(STATUS "cmake-format was not found") +endif() + +# --- cmake-lint --- + +# Try to find cmake-lint and add target if successful +find_program(CMAKE_LINT "cmake-lint") +if(CMAKE_LINT) + add_custom_target( + cmake-lint + COMMAND bash -c "${CMAKE_LINT} ${ALL_CMAKE_FILES}" + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + COMMENT "Running cmake-lint") +else() + message(STATUS "cmake-lint was not found") +endif() diff --git a/deps/AMICI/cmake/version.cmake b/deps/AMICI/cmake/version.cmake index 3310c8467..8dfe45c3c 100644 --- a/deps/AMICI/cmake/version.cmake +++ b/deps/AMICI/cmake/version.cmake @@ -1,16 +1,17 @@ find_package(Git) if(Git_FOUND) - execute_process(COMMAND sh -c "${GIT_EXECUTABLE} describe --abbrev=4 --dirty=-dirty --always --tags | cut -c 2- | tr -d '\n' | sed --regexp-extended s/-/./" - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - OUTPUT_VARIABLE PROJECT_VERSION_GIT - ) + execute_process( + COMMAND + sh -c + "'${GIT_EXECUTABLE}' describe --abbrev=4 --dirty=-dirty --always --tags | cut -c 2- | tr -d '\n' | sed s/-/./" + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + OUTPUT_VARIABLE PROJECT_VERSION_GIT) endif() # get project root directory -get_filename_component(CMAKE_PARENT_LIST_DIR ${CMAKE_PARENT_LIST_FILE} DIRECTORY) +get_filename_component(CMAKE_PARENT_LIST_DIR ${CMAKE_PARENT_LIST_FILE} + DIRECTORY) get_filename_component(CMAKE_PARENT_LIST_DIR ${CMAKE_PARENT_LIST_DIR} DIRECTORY) -execute_process(COMMAND sh -c "cat version.txt | tr -d '\n'" - WORKING_DIRECTORY "${CMAKE_PARENT_LIST_DIR}" - OUTPUT_VARIABLE PROJECT_VERSION - ) +file(STRINGS "${CMAKE_PARENT_LIST_DIR}/version.txt" PROJECT_VERSION) +message(DEBUG "Version number: ${PROJECT_VERSION}") diff --git a/deps/AMICI/codecov.yml b/deps/AMICI/codecov.yml index 457744a4c..15beea9df 100644 --- a/deps/AMICI/codecov.yml +++ b/deps/AMICI/codecov.yml @@ -1,8 +1,8 @@ fixes: - - "build/venv/lib/python3.6/site-packages/::python/" - - "build/venv/lib/python3.7/site-packages/::python/" - - "build/venv/lib/python3.8/site-packages/::python/" - + - "build/venv/lib/python3.9/site-packages/::python/" + - "build/venv/lib/python3.10/site-packages/::python/" + - "build/venv/lib/python3.11/site-packages/::python/" + codecov: require_ci_to_pass: yes @@ -23,11 +23,11 @@ comment: layout: "reach,diff,flags,tree" behavior: default require_changes: no - + ignore: - "tests/*" - "tests/**/*" - + flags: python: carryforward: false @@ -38,5 +38,5 @@ flags: sbmlsuite: paths: - "python/amici/sbml_import.py" - - "python/amici/ode_export.py" + - "python/amici/de_export.py" carryforward: true diff --git a/deps/AMICI/container/Dockerfile b/deps/AMICI/container/Dockerfile new file mode 100644 index 000000000..7eb18686d --- /dev/null +++ b/deps/AMICI/container/Dockerfile @@ -0,0 +1,28 @@ +FROM ubuntu:22.04 + +# install dependencies + +RUN apt-get update && apt-get install -y \ + g++ \ + libatlas-base-dev \ + libboost-serialization-dev \ + libboost-chrono-dev \ + libhdf5-serial-dev \ + python-is-python3 \ + python3 \ + python3-dev \ + python3-pip \ + python3-venv \ + swig + +# prepare python install + +COPY amici.tar.gz /from_host/ + +RUN pip3 install --upgrade pip build && \ + mkdir -p /from_host/amici/ && \ + cd /from_host/amici && \ + tar -xzf ../amici.tar.gz && cd python/sdist && \ + python3 -m build --sdist && \ + pip3 install -v $(ls -t dist/amici-*.tar.gz | head -1)[petab] && \ + rm -rf /from_host diff --git a/deps/AMICI/container/README.md b/deps/AMICI/container/README.md new file mode 100644 index 000000000..f6b51719a --- /dev/null +++ b/deps/AMICI/container/README.md @@ -0,0 +1,51 @@ +# Using AMICI inside containers + +AMICI docker images are regularly published to +https://hub.docker.com/r/dweindl/amici. + +## Docker / Podman + +The commands below are for use with Docker. When using Podman, just replace +`docker` by `podman`. + +### Create an image + +In the AMICI base directory run: + +```bash +git archive -o container/amici.tar.gz --format=tar.gz HEAD +cd container && docker build -t $USER/amici:latest . +``` +Note that this will include files from the last commit, but no uncommitted +changes. + +### Pull published image + +```bash +docker pull dweindl/amici:latest +``` + +## Singularity / Apptainer + +### Create an image + +In the AMICI base directory run: + +```bash +# prepare amici files to be copied to the image +# Note that this will include files from the last commit, but no uncommitted +# changes. +git archive -o container/amici.tar.gz --format=tar.gz HEAD +# install spython if necessary +test -x "$(command -v spython)" || pip install spython +# convert Dockerfile to singularity/apptainer definition using spython +(cd container/ && spython recipe Dockerfile amici.def) +# build image +(cd container/ && singularity build amici.sif amici.def) +``` + +### Pull published image + +```bash +singularity pull docker://dweindl/amici:latest +``` diff --git a/deps/AMICI/docker/Dockerfile b/deps/AMICI/docker/Dockerfile deleted file mode 100644 index a17a87eee..000000000 --- a/deps/AMICI/docker/Dockerfile +++ /dev/null @@ -1,22 +0,0 @@ -FROM ubuntu:20.04 - -RUN apt update \ - && apt-get install -y \ - g++ \ - libatlas-base-dev \ - python3 \ - python3-dev \ - python3-pip \ - python3-venv \ - swig - -COPY amici.tar.gz /tmp - -RUN pip3 install --upgrade pip build \ - && mkdir -p /tmp/amici/ \ - && cd /tmp/amici \ - && tar -xzf ../amici.tar.gz \ - && cd /tmp/amici/python/sdist \ - && python3 -m build --sdist \ - && pip3 install -v $(ls -t /tmp/amici/python/sdist/dist/amici-*.tar.gz | head -1)[petab] \ - && rm -rf /tmp && mkdir /tmp diff --git a/deps/AMICI/docker/README.md b/deps/AMICI/docker/README.md deleted file mode 100644 index 97c072254..000000000 --- a/deps/AMICI/docker/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# AMICI with Docker - -## Create image - -In the AMICI base directory run: - -```bash -git archive -o docker/amici.tar.gz --format=tar.gz HEAD -cd docker && docker build -t $USER/amici:latest . -``` - -## Published images - -AMICI docker images are regularly published to -https://hub.docker.com/r/dweindl/amici. diff --git a/deps/AMICI/documentation/CI.md b/deps/AMICI/documentation/CI.md index 3fd72d99f..34b08355d 100644 --- a/deps/AMICI/documentation/CI.md +++ b/deps/AMICI/documentation/CI.md @@ -12,7 +12,7 @@ This includes the following steps: More details are provided in the sections below. The CI scripts and tests can be found in `tests/` and `scripts/`. Some of the -tests are integrated with CMake, see `make help` in the build directory. +tests are integrated with CMake, see `make help` in the build directory. ## C++ unit and integration tests @@ -61,7 +61,7 @@ obtained from the Python and C++ are compared to results saved in an HDF5 file (`tests/cpp/expectedResults.h5`). Settings and data for the test simulations are also specified in this file. -**Note:** The C++ code for the models is included in the repository under +**Note:** The C++ code for the models is included in the repository under `models/`. This code is to be updated whenever `amici::Model` changes. @@ -72,23 +72,23 @@ Regeneration of the model code has to be done whenever `amici::Model` or the Matlab model import routines change. This is done with - + tests/cpp/wrapTestModels.m **Note:** This is currently only possible from Matlab < R2018a. This should change as soon as 1) all second-order sensitivity code is ported to C++/Python, 2) a non-SBML import exists for Python and 3) support for events has been added for Python. - - + + ### Regenerating expected results To update test results, run `make test` in the build directory, -replace `tests/cpp/expectedResults.h5` by -`tests/cpp/writeResults.h5.bak` +replace `tests/cpp/expectedResults.h5` by +`tests/cpp/writeResults.h5.bak` [ONLY DO THIS AFTER TRIPLE CHECKING CORRECTNESS OF RESULTS] Before replacing the test results, confirm that only expected datasets have -changed, e.g. using +changed, e.g. using h5diff -v --relative 1e-8 tests/cpp/expectedResults.h5 tests/cpp/writeResults.h5.bak | less @@ -96,6 +96,6 @@ changed, e.g. using ## Adding/Updating tests To add new tests add a new corresponding python script (see, e.g., -`./tests/generateTestConfig/example_dirac.py`) and add it to and run +`./tests/generateTestConfig/example_dirac.py`) and add it to and run `tests/generateTestConfigurationForExamples.sh`. Then regenerate the expected test results (see above). diff --git a/deps/AMICI/documentation/CPP.rst b/deps/AMICI/documentation/CPP.rst index 9daac880d..32e07fac4 100644 --- a/deps/AMICI/documentation/CPP.rst +++ b/deps/AMICI/documentation/CPP.rst @@ -3,6 +3,8 @@ C++ interface .. toctree:: :maxdepth: 2 + :caption: C++ + Installation Usage diff --git a/deps/AMICI/documentation/ExampleJax.ipynb b/deps/AMICI/documentation/ExampleJax.ipynb new file mode 100644 index 000000000..1899305b6 --- /dev/null +++ b/deps/AMICI/documentation/ExampleJax.ipynb @@ -0,0 +1,1114 @@ +{ + "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/ExampleSplines.ipynb b/deps/AMICI/documentation/ExampleSplines.ipynb new file mode 120000 index 000000000..5512101de --- /dev/null +++ b/deps/AMICI/documentation/ExampleSplines.ipynb @@ -0,0 +1 @@ +../python/examples/example_splines/ExampleSplines.ipynb \ No newline at end of file diff --git a/deps/AMICI/documentation/ExampleSplinesSwameye2003.ipynb b/deps/AMICI/documentation/ExampleSplinesSwameye2003.ipynb new file mode 120000 index 000000000..1a3b8aab6 --- /dev/null +++ b/deps/AMICI/documentation/ExampleSplinesSwameye2003.ipynb @@ -0,0 +1 @@ +../python/examples/example_splines_swameye/ExampleSplinesSwameye2003.ipynb \ No newline at end of file diff --git a/deps/AMICI/documentation/GettingStarted.ipynb b/deps/AMICI/documentation/GettingStarted.ipynb index c1bc4df6b..91fb9cb12 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 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)](http://sbml.org/Main_Page). \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." ] @@ -26,7 +26,8 @@ "outputs": [], "source": [ "import amici\n", - "sbml_importer = amici.SbmlImporter('model_steadystate_scaled.xml')" + "\n", + "sbml_importer = amici.SbmlImporter(\"model_steadystate_scaled.xml\")" ] }, { @@ -42,8 +43,8 @@ "metadata": {}, "outputs": [], "source": [ - "model_name = 'model_steadystate'\n", - "model_dir = 'model_dir'\n", + "model_name = \"model_steadystate\"\n", + "model_dir = \"model_dir\"\n", "sbml_importer.sbml2amici(model_name, model_dir)" ] }, @@ -52,7 +53,7 @@ "metadata": {}, "source": [ "## Loading the model module\n", - "To run simulations, we need to instantiate [amici.Model](https://amici.readthedocs.io/en/latest/generated/amici.amici.Model.html) and [amici.Solver](https://amici.readthedocs.io/en/latest/generated/amici.amici.Solver.html) instances. As simulations requires instances matching the imported model, they have to be imported from the generated model module. " + "To run simulations, we need to instantiate [amici.Model](https://amici.readthedocs.io/en/latest/generated/amici.amici.Model.html) and [amici.Solver](https://amici.readthedocs.io/en/latest/generated/amici.amici.Solver.html) instances. As simulations require instances matching the imported model, they have to be imported from the generated model module. " ] }, { @@ -82,7 +83,7 @@ "metadata": {}, "outputs": [], "source": [ - "model.setParameterByName('p1',1e-3)" + "model.setParameterByName(\"p1\", 1e-3)" ] }, { @@ -122,7 +123,7 @@ "outputs": [], "source": [ "# set timepoints\n", - "model.setTimepoints([0,1])\n", + "model.setTimepoints([0, 1])\n", "rdata = amici.runAmiciSimulation(model, solver)" ] }, @@ -205,7 +206,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.5" + "version": "3.8.10" }, "toc": { "base_numbering": 1, diff --git a/deps/AMICI/documentation/INSTALL.md b/deps/AMICI/documentation/INSTALL.md deleted file mode 120000 index 71db8b493..000000000 --- a/deps/AMICI/documentation/INSTALL.md +++ /dev/null @@ -1 +0,0 @@ -../INSTALL.md \ No newline at end of file diff --git a/deps/AMICI/documentation/MATLAB.rst b/deps/AMICI/documentation/MATLAB.rst index 3b22878b7..f5eccdcd4 100644 --- a/deps/AMICI/documentation/MATLAB.rst +++ b/deps/AMICI/documentation/MATLAB.rst @@ -3,6 +3,7 @@ Matlab interface .. toctree:: :maxdepth: 2 + :caption: MATLAB Installation Usage diff --git a/deps/AMICI/documentation/MATLAB_.md b/deps/AMICI/documentation/MATLAB_.md index 40ecc87c3..760a228df 100644 --- a/deps/AMICI/documentation/MATLAB_.md +++ b/deps/AMICI/documentation/MATLAB_.md @@ -2,7 +2,7 @@ In the following we will give a detailed overview how to specify models in MATLAB and how to call the generated simulation files. -## Model Definition +## Model Definition This guide will guide the user on how to specify models in MATLAB. For example implementations see the examples in the matlab/examples directory. @@ -120,7 +120,7 @@ Specifying events is optional. Events are specified in terms of a trigger functi Events may depend on states, parameters and constants but __not__ on observables. -For more details about event support see https://doi.org/10.1093/bioinformatics/btw764 +For more details about event support see https://doi.org/10.1093/bioinformatics/btw764 ### Standard Deviation @@ -139,7 +139,7 @@ They can depend on time and parameters but must not depend on the states or obse ### Objective Function -By default, AMICI assumes a normal noise model and uses the corresponding negative log-likelihood +By default, AMICI assumes a normal noise model and uses the corresponding negative log-likelihood J = 1/2*sum(((y_i(t)-my_ti)/sigma_y_i)^2 + log(2*pi*sigma_y^2) @@ -193,7 +193,7 @@ Here for proof of concept: * Install the python package as described in the documentation * Ensure `pyversion` shows the correct python version (3.6 or 3.7) * Then, from within the AMICI `matlab/` directory: - + ``` sbml_importer = py.amici.SbmlImporter('../python/examples/example_steadystate/model_steadystate_scaled.xml') sbml_importer.sbml2amici('steadystate', 'steadystate_example_from_python') diff --git a/deps/AMICI/documentation/PYTHON.rst b/deps/AMICI/documentation/PYTHON.rst index 5c53ea1e4..cba7b08ee 100644 --- a/deps/AMICI/documentation/PYTHON.rst +++ b/deps/AMICI/documentation/PYTHON.rst @@ -3,8 +3,10 @@ Python interface .. toctree:: :maxdepth: 2 + :caption: Python Installation + Examples Usage FAQ API reference diff --git a/deps/AMICI/documentation/README.md b/deps/AMICI/documentation/README.md index aa1de6382..af9f33320 100644 --- a/deps/AMICI/documentation/README.md +++ b/deps/AMICI/documentation/README.md @@ -67,7 +67,7 @@ Graphics for documentation are kept in `documentation/gfx/`. ### When using Markdown -* Note that there are some incompatibilities of Github Markdown and Doxygen +* Note that there are some incompatibilities of GitHub Markdown and Doxygen Markdown. Ideally documentation should be written in a format compatible with both. This affects for example images links which currently cause trouble in @@ -78,12 +78,12 @@ Graphics for documentation are kept in `documentation/gfx/`. for offline use. * Please stick to the limit of 80 characters per line for readability of raw - Markdown files where possible. + Markdown files where possible. However, note that some Markdown interpreters can handle line breaks within links and headings, whereas others cannot. Here, compatibility is preferred - over linebreaks. - + over linebreaks. + * Avoid trailing whitespace ## Maintaining the list of publications diff --git a/deps/AMICI/documentation/_templates/autosummary/class.rst b/deps/AMICI/documentation/_templates/autosummary/class.rst index 030e2a4c1..ed9e3761e 100644 --- a/deps/AMICI/documentation/_templates/autosummary/class.rst +++ b/deps/AMICI/documentation/_templates/autosummary/class.rst @@ -36,5 +36,3 @@ {%- endfor %} {% endif %} {% endblock %} - - diff --git a/deps/AMICI/documentation/_templates/autosummary/module.rst b/deps/AMICI/documentation/_templates/autosummary/module.rst index c494da3a0..88a19df5f 100644 --- a/deps/AMICI/documentation/_templates/autosummary/module.rst +++ b/deps/AMICI/documentation/_templates/autosummary/module.rst @@ -1,35 +1,62 @@ -{{ fullname | escape | underline }} +{{ fullname | escape | underline}} .. automodule:: {{ fullname }} - -.. currentmodule:: {{ fullname }} - -{% if classes %} -.. rubric:: Classes + :members: + :undoc-members: + + {% block attributes %} + {% if attributes %} + .. rubric:: {{ _('Module Attributes') }} + + .. autosummary:: + {% for item in attributes %} + {{ item }} + {%- endfor %} + {% endif %} + {% endblock %} + + {% block functions %} + {% if functions %} + .. rubric:: {{ _('Functions') }} + + .. autosummary:: + {% for item in functions %} + {{ item }} + {%- endfor %} + {% endif %} + {% endblock %} + + {% block classes %} + {% if classes %} + .. rubric:: {{ _('Classes') }} + + .. autosummary:: + {% for item in classes %} + {{ item }} + {%- endfor %} + {% endif %} + {% endblock %} + + {% block exceptions %} + {% if exceptions %} + .. rubric:: {{ _('Exceptions') }} + + .. autosummary:: + {% for item in exceptions %} + {{ item }} + {%- endfor %} + {% endif %} + {% endblock %} + +{% block modules %} +{% if modules %} +.. rubric:: Modules .. autosummary:: - :toctree: . - {% for class in classes %} - {{ class }} - {% endfor %} - + :toctree: + :recursive: +{% for item in modules %} + {{ item }} +{%- endfor %} {% endif %} - -{% if functions %} -.. rubric:: Functions Summary - -.. autosummary:: - {% for function in functions %} - {{ function }} - {% endfor %} - -{% endif %} - -{% if functions %} -.. rubric:: Functions - -{% for function in functions %} -.. autofunction:: {{ function }} -{% endfor %} - -{% endif %} \ No newline at end of file +{% endblock %} diff --git a/deps/AMICI/documentation/amici_refs.bib b/deps/AMICI/documentation/amici_refs.bib index ded97337c..4c31869d8 100644 --- a/deps/AMICI/documentation/amici_refs.bib +++ b/deps/AMICI/documentation/amici_refs.bib @@ -587,7 +587,7 @@ @Article{GerosaChi2020 issn = {2405-4712}, abstract = {Summary Targeted inhibition of oncogenic pathways can be highly effective in halting the rapid growth of tumors but often leads to the emergence of slowly dividing persister cells, which constitute a reservoir for the selection of drug-resistant clones. In BRAFV600E melanomas, RAF and MEK inhibitors efficiently block oncogenic signaling, but persister cells emerge. Here, we show that persister cells escape drug-induced cell-cycle arrest via brief, sporadic ERK pulses generated by transmembrane receptors and growth factors operating in an autocrine/paracrine manner. Quantitative proteomics and computational modeling show that ERK pulsing is enabled by rewiring of mitogen-activated protein kinase (MAPK) signaling: from an oncogenic BRAFV600E monomer-driven configuration that is drug sensitive to a receptor-driven configuration that involves Ras-GTP and RAF dimers and is highly resistant to RAF and MEK inhibitors. Altogether, this work shows that pulsatile MAPK activation by factors in the microenvironment generates a persistent population of melanoma cells that rewires MAPK signaling to sustain non-genetic drug resistance.}, - doi = {https://doi.org/10.1016/j.cels.2020.10.002}, + doi = {10.1016/j.cels.2020.10.002}, keywords = {systems pharmacology, targeted therapy, non-genetic drug resistance, signaling plasticity, cancer persistence, BRAF melanoma, MAPK pathway, kinetic modeling, kinase inhibitors}, timestamp = {2020-11-09}, url = {http://www.sciencedirect.com/science/article/pii/S2405471220303707}, @@ -602,7 +602,7 @@ @Article{StenEli2020 pages = {116827}, volume = {215}, abstract = {The neurovascular coupling (NVC) connects neuronal activity to hemodynamic responses in the brain. This connection is the basis for the interpretation of functional magnetic resonance imaging data. Despite the central role of this coupling, we lack detailed knowledge about cell-specific contributions and our knowledge about NVC is mainly based on animal experiments performed during anesthesia. Anesthetics are known to affect neuronal excitability, but how this affects the vessel diameters is not known. Due to the high complexity of NVC data, mathematical modeling is needed for a meaningful analysis. However, neither the relevant neuronal subtypes nor the effects of anesthetics are covered by current models. Here, we present a mathematical model including GABAergic interneurons and pyramidal neurons, as well as the effect of an anesthetic agent. The model is consistent with data from optogenetic experiments from both awake and anesthetized animals, and it correctly predicts data from experiments with different pharmacological modulators. The analysis suggests that no downstream anesthetic effects are necessary if one of the GABAergic interneuron signaling pathways include a Michaelis-Menten expression. This is the first example of a quantitative model that includes both the cell-specific contributions and the effect of an anesthetic agent on the NVC.}, - doi = {https://doi.org/10.1016/j.neuroimage.2020.116827}, + doi = {10.1016/j.neuroimage.2020.116827}, keywords = {Functional hyperemia, Mathematical modeling, Cerebral hemodynamics, Systems biology, Functional magnetic resonance imaging (fMRI), Blood oxygen level dependent (BOLD) response}, timestamp = {2020-11-09}, url = {http://www.sciencedirect.com/science/article/pii/S1053811920303141}, @@ -665,7 +665,7 @@ @Article{PittGom2018 pages = {72 - 75}, volume = {51}, abstract = {Many biological systems exhibit oscillations in relation to key physiological or cellular functions, such as circadian rhythms, mitosis and DNA synthesis. Mathematical modelling provides a powerful approach to analysing these biosystems. Applying parameter estimation methods to calibrate these models can prove a very challenging task in practice, due to the presence of local solutions, lack of identifiability, and risk of overfitting. This paper presents a comparison of three state-of-the-art methods: frequentist, Bayesian and set-membership estimation. We use the Fitzhugh-Nagumo model with synthetic data as a case study. The computational performance and robustness of these methods is discussed, with a particular focus on their predictive capability using cross-validation.}, - doi = {https://doi.org/10.1016/j.ifacol.2018.09.040}, + doi = {10.1016/j.ifacol.2018.09.040}, keywords = {biological oscillators, model calibration, regularisation, overfitting, identifiability, frequentist estimation, Bayesian estimation, set-membership estimation}, timestamp = {2020-11-09}, url = {http://www.sciencedirect.com/science/article/pii/S2405896318316999}, @@ -734,7 +734,7 @@ @Article{RaimundezDud2021 pages = {100439}, volume = {34}, abstract = {Epidemiological models are widely used to analyze the spread of diseases such as the global COVID-19 pandemic caused by SARS-CoV-2. However, all models are based on simplifying assumptions and often on sparse data. This limits the reliability of parameter estimates and predictions. In this manuscript, we demonstrate the relevance of these limitations and the pitfalls associated with the use of overly simplistic models. We considered the data for the early phase of the COVID-19 outbreak in Wuhan, China, as an example, and perform parameter estimation, uncertainty analysis and model selection for a range of established epidemiological models. Amongst others, we employ Markov chain Monte Carlo sampling, parameter and prediction profile calculation algorithms. Our results show that parameter estimates and predictions obtained for several established models on the basis of reported case numbers can be subject to substantial uncertainty. More importantly, estimates were often unrealistic and the confidence/credibility intervals did not cover plausible values of critical parameters obtained using different approaches. These findings suggest, amongst others, that standard compartmental models can be overly simplistic and that the reported case numbers provide often insufficient information for obtaining reliable and realistic parameter values, and for forecasting the evolution of epidemics.}, - doi = {https://doi.org/10.1016/j.epidem.2021.100439}, + doi = {10.1016/j.epidem.2021.100439}, keywords = {Compartment model, SEIRD, Parameter estimation, Model selection, Uncertainty analysis}, timestamp = {2021-02-19}, url = {https://www.sciencedirect.com/science/article/pii/S1755436521000037}, @@ -749,7 +749,7 @@ @Article{vanRosmalenSmi2021 pages = {74-84}, volume = {64}, abstract = {Constraint-based, genome-scale metabolic models are an essential tool to guide metabolic engineering. However, they lack the detail and time dimension that kinetic models with enzyme dynamics offer. Model reduction can be used to bridge the gap between the two methods and allow for the integration of kinetic models into the Design-Built-Test-Learn cycle. Here we show that these reduced size models can be representative of the dynamics of the original model and demonstrate the automated generation and parameterisation of such models. Using these minimal models of metabolism could allow for further exploration of dynamic responses in metabolic networks.}, - doi = {https://doi.org/10.1016/j.ymben.2021.01.008}, + doi = {10.1016/j.ymben.2021.01.008}, keywords = {Metabolic engineering, DBTL cycle, Model reduction, Model optimisation, Model-driven design, Synthetic biology}, url = {https://www.sciencedirect.com/science/article/pii/S1096717621000161}, } @@ -783,7 +783,7 @@ @PhdThesis{Gaspari2021 @Article{VanhoeferMat2021, author = {Jakob Vanhoefer and Marta R. A. Matos and Dilan Pathirana and Yannik Schälte and Jan Hasenauer}, journal = {Journal of Open Source Software}, - title = {yaml2sbml: Human-readable and -writable specification of ODE models and their conversion to SBML}, + title = {yaml2sbml: Human-readable and -writable specification of {ODE} models and their conversion to {SBML}}, year = {2021}, number = {61}, pages = {3215}, @@ -793,32 +793,6 @@ @Article{VanhoeferMat2021 url = {https://doi.org/10.21105/joss.03215}, } -@article {Froehlich2021.05.20.445065, - author = {Fr{\"o}hlich, Fabian and Sorger, Peter K.}, - title = {Fides: Reliable Trust-Region Optimization for Parameter Estimation of Ordinary Differential Equation Models}, - elocation-id = {2021.05.20.445065}, - year = {2021}, - doi = {10.1101/2021.05.20.445065}, - publisher = {Cold Spring Harbor Laboratory}, - abstract = {Motivation Because they effectively represent mass action kinetics, ordinary differential equation models are widely used to describe biochemical processes. Optimization-based calibration of these models on experimental data can be challenging, even for low-dimensional problems. However, reliable model calibration is a prerequisite for many subsequent analysis steps, including uncertainty analysis, model selection and biological interpretation. Although multiple hypothesis have been advanced to explain why optimization based calibration of biochemical models is challenging, there are few comprehensive studies that test these hypothesis and tools for performing such studies are also lacking.Results We implemented an established trust-region method as a modular python framework (fides) to enable structured comparison of different approaches to ODE model calibration involving Hessian approximation schemes and trust-region subproblem solvers. We evaluate fides on a set of benchmark problems that include experimental data. We find a high variability in optimizer performance among different implementations of the same algorithm, with fides performing more reliably that other implementations investigated. Our investigation of possible sources of poor optimizer performance identify shortcomings in the widely used Gauss-Newton approximation. We address these shortcomings by proposing a novel hybrid Hessian approximation scheme that enhances optimizer performance.Availability Fides is published under the permissive BSD-3-Clause license with source code publicly available at https://github.com/fides-dev/fides. Citeable releases are archived on Zenodo.Contact fabian_froehlich{at}hms.harvard.edu and peter_sorger{at}hms.harvard.eduSupplementary information Supplementary data are available at Bioinformatics online and at https://github.com/fides-dev/fides-benchmark.Competing Interest StatementPKS is a member of the SAB or BOD member of Applied Biomath, RareCyte Inc., and Glencoe Software, which distributes a commercial version of the OMERO database; PKS is also a member of the NanoString SAB. In the last five years the Sorger lab has received research funding from Novartis and Merck. Sorger declares that none of these relationships have related to the content of this manuscript.}, - URL = {https://www.biorxiv.org/content/early/2021/05/22/2021.05.20.445065}, - eprint = {https://www.biorxiv.org/content/early/2021/05/22/2021.05.20.445065.full.pdf}, - journal = {bioRxiv} -} - -@Article{ErdemMut2021, - author = {Erdem, Cemal and Mutsuddy, Arnab and Bensman, Ethan M. and Dodd, William B. and Saint-Antoine, Michael M. and Bouhaddou, Mehdi and Blake, Robert C. and Gross, Sean M. and Heiser, Laura M. and Alex Feltus, F. and Birtwistle, Marc R.}, - journal = {bioRxiv}, - title = {A Scalable, Open-Source Implementation of a Large-Scale Mechanistic Model for Single Cell Proliferation and Death Signaling}, - year = {2021}, - abstract = {Mechanistic models of how single cells respond to different perturbagens can help integrate disparate big data sets or predict response to varied drug combinations. However, the construction and simulation of such models have proved challenging. Our lab previously constructed one of the largest mechanistic models for single mammalian cell regulation of proliferation and death (774 species, 141 genes, 8 ligands, 2400 reactions). However, this, as many other large-scale models, was written using licensed software (MATLAB) with intricate programming structure, impeding alteration, expansion, and sharing. Here, we generated a new foundation for this model, which includes a python-based creation and simulation pipeline converting a few structured text files into an SBML-compatible format. This new open-source model (named SPARCED) is high-performance- and cloud-computing compatible and enables the study of virtual cell population responses at the single-cell level. We applied this new model to a subset of the LINCS MCF10A Data Cube, which observed that IFNγ acts as an anti-proliferative factor, but the reasons why were unknown. After expanding the SPARCED model with an IFNγ signaling module (to 950 species, 150 genes, 9 ligands, 2500 reactions), we ran stochastic single-cell simulations for two different putative crosstalk mechanisms and looked at the number of cycling cells in each case. Our model-based analysis suggested, and experiments support that these observations are better explained by IFNγ-induced SOCS1 expression sequestering activated EGF receptors, thereby downregulating AKT activity, as opposed to direct IFNγ-induced upregulation of p21 expression. This work forms a foundation for increased mechanistic model-based data integration on a single-cell level, an important building block for clinically predictive mechanistic models.Competing Interest StatementThe authors have declared no competing interest.}, - doi = {10.1101/2020.11.09.373407}, - elocation-id = {2020.11.09.373407}, - eprint = {https://www.biorxiv.org/content/early/2021/07/15/2020.11.09.373407.full.pdf}, - publisher = {Cold Spring Harbor Laboratory}, - url = {https://www.biorxiv.org/content/early/2021/07/15/2020.11.09.373407}, -} - @Article{BastBuc2021, author = {Lisa Bast and Michèle C. Buck and Judith S. Hecker and Robert A.J. Oostendorp and Katharina S. Götze and Carsten Marr}, journal = {iScience}, @@ -830,7 +804,7 @@ @Article{BastBuc2021 volume = {24}, abstract = {Summary Classically, hematopoietic stem cell (HSC) differentiation is assumed to occur via progenitor compartments of decreasing plasticity and increasing maturity in a specific, hierarchical manner. The classical hierarchy has been challenged in the past by alternative differentiation pathways. We abstracted experimental evidence into 10 differentiation hierarchies, each comprising 7 cell type compartments. By fitting ordinary differential equation models with realistic waiting time distributions to time-resolved data of differentiating HSCs from 10 healthy human donors, we identified plausible lineage hierarchies and rejected others. We found that, for most donors, the classical model of hematopoiesis is preferred. Surprisingly, multipotent lymphoid progenitor differentiation into granulocyte-monocyte progenitors is plausible in 90% of samples. An in silico analysis confirmed that, even for strong noise, the classical model can be identified robustly. Our computational approach infers differentiation hierarchies in a personalized fashion and can be used to gain insights into kinetic alterations of diseased hematopoiesis.}, - doi = {https://doi.org/10.1016/j.isci.2021.102120}, + doi = {10.1016/j.isci.2021.102120}, keywords = {stem cells research, in silico biology, systems biology}, url = {https://www.sciencedirect.com/science/article/pii/S2589004221000882}, } @@ -869,19 +843,6 @@ @phdthesis{Maier2021 year = {2021}, } -@article {Contento2021.10.01.21263052, - author = {Contento, Lorenzo and Castelletti, Noemi and Raim{\'u}ndez, Elba and Le Gleut, Ronan and Sch{\"a}lte, Yannik and Stapor, Paul and Hinske, Ludwig Christian and H{\"o}lscher, Michael and Wieser, Andreas and Radon, Katja and Fuchs, Christiane and Hasenauer, Jan and the KoCo19 study group}, - title = {Integrative modelling of reported case numbers and seroprevalence reveals time-dependent test efficiency and infection rates}, - elocation-id = {2021.10.01.21263052}, - year = {2021}, - doi = {10.1101/2021.10.01.21263052}, - publisher = {Cold Spring Harbor Laboratory Press}, - 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.Competing Interest StatementThe authors have declared no competing interest.Funding StatementThis study was funded by the Bavarian State Ministry of Science and the Arts, the University Hospital of Ludwig-Maximilians-University Munich, the Helmholtz Centre Munich, the University of Bonn (via the Transdiciplinary Research Areas), the University of Bielefeld, Munich Center of Health (McHealth) and the German Ministry for Education and Research (MoKoCo19, reference number 01KI20271), German Research Foundation (SEPAN, reference number HA7376/3-1), Volkswagen Stiftung (reference number: 99 450). This work was supported by the Deutsche Forschungsgemeinschaft (DFG, German Research Foundation) under Germany{\textquoteright}s Excellence Strategy EXC 2047/1 - 390685813 and EXC 2151 - 390873048. The ORCHESTRA project has received funding from the European Union{\textquoteright}s Horizon 2020 research and innovation programme under grant agreement No 101016167. The views expressed in this paper are the sole responsibility of the authors and the Commission is not responsible for any use that may be made of the information it contains. The funders had no role in study design, data collection, data analyses, data interpretation, writing, or submission of this manuscript.Author DeclarationsI confirm all relevant ethical guidelines have been followed, and any necessary IRB and/or ethics committee approvals have been obtained.YesThe details of the IRB/oversight body that provided approval or exemption for the research described are given below:The study protocol was approved by the Institutional Review Board at the Ludwig-Maximilians-University in Munich, Germany (opinion date 31 March 2020, number 20-275, opinion date amendment: 10 October 2020), prior to study initiation.All necessary patient/participant consent has been obtained and the appropriate institutional forms have been archived.YesI understand that all clinical trials and any other prospective interventional studies must be registered with an ICMJE-approved registry, such as ClinicalTrials.gov. I confirm that any such study reported in the manuscript has been registered and the trial registration ID is provided (note: if posting a prospective study registered retrospectively, please provide a statement in the trial ID field explaining why the study was not registered in advance).YesI have followed all appropriate research reporting guidelines and uploaded the relevant EQUATOR Network research reporting checklist(s) and other pertinent material as supplementary files, if applicable.YesThe data used in this manuscript (except data from public sources such as the Robert Koch Institute) cannot be made public due to patient consent.}, - URL = {https://www.medrxiv.org/content/early/2021/10/01/2021.10.01.21263052}, - eprint = {https://www.medrxiv.org/content/early/2021/10/01/2021.10.01.21263052.full.pdf}, - journal = {medRxiv} -} - @Article{GudinaAli2021, author = {Esayas Kebede Gudina and Solomon Ali and Eyob Girma and Addisu Gize and Birhanemeskel Tegene and Gadissa Bedada Hundie and Wondewosen Tsegaye Sime and Rozina Ambachew and Alganesh Gebreyohanns and Mahteme Bekele and Abhishek Bakuli and Kira Elsbernd and Simon Merkt and Lorenzo Contento and Michael Hoelscher and Jan Hasenauer and Andreas Wieser and Arne Kroidl}, journal = {The Lancet Global Health}, @@ -902,7 +863,7 @@ @Article{GudinaAli2021 SARS-CoV-2 spread in Ethiopia has been highly dynamic among hospital worker and urban communities. We can speculate that the greatest wave of SARS-CoV-2 infections is currently evolving in rural Ethiopia, and thus requires focused attention regarding health-care burden and disease prevention. Funding Bavarian State Ministry of Sciences, Research, and the Arts; Germany Ministry of Education and Research; EU Horizon 2020 programme; Deutsche Forschungsgemeinschaft; and Volkswagenstiftung.}, - doi = {https://doi.org/10.1016/S2214-109X(21)00386-7}, + doi = {10.1016/S2214-109X(21)00386-7}, url = {https://www.sciencedirect.com/science/article/pii/S2214109X21003867}, } @@ -994,11 +955,302 @@ @Article{AdlungSta2021 volume = {36}, abstract = {Summary Survival or apoptosis is a binary decision in individual cells. However, at the cell-population level, a graded increase in survival of colony-forming unit-erythroid (CFU-E) cells is observed upon stimulation with erythropoietin (Epo). To identify components of Janus kinase 2/signal transducer and activator of transcription 5 (JAK2/STAT5) signal transduction that contribute to the graded population response, we extended a cell-population-level model calibrated with experimental data to study the behavior in single cells. The single-cell model shows that the high cell-to-cell variability in nuclear phosphorylated STAT5 is caused by variability in the amount of Epo receptor (EpoR):JAK2 complexes and of SHP1, as well as the extent of nuclear import because of the large variance in the cytoplasmic volume of CFU-E cells. 24–118 pSTAT5 molecules in the nucleus for 120 min are sufficient to ensure cell survival. Thus, variability in membrane-associated processes is sufficient to convert a switch-like behavior at the single-cell level to a graded population-level response.}, - doi = {https://doi.org/10.1016/j.celrep.2021.109507}, + doi = {10.1016/j.celrep.2021.109507}, keywords = {single-cell modeling, JAK/STAT, signal transduction, Epo, heterogeneity, cell fate decision, apoptosis, CFU-E cells, transcription factor, mathematical modeling}, url = {https://www.sciencedirect.com/science/article/pii/S2211124721009372}, } +@Article{SluijsMaa2022, + author = {van Sluijs, Bob and Maas, Roel J. M. and van der Linden, Ardjan J. and de Greef, Tom F. A. and Huck, Wilhelm T. S.}, + journal = {Nature Communications}, + title = {A microfluidic optimal experimental design platform for forward design of cell-free genetic networks}, + year = {2022}, + issn = {2041-1723}, + number = {1}, + pages = {3626}, + volume = {13}, + abstract = {Cell-free protein synthesis has been widely used as a “breadboard” for design of synthetic genetic networks. However, due to a severe lack of modularity, forward engineering of genetic networks remains challenging. Here, we demonstrate how a combination of optimal experimental design and microfluidics allows us to devise dynamic cell-free gene expression experiments providing maximum information content for subsequent non-linear model identification. Importantly, we reveal that applying this methodology to a library of genetic circuits, that share common elements, further increases the information content of the data resulting in higher accuracy of model parameters. To show modularity of model parameters, we design a pulse decoder and bistable switch, and predict their behaviour both qualitatively and quantitatively. Finally, we update the parameter database and indicate that network topology affects parameter estimation accuracy. Utilizing our methodology provides us with more accurate model parameters, a necessity for forward engineering of complex genetic networks.}, + doi = {10.1038/s41467-022-31306-3}, + refid = {van Sluijs2022}, +} + +@Article{VillaverdeRai2022, + author = {Villaverde, Alejandro F. and Raimúndez, Elba and Hasenauer, Jan and Banga, Julio R.}, + journal = {IEEE/ACM Transactions on Computational Biology and Bioinformatics}, + title = {Assessment of Prediction Uncertainty Quantification Methods in Systems Biology}, + year = {2022}, + pages = {1-12}, + doi = {10.1109/TCBB.2022.3213914}, +} + +@Article{MishraWan2023, + author = {Shekhar Mishra and Ziyu Wang and Michael J. Volk and Huimin Zhao}, + journal = {Metabolic Engineering}, + title = {Design and application of a kinetic model of lipid metabolism in Saccharomyces cerevisiae}, + year = {2023}, + issn = {1096-7176}, + pages = {12-18}, + volume = {75}, + abstract = {Lipid biosynthesis plays a vital role in living cells and has been increasingly engineered to overproduce various lipid-based chemicals. However, owing to the tightly constrained and interconnected nature of lipid biosynthesis, both understanding and engineering of lipid metabolism remain challenging, even with the help of mathematical models. Here we report the development of a kinetic metabolic model of lipid metabolism in Saccharomyces cerevisiae that integrates fatty acid biosynthesis, glycerophospholipid metabolism, sphingolipid metabolism, storage lipids, lumped sterol synthesis, and the synthesis and transport of relevant target-chemicals, such as fatty acids and fatty alcohols. The model was trained on lipidomic data of a reference S. cerevisiae strain, single knockout mutants, and lipid overproduction strains reported in literature. The model was used to design mutants for fatty alcohol overproduction and the lipidomic analysis of the resultant mutant strains coupled with model-guided hypothesis led to discovery of a futile cycle in the triacylglycerol biosynthesis pathway. In addition, the model was used to explain successful and unsuccessful mutant designs in metabolic engineering literature. Thus, this kinetic model of lipid metabolism can not only enable the discovery of new phenomenon in lipid metabolism but also the engineering of mutant strains for overproduction of lipids.}, + doi = {10.1016/j.ymben.2022.11.003}, + keywords = {Lipid metabolism, Kinetic model, Free fatty acid, Fatty alcohol}, + url = {https://www.sciencedirect.com/science/article/pii/S1096717622001380}, +} + +@Article{MassonisVil2022, + author = {Massonis, Gemma and Villaverde, Alejandro F and Banga, Julio R}, + journal = {Bioinformatics}, + title = {{Improving dynamic predictions with ensembles of observable models}}, + year = {2022}, + issn = {1367-4803}, + month = {11}, + note = {btac755}, + abstract = {{Dynamic mechanistic modelling in systems biology has been hampered by the complexity and variability associated with the underlying interactions, and by uncertain and sparse experimental measurements. Ensemble modelling, a concept initially developed in statistical mechanics, has been introduced in biological applications with the aim of mitigating those issues. Ensemble modelling uses a collection of different models compatible with the observed data to describe the phenomena of interest. However, since systems biology models often suffer from lack of identifiability and observability, ensembles of models are particularly unreliable when predicting non-observable states.We present a strategy to assess and improve the reliability of a class of model ensembles. In particular, we consider kinetic models described using ordinary differential equations (ODEs) with a fixed structure. Our approach builds an ensemble with a selection of the parameter vectors found when performing parameter estimation with a global optimization metaheuristic. This technique enforces diversity during the sampling of parameter space and it can quantify the uncertainty in the predictions of state trajectories. We couple this strategy with structural identifiability and observability analysis, and when these tests detect possible prediction issues we obtain model reparameterizations that surmount them. The end result is an ensemble of models with the ability to predict the internal dynamics of a biological process. We demonstrate our approach with models of glucose regulation, cell division, circadian oscillations, and the JAK-STAT signalling pathway.The code that implements the methodology and reproduces the results is available at https://doi.org/10.5281/zenodo.6782638Supplementary data are available at Bioinformatics online.}}, + doi = {10.1093/bioinformatics/btac755}, + eprint = {https://academic.oup.com/bioinformatics/advance-article-pdf/doi/10.1093/bioinformatics/btac755/47214747/btac755.pdf}, + url = {https://doi.org/10.1093/bioinformatics/btac755}, +} + +@Article{AlbadryHoe2022, + author = {Albadry, Mohamed and Höpfl, Sebastian and Ehteshamzad, Nadia and König, Matthias and Böttcher, Michael and Neumann, Jasna and Lupp, Amelie and Dirsch, Olaf and Radde, Nicole and Christ, Bruno and Christ, Madlen and Schwen, Lars Ole and Laue, Hendrik and Klopfleisch, Robert and Dahmen, Uta}, + journal = {Scientific Reports}, + title = {Periportal steatosis in mice affects distinct parameters of pericentral drug metabolism}, + year = {2022}, + issn = {2045-2322}, + number = {1}, + pages = {21825}, + volume = {12}, + abstract = {Little is known about the impact of morphological disorders in distinct zones on metabolic zonation. It was described recently that periportal fibrosis did affect the expression of CYP proteins, a set of pericentrally located drug-metabolizing enzymes. Here, we investigated whether periportal steatosis might have a similar effect. Periportal steatosis was induced in C57BL6/J mice by feeding a high-fat diet with low methionine/choline content for either two or four weeks. Steatosis severity was quantified using image analysis. Triglycerides and CYP activity were quantified in photometric or fluorometric assay. The distribution of CYP3A4, CYP1A2, CYP2D6, and CYP2E1 was visualized by immunohistochemistry. Pharmacokinetic parameters of test drugs were determined after injecting a drug cocktail (caffeine, codeine, and midazolam). The dietary model resulted in moderate to severe mixed steatosis confined to periportal and midzonal areas. Periportal steatosis did not affect the zonal distribution of CYP expression but the activity of selected CYPs was associated with steatosis severity. Caffeine elimination was accelerated by microvesicular steatosis, whereas midazolam elimination was delayed in macrovesicular steatosis. In summary, periportal steatosis affected parameters of pericentrally located drug metabolism. This observation calls for further investigations of the highly complex interrelationship between steatosis and drug metabolism and underlying signaling mechanisms.}, + doi = {10.1038/s41598-022-26483-6}, + refid = {Albadry2022}, +} + +@Article{SundqvistSte2022, + author = {Sundqvist, Nicolas and Sten, Sebastian and Thompson, Peter and Andersson, Benjamin Jan and Engström, Maria and Cedersund, Gunnar}, + journal = {PLOS Computational Biology}, + title = {Mechanistic model for human brain metabolism and its connection to the neurovascular coupling}, + year = {2022}, + month = {12}, + number = {12}, + pages = {1-24}, + volume = {18}, + abstract = {The neurovascular and neurometabolic couplings (NVC and NMC) connect cerebral activity, blood flow, and metabolism. This interconnection is used in for instance functional imaging, which analyses the blood-oxygen-dependent (BOLD) signal. The mechanisms underlying the NVC are complex, which warrants a model-based analysis of data. We have previously developed a mechanistically detailed model for the NVC, and others have proposed detailed models for cerebral metabolism. However, existing metabolic models are still not fully utilizing available magnetic resonance spectroscopy (MRS) data and are not connected to detailed models for NVC. Therefore, we herein present a new model that integrates mechanistic modelling of both MRS and BOLD data. The metabolic model covers central metabolism, using a minimal set of interactions, and can describe time-series data for glucose, lactate, aspartate, and glutamate, measured after visual stimuli. Statistical tests confirm that the model can describe both estimation data and predict independent validation data, not used for model training. The interconnected NVC model can simultaneously describe BOLD data and can be used to predict expected metabolic responses in experiments where metabolism has not been measured. This model is a step towards a useful and mechanistically detailed model for cerebral blood flow and metabolism, with potential applications in both basic research and clinical applications.}, + doi = {10.1371/journal.pcbi.1010798}, + publisher = {Public Library of Science}, +} + +@Article{MeyerSoe2022, + author = {Kristian Meyer and Mikkel {Søes Ibsen} and Lisa Vetter-Joss and Ernst {Broberg Hansen} and Jens Abildskov}, + journal = {Journal of Chromatography A}, + title = {Industrial ion-exchange chromatography development using discontinuous Galerkin methods coupled with forward sensitivity analysis}, + year = {2022}, + issn = {0021-9673}, + pages = {463741}, + abstract = {In this work, a discontinuous Galerkin method coupled with forward sensitivity analysis (DG-FSA) is presented. The DG-FSA method is used to reduce computational cost required for model-based ion-exchange chromatography development using industrial load samples. As an example, the design of an anion-exchange chromatography step is considered. This step is used to purify an experimental peptide product called Protein G from Novo Nordisk A/S (Bagsvrd, Denmark). The results demonstrate, that a fourth order DG-FSA method can reduce computational cost of inverse problems by a factor ×16 compared to a second (low) order DG-FSA method. Furthermore, the fourth-order DG-FSA method enable the computation of probability distributions of optimized processing conditions given uncertainty in model parameters or inputs. This analysis is not possible within a reasonable timeframe when applying the second (low) order DG-FSA method. The design procedure facilitates the optimization of the Protein G purification step. In an experimental validation run, the productivity is increased by 70% while sacrificing 4% yield at a similar purity constraint compared to an experiment with baseline performance.}, + doi = {10.1016/j.chroma.2022.463741}, + keywords = {Discontinuous Galerkin method, Forward sensitivity analysis, Model calibration, Uncertainty quantification, Process optimization}, + url = {https://www.sciencedirect.com/science/article/pii/S0021967322009323}, +} + +@Article{LakrisenkoSta2023, + author = {Lakrisenko, Polina and Stapor, Paul and Grein, Stephan and Paszkowski, Łukasz and Pathirana, Dilan and Fröhlich, Fabian and Lines, Glenn Terje and Weindl, Daniel and Hasenauer, Jan}, + journal = {PLOS Computational Biology}, + title = {Efficient computation of adjoint sensitivities at steady-state in ODE models of biochemical reaction networks}, + year = {2023}, + month = {01}, + number = {1}, + pages = {1-19}, + volume = {19}, + abstract = {Dynamical models in the form of systems of ordinary differential equations have become a standard tool in systems biology. Many parameters of such models are usually unknown and have to be inferred from experimental data. Gradient-based optimization has proven to be effective for parameter estimation. However, computing gradients becomes increasingly costly for larger models, which are required for capturing the complex interactions of multiple biochemical pathways. Adjoint sensitivity analysis has been pivotal for working with such large models, but methods tailored for steady-state data are currently not available. We propose a new adjoint method for computing gradients, which is applicable if the experimental data include steady-state measurements. The method is based on a reformulation of the backward integration problem to a system of linear algebraic equations. The evaluation of the proposed method using real-world problems shows a speedup of total simulation time by a factor of up to 4.4. Our results demonstrate that the proposed approach can achieve a substantial improvement in computation time, in particular for large-scale models, where computational efficiency is critical.}, + creationdate = {2023-01-09T08:36:51}, + doi = {10.1371/journal.pcbi.1010783}, + publisher = {Public Library of Science}, + url = {https://doi.org/10.1371/journal.pcbi.1010783}, +} + +@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}, +} + +@Article{FroehlichGer2023, + author = {Fröhlich, Fabian and Gerosa, Luca and Muhlich, Jeremy and Sorger, Peter K}, + journal = {Molecular Systems Biology}, + title = {Mechanistic model of MAPK signaling reveals how allostery and rewiring contribute to drug resistance}, + year = {2023}, + number = {2}, + pages = {e10988}, + volume = {19}, + abstract = {Abstract BRAF is prototypical of oncogenes that can be targeted therapeutically and the treatment of BRAFV600E melanomas with RAF and MEK inhibitors results in rapid tumor regression. However, drug-induced rewiring generates a drug adapted state thought to be involved in acquired resistance and disease recurrence. In this article, we study mechanisms of adaptive rewiring in BRAFV600E melanoma cells using an energy-based implementation of ordinary differential equation (ODE) modeling in combination with proteomic, transcriptomic and imaging data. We develop a method for causal tracing of ODE models and identify two parallel MAPK reaction channels that are differentially sensitive to RAF and MEK inhibitors due to differences in protein oligomerization and drug binding. We describe how these channels, and timescale separation between immediate-early signaling and transcriptional feedback, create a state in which the RAS-regulated MAPK channel can be activated by growth factors under conditions in which the BRAFV600E-driven channel is fully inhibited. Further development of the approaches in this article is expected to yield a unified model of adaptive drug resistance in melanoma.}, + creationdate = {2023-04-15T08:11:24}, + doi = {10.15252/msb.202210988}, + eprint = {https://www.embopress.org/doi/pdf/10.15252/msb.202210988}, + keywords = {allosteric interactions, drug resistance, kinetic modeling, MAPK pathway, rewiring}, + url = {https://www.embopress.org/doi/abs/10.15252/msb.202210988}, +} + +@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}, +} + +@Article{ErdemMut2022, + author = {Erdem, Cemal and Mutsuddy, Arnab and Bensman, Ethan M. and Dodd, William B. and Saint-Antoine, Michael M. and Bouhaddou, Mehdi and Blake, Robert C. and Gross, Sean M. and Heiser, Laura M. and Feltus, F. Alex and Birtwistle, Marc R.}, + journal = {Nature Communications}, + title = {A scalable, open-source implementation of a large-scale mechanistic model for single cell proliferation and death signaling}, + year = {2022}, + issn = {2041-1723}, + number = {1}, + pages = {3555}, + volume = {13}, + abstract = {Mechanistic models of how single cells respond to different perturbations can help integrate disparate big data sets or predict response to varied drug combinations. However, the construction and simulation of such models have proved challenging. Here, we developed a python-based model creation and simulation pipeline that converts a few structured text files into an SBML standard and is high-performance- and cloud-computing ready. We applied this pipeline to our large-scale, mechanistic pan-cancer signaling model (named SPARCED) and demonstrate it by adding an IFNγ pathway submodel. We then investigated whether a putative crosstalk mechanism could be consistent with experimental observations from the LINCS MCF10A Data Cube that IFNγ acts as an anti-proliferative factor. The analyses suggested this observation can be explained by IFNγ-induced SOCS1 sequestering activated EGF receptors. This work forms a foundational recipe for increased mechanistic model-based data integration on a single-cell level, an important building block for clinically-predictive mechanistic models.}, + creationdate = {2023-04-15T08:13:58}, + doi = {10.1038/s41467-022-31138-1}, + refid = {Erdem2022}, + url = {https://doi.org/10.1038/s41467-022-31138-1}, +} + +@Article{ContentoSta2023, + author = {Lorenzo Contento and Paul Stapor and Daniel Weindl and Jan Hasenauer}, + journal = {bioRxiv}, + title = {A more expressive spline representation for {SBML} models improves code generation performance in {AMICI}}, + year = {2023}, + abstract = {Spline interpolants are commonly used for discretizing and estimating functions in mathematical models. While splines can be encoded in the Systems Biology Markup Language (SBML) using piecewise functions, the resulting formulas are very complex and difficult to derive by hand. Tools to create such formulas exist but only deal with numeric data and thus cannot be used for function estimation. Similarly, simulation tools suffer from several limitations when handling splines. For example, in the AMICI library splines with large numbers of nodes lead to long model import times. We have developed a set of SBML annotations to mark assignment rules as spline formulas. These compact representations are human-readable and easy to edit, in contrast to the piecewise representation. Different boundary conditions and extrapolation methods can also be specified. By extending AMICI to create and recognize these annotations, model import can be sped up significantly. This allows practitioners to increase the expressivity of their models. While the performance improvement is limited to AMICI, our tools for creating spline formulas can be used for other tools as well and our syntax for compact spline representation may be a starting point for an SBML-native way to represent spline interpolants.Competing Interest StatementThe authors have declared no competing interest.}, + creationdate = {2023-07-06T10:25:17}, + doi = {10.1101/2023.06.29.547120}, + elocation-id = {2023.06.29.547120}, + eprint = {https://www.biorxiv.org/content/early/2023/07/01/2023.06.29.547120.full.pdf}, + modificationdate = {2023-07-06T10:25:30}, + publisher = {Cold Spring Harbor Laboratory}, + url = {https://www.biorxiv.org/content/early/2023/07/01/2023.06.29.547120}, +} + +@InBook{Froehlich2023, + author = {Fr{\"o}hlich, Fabian}, + editor = {Nguyen, Lan K.}, + pages = {59--86}, + publisher = {Springer US}, + title = {A Practical Guide for the Efficient Formulation and Calibration of Large, Energy- and Rule-Based Models of Cellular Signal Transduction}, + year = {2023}, + address = {New York, NY}, + isbn = {978-1-0716-3008-2}, + abstract = {Aberrant signal transduction leads to complex diseases such as cancer. To rationally design treatment strategies with small molecule inhibitors, computational models have to be employed. Energy- and rule-based models allow the construction of mechanistic ordinary differential equation models based on structural insights. The detailed, energy-based description often generates large models, which are difficult to calibrate on experimental data. In this chapter, we provide a detailed, interactive protocol for the programmatic formulation and calibration of such large, energy- and rule-based models of cellular signal transduction based on an example model describing the action of RAF inhibitors on MAPK signaling. An interactive version of this chapter is available as Jupyter Notebook at github.com/FFroehlich/energy{\_}modeling{\_}chapter.}, + booktitle = {Computational Modeling of Signaling Networks}, + creationdate = {2023-07-06T10:31:07}, + doi = {10.1007/978-1-0716-3008-2_3}, + modificationdate = {2023-07-06T10:36:35}, + 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}, + title = {Progressive Disruption of Hematopoietic Architecture from Clonal Hematopoiesis to {MDS}}, + year = {2023}, + issn = {2589-0042}, + pages = {107328}, + abstract = {Summary +Clonal hematopoiesis of indeterminate potential (CHIP) describes the age-related acquisition of somatic mutations in hematopoietic stem/progenitor cells (HSPC) leading to clonal blood cell expansion. Although CHIP mutations drive myeloid malignancies like myelodysplastic syndromes (MDS) it is unknown if clonal expansion is attributable to changes in cell type kinetics, or involves reorganization of the hematopoietic hierarchy. Using computational modeling we analyzed differentiation and proliferation kinetics of cultured hematopoietic stem cells (HSC) from 8 healthy individuals, 7 CHIP, and 10 MDS patients. While the standard hematopoietic hierarchy explained HSPC kinetics in healthy samples, 57% of CHIP and 70% of MDS samples were best described with alternative hierarchies. Deregulated kinetics were found at various HSPC compartments with high inter-individual heterogeneity in CHIP and MDS, while altered HSC rates were most relevant in MDS. Quantifying kinetic heterogeneity in detail, we show that reorganization of the HSPC compartment is detectable in the premalignant CHIP state.}, + creationdate = {2023-07-17T09:12:31}, + doi = {10.1016/j.isci.2023.107328}, + keywords = {clonal hematopoiesis, myelodysplastic syndrome, computational modeling, hematopoietic hierarchy}, + modificationdate = {2023-07-17T09:12:50}, + url = {https://www.sciencedirect.com/science/article/pii/S2589004223014050}, +} + +@Article{TunedalVio2023, + author = {Tunedal, Kajsa and Viola, Federica and Garcia, Belén Casas and Bolger, Ann and Nyström, Fredrik H. and Östgren, Carl Johan and Engvall, Jan and Lundberg, Peter and Dyverfeldt, Petter and Carlhäll, Carl-Johan and Cedersund, Gunnar and Ebbers, Tino}, + journal = {The Journal of Physiology}, + title = {Haemodynamic effects of hypertension and type 2 diabetes: Insights from a {4D} flow {MRI-based} personalized cardiovascular mathematical model}, + year = {2023}, + number = {n/a}, + volume = {n/a}, + abstract = {Abstract Type 2 diabetes (T2D) and hypertension increase the risk of cardiovascular diseases mediated by whole-body changes to metabolism, cardiovascular structure and haemodynamics. The haemodynamic changes related to hypertension and T2D are complex and subject-specific, however, and not fully understood. We aimed to investigate the haemodynamic mechanisms in T2D and hypertension by comparing the haemodynamics between healthy controls and subjects with T2D, hypertension, or both. For all subjects, we combined 4D flow magnetic resonance imaging data, brachial blood pressure and a cardiovascular mathematical model to create a comprehensive subject-specific analysis of central haemodynamics. When comparing the subject-specific haemodynamic parameters between the four groups, the predominant haemodynamic difference is impaired left ventricular relaxation in subjects with both T2D and hypertension compared to subjects with only T2D, only hypertension and controls. The impaired relaxation indicates that, in this cohort, the long-term changes in haemodynamic load of co-existing T2D and hypertension cause diastolic dysfunction demonstrable at rest, whereas either disease on its own does not. However, through subject-specific predictions of impaired relaxation, we show that altered relaxation alone is not enough to explain the subject-specific and group-related differences; instead, a combination of parameters is affected in T2D and hypertension. These results confirm previous studies that reported more adverse effects from the combination of T2D and hypertension compared to either disease on its own. Furthermore, this shows the potential of personalized cardiovascular models in providing haemodynamic mechanistic insights and subject-specific predictions that could aid in the understanding and treatment planning of patients with T2D and hypertension. Key points The combination of 4D flow magnetic resonance imaging data and a cardiovascular mathematical model allows for a comprehensive analysis of subject-specific haemodynamic parameters that otherwise cannot be derived non-invasively. Using this combination, we show that diastolic dysfunction in subjects with both type 2 diabetes (T2D) and hypertension is the main group-level difference between controls, subjects with T2D, subjects with hypertension, and subjects with both T2D and hypertension. These results suggest that, in this relatively healthy population, the additional load of both hypertension and T2D affects the haemodynamic function of the left ventricle, whereas each disease on its own is not enough to cause significant effects under resting conditions. Finally, using the subject-specific model, we show that the haemodynamic effects of diastolic dysfunction alone are not sufficient to explain all the observed haemodynamic differences. Instead, additional subject-specific variations in cardiac and vascular function combine to explain the complex haemodynamics of subjects affected by hypertension and/or T2D.}, + creationdate = {2023-07-27T08:14:11}, + doi = {10.1113/JP284652}, + eprint = {https://physoc.onlinelibrary.wiley.com/doi/pdf/10.1113/JP284652}, + keywords = {cardiovascular modelling, clinical data, computer modelling, diabetes, hemodynamic, hypertension, mathematical model}, + modificationdate = {2023-07-27T08:19:05}, + 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}}, + title = {Posterior marginalization accelerates Bayesian inference for dynamical models of biological processes}, + year = {2023}, + month = {sep}, + pages = {108083}, + creationdate = {2023-10-04T14:12:00}, + doi = {10.1016/j.isci.2023.108083}, + modificationdate = {2023-10-04T14:12:00}, + publisher = {Elsevier {BV}}, +} + +@Article{Mendes2023, + author = {Mendes, Pedro}, + journal = {Frontiers in Cell and Developmental Biology}, + title = {Reproducibility and FAIR principles: the case of a segment polarity network model}, + year = {2023}, + issn = {2296-634X}, + volume = {11}, + abstract = {The issue of reproducibility of computational models and the related FAIR principles (findable, accessible, interoperable, and reusable) are examined in a specific test case. I analyze a computational model of the segment polarity network in Drosophila embryos published in 2000. Despite the high number of citations to this publication, 23 years later the model is barely accessible, and consequently not interoperable. Following the text of the original publication allowed successfully encoding the model for the open source software COPASI. Subsequently saving the model in the SBML format allowed it to be reused in other open source software packages. Submission of this SBML encoding of the model to the BioModels database enables its findability and accessibility. This demonstrates how the FAIR principles can be successfully enabled by using open source software, widely adopted standards, and public repositories, facilitating reproducibility and reuse of computational cell biology models that will outlive the specific software used.}, + creationdate = {2023-10-28T19:05:54}, + doi = {10.3389/fcell.2023.1201673}, + modificationdate = {2023-10-28T19:05:54}, + url = {https://www.frontiersin.org/articles/10.3389/fcell.2023.1201673}, +} + +@Misc{HuckBal2023, + author = {Wilhelm Huck and Mathieu Baltussen and Thijs de Jong and Quentin Duez and William Robinson}, + title = {Chemical reservoir computation in a self-organizing reaction network}, + year = {2023}, + creationdate = {2023-11-18T09:09:45}, + doi = {10.21203/rs.3.rs-3487081/v1}, + modificationdate = {2023-11-18T09:10:08}, + publisher = {Research Square Platform 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 f9123f694..2a2e74867 100644 --- a/deps/AMICI/documentation/background.rst +++ b/deps/AMICI/documentation/background.rst @@ -10,26 +10,36 @@ Some mathematical background for AMICI is provided in the following publications: * Fröhlich, F., Kaltenbacher, B., Theis, F. J., & Hasenauer, J. (2017). - Scalable Parameter Estimation for Genome-Scale Biochemical Reaction Networks. + **Scalable Parameter Estimation for Genome-Scale Biochemical Reaction Networks.** PLOS Computational Biology, 13(1), e1005331. doi:`10.1371/journal.pcbi.1005331 `_. * Fröhlich, F., Theis, F. J., Rädler, J. O., & Hasenauer, J. (2017). - Parameter estimation for dynamical systems with discrete events and logical - operations. Bioinformatics, 33(7), 1049-1056. + **Parameter estimation for dynamical systems with discrete events and logical + operations.** Bioinformatics, 33(7), 1049-1056. doi:`10.1093/bioinformatics/btw764 `_. * 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. + 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. DOI: `10.1016/j.ifacol.2019.12.232 `_. * 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–i159. + **Optimization and Profile Calculation of ODE Models Using Second Order + Adjoint Sensitivity Analysis.** *Bioinformatics* 34 (13): i151–i159. DOI: `10.1093/bioinformatics/bty230 `_. +* Lakrisenko, Polina, Paul Stapor, Stephan Grein, Łukasz Paszkowski, + Dilan Pathirana, Fabian Fröhlich, Glenn Terje Lines, Daniel Weindl, + and Jan Hasenauer. 2022. + **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 `_. + +* 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/code_review_guide.md b/deps/AMICI/documentation/code_review_guide.md index 456b78709..43d6e815c 100644 --- a/deps/AMICI/documentation/code_review_guide.md +++ b/deps/AMICI/documentation/code_review_guide.md @@ -5,7 +5,7 @@ A guide for reviewing code and having your code reviewed by others. ## Everyone * Don't be too protective of your code -* Accept that, to a large extent, coding decisions are a matter of personal +* Accept that, to a large extent, coding decisions are a matter of personal preference * Don't get personal * Ask for clarification @@ -13,25 +13,25 @@ A guide for reviewing code and having your code reviewed by others. * Try to understand your counterpart's perspective * Clarify how strong you feel about each discussion point -## Reviewing code +## Reviewing code * If there are no objective advantages, don't force your style on others * Ask questions instead of making demands * Assume the author gave his best -* Mind the scope (many things are nice to have, but might be out of scope - of the current change - open a new issue) -* The goal is "good enough", not "perfect" +* Mind the scope (many things are nice to have, but might be out of scope + of the current change - open a new issue) +* The goal is "good enough", not "perfect" * Be constructive -* You do not always have to request changes +* You do not always have to request changes -## Having your code reviewed +## Having your code reviewed * Don't take it personal - the review is on the code, not on you * Code reviews take time, appreciate the reviewer's comments * Assume the reviewer did his best (but might still be wrong) -* Keep code changes small (e.g. separate wide reformatting from actual code +* Keep code changes small (e.g. separate wide reformatting from actual code changes to facility review) -* If the reviewer does not understand your code, probably many others won't +* If the reviewer does not understand your code, probably many others won't either ## Checklist @@ -42,10 +42,10 @@ A guide for reviewing code and having your code reviewed by others. * [ ] Meaningful identifiers are used * [ ] Corner-cases are covered, cases not covered fail loudly * [ ] The code can be expected to scale well (enough) -* [ ] The code is well documented (e.g., input, operation, output), but +* [ ] The code is well documented (e.g., input, operation, output), but without trivial comments * [ ] The code is [SOLID](https://en.wikipedia.org/wiki/SOLID) -* [ ] New code is added in the most meaningful place (i.e. matches the +* [ ] New code is added in the most meaningful place (i.e. matches the current architecture) * [ ] No magic numbers * [ ] No hard-coded values that should be user inputs diff --git a/deps/AMICI/documentation/conf.py b/deps/AMICI/documentation/conf.py index c647bda32..ba88b25a8 100644 --- a/deps/AMICI/documentation/conf.py +++ b/deps/AMICI/documentation/conf.py @@ -10,18 +10,18 @@ import re import subprocess import sys -import typing +# need to import before setting typing.TYPE_CHECKING=True, fails otherwise +import amici import exhale.deploy import exhale_multiproject_monkeypatch import mock +import pandas as pd +import sympy as sp from exhale import configs as exhale_configs from sphinx.transforms.post_transforms import ReferencesResolver -# need to import before setting typing.TYPE_CHECKING=True, fails otherwise -import pandas as pd - -exhale_multiproject_monkeypatch, pd # to avoid removal of unused import +exhale_multiproject_monkeypatch, pd, sp # to avoid removal of unused import # BEGIN Monkeypatch exhale from exhale.deploy import _generate_doxygen as exhale_generate_doxygen @@ -32,10 +32,11 @@ def my_exhale_generate_doxygen(doxygen_input): # run mtocpp_post doxy_xml_dir = exhale_configs._doxygen_xml_output_directory - if 'matlab' in doxy_xml_dir: - print('Running mtocpp_post on ', doxy_xml_dir) - mtocpp_post = os.path.join(amici_dir, 'ThirdParty', 'mtocpp-master', - 'build', 'mtocpp_post') + if "matlab" in doxy_xml_dir: + print("Running mtocpp_post on ", doxy_xml_dir) + mtocpp_post = os.path.join( + amici_dir, "ThirdParty", "mtocpp-master", "build", "mtocpp_post" + ) subprocess.run([mtocpp_post, doxy_xml_dir]) # let exhale do its job @@ -47,27 +48,30 @@ def my_exhale_generate_doxygen(doxygen_input): # BEGIN Monkeypatch breathe -from breathe.renderer.sphinxrenderer import \ - DomainDirectiveFactory as breathe_DomainDirectiveFactory +from breathe.renderer.sphinxrenderer import ( + DomainDirectiveFactory as breathe_DomainDirectiveFactory, +) -old_breathe_DomainDirectiveFactory_create = \ +old_breathe_DomainDirectiveFactory_create = ( breathe_DomainDirectiveFactory.create +) def my_breathe_DomainDirectiveFactory_create(domain: str, args): - if domain != 'mat': + if domain != "mat": return old_breathe_DomainDirectiveFactory_create(domain, args) - from sphinxcontrib.matlab import MATLABDomain, MatClassmember + from sphinxcontrib.matlab import MatClassmember, MATLABDomain matlab_classes = {k: (v, k) for k, v in MATLABDomain.directives.items()} - matlab_classes['variable'] = (MatClassmember, 'attribute') + matlab_classes["variable"] = (MatClassmember, "attribute") cls, name = matlab_classes[args[0]] - return cls(domain + ':' + name, *args[1:]) + return cls(domain + ":" + name, *args[1:]) -breathe_DomainDirectiveFactory.create = \ +breathe_DomainDirectiveFactory.create = ( my_breathe_DomainDirectiveFactory_create +) # END Monkeypatch breathe @@ -75,70 +79,36 @@ def my_breathe_DomainDirectiveFactory_create(domain: str, args): def install_mtocpp(): """Install mtocpp (Matlab doxygen filter)""" - cmd = os.path.join(amici_dir, 'scripts', 'downloadAndBuildMtocpp.sh') - ret = subprocess.run(cmd, shell=True, - stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + cmd = os.path.join(amici_dir, "scripts", "downloadAndBuildMtocpp.sh") + ret = subprocess.run( + cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT + ) if ret.returncode != 0: - print(ret.stdout.decode('utf-8')) - raise RuntimeError('downloadAndBuildMtocpp.sh failed') - - -def install_amici_deps_rtd(): - """Install AMICI dependencies and set up environment for use on RTD""" - - # cblas -- manually install ubuntu deb package - cblas_root = os.path.join(amici_dir, 'ThirdParty', 'libatlas-base-dev', - 'usr') - - if os.path.isdir(cblas_root): - # If this exists, it means this has been run before. On RTD, sphinx is - # being run several times and we don't want to reinstall dependencies - # every time. - return - - cblas_inc_dir = os.path.join(cblas_root, "include", "x86_64-linux-gnu") - cblas_lib_dir = os.path.join(cblas_root, "lib", "x86_64-linux-gnu") - cmd = (f"cd '{os.path.join(amici_dir, 'ThirdParty')}' " - "&& apt download libatlas-base-dev && mkdir libatlas-base-dev " - "&& cd libatlas-base-dev " - "&& ar x ../libatlas-base-dev_3.10.3-8ubuntu7_amd64.deb " - "&& tar -xJf data.tar.xz " - f"&& ln -s {cblas_inc_dir}/cblas-atlas.h {cblas_inc_dir}/cblas.h " - ) - subprocess.run(cmd, shell=True, check=True) - os.environ['BLAS_CFLAGS'] = f'-I{cblas_inc_dir}' - os.environ['BLAS_LIBS'] = (f'-L{cblas_lib_dir}/atlas -L{cblas_lib_dir} ' - '-lcblas -latlas -lblas -lm') - - # build swig4.0 - subprocess.run(os.path.join(amici_dir, 'scripts', - 'downloadAndBuildSwig.sh'), check=True) - - # add swig to path - swig_dir = os.path.join(amici_dir, 'ThirdParty', 'swig-4.0.2', 'install', - 'bin') - os.environ['SWIG'] = os.path.join(swig_dir, 'swig') + print(ret.stdout.decode("utf-8")) + raise RuntimeError("downloadAndBuildMtocpp.sh failed") def install_doxygen(): """Get a more recent doxygen""" - version = '1.9.3' - doxygen_exe = os.path.join(amici_dir, 'ThirdParty', - f'doxygen-{version}', 'bin', 'doxygen') + version = "1.9.7" + doxygen_exe = os.path.join( + amici_dir, "ThirdParty", f"doxygen-{version}", "bin", "doxygen" + ) # to create a symlink to doxygen in a location that is already on PATH - some_dir_on_path = os.environ['PATH'].split(os.pathsep)[0] + some_dir_on_path = os.environ["PATH"].split(os.pathsep)[0] cmd = ( f"cd '{os.path.join(amici_dir, 'ThirdParty')}' " - f"&& wget 'https://doxygen.nl/files/" + f"&& wget 'https://www.doxygen.nl/files/" f"doxygen-{version}.linux.bin.tar.gz' " f"&& tar -xzf doxygen-{version}.linux.bin.tar.gz " - f"&& ln -s '{doxygen_exe}' '{some_dir_on_path}'" + f"&& ln -sf '{doxygen_exe}' '{some_dir_on_path}'" ) subprocess.run(cmd, shell=True, check=True) - assert os.path.islink(os.path.join(some_dir_on_path, 'doxygen')) + assert os.path.islink(os.path.join(some_dir_on_path, "doxygen")) # verify it's available - res = subprocess.run(['doxygen', '--version'], - check=False, capture_output=True) + res = subprocess.run( + ["doxygen", "--version"], check=False, capture_output=True + ) print(res.stdout.decode(), res.stderr.decode()) assert version in res.stdout.decode() @@ -154,35 +124,12 @@ def install_doxygen(): # -- RTD custom build -------------------------------------------------------- # only execute those commands when running from RTD -if 'READTHEDOCS' in os.environ and os.environ['READTHEDOCS']: - install_amici_deps_rtd() +if "READTHEDOCS" in os.environ and os.environ["READTHEDOCS"]: install_doxygen() # Required for matlab doxygen processing install_mtocpp() -# Install AMICI if not already present -typing.TYPE_CHECKING = True - -try: - import amici -except ModuleNotFoundError: - subprocess.run([ - 'python', '-m', 'pip', 'install', '--verbose', '-e', - os.path.join(amici_dir, 'python', 'sdist') - ], check=True) - - from importlib import invalidate_caches - - invalidate_caches() - - sys.path.insert(0, amici_dir) - sys.path.insert(0, os.path.join(amici_dir, 'python', 'sdist')) - - import amici - -typing.TYPE_CHECKING = False - # -- Project information ----------------------------------------------------- # The short X.Y version @@ -190,15 +137,15 @@ def install_doxygen(): # The full version, including alpha/beta/rc tags release = version -project = 'AMICI' -copyright = '2020, The AMICI developers' -author = 'The AMICI developers' -title = 'AMICI Documentation' +project = "AMICI" +copyright = "2020, The AMICI developers" +author = "The AMICI developers" +title = "AMICI Documentation" # -- Mock out some problematic modules------------------------------------- # Note that for sub-modules, all parent modules must be listed explicitly. -autodoc_mock_imports = ['_amici', 'amici._amici'] +autodoc_mock_imports = ["_amici", "amici._amici"] for mod_name in autodoc_mock_imports: sys.modules[mod_name] = mock.MagicMock() @@ -212,76 +159,103 @@ def install_doxygen(): # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'readthedocs_ext.readthedocs', + "readthedocs_ext.readthedocs", # Required, e.g. for PEtab-derived classes where the base class has non-rst # docstrings - 'sphinx.ext.napoleon', - 'sphinx.ext.autodoc', - 'sphinx.ext.doctest', - 'sphinx.ext.coverage', - 'sphinx.ext.intersphinx', - 'sphinx.ext.autosummary', - 'sphinx.ext.viewcode', - 'sphinx.ext.mathjax', - 'sphinxcontrib.matlab', - 'nbsphinx', - 'IPython.sphinxext.ipython_console_highlighting', - 'recommonmark', - 'sphinx_autodoc_typehints', - 'hoverxref.extension', - 'breathe', - 'exhale', + "sphinx.ext.napoleon", + "sphinx.ext.autodoc", + "sphinx.ext.doctest", + "sphinx.ext.coverage", + "sphinx.ext.intersphinx", + "sphinx.ext.autosummary", + "sphinx.ext.viewcode", + "sphinx.ext.mathjax", + "sphinxcontrib.matlab", + "nbsphinx", + "IPython.sphinxext.ipython_console_highlighting", + "recommonmark", + "sphinx_autodoc_typehints", + "hoverxref.extension", + "breathe", + "exhale", ] intersphinx_mapping = { - 'pysb': ('https://pysb.readthedocs.io/en/stable/', None), - 'petab': ('https://petab.readthedocs.io/en/stable/', None), - 'pandas': ('https://pandas.pydata.org/docs/', None), - 'numpy': ('https://numpy.org/devdocs/', None), - 'sympy': ('https://docs.sympy.org/latest/', None), - 'python': ('https://docs.python.org/3', None), + "pysb": ("https://pysb.readthedocs.io/en/stable/", None), + "petab": ( + "https://petab.readthedocs.io/projects/libpetab-python/en/latest/", + None, + ), + "pandas": ("https://pandas.pydata.org/docs/", None), + "numpy": ("https://numpy.org/devdocs/", None), + "sympy": ("https://docs.sympy.org/latest/", None), + "python": ("https://docs.python.org/3", None), } +# Add notebooks prolog with binder links +# get current git reference +ret = subprocess.run("git rev-parse HEAD".split(" "), capture_output=True) +ref = ret.stdout.rstrip().decode() +nbsphinx_prolog = ( + f"{{% set {ref=} %}}" + r""" + {% set docname = "documentation/" + env.doc2path(env.docname, base=False) %} + .. raw:: html + +
+ + """ +) + # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] -source_suffix = ['.rst', '.md'] +source_suffix = [".rst", ".md"] # The master toctree document. -master_doc = 'index' +master_doc = "index" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. -language = None +language = "en" # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This pattern also affects html_static_path and html_extra_path . exclude_patterns = [ - '_build', - 'Thumbs.db', - '.DS_Store', - '**.ipynb_checkpoints', - 'numpy.py', - 'INSTALL.md', - 'MATLAB_.md', - 'CPP_.md', - 'gfx' + "_build", + "Thumbs.db", + ".DS_Store", + "**.ipynb_checkpoints", + "numpy.py", + "INSTALL.md", + "MATLAB_.md", + "CPP_.md", + "gfx", ] # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = "sphinx" # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = False +# autodoc +autodoc_default_options = { + "special-members": "__init__", + "inherited-members": True, +} + # sphinx-autodoc-typehints typehints_fully_qualified = True typehints_document_rtype = True @@ -289,17 +263,17 @@ def install_doxygen(): # hoverxref hoverxref_auto_ref = True -hoverxref_roles = ['term'] -hoverxref_domains = ['py'] +hoverxref_roles = ["term"] +hoverxref_domains = ["py"] hoverxref_role_types = { - 'hoverxref': 'tooltip', - 'ref': 'tooltip', - 'term': 'tooltip', - 'obj': 'tooltip', - 'func': 'tooltip', - 'mod': 'tooltip', - 'meth': 'tooltip', - 'class': 'tooltip', + "hoverxref": "tooltip", + "ref": "tooltip", + "term": "tooltip", + "obj": "tooltip", + "func": "tooltip", + "mod": "tooltip", + "meth": "tooltip", + "class": "tooltip", } # breathe settings @@ -326,47 +300,49 @@ def install_doxygen(): "verboseBuild": True, } -mtocpp_filter = os.path.join(amici_dir, 'matlab', 'mtoc', - 'config', 'mtocpp_filter.sh') +mtocpp_filter = os.path.join( + amici_dir, "matlab", "mtoc", "config", "mtocpp_filter.sh" +) exhale_projects_args = { "AMICI_CPP": { - "exhaleDoxygenStdin": "\n".join([ - "INPUT = ../include", - "BUILTIN_STL_SUPPORT = YES", - "PREDEFINED += EXHALE_DOXYGEN_SHOULD_SKIP_THIS", - "EXCLUDE += ../include/amici/interface_matlab.h", - "EXCLUDE += ../include/amici/returndata_matlab.h", - "EXCLUDE += ../include/amici/spline.h", - # amici::log collides with amici::${some_enum}::log - # potentially fixed in - # https://github.com/svenevs/exhale/commit/c924df2e139a09fbacd07587779c55fd0ee4e00b - # and can be un-excluded after the next exhale release - "EXCLUDE += ../include/amici/symbolic_functions.h", - ]), + "exhaleDoxygenStdin": "\n".join( + [ + "INPUT = ../include/amici", + "BUILTIN_STL_SUPPORT = YES", + "PREDEFINED += EXHALE_DOXYGEN_SHOULD_SKIP_THIS", + "EXCLUDE += ../include/amici/interface_matlab.h", + "EXCLUDE += ../include/amici/returndata_matlab.h", + "EXCLUDE += ../include/amici/spline.h", + # amici::log collides with amici::${some_enum}::log + # potentially fixed in + # https://github.com/svenevs/exhale/commit/c924df2e139a09fbacd07587779c55fd0ee4e00b + # and can be un-excluded after the next exhale release + "EXCLUDE += ../include/amici/symbolic_functions.h", + ] + ), "containmentFolder": "_exhale_cpp_api", "rootFileTitle": "AMICI C++ API", - "afterTitleDescription": - "AMICI C++ library functions", + "afterTitleDescription": "AMICI C++ library functions", }, # Third Party Project Includes "AMICI_Matlab": { - "exhaleDoxygenStdin": "\n".join([ - "INPUT = ../matlab", - "EXTENSION_MAPPING = .m=C++", - "FILTER_PATTERNS = " - f"*.m={mtocpp_filter}", - "EXCLUDE += ../matlab/examples", - "EXCLUDE += ../matlab/mtoc", - "EXCLUDE += ../matlab/SBMLimporter", - "EXCLUDE += ../matlab/auxiliary", - "EXCLUDE += ../matlab/tests", - "PREDEFINED += EXHALE_DOXYGEN_SHOULD_SKIP_THIS", - ]), + "exhaleDoxygenStdin": "\n".join( + [ + "INPUT = ../matlab", + "EXTENSION_MAPPING = .m=C++", + "FILTER_PATTERNS = " f"*.m={mtocpp_filter}", + "EXCLUDE += ../matlab/examples", + "EXCLUDE += ../matlab/mtoc", + "EXCLUDE += ../matlab/SBMLimporter", + "EXCLUDE += ../matlab/auxiliary", + "EXCLUDE += ../matlab/tests", + "PREDEFINED += EXHALE_DOXYGEN_SHOULD_SKIP_THIS", + ] + ), "containmentFolder": "_exhale_matlab_api", "rootFileTitle": "AMICI Matlab API", - "afterTitleDescription": - "AMICI Matlab library functions", - "lexerMapping": {r'.*\.m$': 'matlab'} + "afterTitleDescription": "AMICI Matlab library functions", + "lexerMapping": {r".*\.m$": "matlab"}, }, } # -- Options for HTML output ------------------------------------------------- @@ -374,7 +350,7 @@ def install_doxygen(): # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -html_theme = 'sphinx_rtd_theme' +html_theme = "sphinx_rtd_theme" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the @@ -402,7 +378,7 @@ def install_doxygen(): # -- Options for HTMLHelp output --------------------------------------------- # Output file base name for HTML help builder. -htmlhelp_basename = 'AMICIdoc' +htmlhelp_basename = "AMICIdoc" # -- Options for LaTeX output ------------------------------------------------ @@ -410,15 +386,12 @@ def install_doxygen(): # The paper size ('letterpaper' or 'a4paper'). # # 'papersize': 'letterpaper', - # The font size ('10pt', '11pt' or '12pt'). # # 'pointsize': '10pt', - # Additional stuff for the LaTeX preamble. # # 'preamble': '', - # Latex figure (float) alignment # # 'figure_align': 'htbp', @@ -428,18 +401,14 @@ def install_doxygen(): # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - (master_doc, 'AMICI.tex', title, - author, 'manual'), + (master_doc, "AMICI.tex", title, author, "manual"), ] # -- Options for manual page output ------------------------------------------ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -man_pages = [ - (master_doc, 'amici', title, - [author], 1) -] +man_pages = [(master_doc, "amici", title, [author], 1)] # -- Options for Texinfo output ---------------------------------------------- @@ -447,95 +416,80 @@ def install_doxygen(): # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - (master_doc, 'AMICI', title, - author, 'AMICI', 'Advanced Multilanguage Interface for CVODES and IDAS.', - 'Miscellaneous'), + ( + master_doc, + "AMICI", + title, + author, + "AMICI", + "Advanced Multilanguage Interface for CVODES and IDAS.", + "Miscellaneous", + ), ] # Custom processing routines for docstrings and signatures typemaps = { - 'std::vector< amici::realtype,std::allocator< amici::realtype > >': - 'DoubleVector', - 'std::vector< double,std::allocator< double > >': - 'DoubleVector', - 'std::vector< int,std::allocator< int > >': - 'IntVector', - 'std::vector< amici::ParameterScaling,std::allocator< ' - 'amici::ParameterScaling >': 'ParameterScalingVector', - 'std::vector< std::string,std::allocator< std::string > >': - 'StringVector', - 'std::vector< bool,std::allocator< bool > >': - 'BoolVector', - 'std::map< std::string,amici::realtype,std::less< std::string >,' - 'std::allocator< std::pair< std::string const,amici::realtype > > >': - 'StringDoubleMap', - 'std::vector< amici::ExpData *,std::allocator< amici::ExpData * > >': - 'ExpDataPtrVector', - 'std::vector< std::unique_ptr< amici::ReturnData >,std::allocator< ' - 'std::unique_ptr< amici::ReturnData > > >': - 'Iterable[ReturnData]', - 'std::unique_ptr< amici::ExpData >': - 'ExpData', - 'std::unique_ptr< amici::ReturnData >': - 'ReturnData', - 'std::unique_ptr< amici::Solver >': - 'Solver', - 'amici::realtype': - 'float', + "std::vector< amici::realtype,std::allocator< amici::realtype > >": "DoubleVector", + "std::vector< double,std::allocator< double > >": "DoubleVector", + "std::vector< int,std::allocator< int > >": "IntVector", + "std::vector< amici::ParameterScaling,std::allocator< " + "amici::ParameterScaling >": "ParameterScalingVector", + "std::vector< std::string,std::allocator< std::string > >": "StringVector", + "std::vector< bool,std::allocator< bool > >": "BoolVector", + "std::map< std::string,amici::realtype,std::less< std::string >," + "std::allocator< std::pair< std::string const,amici::realtype > > >": "StringDoubleMap", + "std::vector< amici::ExpData *,std::allocator< amici::ExpData * > >": "ExpDataPtrVector", + "std::vector< std::unique_ptr< amici::ReturnData >,std::allocator< " + "std::unique_ptr< amici::ReturnData > > >": "Iterable[ReturnData]", + "std::unique_ptr< amici::ExpData >": "ExpData", + "std::unique_ptr< amici::ReturnData >": "ReturnData", + "std::unique_ptr< amici::Solver >": "Solver", + "amici::realtype": "float", } vector_types = { - 'IntVector': ':class:`int`', - 'BoolVector': ':class:`bool`', - 'DoubleVector': ':class:`float`', - 'StringVector': ':class:`str`', - 'ExpDataPtrVector': ':class:`amici.amici.ExpData`', + "IntVector": ":class:`int`", + "BoolVector": ":class:`bool`", + "DoubleVector": ":class:`float`", + "StringVector": ":class:`str`", + "ExpDataPtrVector": ":class:`amici.amici.ExpData`", } def process_docstring(app, what, name, obj, options, lines): # only apply in the amici.amici module - if len(name.split('.')) < 2 or name.split('.')[1] != 'amici': + if len(name.split(".")) < 2 or name.split(".")[1] != "amici": return # add custom doc to swig generated classes - if len(name.split('.')) == 3 and name.split('.')[2] in \ - ['IntVector', 'BoolVector', 'DoubleVector', 'StringVector', - 'ExpDataPtrVector']: - cname = name.split('.')[2] - lines.append( - f'Swig-Generated class templating common python ' - f'types including :class:`Iterable` ' - f'[{vector_types[cname]}] ' - f'and ' - f':class:`numpy.array` [{vector_types[cname]}] to facilitate' - ' interfacing with C++ bindings.' - ) - return - - if name == 'amici.amici.StringDoubleMap': - lines.append( - 'Swig-Generated class templating :class:`Dict` ' - '[:class:`str`, :class:`float`] to facilitate' - ' interfacing with C++ bindings.' - ) - return - - if name == 'amici.amici.ParameterScalingVector': + if len(name.split(".")) == 3 and name.split(".")[2] in [ + "IntVector", + "BoolVector", + "DoubleVector", + "StringVector", + "ExpDataPtrVector", + ]: + cname = name.split(".")[2] lines.append( - 'Swig-Generated class, which, in contrast to other Vector ' - 'classes, does not allow for simple interoperability with common ' - 'python types, but must be created using ' - ':func:`amici.amici.parameterScalingFromIntVector`' + f"Swig-Generated class templating common python " + f"types including :class:`Iterable` " + f"[{vector_types[cname]}] " + f"and " + f":class:`numpy.array` [{vector_types[cname]}] to facilitate" + " interfacing with C++ bindings." ) return - if len(name.split('.')) == 3 and name.split('.')[2] in \ - ['ExpDataPtr', 'ReturnDataPtr', 'ModelPtr', 'SolverPtr']: - cname = name.split('.')[2] + if len(name.split(".")) == 3 and name.split(".")[2] in [ + "ExpDataPtr", + "ReturnDataPtr", + "ModelPtr", + "SolverPtr", + ]: + cname = name.split(".")[2] lines.append( - f'Swig-Generated class that implements smart pointers to ' + f"Swig-Generated class that implements smart pointers to " f'{cname.replace("Ptr", "")} as objects.' ) return @@ -546,9 +500,12 @@ def process_docstring(app, what, name, obj, options, lines): while len(lines): line = lines.pop(0) - if re.match(r':(type|rtype|param|return)', line) and \ - len(lines_clean) and lines_clean[-1] != '': - lines_clean.append('') + if ( + re.match(r":(type|rtype|param|return)", line) + and len(lines_clean) + and lines_clean[-1] != "" + ): + lines_clean.append("") lines_clean.append(line) lines.extend(lines_clean) @@ -558,14 +515,14 @@ def process_docstring(app, what, name, obj, options, lines): for old, new in typemaps.items(): lines[i] = lines[i].replace(old, new) lines[i] = re.sub( - r'amici::(Model|Solver|ExpData) ', - r':class:`amici\.amici\.\1\`', - lines[i] + r"amici::(Model|Solver|ExpData) ", + r":class:`amici\.amici\.\1\`", + lines[i], ) lines[i] = re.sub( - r'amici::(runAmiciSimulation[s]?)', - r':func:`amici\.amici\.\1`', - lines[i] + r"amici::(runAmiciSimulation[s]?)", + r":func:`amici\.amici\.\1`", + lines[i], ) @@ -576,44 +533,45 @@ def fix_typehints(sig: str) -> str: for old, new in typemaps.items(): sig = sig.replace(old, new) - sig = sig.replace('void', 'None') - sig = sig.replace('amici::realtype', 'float') - sig = sig.replace('std::string', 'str') - sig = sig.replace('double', 'float') - sig = sig.replace('long', 'int') - sig = sig.replace('char const *', 'str') - sig = sig.replace('amici::', '') - sig = sig.replace('sunindextype', 'int') - sig = sig.replace('H5::H5File', 'object') + sig = sig.replace("void", "None") + sig = sig.replace("amici::realtype", "float") + sig = sig.replace("std::string", "str") + sig = sig.replace("double", "float") + sig = sig.replace("long", "int") + sig = sig.replace("char const *", "str") + sig = sig.replace("amici::", "") + 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) + 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) + sig = re.sub(r" &(,|\))", r"\1", sig) + sig = re.sub(r" &$", r"", sig) # turn gsl_spans and pointers int Iterables - sig = re.sub(r'([\w.]+) \*', r'Iterable[\1]', sig) - sig = re.sub(r'gsl::span< ([\w.]+) >', r'Iterable[\1]', sig) + sig = re.sub(r"([\w.]+) \*", r"Iterable[\1]", sig) + sig = re.sub(r"gsl::span< ([\w.]+) >", r"Iterable[\1]", sig) # fix garbled output - sig = sig.replace(' >', '') + sig = sig.replace(" >", "") return sig -def process_signature(app, what: str, name: str, obj, options, signature, - return_annotation): +def process_signature( + app, what: str, name: str, obj, options, signature, return_annotation +): if signature is None: return # only apply in the amici.amici module - if name.split('.')[1] != 'amici': + if name.split(".")[1] != "amici": return signature = fix_typehints(signature) - if hasattr(obj, '__annotations__'): + if hasattr(obj, "__annotations__"): for ann in obj.__annotations__: obj.__annotations__[ann] = fix_typehints(obj.__annotations__[ann]) @@ -623,71 +581,92 @@ def process_signature(app, what: str, name: str, obj, options, signature, # this code fixes references in symlinked md files in documentation folder # link replacements must be in env.domains['std'].labels doclinks = { - 'documentation/development': '/development.md', - 'documentation/CI': '/ci.md', - 'documentation/code_review_guide': '/code_review_guide.md', + "documentation/development": "/development.md", + "documentation/CI": "/ci.md", + "documentation/code_review_guide": "/code_review_guide.md", } def process_missing_ref(app, env, node, contnode): - if not any(link in node['reftarget'] for link in doclinks): + if not any(link in node["reftarget"] for link in doclinks): return # speedup futile processing for old, new in doclinks.items(): - node['reftarget'] = node['reftarget'].replace(old, new) + node["reftarget"] = node["reftarget"].replace(old, new) cnode = node[0] - if 'refuri' in cnode: + if "refuri" in cnode: for old, new in doclinks.items(): - cnode['refuri'] = cnode['refuri'].replace(old, new) + cnode["refuri"] = cnode["refuri"].replace(old, new) - refdoc = node.get('refdoc', env.docname) + refdoc = node.get("refdoc", env.docname) resolver = ReferencesResolver(env.get_doctree(refdoc)) result = resolver.resolve_anyref(refdoc, node, cnode) return result def skip_member(app, what, name, obj, skip, options): - ignored = ['AbstractModel', 'CVodeSolver', 'IDASolver', 'Model_ODE', - 'Model_DAE', 'ConditionContext', 'checkSigmaPositivity', - 'createGroup', 'createGroup', 'equals', 'printErrMsgIdAndTxt', - 'wrapErrHandlerFn', 'printWarnMsgIdAndTxt', - 'AmiciApplication', 'writeReturnData', - 'writeReturnDataDiagnosis', 'attributeExists', 'locationExists', - 'createAndWriteDouble1DDataset', - 'createAndWriteDouble2DDataset', - 'createAndWriteDouble3DDataset', - 'createAndWriteInt1DDataset', 'createAndWriteInt2DDataset', - 'createAndWriteInt3DDataset', 'getDoubleDataset1D', - 'getDoubleDataset2D', 'getDoubleDataset3D', 'getIntDataset1D', - 'getIntScalarAttribute', 'getDoubleScalarAttribute', - 'stdVec2ndarray', 'SwigPyIterator', 'thisown'] + ignored = [ + "AbstractModel", + "CVodeSolver", + "IDASolver", + "Model_ODE", + "Model_DAE", + "ConditionContext", + "checkSigmaPositivity", + "createGroup", + "createGroup", + "equals", + "printErrMsgIdAndTxt", + "wrapErrHandlerFn", + "printWarnMsgIdAndTxt", + "AmiciApplication", + "writeReturnData", + "writeReturnDataDiagnosis", + "attributeExists", + "locationExists", + "createAndWriteDouble1DDataset", + "createAndWriteDouble2DDataset", + "createAndWriteDouble3DDataset", + "createAndWriteInt1DDataset", + "createAndWriteInt2DDataset", + "createAndWriteInt3DDataset", + "getDoubleDataset1D", + "getDoubleDataset2D", + "getDoubleDataset3D", + "getIntDataset1D", + "getIntScalarAttribute", + "getDoubleScalarAttribute", + "stdVec2ndarray", + "SwigPyIterator", + "thisown", + ] if name in ignored: return True - if name.startswith('_') and name != '__init__': + if name.startswith("_") and name != "__init__": return True # ignore various functions for std::vector<> types - if re.match(r'^`__. -New releases are created on Github and are automatically deployed to -`Zenodo `__ for +New releases are created on GitHub and are automatically deployed to +`Zenodo `__ for archiving and to obtain a digital object identifier (DOI) to make them citable. Furthermore, our `CI pipeline `__ will automatically create and deploy a new release on @@ -51,17 +51,11 @@ process described below: - Submit a pull request to the ``develop`` branch -- Make sure your code is documented appropriately - - - Run ``scripts/run-doxygen.sh`` to check completeness of your - documentation - -- Make sure your code is compatible with C++11, ``gcc`` and ``clang`` - (our CI pipeline will do this for you) +- Ensure all tests pass - When adding new functionality, please also provide test cases (see - ``tests/cpp/`` and - `documentation/CI.md `__) + ``tests/cpp/``, ``python/tests/``, + and `documentation/CI.md `__) - Write meaningful commit messages @@ -84,8 +78,8 @@ process described below: - Wait for feedback. If you do not receive feedback to your pull request within a week, please give us a friendly reminder. -Style guide -~~~~~~~~~~~ +Style/compatibility guide +~~~~~~~~~~~~~~~~~~~~~~~~~ General ^^^^^^^ @@ -99,17 +93,24 @@ General Python ^^^^^^ -- We want to be compatible with Python 3.7 +- In terms of Python compatibility, we follow numpy's + `NEP 29 `__. - For the Python code we want to follow `PEP8 `__. Although this is not the case for all existing code, any new contributions should - do so. + do so. We use `black `__ + for code formatting. + + To run black as pre-commit hook, install the + `pre-commit `_ package + (e.g. ``pip install pre-commit``), and enable AMICI-hooks by running + ``pre-commit install`` from within the AMICI directory. - We use Python `type hints `__ for all functions (but not for class attributes, since they are not supported - by the current Python doxygen filter). In Python code type hints + by the current Python doxygen filter). In Python code, type hints should be used instead of doxygen ``@type``. For function docstrings, follow this format: @@ -134,15 +135,14 @@ Python C++ ^^^ -- We use C++14 +- We use C++17 -- We want to maintain compatibility with g++, clang and the Intel C++ +- We want to maintain compatibility with g++, clang, and the Intel C++ compiler -- For code formatting, we use the settings from ``.clang-format`` in - the root directory - -- *Details to be defined* +- For code formatting, we use ``clang-format`` and ``cmake-format``. They can + be invoked by ``make clang-format cmake-format`` from the CMake build + directory. Matlab ^^^^^^ @@ -158,3 +158,4 @@ Further topics Organization of the documentation code_review_guide CI + debugging diff --git a/deps/AMICI/documentation/example_errors.ipynb b/deps/AMICI/documentation/example_errors.ipynb new file mode 120000 index 000000000..96b3e9737 --- /dev/null +++ b/deps/AMICI/documentation/example_errors.ipynb @@ -0,0 +1 @@ +../python/examples/example_errors.ipynb \ No newline at end of file diff --git a/deps/AMICI/documentation/example_large_models b/deps/AMICI/documentation/example_large_models new file mode 120000 index 000000000..bddb4a038 --- /dev/null +++ b/deps/AMICI/documentation/example_large_models @@ -0,0 +1 @@ +../python/examples/example_large_models/ \ No newline at end of file diff --git a/deps/AMICI/documentation/gfx/amici_workflow.png b/deps/AMICI/documentation/gfx/amici_workflow.png index 302f7fc75..75f904ff7 100644 Binary files a/deps/AMICI/documentation/gfx/amici_workflow.png and b/deps/AMICI/documentation/gfx/amici_workflow.png differ diff --git a/deps/AMICI/documentation/gfx/amici_workflow.svg b/deps/AMICI/documentation/gfx/amici_workflow.svg index 88544f256..ee1a86fcc 100644 --- a/deps/AMICI/documentation/gfx/amici_workflow.svg +++ b/deps/AMICI/documentation/gfx/amici_workflow.svg @@ -2,25 +2,25 @@ + inkscape:export-ydpi="90" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:i="http://ns.adobe.com/AdobeIllustrator/10.0/"> + fit-margin-bottom="0" + inkscape:pagecheckerboard="0" /> @@ -592,7 +593,6 @@ image/svg+xml - @@ -665,16 +665,16 @@ id="text22112" y="65.034897" x="149.03789" - style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.96875px;line-height:125%;font-family:Arial;-inkscape-font-specification:'Arial, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.96875px;line-height:125%;font-family:Arial;-inkscape-font-specification:'Arial, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" xml:space="preserve">.a / .so amici::runAmiciSimulation(...) + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:monospace;-inkscape-font-specification:monospace;stroke-width:0.264583px">amici::runAmiciSimulation(...) Symbolic Math ToolboxMatlab < R2018a @@ -1103,7 +1103,7 @@ width="23.868874" /> Matlab + style="stroke-width:0.264583px">Matlab .mex + style="stroke-width:0.264583px">.mex >= 3.7 + style="stroke-width:0.264583px">>= 3.9 Python Python module / package + style="text-align:center;text-anchor:middle;stroke-width:0.264583px">module / package Model Model importModel importsymbolic processing& C++ code generation Model compilation Model simulation AMICI interfaces diff --git a/deps/AMICI/documentation/gfx/logo_template.svg b/deps/AMICI/documentation/gfx/logo_template.svg index eb41200e3..f4bfb6169 100644 --- a/deps/AMICI/documentation/gfx/logo_template.svg +++ b/deps/AMICI/documentation/gfx/logo_template.svg @@ -169,25 +169,25 @@ - + - + - + - + - + - + - + - + - + - + diff --git a/deps/AMICI/documentation/glossary.rst b/deps/AMICI/documentation/glossary.rst index 4f92d18e1..c732af7a3 100644 --- a/deps/AMICI/documentation/glossary.rst +++ b/deps/AMICI/documentation/glossary.rst @@ -5,6 +5,10 @@ Glossary .. glossary:: :sorted: + BNGL + `BioNetGenLanguage `_ is a language for + modular, structure-based modeling of biochemical reaction networks. + CVODES `CVODES `_ is a solver for stiff and non-stiff :term:`ODE` systems with sensitivity @@ -46,8 +50,8 @@ Glossary biology models as Python code. SBML - `SBML `_ is a commonly used format for specifying - systems biology models. + The `Systems Biology Markup Language `_ is a + commonly used format for specifying systems biology models. SUNDIALS `SUNDIALS `_: diff --git a/deps/AMICI/documentation/implementation_discontinuities.rst b/deps/AMICI/documentation/implementation_discontinuities.rst index fc04bacce..45e2d78ab 100644 --- a/deps/AMICI/documentation/implementation_discontinuities.rst +++ b/deps/AMICI/documentation/implementation_discontinuities.rst @@ -72,15 +72,15 @@ respective root function as argument. These will be automatically updated during events and take either 0 or 1 values as appropriate pre/post event limits. -In order to fully support SBML events and Piecewise functions, AMICI uses -the SUNDIALS functionality to only track zero crossings from negative to -positive. Accordingly, two root functions are necessary to keep track of -Heaviside functions and two Heaviside function helper variables will be -created, where one corresponds to the value of `Heaviside(...)` and one -to the value of `1-Heaviside(...)`. To ensure that Heaviside functions are -correctly evaluated at the beginning of the simulation, Heaviside functions -are implement as unit steps that evaluate to `1` at `0`. The arguments of -Heaviside functions are normalized such that respective properties of +In order to fully support SBML events and Piecewise functions, AMICI uses +the SUNDIALS functionality to only track zero crossings from negative to +positive. Accordingly, two root functions are necessary to keep track of +Heaviside functions and two Heaviside function helper variables will be +created, where one corresponds to the value of `Heaviside(...)` and one +to the value of `1-Heaviside(...)`. To ensure that Heaviside functions are +correctly evaluated at the beginning of the simulation, Heaviside functions +are implement as unit steps that evaluate to `1` at `0`. The arguments of +Heaviside functions are normalized such that respective properties of Piecewise functions are conserved for the first Heaviside function variable. Accordingly, the value of of the second helper variable is incorrect when simulation starts when the respective Heaviside function evaluates to zero diff --git a/deps/AMICI/documentation/index.rst b/deps/AMICI/documentation/index.rst index 8bfa399ef..73343e76c 100644 --- a/deps/AMICI/documentation/index.rst +++ b/deps/AMICI/documentation/index.rst @@ -30,13 +30,9 @@ Welcome to AMICI's documentation! glossary contributing -.. toctree:: - :maxdepth: 2 - :caption: User's guide - - PYTHON - CPP - MATLAB +.. include:: PYTHON.rst +.. include:: CPP.rst +.. include:: MATLAB.rst .. toctree:: :maxdepth: 2 diff --git a/deps/AMICI/documentation/model_steadystate_scaled_without_observables.xml b/deps/AMICI/documentation/model_steadystate_scaled_without_observables.xml new file mode 120000 index 000000000..be8991d93 --- /dev/null +++ b/deps/AMICI/documentation/model_steadystate_scaled_without_observables.xml @@ -0,0 +1 @@ +../python/examples/example_steadystate/model_steadystate_scaled_without_observables.xml \ No newline at end of file diff --git a/deps/AMICI/documentation/python_examples.rst b/deps/AMICI/documentation/python_examples.rst new file mode 100644 index 000000000..286ebf3ff --- /dev/null +++ b/deps/AMICI/documentation/python_examples.rst @@ -0,0 +1,21 @@ +Examples +======== + +Various example notebooks. + +.. image:: https://mybinder.org/badge_logo.svg + :target: https://mybinder.org/v2/gh/AMICI-dev/AMICI/develop?labpath=binder%2Foverview.ipynb + +.. toctree:: + :maxdepth: 1 + + GettingStarted.ipynb + ExampleSteadystate.ipynb + petab.ipynb + ExampleExperimentalConditions.ipynb + ExampleEquilibrationLogic.ipynb + example_errors.ipynb + example_large_models/example_performance_optimization.ipynb + ExampleJax.ipynb + ExampleSplines.ipynb + ExampleSplinesSwameye2003.ipynb diff --git a/deps/AMICI/documentation/python_installation.rst b/deps/AMICI/documentation/python_installation.rst index 59e98dc28..6cc3402e6 100644 --- a/deps/AMICI/documentation/python_installation.rst +++ b/deps/AMICI/documentation/python_installation.rst @@ -8,12 +8,12 @@ Short guide Installation of the AMICI Python package has the following prerequisites: -* Python>=3.7 +* Python>=3.9 * :term:`SWIG`>=3.0 * CBLAS compatible BLAS library (e.g., OpenBLAS, CBLAS, Atlas, Accelerate, Intel MKL) -* a C++14 compatible C++ compiler and a C compiler - (e.g., g++, clang, Intel C++ compiler, mingw) +* a C++17 compatible C++ compiler and a C compiler + (e.g., g++>=9.1, clang>=12, Intel C++ compiler, mingw) If these requirements are fulfilled and all relevant paths are setup properly, AMICI can be installed using: @@ -31,7 +31,7 @@ If this does not work for you, please follow the full instructions below. Installation on Linux +++++++++++++++++++++ -Ubuntu 20.04 +Ubuntu 22.04 ------------ Install the AMICI dependencies via ``apt`` @@ -44,6 +44,9 @@ 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 + Install AMICI: .. code-block:: bash @@ -66,6 +69,43 @@ Install AMICI: pip3 install amici +Arch Linux +---------- + +Install the AMICI dependencies via ``pacman`` +(this requires superuser privileges): + +.. code-block:: bash + + sudo pacman -S python swig openblas gcc hdf5 boost-libs + +Install AMICI: + +.. code-block:: bash + + pip3 install amici + +Alternatively: + +1. Check if packages are already installed with the required versions for AMICI installation. + +.. code-block:: bash + + sudo pacman -Si python swig openblas gcc hdf5 boost-libs + +2. Upgrade installed packages if required mininum versions are not satisfied for AMICI installation. + +.. code-block:: bash + + sudo pacman -Su python swig openblas gcc hdf5 boost-libs + +3. Install AMICI: + +.. code-block:: bash + + pip3 install amici + + Installation on OSX +++++++++++++++++++ @@ -80,6 +120,13 @@ Install the AMICI dependencies using homebrew: # optionally for parallel simulations: brew install libomp + # followed by either `brew link openmp` once, + # or `export OpenMP_ROOT=$(brew --prefix)/opt/libomp"` where `OpenMP_ROOT` will have to be set during every re-installation of AMICI or any new model import + + # optionally for boost support (thread-specific CPU times, extended math functions, serialization) + brew install boost && export BOOST_ROOT=$(brew --prefix)/opt/boost + # followed by either `brew link boost` once, + # or `export BOOST_ROOT=$(brew --prefix)/opt/boost"` where `BOOST_ROOT` will have to be set during every re-installation of AMICI or any new model import Install AMICI: @@ -93,6 +140,8 @@ Installation on Windows Some general remarks: +* Consider using the `Windows Subsystem for Linux (WSL) `__ and follow the instructions for + installation on linux. * Install all libraries in a path not containing white spaces, e.g. directly under C:. * Replace the following paths according to your installation. @@ -101,74 +150,9 @@ Some general remarks: * See also [#425](https://github.com/AMICI-dev/amici/issues/425) for further discussion. -Using the MinGW compilers -------------------------- - -* Install `MinGW-W64 `_ - (the 32bit version will succeed to compile, but fail during linking). - - MinGW-W64 GCC-8.1.0 for ``x86_64-posix-sjlj`` - (`direct link `_) has been shown to work on Windows 7 and 10 test systems. - -* Add the following directory to your ``PATH``: - ``C:\mingw-w64\x86_64-8.1.0-posix-sjlj-rt_v6-rev0\mingw64\bin`` - -* Make sure that this is the compiler that is found by the system - (e.g. ``where gcc`` in a ``cmd`` should point to this installation). - -* Download CBLAS headers and libraries, e.g. - `OpenBLAS `_, - binary distribution 0.2.19. - - Set the following environment variables: - - + ``BLAS_CFLAGS=-IC:/OpenBLAS-v0.2.19-Win64-int32/include`` - + ``BLAS_LIBS=-Wl,-Bstatic -LC:/OpenBLAS-v0.2.19-Win64-int32/lib -lopenblas -Wl,-Bdynamic`` - -* Install `SWIG `_ - and add the SWIG directory to ``PATH`` - (e.g. ``C:\swigwin-3.0.12`` for version 3.0.12) - -* Install AMICI using: - - .. code-block:: bash - - pip install --global-option="build_clib" \ - --global-option="--compiler=mingw32" \ - --global-option="build_ext" \ - --global-option="--compiler=mingw32" \ - amici --no-cache-dir --verbose` - -.. note:: - - **Possible sources of errors:** - - * On recent Windows versions, - ``anaconda3\Lib\distutils\cygwinccompiler.py`` fails linking - ``msvcr140.dll`` with - ``[...] x86_64-w64-mingw32/bin/ld.exe: cannot find -lmsvcr140``. - This is not required for amici, so in ``cygwinccompiler.py`` - ``return ['msvcr140']`` can be changed to ``return []``. - - * If you use a python version where - `python/cpython#880 `_ - has not been fixed yet, you need to disable - ``define hypot _hypot`` in ``anaconda3\include/pyconfig.h`` yourself. - - * ``import amici`` in Python resulting in the very informative - - ImportError: DLL load failed: The specified module could not be found. - - means that some amici module dependencies were not found (not the - AMICI module itself). - `DependencyWalker `_ can show you - which ones. - Using the Microsoft Visual Studio --------------------------------- -.. note:: Support for MSVC is experimental. - We assume that Visual Studio (not to be confused with Visual Studio Code) is already installed. Using Visual Studio Installer, the following components need to be included: @@ -182,7 +166,11 @@ OpenBLAS ^^^^^^^^ There are prebuilt OpenBLAS binaries available, but they did not seem to work -well here. Therefore, we recommend building OpenBLAS from scratch. +well here. Therefore, we recommend building OpenBLAS from scratch. This +requires an installation of CMake. CMake can be installed from +https://cmake.org/download/ (system-wide), or via ``pip install cmake`` +(in the current Python environment). + To build OpenBLAS, download the following scripts from the AMICI repository: @@ -192,29 +180,34 @@ To build OpenBLAS, download the following scripts from the AMICI repository: The first script needs to be called in Powershell, and it needs to call ``compileBLAS.cmd``, so you will need to modify line 11: - C: \\Users\\travis\\build\\AMICI\\scripts\\compileBLAS.cmd + cmd /c "scripts\compileBLAS.cmd $version" + +Additionally, in ``compileBLAS.cmd`` make sure that you point to your +Visual Studio installation on line 3. +Newer installations could be located under +``C:\Program Files\Microsoft Visual Studio\...\VC\Auxiliary\Build\vcvars64.bat``. so that it matches your directory structure. This will download OpenBLAS and compile it, creating - C:\\BLAS\lib\\openblas.lib - C:\\BLAS\\bin\\openblas.dll + C:\\BLAS\\OpenBLAS\\lib\\openblas.lib + C:\\BLAS\\OpenBLAS\\bin\\openblas.dll You will also need to define two environment variables: .. code-block:: text - BLAS_LIBS="/LIBPATH:C:\BLAS\lib openblas.lib" - BLAS_CFLAGS="/IC:/BLAS/OpenBLAS-0.3.12/OpenBLAS-0.3.12" + BLAS_LIBS="-LIBPATH:C:/BLAS/OpenBLAS/lib openblas.lib" + BLAS_CFLAGS="-IC:/BLAS/OpenBLAS" One way to do that is to run a PowerShell script with the following commands: .. code-block:: text - [System.Environment]::SetEnvironmentVariable("BLAS_LIBS", "/LIBPATH:C:/BLAS/lib openblas.lib", [System.EnvironmentVariableTarget]::User) - [System.Environment]::SetEnvironmentVariable("BLAS_LIBS", "/LIBPATH:C:/BLAS/lib openblas.lib", [System.EnvironmentVariableTarget]::Process) - [System.Environment]::SetEnvironmentVariable("BLAS_CFLAGS", "-IC:/BLAS/OpenBLAS-0.3.12/OpenBLAS-0.3.12", [System.EnvironmentVariableTarget]::User) - [System.Environment]::SetEnvironmentVariable("BLAS_CFLAGS", "-IC:/BLAS/OpenBLAS-0.3.12/OpenBLAS-0.3.12", [System.EnvironmentVariableTarget]::Process) + [System.Environment]::SetEnvironmentVariable("BLAS_LIBS", "-LIBPATH:C:/BLAS/OpenBLAS/lib openblas.lib", [System.EnvironmentVariableTarget]::User) + [System.Environment]::SetEnvironmentVariable("BLAS_LIBS", "-LIBPATH:C:/BLAS/OpenBLAS/lib openblas.lib", [System.EnvironmentVariableTarget]::Process) + [System.Environment]::SetEnvironmentVariable("BLAS_CFLAGS", "-IC:/BLAS/OpenBLAS/include/openblas", [System.EnvironmentVariableTarget]::User) + [System.Environment]::SetEnvironmentVariable("BLAS_CFLAGS", "-IC:/BLAS/OpenBLAS/include/openblas", [System.EnvironmentVariableTarget]::Process) The call ending in ``Process`` sets the environment variable in the current process, and it is no longer in effect in the next process. The call ending in @@ -224,7 +217,7 @@ 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\\bin + 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 @@ -235,14 +228,6 @@ following error upon ``import amici``: ImportError: DLL load failed: The specified module could not be found. -This can be tested using the "where" command. For example - - where openblas.dll - -should return - - C:\\BLAS\\bin\\openblas.dll - 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, here is a list of some DLLs required by AMICI (when compiled with MSVC): @@ -268,16 +253,17 @@ by MSVC (see Visual Studio above). ``KERNEL32.dll`` is part of Windows and in .. note:: - Since Python 3.8, the library directory needs to be set as follows: + Since Python 3.8, the library directory needs to be set either from Python: .. code-block:: python import os # directory containing `openblas.dll` - os.add_dll_directory("C:\\BLAS\\bin") + os.add_dll_directory("C:\\BLAS\\OpenBLAS\\bin") import amici - Adding it to ``PATH`` will not work. + or via the environment variable ``AMICI_DLL_DIRS="C:\BLAS\OpenBLAS\bin"``. + Further topics ++++++++++++++ @@ -322,6 +308,8 @@ dependencies, you can run applied to *all* installed packages, including dependencies.) +.. _amici_python_install_env_vars: + Custom installation ------------------- @@ -354,6 +342,10 @@ environment variables: | | processes to be used for C(++) | | | | compilation (defaults to 1) | | +----------------------------+----------------------------------+---------------------------------+ +| ``AMICI_TRY_ENABLE_HDF5`` | Whether to build AMICI with | ``AMICI_TRY_ENABLE_HDF5=OFF`` | +| | HDF5-support if possible. | | +| | Default: ``ON`` | | ++----------------------------+----------------------------------+---------------------------------+ Installation under Anaconda --------------------------- diff --git a/deps/AMICI/documentation/python_interface.rst b/deps/AMICI/documentation/python_interface.rst index 9db3a06c7..a919e925c 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 996 out of the 1780 (~56%) test cases** from +Python-AMICI currently **passes 1247 out of the 1821 (~68%) test cases** from the semantic `SBML Test Suite `_ (`current status `_). @@ -37,17 +37,28 @@ The following SBML test suite tags are currently supported **Component tags:** +* AlgebraicRule * AssignmentRule +* comp * Compartment * CSymbolAvogadro +* CSymbolRateOf * CSymbolTime +* Deletion * EventNoDelay +* ExternalModelDefinition * FunctionDefinition * InitialAssignment +* ModelDefinition * Parameter +* Port * RateRule * Reaction +* ReplacedBy +* ReplacedElement +* SBaseRef * Species +* Submodel **Test tags:** @@ -57,10 +68,14 @@ The following SBML test suite tags are currently supported * AssignedVariableStoichiometry * BoolNumericSwap * BoundaryCondition +* comp * Concentration * ConstantSpecies +* ConversionFactor * ConversionFactors +* DefaultValue * EventT0Firing +* ExtentConversionFactor * HasOnlySubstanceUnits * InitialValueReassigned * L3v2MathML @@ -73,18 +88,14 @@ The following SBML test suite tags are currently supported * NonUnityStoichiometry * ReversibleReaction * SpeciesReferenceInMath +* SubmodelOutput +* TimeConversionFactor * UncommonMathML * VolumeConcentrationRates -In addition, we currently plan to add support for the following features -(see corresponding `issues `_ -for details and progress): +Additional support may be added in the future. However, the following features are +unlikely to be supported: -- Algebraic rules (`#760 `_) - -However, the following features are unlikely to be supported: - -- any SBML extensions - `factorial()`, `ceil()`, `floor()`, due to incompatibility with symbolic sensitivity computations - `delay()` due to missing :term:`SUNDIALS` solver support @@ -104,9 +115,11 @@ PySB import AMICI can import :term:`PySB` models via :py:func:`amici.pysb_import.pysb2amici`. -`BioNetGen `_ and -`Kappa `_ models can be imported into AMICI using -PySB. +BNGL import +----------- + +AMICI can import :term:`BNGL` models via +:py:func:`amici.bngl_import.bngl2amici`. PEtab import ------------ @@ -120,9 +133,57 @@ Importing plain ODEs The AMICI Python interface does not currently support direct import of ODEs. However, it is straightforward to encode them as RateRules in an SBML model. -The `yaml2sbml `_ package may come in -handy, as it facilitates generating SBML models from a YAML-based specification -of an ODE model. Besides the SBML model it can also create +The most convenient options to do that are maybe +`Antimony `_ +and `yaml2sbml `_. + +An example using Antimony to specify the Lotka-Volterra equations is shown below: + +.. code-block:: python + + ant_model = """ + + model lotka_volterra + # see https://en.wikipedia.org/wiki/Lotka%E2%80%93Volterra_equations + + # initial conditions + prey_density = 10; + predator_density = 10; + + # parameters + prey_growth_rate = 1.1; + predator_effect_on_prey = 0.4; + predator_death_rate = 0.4; + prey_effect_on_predator = 0.1; + + # dx/dt + prey_density' = prey_growth_rate * prey_density - predator_effect_on_prey * prey_density * predator_density; + predator_density' = prey_effect_on_predator * prey_density * predator_density - predator_death_rate * predator_density; + end + """ + module_name = "test_antimony_example_lv" + from amici.antimony_import import antimony2amici + antimony2amici( + ant_model, + model_name=module_name, + output_dir=module_name, + ) + 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) + + from amici.plotting import plot_state_trajectories + plot_state_trajectories(rdata, model=amici_model) + + +The `yaml2sbml `_ package creates SBML models +from a YAML-based specification of an ODE model. Various examples are +`provided `_. +Besides the SBML model, yaml2sbml can also create `PEtab `_ files. SED-ML import @@ -131,17 +192,34 @@ SED-ML import We also plan to implement support for the `Simulation Experiment Description Markup Language (SED-ML) `_. -Examples -======== - -.. toctree:: - :maxdepth: 1 - - GettingStarted.ipynb - ExampleSteadystate.ipynb - petab.ipynb - ExampleExperimentalConditions.ipynb - ExampleEquilibrationLogic.ipynb +Environment variables affecting model import +============================================ + +In addition to the environment variables listed +:ref:`here `, the following environment +variables control various behaviours during model import and compilation: + +.. list-table:: Environment variables affecting model import + :widths: 25 50 25 + :header-rows: 1 + + * - Variable + - Purpose + - Example + * - ``AMICI_EXTRACT_CSE`` + - Extract common subexpressions. May significantly reduce file size and + compile time for large models, but makes the generated code less + readable. Disabled by default. + - ``AMICI_EXTRACT_CSE=1`` + * - ``AMICI_IMPORT_NPROCS`` + - Number of processes to be used for model import. Defaults to 1. + Speeds up import of large models. Will slow down import of small models, + benchmarking recommended. + - ``AMICI_IMPORT_NPROCS=4`` + * - ``AMICI_EXPERIMENTAL_SBML_NONCONST_CLS`` + - Compute conservation laws for non-constant species. SBML-import only. + See :py:func:`amici.sbml_import.SbmlImporter.sbml2amici`. + - Miscellaneous diff --git a/deps/AMICI/documentation/python_modules.rst b/deps/AMICI/documentation/python_modules.rst index b493fb7ff..5481865a7 100644 --- a/deps/AMICI/documentation/python_modules.rst +++ b/deps/AMICI/documentation/python_modules.rst @@ -10,14 +10,21 @@ AMICI Python API amici.amici amici.sbml_import amici.pysb_import + amici.bngl_import amici.petab_import amici.petab_import_pysb amici.petab_objective amici.petab_simulate amici.import_utils - amici.ode_export + amici.de_export + amici.de_model amici.plotting amici.pandas amici.logging amici.gradient_check amici.parameter_mapping + amici.conserved_quantities_demartino + amici.conserved_quantities_rref + amici.numpy + amici.sbml_utils + amici.splines diff --git a/deps/AMICI/documentation/recreate_reference_list.py b/deps/AMICI/documentation/recreate_reference_list.py index 02d7873ef..034c884c4 100755 --- a/deps/AMICI/documentation/recreate_reference_list.py +++ b/deps/AMICI/documentation/recreate_reference_list.py @@ -8,28 +8,29 @@ Requires pandoc """ -import biblib.bib -import biblib.messages -import biblib.algo import os -import sys import subprocess +import sys + +import biblib.algo +import biblib.bib +import biblib.messages def get_keys_by_year(bibfile): """Get bibtex entry keys as dict by year""" - with open(bibfile, 'r') as f: + with open(bibfile, "r") as f: db = biblib.bib.Parser().parse(f, log_fp=sys.stderr).get_entries() recoverer = biblib.messages.InputErrorRecoverer() by_year = {} for ent in db.values(): with recoverer: - if 'year' in ent: + if "year" in ent: try: - by_year[ent['year']].append(ent.key) + by_year[ent["year"]].append(ent.key) except KeyError: - by_year[ent['year']] = [ent.key] + by_year[ent["year"]] = [ent.key] else: print("Missing year for entry", ent.key) recoverer.reraise() @@ -39,39 +40,56 @@ def get_keys_by_year(bibfile): def get_sub_bibliography(year, by_year, bibfile): """Get HTML bibliography for the given year""" - entries = ','.join(['@' + x for x in by_year[year]]) - stdin_input = '---\n' \ - f'bibliography: {bibfile}\n' \ - f'nocite: "{entries}"\n...\n' \ - f'# {year}' - - out = subprocess.run(['pandoc', '--filter=pandoc-citeproc', - '-f', 'markdown'], - input=stdin_input, capture_output=True, - encoding='utf-8') + entries = ",".join(["@" + x for x in by_year[year]]) + stdin_input = ( + "---\n" + f"bibliography: {bibfile}\n" + f'nocite: "{entries}"\n...\n' + f"# {year}" + ) + + out = subprocess.run( + ["pandoc", "--citeproc", "-f", "markdown"], + input=stdin_input, + capture_output=True, + encoding="utf-8", + ) if out.returncode != 0: raise AssertionError(out.stderr) + return out.stdout def main(): script_path = os.path.dirname(os.path.realpath(__file__)) - bibfile = os.path.join(script_path, 'amici_refs.bib') - outfile = os.path.join(script_path, 'references.md') + bibfile = os.path.join(script_path, "amici_refs.bib") + outfile = os.path.join(script_path, "references.md") by_year = get_keys_by_year(bibfile) num_total = sum(map(len, by_year.values())) - with open(outfile, 'w') as f: - f.write('# References\n\n') - f.write('List of publications using AMICI. ' - f'Total number is {num_total}.\n\n') - 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') - + with open(outfile, "w") as f: + f.write("# References\n\n") + f.write( + "List of publications using AMICI. " + f"Total number is {num_total}.\n\n" + ) + 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" + ) + f.write( + """ +\n +""" + ) for year in reversed(sorted(by_year.keys())): cur_bib = get_sub_bibliography(year, by_year, bibfile) f.write(cur_bib) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/deps/AMICI/documentation/references.md b/deps/AMICI/documentation/references.md index 4eb30a501..00c3f40cc 100644 --- a/deps/AMICI/documentation/references.md +++ b/deps/AMICI/documentation/references.md @@ -1,222 +1,659 @@ # References -List of publications using AMICI. Total number is 64. +List of publications using AMICI. Total number is 82. -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. -

2022

-
-
-

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.

-
-
-

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 ODE models on large-scale datasets.” Nature Communications 13 (1): 34. https://doi.org/10.1038/s41467-021-27374-6.

-
-
-

2021

-
-
-

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 Cytoplasmic Volumes Defines Survival Threshold in Erythroid Progenitor Cells.” Cell Reports 36 (6): 109507. https://doi.org/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 Hematopoietic Lineage Hierarchies.” iScience 24 (2): 102120. https://doi.org/https://doi.org/10.1016/j.isci.2021.102120.

-
-
-

Contento, Lorenzo, Noemi Castelletti, Elba Raimúndez, Ronan Le Gleut, Yannik Schälte, Paul Stapor, Ludwig Christian Hinske, et al. 2021. “Integrative Modelling of Reported Case Numbers and Seroprevalence Reveals Time-Dependent Test Efficiency and Infection Rates.” medRxiv. https://doi.org/10.1101/2021.10.01.21263052.

-
-
-

Erdem, Cemal, Arnab Mutsuddy, Ethan M. Bensman, William B. Dodd, Michael M. Saint-Antoine, Mehdi Bouhaddou, Robert C. Blake, et al. 2021. “A Scalable, Open-Source Implementation of a Large-Scale Mechanistic Model for Single Cell Proliferation and Death Signaling.” bioRxiv. https://doi.org/10.1101/2020.11.09.373407.

-
-
-

Fröhlich, Fabian, and Peter K. Sorger. 2021. “Fides: Reliable Trust-Region Optimization for Parameter Estimation of Ordinary Differential Equation Models.” bioRxiv. https://doi.org/10.1101/2021.05.20.445065.

-
-
-

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 prediction of SARS-CoV-2 in Ethiopia: longitudinal cohort study among front-line hospital workers and communities.” The Lancet Global Health 9 (11): e1517–e1527. https://doi.org/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 Case Numbers for Epidemiological Modeling.” Epidemics 34: 100439. https://doi.org/https://doi.org/10.1016/j.epidem.2021.100439.

-
-
-

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.

+ + + +

2023

+
+
+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 +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 +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 +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 MRI-based Personalized Cardiovascular Mathematical +Model.” The Journal of Physiology n/a (n/a). https://doi.org/10.1113/JP284652.
-
-

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.

-
-

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 Sbml.” Journal of Open Source Software 6 (61): 3215. https://doi.org/10.21105/joss.03215.

+

2022

+
+
+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 +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 +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 +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 +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 +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 +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 +Computational Biology and Bioinformatics, 1–12. https://doi.org/10.1109/TCBB.2022.3213914.
-
-

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/https://doi.org/10.1016/j.ymben.2021.01.008.

-
-

Villaverde, Alejandro F, Dilan Pathirana, Fabian Fröhlich, Jan Hasenauer, and Julio R Banga. 2021. “A protocol for dynamic model calibration.” Briefings in Bioinformatics, October. https://doi.org/10.1093/bib/bbab387.

+

2021

+
+
+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 +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 +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 +prediction of SARS-CoV-2 in Ethiopia: longitudinal cohort study among +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 +Case Numbers for Epidemiological Modeling.” Epidemics +34: 100439. https://doi.org/10.1016/j.epidem.2021.100439. +
+
+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 +SBML.” Journal of Open Source Software 6 +(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 +Bioinformatics, October. https://doi.org/10.1093/bib/bbab387.

2020

-
-
-

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 Reports 30 (4): 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, 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 Persistence of Drug-Adapted Braf-Mutant Melanoma Cells.” Cell Systems. https://doi.org/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.” http://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 computation.” Bioinformatics 36 (July): i551–i559. 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 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, Division of Diagnostics; Specialist Medicine, Faculty of Medicine; 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/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 Kinetics in Pseudomonas Putida Mt-2.” Bioprocess and Biosystems Engineering 43 (9): 1671–88. https://doi.org/10.1007/s00449-020-02360-2.

+
+
+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 +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, +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 +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 +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 +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, +Division of Diagnostics; Specialist Medicine, Faculty of Medicine; +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 +Kinetics in Pseudomonas Putida Mt-2.” Bioprocess and +Biosystems Engineering 43 (9): 1671–88. https://doi.org/10.1007/s00449-020-02360-2.

2019

-
-
-

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 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 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 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 (1): 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.12380/256855.

+
+
+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 +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 +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 +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.12380/256855.

2018

-
-
-

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–i501. 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 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 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/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–i159. 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 models.” Bioinformatics 35 (5): 830–38. https://doi.org/10.1093/bioinformatics/bty736.

+
+
+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 +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 +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 +models.” Bioinformatics 35 (5): 830–38. https://doi.org/10.1093/bioinformatics/bty736.

2017

-
-
-

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–i300. 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/bioinformatics/btw703.

+
+
+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/bioinformatics/btw703.

2016

-
-
-

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 IFAC Conf. Found. Syst. Biol. Eng., edited by R. 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 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. Biol., edited by E. Bartocci, P. Lio, and N. Paoletti, 186–200. Lecture Notes in Bioinformatics. Springer International Publishing. https://doi.org/10.1007/978-3-319-45177-0.

+
+
+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 +IFAC Conf. Found. Syst. Biol. Eng., edited by R. +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 +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. +Biol., edited by E. Bartocci, P. Lio, and N. Paoletti, 186–200. +Lecture Notes in Bioinformatics. Springer International Publishing. https://doi.org/10.1007/978-3-319-45177-0.

2015

-
-
-

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. Springer International Publishing. https://doi.org/10.1007/978-3-319-23401-4_6.

+
+
+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. +Springer International Publishing. https://doi.org/10.1007/978-3-319-23401-4_6.
diff --git a/deps/AMICI/documentation/rtd_requirements.txt b/deps/AMICI/documentation/rtd_requirements.txt index fcb906179..64bc03e51 100644 --- a/deps/AMICI/documentation/rtd_requirements.txt +++ b/deps/AMICI/documentation/rtd_requirements.txt @@ -1,23 +1,25 @@ -sphinx==4.2.0 -mock>=4.0.1 -setuptools>=45.2.0 +# NOTE: relative paths are expected to be relative to the repository root +sphinx<7 +mock>=5.0.2 +setuptools==67.7.2 pysb>=1.11.0 -matplotlib>=3.4.3 -pkgconfig>=1.5.5 -nbsphinx>=0.8.7 -recommonmark>=0.6.0 -sphinx_rtd_theme>=1.0.0 -petab>=0.1.20 -sphinx-autodoc-typehints==1.13.0 -git+https://github.com/readthedocs/sphinx-hoverxref@master -ipython>=7.28.0 -breathe==4.31.0 -#exhale>=0.2.3 -git+https://github.com/dweindl/exhale@ea77a313777c1382a7830ce9ee6c405ce47f5f3b#egg=exhale -# Newer versions cause trouble with including notebooks: -# sphinx.errors.SphinxParallelError: ImportError: cannot import name 'Application' from partially initialized module 'prompt_toolkit.application.application' (most likely due to a circular import) -# https://github.com/svenevs/exhale/issues/27 https://github.com/mithro/sphinx-contrib-mithro/tree/master/sphinx-contrib-exhale-multiproject +matplotlib==3.7.1 +nbsphinx==0.9.1 +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 +git+https://github.com/readthedocs/sphinx-hoverxref@main +ipython==8.13.2 +breathe==4.35.0 +#exhale>=0.3.5 -e git+https://github.com/mithro/sphinx-contrib-mithro#egg=sphinx-contrib-exhale-multiproject&subdirectory=sphinx-contrib-exhale-multiproject -sphinxcontrib-matlabdomain>=0.12.0 -sphinxcontrib-napoleon -pygments==2.10.0 +sphinxcontrib-matlabdomain<0.19.0 +sphinxcontrib-napoleon>=0.7 +pygments==2.15.1 +Jinja2==3.1.2 +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/ diff --git a/deps/AMICI/documentation/rtd_requirements2.txt b/deps/AMICI/documentation/rtd_requirements2.txt new file mode 100644 index 000000000..5a39f8e68 --- /dev/null +++ b/deps/AMICI/documentation/rtd_requirements2.txt @@ -0,0 +1 @@ +exhale>=0.3.6 diff --git a/deps/AMICI/include/amici/abstract_model.h b/deps/AMICI/include/amici/abstract_model.h index dc11b13e9..bb824577b 100644 --- a/deps/AMICI/include/amici/abstract_model.h +++ b/deps/AMICI/include/amici/abstract_model.h @@ -2,6 +2,7 @@ #define AMICI_ABSTRACT_MODEL_H #include "amici/defines.h" +#include "amici/splinefunctions.h" #include "amici/sundials_matrix_wrapper.h" #include "amici/vector.h" @@ -39,8 +40,10 @@ class AbstractModel { * @param dx time derivative of state (DAE only) * @param root array to which values of the root function will be written */ - virtual void froot(const realtype t, const AmiVector &x, - const AmiVector &dx, gsl::span root) = 0; + virtual void froot( + const realtype t, AmiVector const& x, AmiVector const& dx, + gsl::span root + ) = 0; /** * @brief Residual function @@ -50,8 +53,10 @@ class AbstractModel { * @param xdot array to which values of the residual function will be * written */ - virtual void fxdot(const realtype t, const AmiVector &x, - const AmiVector &dx, AmiVector &xdot) = 0; + virtual void fxdot( + const realtype t, AmiVector const& x, AmiVector const& dx, + AmiVector& xdot + ) = 0; /** * @brief Sensitivity Residual function @@ -64,9 +69,10 @@ class AbstractModel { * @param sxdot array to which values of the sensitivity residual function * will be written */ - virtual void fsxdot(const realtype t, const AmiVector &x, - const AmiVector &dx, int ip, const AmiVector &sx, - const AmiVector &sdx, AmiVector &sxdot) = 0; + virtual void fsxdot( + const realtype t, AmiVector const& x, AmiVector const& dx, int ip, + AmiVector const& sx, AmiVector const& sdx, AmiVector& sxdot + ) = 0; /** * @brief Residual function backward when running in steady state mode @@ -76,8 +82,10 @@ class AbstractModel { * @param xBdot array to which values of the residual function will be * written */ - virtual void fxBdot_ss(const realtype t, const AmiVector &xB, - const AmiVector &dxB, AmiVector &xBdot) = 0; + virtual void fxBdot_ss( + const realtype t, AmiVector const& xB, AmiVector const& dxB, + AmiVector& xBdot + ) = 0; /** * @brief Sparse Jacobian function backward, steady state case @@ -96,10 +104,10 @@ class AbstractModel { * @param dxB Vector with the adjoint derivative states * @param xBdot Vector with the adjoint state right hand side */ - virtual void writeSteadystateJB(const realtype t, realtype cj, - const AmiVector &x, const AmiVector &dx, - const AmiVector &xB, const AmiVector &dxB, - const AmiVector &xBdot) = 0; + virtual void writeSteadystateJB( + const realtype t, realtype cj, AmiVector const& x, AmiVector const& dx, + AmiVector const& xB, AmiVector const& dxB, AmiVector const& xBdot + ) = 0; /** * @brief Dense Jacobian function @@ -110,9 +118,10 @@ class AbstractModel { * @param xdot values of residual function (unused) * @param J dense matrix to which values of the jacobian will be written */ - virtual void fJ(const realtype t, realtype cj, const AmiVector &x, - const AmiVector &dx, const AmiVector &xdot, - SUNMatrix J) = 0; + virtual void + fJ(const realtype t, realtype cj, AmiVector const& x, AmiVector const& dx, + AmiVector const& xdot, SUNMatrix J) + = 0; /** * @brief Dense Jacobian function @@ -125,10 +134,11 @@ class AbstractModel { * @param xBdot Vector with the adjoint right hand side (unused) * @param JB dense matrix to which values of the jacobian will be written */ - virtual void fJB(const realtype t, realtype cj, const AmiVector &x, - const AmiVector &dx, const AmiVector &xB, - const AmiVector &dxB, const AmiVector &xBdot, - SUNMatrix JB) = 0; + virtual void + fJB(const realtype t, realtype cj, AmiVector const& x, AmiVector const& dx, + AmiVector const& xB, AmiVector const& dxB, AmiVector const& xBdot, + SUNMatrix JB) + = 0; /** * @brief Sparse Jacobian function @@ -139,9 +149,10 @@ class AbstractModel { * @param xdot values of residual function (unused) * @param J sparse matrix to which values of the Jacobian will be written */ - virtual void fJSparse(const realtype t, realtype cj, const AmiVector &x, - const AmiVector &dx, const AmiVector &xdot, - SUNMatrix J) = 0; + virtual void fJSparse( + const realtype t, realtype cj, AmiVector const& x, AmiVector const& dx, + AmiVector const& xdot, SUNMatrix J + ) = 0; /** * @brief Sparse Jacobian function @@ -154,10 +165,11 @@ class AbstractModel { * @param xBdot Vector with the adjoint right hand side (unused) * @param JB dense matrix to which values of the jacobian will be written */ - virtual void fJSparseB(const realtype t, realtype cj, const AmiVector &x, - const AmiVector &dx, const AmiVector &xB, - const AmiVector &dxB, const AmiVector &xBdot, - SUNMatrix JB) = 0; + virtual void fJSparseB( + const realtype t, realtype cj, AmiVector const& x, AmiVector const& dx, + AmiVector const& xB, AmiVector const& dxB, AmiVector const& xBdot, + SUNMatrix JB + ) = 0; /** * @brief Diagonal Jacobian function @@ -167,8 +179,10 @@ class AbstractModel { * @param x state * @param dx time derivative of state (DAE only) */ - virtual void fJDiag(const realtype t, AmiVector &Jdiag, realtype cj, - const AmiVector &x, const AmiVector &dx) = 0; + virtual void fJDiag( + const realtype t, AmiVector& Jdiag, realtype cj, AmiVector const& x, + AmiVector const& dx + ) = 0; /** * @brief Model-specific sparse implementation of explicit parameter @@ -177,8 +191,9 @@ class AbstractModel { * @param x state * @param dx time derivative of state (DAE only) */ - virtual void fdxdotdp(const realtype t, const AmiVector &x, - const AmiVector &dx) = 0; + virtual void + fdxdotdp(const realtype t, AmiVector const& x, AmiVector const& dx) + = 0; /** * @brief Jacobian multiply function @@ -190,9 +205,10 @@ class AbstractModel { * @param nJv array to which result of multiplication will be written * @param cj scaling factor (inverse of timestep, DAE only) */ - virtual void fJv(const realtype t, const AmiVector &x, const AmiVector &dx, - const AmiVector &xdot, const AmiVector &v, AmiVector &nJv, - realtype cj) = 0; + virtual void + fJv(const realtype t, AmiVector const& x, AmiVector const& dx, + AmiVector const& xdot, AmiVector const& v, AmiVector& nJv, realtype cj) + = 0; /** * @brief Returns the AMICI version that was used to generate the model @@ -213,8 +229,8 @@ class AbstractModel { * @param p parameter vector * @param k constant vector */ - virtual void fx0(realtype *x0, const realtype t, const realtype *p, - const realtype *k); + virtual void + fx0(realtype* x0, const realtype t, realtype const* p, realtype const* k); /** * @brief Function indicating whether reinitialization of states depending @@ -233,9 +249,10 @@ class AbstractModel { * @param reinitialization_state_idxs Indices of states to be reinitialized * based on provided constants / fixed parameters. */ - virtual void fx0_fixedParameters(realtype *x0, const realtype t, - const realtype *p, const realtype *k, - gsl::span reinitialization_state_idxs); + virtual void fx0_fixedParameters( + realtype* x0, const realtype t, realtype const* p, realtype const* k, + gsl::span reinitialization_state_idxs + ); /** * @brief Model-specific implementation of fsx0_fixedParameters @@ -248,10 +265,11 @@ class AbstractModel { * @param reinitialization_state_idxs Indices of states to be reinitialized * based on provided constants / fixed parameters. */ - virtual void fsx0_fixedParameters(realtype *sx0, const realtype t, - const realtype *x0, const realtype *p, - const realtype *k, int ip, - gsl::span reinitialization_state_idxs); + virtual void fsx0_fixedParameters( + realtype* sx0, const realtype t, realtype const* x0, realtype const* p, + realtype const* k, int ip, + gsl::span reinitialization_state_idxs + ); /** * @brief Model-specific implementation of fsx0 @@ -262,8 +280,10 @@ class AbstractModel { * @param k constant vector * @param ip sensitivity index */ - virtual void fsx0(realtype *sx0, const realtype t, const realtype *x0, - const realtype *p, const realtype *k, int ip); + virtual void fsx0( + realtype* sx0, const realtype t, realtype const* x0, realtype const* p, + realtype const* k, int ip + ); /** * @brief Initial value for time derivative of states (only necessary for @@ -272,7 +292,7 @@ class AbstractModel { * @param dx0 Vector to which the initial derivative states will be written * (only DAE) */ - virtual void fdx0(AmiVector &x0, AmiVector &dx0); + virtual void fdx0(AmiVector& x0, AmiVector& dx0); /** * @brief Model-specific implementation of fstau @@ -282,13 +302,16 @@ class AbstractModel { * @param p parameter vector * @param k constant vector * @param h Heaviside vector + * @param tcl total abundances for conservation laws * @param sx current state sensitivity * @param ip sensitivity index * @param ie event index */ - virtual void fstau(realtype *stau, const realtype t, const realtype *x, - const realtype *p, const realtype *k, const realtype *h, - const realtype *sx, int ip, int ie); + virtual void fstau( + realtype* stau, const realtype t, realtype const* x, realtype const* p, + realtype const* k, realtype const* h, realtype const* tcl, + realtype const* sx, int ip, int ie + ); /** * @brief Model-specific implementation of fy @@ -300,12 +323,12 @@ class AbstractModel { * @param h Heaviside vector * @param w repeating elements vector */ - virtual void fy(realtype *y, const realtype t, const realtype *x, - const realtype *p, const realtype *k, const realtype *h, - const realtype *w); + virtual void + fy(realtype* y, const realtype t, realtype const* x, realtype const* p, + realtype const* k, realtype const* h, realtype const* w); /** - * @brief Model-specific implementation of fdydp + * @brief Model-specific implementation of fdydp (MATLAB-only) * @param dydp partial derivative of observables y w.r.t. model parameters p * @param t current time * @param x current state @@ -316,9 +339,34 @@ class AbstractModel { * @param w repeating elements vector * @param dwdp Recurring terms in xdot, parameter derivative */ - virtual void fdydp(realtype *dydp, const realtype t, const realtype *x, - const realtype *p, const realtype *k, const realtype *h, - int ip, const realtype *w, const realtype *dwdp); + virtual void fdydp( + realtype* dydp, const realtype t, realtype const* x, realtype const* p, + realtype const* k, realtype const* h, int ip, realtype const* w, + realtype const* dwdp + ); + + /** + * @brief Model-specific implementation of fdydp (Python) + * @param dydp partial derivative of observables y w.r.t. model parameters p + * @param t current time + * @param x current state + * @param p parameter vector + * @param k constant vector + * @param h Heaviside vector + * @param ip parameter index w.r.t. which the derivative is requested + * @param w repeating elements vector + * @param tcl total abundances for conservation laws + * @param dtcldp Sensitivities of total abundances for conservation laws + * @param spl spline value vector + * @param sspl sensitivities of spline values vector w.r.t. parameters \f$ p + * \f$ + */ + virtual void fdydp( + realtype* dydp, const realtype 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 + ); /** * @brief Model-specific implementation of fdydx @@ -331,9 +379,11 @@ class AbstractModel { * @param w repeating elements vector * @param dwdx Recurring terms in xdot, state derivative */ - virtual void fdydx(realtype *dydx, const realtype t, const realtype *x, - const realtype *p, const realtype *k, const realtype *h, - const realtype *w, const realtype *dwdx); + virtual void fdydx( + realtype* dydx, const realtype t, realtype const* x, realtype const* p, + realtype const* k, realtype const* h, realtype const* w, + realtype const* dwdx + ); /** * @brief Model-specific implementation of fz @@ -345,8 +395,9 @@ class AbstractModel { * @param k constant vector * @param h Heaviside vector */ - virtual void fz(realtype *z, int ie, const realtype t, const realtype *x, - const realtype *p, const realtype *k, const realtype *h); + virtual void + fz(realtype* z, int ie, const realtype t, realtype const* x, + realtype const* p, realtype const* k, realtype const* h); /** * @brief Model-specific implementation of fsz @@ -360,9 +411,10 @@ class AbstractModel { * @param sx current state sensitivity * @param ip sensitivity index */ - virtual void fsz(realtype *sz, int ie, const realtype t, const realtype *x, - const realtype *p, const realtype *k, const realtype *h, - const realtype *sx, int ip); + virtual void + fsz(realtype* sz, int ie, const realtype t, realtype const* x, + realtype const* p, realtype const* k, realtype const* h, + realtype const* sx, int ip); /** * @brief Model-specific implementation of frz @@ -375,8 +427,9 @@ class AbstractModel { * @param k constant vector * @param h Heaviside vector */ - virtual void frz(realtype *rz, int ie, const realtype t, const realtype *x, - const realtype *p, const realtype *k, const realtype *h); + virtual void + frz(realtype* rz, int ie, const realtype t, realtype const* x, + realtype const* p, realtype const* k, realtype const* h); /** * @brief Model-specific implementation of fsrz @@ -390,9 +443,11 @@ class AbstractModel { * @param h Heaviside vector * @param ip sensitivity index */ - virtual void fsrz(realtype *srz, int ie, const realtype t, - const realtype *x, const realtype *p, const realtype *k, - const realtype *h, const realtype *sx, int ip); + virtual void fsrz( + realtype* srz, int ie, const realtype t, realtype const* x, + realtype const* p, realtype const* k, realtype const* h, + realtype const* sx, int ip + ); /** * @brief Model-specific implementation of fdzdp @@ -406,9 +461,10 @@ class AbstractModel { * @param h Heaviside vector * @param ip parameter index w.r.t. which the derivative is requested */ - virtual void fdzdp(realtype *dzdp, int ie, const realtype t, - const realtype *x, const realtype *p, const realtype *k, - const realtype *h, int ip); + virtual void fdzdp( + realtype* dzdp, int ie, const realtype t, realtype const* x, + realtype const* p, realtype const* k, realtype const* h, int ip + ); /** * @brief Model-specific implementation of fdzdx @@ -421,9 +477,10 @@ class AbstractModel { * @param k constant vector * @param h Heaviside vector */ - virtual void fdzdx(realtype *dzdx, int ie, const realtype t, - const realtype *x, const realtype *p, const realtype *k, - const realtype *h); + virtual void fdzdx( + realtype* dzdx, int ie, const realtype t, realtype const* x, + realtype const* p, realtype const* k, realtype const* h + ); /** * @brief Model-specific implementation of fdrzdp @@ -437,9 +494,10 @@ class AbstractModel { * @param h Heaviside vector * @param ip parameter index w.r.t. which the derivative is requested */ - virtual void fdrzdp(realtype *drzdp, int ie, const realtype t, - const realtype *x, const realtype *p, const realtype *k, - const realtype *h, int ip); + virtual void fdrzdp( + realtype* drzdp, int ie, const realtype t, realtype const* x, + realtype const* p, realtype const* k, realtype const* h, int ip + ); /** * @brief Model-specific implementation of fdrzdx @@ -451,9 +509,10 @@ class AbstractModel { * @param k constant vector * @param h Heaviside vector */ - virtual void fdrzdx(realtype *drzdx, int ie, const realtype t, - const realtype *x, const realtype *p, const realtype *k, - const realtype *h); + virtual void fdrzdx( + realtype* drzdx, int ie, const realtype t, realtype const* x, + realtype const* p, realtype const* k, realtype const* h + ); /** * @brief Model-specific implementation of fdeltax @@ -467,10 +526,11 @@ class AbstractModel { * @param xdot new model right hand side * @param xdot_old previous model right hand side */ - virtual void fdeltax(realtype *deltax, const realtype t, const realtype *x, - const realtype *p, const realtype *k, - const realtype *h, int ie, const realtype *xdot, - const realtype *xdot_old); + virtual void fdeltax( + realtype* deltax, const realtype t, realtype const* x, + realtype const* p, realtype const* k, realtype const* h, int ie, + realtype const* xdot, realtype const* xdot_old + ); /** * @brief Model-specific implementation of fdeltasx @@ -487,13 +547,15 @@ class AbstractModel { * @param xdot_old previous model right hand side * @param sx state sensitivity * @param stau event-time sensitivity + * @param tcl total abundances for conservation laws */ - virtual void fdeltasx(realtype *deltasx, const realtype t, - const realtype *x, const realtype *p, - const realtype *k, const realtype *h, - const realtype *w, int ip, int ie, - const realtype *xdot, const realtype *xdot_old, - const realtype *sx, const realtype *stau); + virtual void fdeltasx( + realtype* deltasx, const realtype 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, + realtype const* tcl + ); /** * @brief Model-specific implementation of fdeltaxB @@ -508,11 +570,11 @@ class AbstractModel { * @param xdot_old previous model right hand side * @param xB current adjoint state */ - virtual void fdeltaxB(realtype *deltaxB, const realtype t, - const realtype *x, const realtype *p, - const realtype *k, const realtype *h, int ie, - const realtype *xdot, const realtype *xdot_old, - const realtype *xB); + virtual void fdeltaxB( + realtype* deltaxB, const realtype 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 + ); /** * @brief Model-specific implementation of fdeltaqB @@ -528,11 +590,11 @@ class AbstractModel { * @param xdot_old previous model right hand side * @param xB adjoint state */ - virtual void fdeltaqB(realtype *deltaqB, const realtype t, - const realtype *x, const realtype *p, - const realtype *k, const realtype *h, int ip, int ie, - const realtype *xdot, const realtype *xdot_old, - const realtype *xB); + virtual void fdeltaqB( + realtype* deltaqB, const realtype 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 + ); /** * @brief Model-specific implementation of fsigmay @@ -540,20 +602,39 @@ class AbstractModel { * @param t current time * @param p parameter vector * @param k constant vector + * @param y model output at timepoint t */ - virtual void fsigmay(realtype *sigmay, const realtype t, const realtype *p, - const realtype *k); + virtual void fsigmay( + realtype* sigmay, const realtype t, realtype const* p, + realtype const* k, realtype const* y + ); /** - * @brief Model-specific implementation of fsigmay + * @brief Model-specific implementation of fdsigmaydp * @param dsigmaydp partial derivative of standard deviation of measurements * @param t current time * @param p parameter vector * @param k constant vector + * @param y model output at timepoint t * @param ip sensitivity index */ - virtual void fdsigmaydp(realtype *dsigmaydp, const realtype t, - const realtype *p, const realtype *k, int ip); + virtual void fdsigmaydp( + realtype* dsigmaydp, const realtype t, realtype const* p, + realtype const* k, realtype const* y, int ip + ); + /** + * @brief Model-specific implementation of fsigmay + * @param dsigmaydy partial derivative of standard deviation of measurements + * w.r.t. model outputs + * @param t current time + * @param p parameter vector + * @param k constant vector + * @param y model output at timepoint t + */ + virtual void fdsigmaydy( + realtype* dsigmaydy, const realtype t, realtype const* p, + realtype const* k, realtype const* y + ); /** * @brief Model-specific implementation of fsigmaz @@ -562,8 +643,9 @@ class AbstractModel { * @param p parameter vector * @param k constant vector */ - virtual void fsigmaz(realtype *sigmaz, const realtype t, const realtype *p, - const realtype *k); + virtual void fsigmaz( + realtype* sigmaz, const realtype t, realtype const* p, realtype const* k + ); /** * @brief Model-specific implementation of fsigmaz @@ -574,8 +656,10 @@ class AbstractModel { * @param k constant vector * @param ip sensitivity index */ - virtual void fdsigmazdp(realtype *dsigmazdp, const realtype t, - const realtype *p, const realtype *k, int ip); + virtual void fdsigmazdp( + realtype* dsigmazdp, const realtype t, realtype const* p, + realtype const* k, int ip + ); /** * @brief Model-specific implementation of fJy @@ -587,9 +671,9 @@ class AbstractModel { * @param sigmay measurement standard deviation at timepoint * @param my measurements at timepoint */ - virtual void fJy(realtype *nllh, int iy, const realtype *p, - const realtype *k, const realtype *y, - const realtype *sigmay, const realtype *my); + virtual void + fJy(realtype* nllh, int iy, realtype const* p, realtype const* k, + realtype const* y, realtype const* sigmay, realtype const* my); /** * @brief Model-specific implementation of fJz @@ -601,9 +685,9 @@ class AbstractModel { * @param sigmaz event measurement standard deviation at timepoint * @param mz event measurements at timepoint */ - virtual void fJz(realtype *nllh, int iz, const realtype *p, - const realtype *k, const realtype *z, - const realtype *sigmaz, const realtype *mz); + virtual void + fJz(realtype* nllh, int iz, realtype const* p, realtype const* k, + realtype const* z, realtype const* sigmaz, realtype const* mz); /** * @brief Model-specific implementation of fJrz @@ -614,9 +698,10 @@ class AbstractModel { * @param z model event output at timepoint * @param sigmaz event measurement standard deviation at timepoint */ - virtual void fJrz(realtype *nllh, int iz, const realtype *p, - const realtype *k, const realtype *z, - const realtype *sigmaz); + virtual void fJrz( + realtype* nllh, int iz, realtype const* p, realtype const* k, + realtype const* z, realtype const* sigmaz + ); /** * @brief Model-specific implementation of fdJydy @@ -629,23 +714,24 @@ class AbstractModel { * @param sigmay measurement standard deviation at timepoint * @param my measurement at timepoint */ - virtual void fdJydy(realtype *dJydy, int iy, const realtype *p, - const realtype *k, const realtype *y, - const realtype *sigmay, const realtype *my); + virtual void fdJydy( + realtype* dJydy, int iy, realtype const* p, realtype const* k, + realtype const* y, realtype const* sigmay, realtype const* my + ); /** * @brief Model-specific implementation of fdJydy colptrs * @param dJydy sparse matrix to which colptrs will be written * @param index ytrue index */ - virtual void fdJydy_colptrs(SUNMatrixWrapper &dJydy, int index); + virtual void fdJydy_colptrs(SUNMatrixWrapper& dJydy, int index); /** * @brief Model-specific implementation of fdJydy rowvals * @param dJydy sparse matrix to which rowvals will be written * @param index `ytrue` index */ - virtual void fdJydy_rowvals(SUNMatrixWrapper &dJydy, int index); + virtual void fdJydy_rowvals(SUNMatrixWrapper& dJydy, int index); /** * @brief Model-specific implementation of fdJydsigma @@ -658,9 +744,10 @@ class AbstractModel { * @param sigmay measurement standard deviation at timepoint * @param my measurement at timepoint */ - virtual void fdJydsigma(realtype *dJydsigma, int iy, const realtype *p, - const realtype *k, const realtype *y, - const realtype *sigmay, const realtype *my); + virtual void fdJydsigma( + realtype* dJydsigma, int iy, realtype const* p, realtype const* k, + realtype const* y, realtype const* sigmay, realtype const* my + ); /** * @brief Model-specific implementation of fdJzdz @@ -673,9 +760,10 @@ class AbstractModel { * @param sigmaz event measurement standard deviation at timepoint * @param mz event measurement at timepoint */ - virtual void fdJzdz(realtype *dJzdz, int iz, const realtype *p, - const realtype *k, const realtype *z, - const realtype *sigmaz, const realtype *mz); + virtual void fdJzdz( + realtype* dJzdz, int iz, realtype const* p, realtype const* k, + realtype const* z, realtype const* sigmaz, realtype const* mz + ); /** * @brief Model-specific implementation of fdJzdsigma @@ -688,9 +776,10 @@ class AbstractModel { * @param sigmaz event measurement standard deviation at timepoint * @param mz event measurement at timepoint */ - virtual void fdJzdsigma(realtype *dJzdsigma, int iz, const realtype *p, - const realtype *k, const realtype *z, - const realtype *sigmaz, const realtype *mz); + virtual void fdJzdsigma( + realtype* dJzdsigma, int iz, realtype const* p, realtype const* k, + realtype const* z, realtype const* sigmaz, realtype const* mz + ); /** * @brief Model-specific implementation of fdJrzdz @@ -701,9 +790,10 @@ class AbstractModel { * @param rz model root output at timepoint * @param sigmaz event measurement standard deviation at timepoint */ - virtual void fdJrzdz(realtype *dJrzdz, int iz, const realtype *p, - const realtype *k, const realtype *rz, - const realtype *sigmaz); + virtual void fdJrzdz( + realtype* dJrzdz, int iz, realtype const* p, realtype const* k, + realtype const* rz, realtype const* sigmaz + ); /** * @brief Model-specific implementation of fdJrzdsigma @@ -715,9 +805,10 @@ class AbstractModel { * @param rz model root output at timepoint * @param sigmaz event measurement standard deviation at timepoint */ - virtual void fdJrzdsigma(realtype *dJrzdsigma, int iz, const realtype *p, - const realtype *k, const realtype *rz, - const realtype *sigmaz); + virtual void fdJrzdsigma( + realtype* dJrzdsigma, int iz, realtype const* p, realtype const* k, + realtype const* rz, realtype const* sigmaz + ); /** * @brief Model-specific implementation of fw @@ -728,10 +819,12 @@ class AbstractModel { * @param k constants vector * @param h Heaviside vector * @param tcl total abundances for conservation laws + * @param spl spline value vector */ - virtual void fw(realtype *w, const realtype t, const realtype *x, - const realtype *p, const realtype *k, const realtype *h, - const realtype *tcl); + virtual void + fw(realtype* w, const realtype t, realtype const* x, realtype const* p, + realtype const* k, realtype const* h, realtype const* tcl, + realtype const* spl); /** * @brief Model-specific sparse implementation of dwdp @@ -744,23 +837,28 @@ class AbstractModel { * @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 w.r.t. parameters \f$ p + * \f$ */ - virtual 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); + 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 + ); /** * @brief Model-specific implementation for dwdp, column pointers * @param dwdp sparse matrix to which colptrs will be written */ - virtual void fdwdp_colptrs(SUNMatrixWrapper &dwdp); + virtual void fdwdp_colptrs(SUNMatrixWrapper& dwdp); /** * @brief Model-specific implementation for dwdp, row values * @param dwdp sparse matrix to which rowvals will be written */ - virtual void fdwdp_rowvals(SUNMatrixWrapper &dwdp); + virtual void fdwdp_rowvals(SUNMatrixWrapper& dwdp); /** * @brief Model-specific sensitivity implementation of dwdp @@ -773,12 +871,16 @@ class AbstractModel { * @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, const realtype *x, - const realtype *p, const realtype *k, const realtype *h, - const realtype *w, const realtype *tcl, - const realtype *stcl, int ip); + 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 @@ -790,22 +892,25 @@ class AbstractModel { * @param h Heaviside vector * @param w vector with helper variables * @param tcl total abundances for conservation laws + * @param spl spline value vector */ - virtual 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); + virtual void fdwdx( + realtype* dwdx, const realtype t, realtype const* x, realtype const* p, + realtype const* k, realtype const* h, realtype const* w, + realtype const* tcl, realtype const* spl + ); /** * @brief Model-specific implementation for dwdx, column pointers * @param dwdx sparse matrix to which colptrs will be written */ - virtual void fdwdx_colptrs(SUNMatrixWrapper &dwdx); + virtual void fdwdx_colptrs(SUNMatrixWrapper& dwdx); /** * @brief Model-specific implementation for dwdx, row values * @param dwdx sparse matrix to which rowvals will be written */ - virtual void fdwdx_rowvals(SUNMatrixWrapper &dwdx); + virtual void fdwdx_rowvals(SUNMatrixWrapper& dwdx); /** * @brief Model-specific implementation of fdwdw, no w chainrule (Py) @@ -818,21 +923,162 @@ class AbstractModel { * @param w vector with helper variables * @param tcl Total abundances for conservation laws */ - virtual void fdwdw(realtype *dwdw, realtype t, const realtype *x, - const realtype *p, const realtype *k, const realtype *h, - const realtype *w, const realtype *tcl); + 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 + ); /** * @brief Model-specific implementation of fdwdw, colptrs part * @param dwdw sparse matrix to which colptrs will be written */ - virtual void fdwdw_colptrs(SUNMatrixWrapper &dwdw); + virtual void fdwdw_colptrs(SUNMatrixWrapper& dwdw); /** * @brief Model-specific implementation of fdwdw, rowvals part * @param dwdw sparse matrix to which rowvals will be written */ - virtual void fdwdw_rowvals(SUNMatrixWrapper &dwdw); + virtual void fdwdw_rowvals(SUNMatrixWrapper& dwdw); + + /** + * @brief Compute dx_rdata / dx_solver + * @param dx_rdatadx_solver dx_rdata / dx_solver + * @param p parameter vector + * @param k constant vector + * @param x State variables with conservation laws applied + * @param tcl Total abundances for conservation laws + */ + virtual void fdx_rdatadx_solver( + realtype* dx_rdatadx_solver, realtype const* x, realtype const* tcl, + realtype const* p, realtype const* k + ); + + /** + * @brief Model-specific implementation of fdx_rdatadx_solver, colptrs part + * @param dxrdatadxsolver sparse matrix to which colptrs will be written + */ + virtual void fdx_rdatadx_solver_colptrs(SUNMatrixWrapper& dxrdatadxsolver); + + /** + * @brief Model-specific implementation of fdx_rdatadx_solver, rowvals part + * @param dxrdatadxsolver sparse matrix to which rowvals will be written + */ + virtual void fdx_rdatadx_solver_rowvals(SUNMatrixWrapper& dxrdatadxsolver); + + /** + * @brief Compute dx_rdata / dp + * @param dx_rdatadp dx_rdata / dp + * @param p parameter vector + * @param k constant vector + * @param x State variables with conservation laws applied + * @param tcl Total abundances for conservation laws + * @param ip Sensitivity index + */ + virtual void fdx_rdatadp( + realtype* dx_rdatadp, realtype const* x, realtype const* tcl, + realtype const* p, realtype const* k, int const ip + ); + + /** + * @brief Compute dx_rdata / dtcl + * @param dx_rdatadtcl dx_rdata / dtcl + * @param p parameter vector + * @param k constant vector + * @param x State variables with conservation laws applied + * @param tcl Total abundances for conservation laws + */ + virtual void fdx_rdatadtcl( + realtype* dx_rdatadtcl, realtype const* x, realtype const* tcl, + realtype const* p, realtype const* k + ); + + /** + * @brief Model-specific implementation of fdx_rdatadtcl, colptrs part + * @param dx_rdatadtcl sparse matrix to which colptrs will be written + */ + virtual void fdx_rdatadtcl_colptrs(SUNMatrixWrapper& dx_rdatadtcl); + + /** + * @brief Model-specific implementation of fdx_rdatadtcl, rowvals part + * @param dx_rdatadtcl sparse matrix to which rowvals will be written + */ + virtual void fdx_rdatadtcl_rowvals(SUNMatrixWrapper& dx_rdatadtcl); + + /** + * @brief Compute dtotal_cl / dp + * @param dtotal_cldp dtotal_cl / dp + * @param x_rdata State variables with conservation laws applied + * @param p parameter vector + * @param k constant vector + * @param ip Sensitivity index + */ + virtual void fdtotal_cldp( + realtype* dtotal_cldp, realtype const* x_rdata, realtype const* p, + realtype const* k, int const ip + ); + + /** + * @brief Compute dtotal_cl / dx_rdata + * @param dtotal_cldx_rdata dtotal_cl / dx_rdata + * @param x_rdata State variables with conservation laws applied + * @param p parameter vector + * @param k constant vector + * @param tcl Total abundances for conservation laws + */ + virtual void fdtotal_cldx_rdata( + realtype* dtotal_cldx_rdata, realtype const* x_rdata, realtype const* p, + realtype const* k, realtype const* tcl + ); + + /** + * @brief Model-specific implementation of fdtotal_cldx_rdata, colptrs part + * @param dtotal_cldx_rdata sparse matrix to which colptrs will be written + */ + virtual void fdtotal_cldx_rdata_colptrs(SUNMatrixWrapper& dtotal_cldx_rdata + ); + + /** + * @brief Model-specific implementation of fdtotal_cldx_rdata, rowvals part + * @param dtotal_cldx_rdata sparse matrix to which rowvals will be written + */ + virtual void fdtotal_cldx_rdata_rowvals(SUNMatrixWrapper& dtotal_cldx_rdata + ); + + /** + * @brief Model-specific implementation of spline creation + * @param p parameter vector + * @param k constants vector + * @return Vector of splines used in the model + */ + virtual std::vector + fcreate_splines(realtype const* p, realtype const* k); + + /** + * @brief Model-specific implementation the parametric derivatives + * of spline node values + * @param dspline_valuesdp vector to which derivatives will be written + * @param p parameter vector + * @param k constants vector + * @param ip Sensitivity index + */ + virtual void fdspline_valuesdp( + realtype* dspline_valuesdp, realtype const* p, realtype const* k, + int const ip + ); + + /** + * @brief Model-specific implementation the parametric derivatives + * of slopevalues at spline nodes + * @param dspline_slopesdp vector to which derivatives will be written + * @param p parameter vector + * @param k constants vector + * @param ip Sensitivity index + */ + virtual void fdspline_slopesdp( + realtype* dspline_slopesdp, realtype const* p, realtype const* k, + int const ip + ); }; } // namespace amici diff --git a/deps/AMICI/include/amici/amici.h b/deps/AMICI/include/amici/amici.h index b3fe221de..93b8daed9 100644 --- a/deps/AMICI/include/amici/amici.h +++ b/deps/AMICI/include/amici/amici.h @@ -1,113 +1,13 @@ #ifndef amici_h #define amici_h -#include "amici/cblas.h" -#include "amici/defines.h" #include "amici/edata.h" -#include "amici/exception.h" #include "amici/model.h" #include "amici/rdata.h" #include "amici/solver.h" -#include "amici/symbolic_functions.h" namespace amici { -/*! - * @brief Prints a specified error message associated with the specified - * identifier - * - * @param id error identifier - * @param message error message - */ -void printErrMsgIdAndTxt(std::string const &id, std::string const &message); - -/*! - * @brief Prints a specified warning message associated with the specified - * identifier - * - * @param id warning identifier - * @param message warning message - */ -void printWarnMsgIdAndTxt(std::string const &id, std::string const &message); - -/** - * @brief Main class for making calls to AMICI. - * - * This class is used to provide separate AMICI contexts, for example, for use - * in multi-threaded applications where different threads want to use AMICI with - * different settings, such custom logging functions. - * - * NOTE: For this moment, the context object needs to be set manually to any - * Model and Solver object. If not set, they will use the default output - * channel. - */ -class AmiciApplication { - public: - AmiciApplication() = default; - - /** - * @brief Core integration routine. Initializes the solver and runs the - * forward and backward problem. - * - * @param solver Solver instance - * @param edata pointer to experimental data object - * @param model model specification object - * @param rethrow rethrow integration exceptions? - * @return rdata pointer to return data object - */ - std::unique_ptr runAmiciSimulation(Solver &solver, - const ExpData *edata, - Model &model, - bool rethrow = false); - - /** - * @brief Same as runAmiciSimulation, but for multiple ExpData instances. - * - * @param solver Solver instance - * @param edatas experimental data objects - * @param model model specification object - * @param failfast flag to allow early termination - * @param num_threads number of threads for parallel execution - * @return vector of pointers to return data objects - */ - std::vector> - runAmiciSimulations(Solver const &solver, - const std::vector &edatas, - Model const &model, bool failfast, int num_threads); - - /** Function to process warnings */ - outputFunctionType warning = printWarnMsgIdAndTxt; - - /** Function to process errors */ - outputFunctionType error = printErrMsgIdAndTxt; - - /** - * @brief printf interface to warning() - * @param identifier warning identifier - * @param format string with warning message printf-style format - * @param ... arguments to be formatted - */ - void warningF(const char *identifier, const char *format, ...) const; - - /** - * @brief printf interface to error() - * @param identifier warning identifier - * @param format string with error message printf-style format - * @param ... arguments to be formatted - */ - void errorF(const char *identifier, const char *format, ...) const; - - /** - * @brief Checks the values in an array for NaNs and Infs - * - * @param array array - * @param fun name of calling function - * @return AMICI_RECOVERABLE_ERROR if a NaN/Inf value was found, - * AMICI_SUCCESS otherwise - */ - int checkFinite(gsl::span array, const char *fun); -}; - /** * @brief Core integration routine. Initializes the solver and runs the forward * and backward problem. @@ -118,10 +18,9 @@ class AmiciApplication { * @param rethrow rethrow integration exceptions? * @return rdata pointer to return data object */ -std::unique_ptr runAmiciSimulation(Solver &solver, - const ExpData *edata, - Model &model, - bool rethrow = false); +std::unique_ptr runAmiciSimulation( + Solver& solver, ExpData const* edata, Model& model, bool rethrow = false +); /** * @brief Same as runAmiciSimulation, but for multiple ExpData instances. When @@ -134,9 +33,18 @@ std::unique_ptr runAmiciSimulation(Solver &solver, * @param num_threads number of threads for parallel execution * @return vector of pointers to return data objects */ -std::vector> -runAmiciSimulations(Solver const &solver, const std::vector &edatas, - Model const &model, bool failfast, int num_threads); +std::vector> runAmiciSimulations( + Solver const& solver, std::vector const& edatas, + Model const& model, bool failfast, int num_threads +); + +/** + * @brief Get the string representation of the given simulation status code + * (see ReturnData::status). + * @param status Status code + * @return Name of the variable representing this status code. + */ +std::string simulation_status_to_str(int status); } // namespace amici diff --git a/deps/AMICI/include/amici/backwardproblem.h b/deps/AMICI/include/amici/backwardproblem.h index e59c93ec6..1c26186c1 100644 --- a/deps/AMICI/include/amici/backwardproblem.h +++ b/deps/AMICI/include/amici/backwardproblem.h @@ -27,8 +27,9 @@ class BackwardProblem { * @param fwd pointer to corresponding forward problem * @param posteq pointer to postequilibration problem, can be nullptr */ - explicit BackwardProblem(const ForwardProblem &fwd, - const SteadystateProblem *posteq); + explicit BackwardProblem( + ForwardProblem const& fwd, SteadystateProblem const* posteq + ); /** * @brief Solve the backward problem. @@ -43,51 +44,39 @@ class BackwardProblem { * @brief Accessor for current time t * @return t */ - realtype gett() const { - return t_; - } + realtype gett() const { return t_; } /** * @brief Accessor for which * @return which */ - int getwhich() const { - return which; - } + int getwhich() const { return which; } /** * @brief Accessor for pointer to which * @return which */ - int *getwhichptr() { - return &which; - } + int* getwhichptr() { return &which; } /** * @brief Accessor for dJydx * @return dJydx */ - std::vector const& getdJydx() const { - return dJydx_; - } + std::vector const& getdJydx() const { return dJydx_; } /** * @brief Accessor for xB * @return xB */ - AmiVector const& getAdjointState() const { - return xB_; - } + AmiVector const& getAdjointState() const { return xB_; } /** * @brief Accessor for xQB * @return xQB */ - AmiVector const& getAdjointQuadrature() const { - return xQB_; - } + AmiVector const& getAdjointQuadrature() const { return xQB_; } -private: + private: /** * @brief Execute everything necessary for the handling of events * for the backward problem @@ -102,7 +91,6 @@ class BackwardProblem { */ void handleDataPointB(int it); - /** * @brief Compute the next timepoint to integrate to. * @@ -114,9 +102,9 @@ class BackwardProblem { */ realtype getTnext(int it); - Model *model_; - Solver *solver_; - const ExpData *edata_; + Model* model_; + Solver* solver_; + ExpData const* edata_; /** current time */ realtype t_; diff --git a/deps/AMICI/include/amici/cblas.h b/deps/AMICI/include/amici/cblas.h index 6aed56ba4..d8f320c82 100644 --- a/deps/AMICI/include/amici/cblas.h +++ b/deps/AMICI/include/amici/cblas.h @@ -6,33 +6,36 @@ namespace amici { /** - * amici_dgemm provides an interface to the CBlas matrix vector multiplication - * routine dgemv. This routines computes - * y = alpha*A*x + beta*y with A: [MxN] x:[Nx1] y:[Mx1] + * @brief CBLAS matrix vector multiplication (dgemv). * - * @param layout always needs to be AMICI_BLAS_ColMajor. + * Computes \f$ y = alpha*A*x + beta*y \f$ with A: [MxN] x:[Nx1] y:[Mx1] + * + * @param layout Matrix layout, column major or row major. * @param TransA flag indicating whether A should be transposed before * multiplication * @param M number of rows in A * @param N number of columns in A * @param alpha coefficient alpha * @param A matrix A - * @param lda leading dimension of A (m or n) + * @param lda leading dimension / stride of A (>=N if row-major, + * >=M if col-major) * @param X vector X * @param incX increment for entries of X * @param beta coefficient beta * @param Y vector Y * @param incY increment for entries of Y */ -void amici_dgemv(BLASLayout layout, BLASTranspose TransA, - int M, int N, double alpha, const double *A, - int lda, const double *X, int incX, - double beta, double *Y, int incY); +void amici_dgemv( + BLASLayout layout, BLASTranspose TransA, int M, int N, double alpha, + double const* A, int lda, double const* X, int incX, double beta, double* Y, + int incY +); /** - * amici_dgemm provides an interface to the CBlas matrix matrix multiplication - * routine dgemm. This routines computes - * C = alpha*A*B + beta*C with A: [MxK] B:[KxN] C:[MxN] + * @brief CBLAS matrix matrix multiplication (dgemm) + * + * This routines computes \f$ C = alpha*A*B + beta*C \f$ + * with A: [MxK] B:[KxN] C:[MxN] * * @param layout memory layout. * @param TransA flag indicating whether A should be transposed before @@ -44,18 +47,18 @@ void amici_dgemv(BLASLayout layout, BLASTranspose TransA, * @param K number of rows in B, number of columns in A * @param alpha coefficient alpha * @param A matrix A - * @param lda leading dimension of A (m or k) + * @param lda leading dimension of A (>=M or >=K) * @param B matrix B - * @param ldb leading dimension of B (k or n) + * @param ldb leading dimension of B (>=K or >=N) * @param beta coefficient beta * @param C matrix C - * @param ldc leading dimension of C (m or n) + * @param ldc leading dimension of C (>=M or >= N) */ -void amici_dgemm(BLASLayout layout, BLASTranspose TransA, - BLASTranspose TransB, int M, int N, - int K, double alpha, const double *A, - int lda, const double *B, int ldb, - double beta, double *C, int ldc); +void amici_dgemm( + BLASLayout layout, BLASTranspose TransA, BLASTranspose TransB, int M, int N, + int K, double alpha, double const* A, int lda, double const* B, int ldb, + double beta, double* C, int ldc +); /** * @brief Compute y = a*x + y @@ -66,7 +69,9 @@ void amici_dgemm(BLASLayout layout, BLASTranspose TransA, * @param y vector of length n*incy * @param incy y stride */ -void amici_daxpy(int n, double alpha, const double *x, int incx, double *y, int incy); +void amici_daxpy( + int n, double alpha, double const* x, int incx, double* y, int incy +); } // namespace amici diff --git a/deps/AMICI/include/amici/defines.h b/deps/AMICI/include/amici/defines.h index 852b0c437..068b8a9d5 100644 --- a/deps/AMICI/include/amici/defines.h +++ b/deps/AMICI/include/amici/defines.h @@ -5,50 +5,49 @@ #define _USE_MATH_DEFINES #endif -#include -#include #include +#include /* Math constants in case _USE_MATH_DEFINES is not supported */ #if defined(_USE_MATH_DEFINES) #if !defined(M_E) -#define M_E 2.71828182845904523536 +#define M_E 2.71828182845904523536 #endif #if !defined(M_LOG2E) -#define M_LOG2E 1.44269504088896340736 +#define M_LOG2E 1.44269504088896340736 #endif #if !defined(M_LOG10E) -#define M_LOG10E 0.434294481903251827651 +#define M_LOG10E 0.434294481903251827651 #endif #if !defined(M_LN2) -#define M_LN2 0.693147180559945309417 +#define M_LN2 0.693147180559945309417 #endif #if !defined(M_LN10) -#define M_LN10 2.30258509299404568402 +#define M_LN10 2.30258509299404568402 #endif #if !defined(M_PI) -#define M_PI 3.14159265358979323846 +#define M_PI 3.14159265358979323846 #endif #if !defined(M_PI_2) -#define M_PI_2 1.57079632679489661923 +#define M_PI_2 1.57079632679489661923 #endif #if !defined(M_PI_4) -#define M_PI_4 0.785398163397448309616 +#define M_PI_4 0.785398163397448309616 #endif #if !defined(M_1_PI) -#define M_1_PI 0.318309886183790671538 +#define M_1_PI 0.318309886183790671538 #endif #if !defined(M_2_PI) -#define M_2_PI 0.636619772367581343076 +#define M_2_PI 0.636619772367581343076 #endif #if !defined(M_2_SQRTPI) #define M_2_SQRTPI 1.12837916709551257390 #endif #if !defined(M_SQRT2) -#define M_SQRT2 1.41421356237309504880 +#define M_SQRT2 1.41421356237309504880 #endif #if !defined(M_SQRT1_2) -#define M_SQRT1_2 0.707106781186547524401 +#define M_SQRT1_2 0.707106781186547524401 #endif #endif @@ -56,12 +55,14 @@ namespace amici { constexpr double pi = M_PI; - // clang-format off constexpr int AMICI_ONEOUTPUT= 5; -/* Return codes */ +// Return codes +// +// NOTE: When adding / removing / renaming return codes, +// please update simulation_status_to_str_map in amici.h constexpr int AMICI_RECOVERABLE_ERROR= 1; constexpr int AMICI_UNRECOVERABLE_ERROR= -10; constexpr int AMICI_TOO_MUCH_WORK= -1; @@ -69,6 +70,7 @@ constexpr int AMICI_TOO_MUCH_ACC= -2; constexpr int AMICI_ERR_FAILURE= -3; constexpr int AMICI_CONV_FAILURE= -4; constexpr int AMICI_RHSFUNC_FAIL= -8; +constexpr int AMICI_FIRST_RHSFUNC_ERR= -9; constexpr int AMICI_ILL_INPUT= -22; constexpr int AMICI_ERROR= -99; constexpr int AMICI_NO_STEADY_STATE= -81; @@ -76,6 +78,7 @@ constexpr int AMICI_DAMPING_FACTOR_ERROR= -86; constexpr int AMICI_SINGULAR_JACOBIAN= -809; constexpr int AMICI_NOT_IMPLEMENTED= -999; constexpr int AMICI_MAX_TIME_EXCEEDED = -1000; +constexpr int AMICI_NOT_RUN= -1001; constexpr int AMICI_SUCCESS= 0; constexpr int AMICI_DATA_RETURN= 1; constexpr int AMICI_ROOT_RETURN= 2; @@ -125,15 +128,21 @@ enum class SecondOrderMode { /** orders of sensitivity analysis */ enum class SensitivityOrder { + /** Don't compute sensitivities. */ none, + /** First-order sensitivities. */ first, + /** Second-order sensitivities. */ second }; /** methods for sensitivity computation */ enum class SensitivityMethod { + /** Don't compute sensitivities. */ none, + /** Forward sensitivity analysis. */ forward, + /** Adjoint sensitivity analysis. */ adjoint }; @@ -177,10 +186,18 @@ enum class NonlinearSolverIteration { newton = 2 }; +/** Steady-state computation mode in steadyStateProblem */ +enum class SteadyStateComputationMode { + newtonOnly, + integrationOnly, + integrateIfNewtonFails +}; + /** Sensitivity computation mode in steadyStateProblem */ enum class SteadyStateSensitivityMode { newtonOnly, - simulationFSA + integrationOnly, + integrateIfNewtonFails }; /** State in which the steady state computation finished */ @@ -220,11 +237,23 @@ enum class RDataReporting { likelihood, }; -/** - * Type for function to process warnings or error messages. - */ -using outputFunctionType = std::function; +/** boundary conditions for splines */ +enum class SplineBoundaryCondition { + given = -1, + zeroDerivative = 0, + natural = 1, + naturalZeroDerivative = 2, + periodic = 3, +}; + +/** extrapolation methods for splines */ +enum class SplineExtrapolation { + noExtrapolation = -1, + constant = 0, + linear = 1, + polynomial = 2, + periodic = 3, +}; // clang-format on diff --git a/deps/AMICI/include/amici/edata.h b/deps/AMICI/include/amici/edata.h index 8c1e821ae..f8639ca2e 100644 --- a/deps/AMICI/include/amici/edata.h +++ b/deps/AMICI/include/amici/edata.h @@ -2,7 +2,6 @@ #define AMICI_EDATA_H #include "amici/defines.h" -#include "amici/vector.h" #include "amici/misc.h" #include "amici/simulation_parameters.h" @@ -29,7 +28,7 @@ class ExpData : public SimulationParameters { * @brief Copy constructor, needs to be declared to be generated in * swig */ - ExpData(const ExpData &) = default; + ExpData(ExpData const&) = default; /** * @brief constructor that only initializes dimensions @@ -60,8 +59,10 @@ class ExpData : public SimulationParameters { * @param ts Timepoints (dimension: nt) * @param fixedParameters Model constants (dimension: nk) */ - ExpData(int nytrue, int nztrue, int nmaxevent, std::vector ts, - std::vector fixedParameters); + ExpData( + int nytrue, int nztrue, int nmaxevent, std::vector ts, + std::vector fixedParameters + ); /** * @brief constructor that initializes timepoints and data from vectors @@ -78,18 +79,20 @@ class ExpData : public SimulationParameters { * @param observedEventsStdDev standard deviation of observed events/roots * (dimension: nmaxevents x nztrue, row-major) */ - ExpData(int nytrue, int nztrue, int nmaxevent, std::vector ts, - std::vector const &observedData, - std::vector const &observedDataStdDev, - std::vector const &observedEvents, - std::vector const &observedEventsStdDev); + ExpData( + int nytrue, int nztrue, int nmaxevent, std::vector ts, + std::vector const& observedData, + std::vector const& observedDataStdDev, + std::vector const& observedEvents, + std::vector const& observedEventsStdDev + ); /** * @brief constructor that initializes with Model * * @param model pointer to model specification object */ - explicit ExpData(const Model &model); + explicit ExpData(Model const& model); /** * @brief constructor that initializes with returnData, adds noise according @@ -99,7 +102,7 @@ class ExpData : public SimulationParameters { * @param sigma_y scalar standard deviations for all observables * @param sigma_z scalar standard deviations for all event observables */ - ExpData(const ReturnData &rdata, realtype sigma_y, realtype sigma_z); + ExpData(ReturnData const& rdata, realtype sigma_y, realtype sigma_z); /** * @brief constructor that initializes with returnData, adds noise according @@ -111,11 +114,15 @@ class ExpData : public SimulationParameters { * @param sigma_z vector of standard deviations for event observables * (dimension: nztrue or nmaxevent x nztrue, row-major) */ - ExpData(const ReturnData &rdata, std::vector sigma_y, - std::vector sigma_z); + ExpData( + ReturnData const& rdata, std::vector sigma_y, + std::vector sigma_z + ); ~ExpData() = default; + friend inline bool operator==(ExpData const& lhs, ExpData const& rhs); + /** * @brief number of observables of the non-augmented model * @@ -145,21 +152,21 @@ class ExpData : public SimulationParameters { int nt() const; /** - * @brief Set function that copies data from input to ExpData::ts + * @brief Set output timepoints. * * @param ts timepoints */ - void setTimepoints(const std::vector &ts); + void setTimepoints(std::vector const& ts); /** - * @brief get function that copies data from ExpData::ts to output + * @brief Get output timepoints. * * @return ExpData::ts */ - std::vector const &getTimepoints() const; + std::vector const& getTimepoints() const; /** - * @brief get function that returns timepoint at index + * @brief Get timepoint for the given index * * @param it timepoint index * @@ -168,23 +175,23 @@ class ExpData : public SimulationParameters { realtype getTimepoint(int it) const; /** - * @brief set function that copies data from input to ExpData::my + * @brief Set all measurements. * * @param observedData observed data (dimension: nt x nytrue, row-major) */ - void setObservedData(const std::vector &observedData); + void setObservedData(std::vector const& observedData); /** - * @brief set function that copies observed data for specific observable + * @brief Set measurements for a given observable index * * @param observedData observed data (dimension: nt) * @param iy observed data index */ - void setObservedData(const std::vector &observedData, int iy); + void setObservedData(std::vector const& observedData, int iy); /** - * @brief get function that checks whether data at specified indices has - * been set + * @brief Whether there is a measurement for the given time- and observable- + * index. * * @param it time index * @param iy observable index @@ -194,52 +201,51 @@ class ExpData : public SimulationParameters { bool isSetObservedData(int it, int iy) const; /** - * @brief get function that copies data from ExpData::observedData to output + * @brief Get all measurements. * * @return observed data (dimension: nt x nytrue, row-major) */ - std::vector const &getObservedData() const; + std::vector const& getObservedData() const; /** - * @brief get function that returns a pointer to observed data at index + * @brief Get measurements for a given timepoint index. * * @param it timepoint index * * @return pointer to observed data at index (dimension: nytrue) */ - const realtype *getObservedDataPtr(int it) const; + realtype const* getObservedDataPtr(int it) const; /** - * @brief set function that copies data from input to - * ExpData::observedDataStdDev + * @brief Set standard deviations for measurements. * * @param observedDataStdDev standard deviation of observed data (dimension: * nt x nytrue, row-major) */ - void setObservedDataStdDev(const std::vector &observedDataStdDev); + void setObservedDataStdDev(std::vector const& observedDataStdDev); /** - * @brief set function that sets all ExpData::observedDataStdDev to the - * input value + * @brief Set indentical standard deviation for all measurements. * * @param stdDev standard deviation (dimension: scalar) */ void setObservedDataStdDev(realtype stdDev); /** - * @brief set function that copies standard deviation of observed data for - * specific observable + * @brief Set standard deviations of observed data for a + * specific observable index. * * @param observedDataStdDev standard deviation of observed data (dimension: * nt) * @param iy observed data index */ - void setObservedDataStdDev(const std::vector &observedDataStdDev, - int iy); + void setObservedDataStdDev( + std::vector const& observedDataStdDev, int iy + ); /** - * @brief set function that sets all standard deviation of a specific - * observable to the input value + * @brief Set all standard deviation for a given observable index to the + * input value. * * @param stdDev standard deviation (dimension: scalar) * @param iy observed data index @@ -247,8 +253,8 @@ class ExpData : public SimulationParameters { void setObservedDataStdDev(realtype stdDev, int iy); /** - * @brief get function that checks whether standard deviation of data at - * specified indices has been set + * @brief Whether standard deviation for a measurement at + * specified timepoint- and observable index has been set. * * @param it time index * @param iy observable index @@ -257,21 +263,19 @@ class ExpData : public SimulationParameters { bool isSetObservedDataStdDev(int it, int iy) const; /** - * @brief get function that copies data from ExpData::observedDataStdDev to - * output + * @brief Get measurement standard deviations. * * @return standard deviation of observed data */ - std::vector const &getObservedDataStdDev() const; + std::vector const& getObservedDataStdDev() const; /** - * @brief get function that returns a pointer to standard deviation of - * observed data at index + * @brief Get pointer to measurement standard deviations. * * @param it timepoint index * @return pointer to standard deviation of observed data at index */ - const realtype *getObservedDataStdDevPtr(int it) const; + realtype const* getObservedDataStdDevPtr(int it) const; /** * @brief set function that copies observed event data from input to @@ -280,7 +284,7 @@ class ExpData : public SimulationParameters { * @param observedEvents observed data (dimension: nmaxevent x nztrue, * row-major) */ - void setObservedEvents(const std::vector &observedEvents); + void setObservedEvents(std::vector const& observedEvents); /** * @brief set function that copies observed event data for specific event @@ -289,7 +293,7 @@ class ExpData : public SimulationParameters { * @param observedEvents observed data (dimension: nmaxevent) * @param iz observed event data index */ - void setObservedEvents(const std::vector &observedEvents, int iz); + void setObservedEvents(std::vector const& observedEvents, int iz); /** * @brief get function that checks whether event data at specified indices @@ -306,7 +310,7 @@ class ExpData : public SimulationParameters { * * @return observed event data */ - std::vector const &getObservedEvents() const; + std::vector const& getObservedEvents() const; /** * @brief get function that returns a pointer to observed data at ieth @@ -316,7 +320,7 @@ class ExpData : public SimulationParameters { * * @return pointer to observed event data at ieth occurrence */ - const realtype *getObservedEventsPtr(int ie) const; + realtype const* getObservedEventsPtr(int ie) const; /** * @brief set function that copies data from input to @@ -325,7 +329,7 @@ class ExpData : public SimulationParameters { * @param observedEventsStdDev standard deviation of observed event data */ void - setObservedEventsStdDev(const std::vector &observedEventsStdDev); + setObservedEventsStdDev(std::vector const& observedEventsStdDev); /** * @brief set function that sets all ExpData::observedDataStdDev to the @@ -343,9 +347,9 @@ class ExpData : public SimulationParameters { * (dimension: nmaxevent) * @param iz observed data index */ - void - setObservedEventsStdDev(const std::vector &observedEventsStdDev, - int iz); + void setObservedEventsStdDev( + std::vector const& observedEventsStdDev, int iz + ); /** * @brief set function that sets all standard deviation of a specific @@ -372,7 +376,7 @@ class ExpData : public SimulationParameters { * * @return standard deviation of observed event data */ - std::vector const &getObservedEventsStdDev() const; + std::vector const& getObservedEventsStdDev() const; /** * @brief get function that returns a pointer to standard deviation of @@ -383,7 +387,7 @@ class ExpData : public SimulationParameters { * @return pointer to standard deviation of observed event data at ie-th * occurrence */ - const realtype *getObservedEventsStdDevPtr(int ie) const; + realtype const* getObservedEventsStdDevPtr(int ie) const; /** * @brief Arbitrary (not necessarily unique) identifier. @@ -413,8 +417,9 @@ class ExpData : public SimulationParameters { * @param input vector input to be checked * @param fieldname name of the input */ - void checkDataDimension(std::vector const &input, - const char *fieldname) const; + void checkDataDimension( + std::vector const& input, char const* fieldname + ) const; /** * @brief checker for dimensions of input observedEvents or @@ -423,8 +428,9 @@ class ExpData : public SimulationParameters { * @param input vector input to be checked * @param fieldname name of the input */ - void checkEventsDimension(std::vector const &input, - const char *fieldname) const; + void checkEventsDimension( + std::vector const& input, char const* fieldname + ) const; /** @brief number of observables */ int nytrue_{0}; @@ -456,14 +462,34 @@ class ExpData : public SimulationParameters { std::vector observed_events_std_dev_; }; +/** + * @brief Equality operator + * @param lhs some object + * @param rhs another object + * @return `true`, if both arguments are equal; `false` otherwise. + */ +inline bool operator==(ExpData const& lhs, ExpData const& rhs) { + return *dynamic_cast(&lhs) + == *dynamic_cast(&rhs) + && lhs.id == rhs.id && lhs.nytrue_ == rhs.nytrue_ + && lhs.nztrue_ == rhs.nztrue_ && lhs.nmaxevent_ == rhs.nmaxevent_ + && is_equal(lhs.observed_data_, rhs.observed_data_) + && is_equal(lhs.observed_data_std_dev_, rhs.observed_data_std_dev_) + && is_equal(lhs.observed_events_, rhs.observed_events_) + && is_equal( + lhs.observed_events_std_dev_, rhs.observed_events_std_dev_ + ); +}; + /** * @brief checks input vector of sigmas for not strictly positive values * * @param sigmaVector vector input to be checked * @param vectorName name of the input */ -void checkSigmaPositivity(std::vector const &sigmaVector, - const char *vectorName); +void checkSigmaPositivity( + std::vector const& sigmaVector, char const* vectorName +); /** * @brief checks input scalar sigma for not strictly positive value @@ -471,7 +497,7 @@ void checkSigmaPositivity(std::vector const &sigmaVector, * @param sigma input to be checked * @param sigmaName name of the input */ -void checkSigmaPositivity(realtype sigma, const char *sigmaName); +void checkSigmaPositivity(realtype sigma, char const* sigmaName); /** * @brief The ConditionContext class applies condition-specific amici::Model @@ -488,10 +514,11 @@ class ConditionContext : public ContextManager { * @param fpc flag indicating which fixedParameter from edata to apply */ explicit ConditionContext( - Model *model, const ExpData *edata = nullptr, - FixedParameterContext fpc = FixedParameterContext::simulation); + Model* model, ExpData const* edata = nullptr, + FixedParameterContext fpc = FixedParameterContext::simulation + ); - ConditionContext &operator=(const ConditionContext &other) = delete; + ConditionContext& operator=(ConditionContext const& other) = delete; ~ConditionContext(); @@ -503,8 +530,7 @@ class ConditionContext : public ContextManager { * @param edata * @param fpc flag indicating which fixedParameter from edata to apply */ - void applyCondition(const ExpData *edata, - FixedParameterContext fpc); + void applyCondition(ExpData const* edata, FixedParameterContext fpc); /** * @brief Restore original settings on constructor-supplied amici::Model. @@ -514,11 +540,12 @@ class ConditionContext : public ContextManager { void restore(); private: - Model *model_ = nullptr; + Model* model_ = nullptr; std::vector original_x0_; std::vector original_sx0_; std::vector original_parameters_; std::vector original_fixed_parameters_; + realtype original_tstart_; std::vector original_timepoints_; std::vector original_parameter_list_; std::vector original_scaling_; diff --git a/deps/AMICI/include/amici/exception.h b/deps/AMICI/include/amici/exception.h index 5d767fd41..7ee91d511 100644 --- a/deps/AMICI/include/amici/exception.h +++ b/deps/AMICI/include/amici/exception.h @@ -3,8 +3,9 @@ #include "amici/defines.h" // necessary for realtype -#include #include +#include +#include namespace amici { @@ -14,13 +15,12 @@ namespace amici { * Has a printf style interface to allow easy generation of error messages */ class AmiException : public std::exception { -public: + public: /** - * @brief Constructor with printf style interface - * @param fmt error message with printf format - * @param ... printf formatting variables + * @brief Default ctor. + * @param first_frame Index of first frame to include */ - AmiException(); + AmiException(int const first_frame = 3); /** * @brief Constructor with printf style interface @@ -33,19 +33,20 @@ class AmiException : public std::exception { * @brief Override of default error message function * @return msg error message */ - const char* what() const noexcept override; + char const* what() const noexcept override; /** * @brief Returns the stored backtrace * @return trace backtrace */ - const char *getBacktrace() const; + char const* getBacktrace() const; /** * @brief Stores the current backtrace * @param nMaxFrames number of frames to go back in stacktrace + * @param first_frame Index of first frame to include */ - void storeBacktrace(int nMaxFrames); + void storeBacktrace(int nMaxFrames, int const first_frame); protected: /** @@ -53,42 +54,39 @@ class AmiException : public std::exception { * @param fmt error message with printf format * @param argptr pointer to variadic argument list */ - void storeMessage(const char *fmt, va_list argptr); + void storeMessage(char const* fmt, va_list argptr); -private: + private: std::array msg_; std::array trace_; }; - /** * @brief cvode exception handler class */ -class CvodeException : public AmiException { -public: +class CvodeException : public AmiException { + public: /** * @brief Constructor * @param error_code error code returned by cvode function * @param function cvode function name */ - CvodeException(int error_code, const char *function); + CvodeException(int error_code, char const* function); }; - /** * @brief ida exception handler class */ -class IDAException : public AmiException { -public: +class IDAException : public AmiException { + public: /** * @brief Constructor * @param error_code error code returned by ida function * @param function ida function name */ - IDAException(int error_code, const char *function); + IDAException(int error_code, char const* function); }; - /** * @brief Integration failure exception for the forward problem * @@ -96,7 +94,7 @@ class IDAException : public AmiException { * for this exception we can assume that we can recover from the exception * and return a solution struct to the user */ -class IntegrationFailure : public AmiException { +class IntegrationFailure : public AmiException { public: /** * @brief Constructor @@ -112,7 +110,6 @@ class IntegrationFailure : public AmiException { realtype time; }; - /** * @brief Integration failure exception for the backward problem * @@ -120,7 +117,7 @@ class IntegrationFailure : public AmiException { * for this exception we can assume that we can recover from the exception * and return a solution struct to the user */ -class IntegrationFailureB : public AmiException { +class IntegrationFailureB : public AmiException { public: /** * @brief Constructor @@ -136,7 +133,6 @@ class IntegrationFailureB : public AmiException { realtype time; }; - /** * @brief Setup failure exception * @@ -152,10 +148,8 @@ class SetupFailure : public AmiException { * @param ... printf formatting variables */ explicit SetupFailure(char const* fmt, ...); - }; - /** * @brief Newton failure exception * @@ -164,13 +158,13 @@ class SetupFailure : public AmiException { * recover from the exception and return a solution struct to the user */ class NewtonFailure : public AmiException { -public: + public: /** * @brief Constructor, simply calls AmiException constructor * @param function name of the function in which the error occurred * @param code error code */ - NewtonFailure(int code, const char *function); + NewtonFailure(int code, char const* function); /** error code returned by solver */ int error_code; diff --git a/deps/AMICI/include/amici/forwardproblem.h b/deps/AMICI/include/amici/forwardproblem.h index 712377d95..dfe3bd8f2 100644 --- a/deps/AMICI/include/amici/forwardproblem.h +++ b/deps/AMICI/include/amici/forwardproblem.h @@ -2,14 +2,14 @@ #define AMICI_FORWARDPROBLEM_H #include "amici/defines.h" -#include "amici/vector.h" -#include "amici/model.h" #include "amici/misc.h" -#include "amici/sundials_matrix_wrapper.h" +#include "amici/model.h" +#include "amici/vector.h" +#include +#include #include #include -#include namespace amici { @@ -18,24 +18,6 @@ class Solver; class SteadystateProblem; class FinalStateStorer; -/** - * @brief implements an exchange format to store and transfer the state of a simulation at a - * specific timepoint. - */ -struct SimulationState{ - /** timepoint */ - realtype t; - /** state variables */ - AmiVector x; - /** state variables */ - AmiVector dx; - /** state variable sensitivity */ - AmiVectorArray sx; - /** state of the model that was used for simulation */ - ModelState state; -}; - - /** * @brief The ForwardProblem class groups all functions for solving the * forward problem. @@ -47,11 +29,13 @@ class ForwardProblem { * @param edata pointer to ExpData instance * @param model pointer to Model instance * @param solver pointer to Solver instance - * @param preeq preequilibration with which to initialize the forward problem, - * pass nullptr for no initialization + * @param preeq preequilibration with which to initialize the forward + * problem, pass nullptr for no initialization */ - ForwardProblem(const ExpData *edata, Model *model, Solver *solver, - const SteadystateProblem *preeq); + ForwardProblem( + ExpData const* edata, Model* model, Solver* solver, + SteadystateProblem const* preeq + ); ~ForwardProblem() = default; @@ -61,48 +45,42 @@ class ForwardProblem { /** * @brief Solve the forward problem. * - * If forward sensitivities are enabled this will also compute sensitivities. + * If forward sensitivities are enabled this will also compute + * sensitivities. */ void workForwardProblem(); /** - * @brief computes adjoint updates dJydx according to provided model and expdata + * @brief computes adjoint updates dJydx according to provided model and + * expdata * @param model Model instance * @param edata experimental data */ - void getAdjointUpdates(Model &model, const ExpData &edata); + void getAdjointUpdates(Model& model, ExpData const& edata); /** * @brief Accessor for t * @return t */ - realtype getTime() const { - return t_; - } + realtype getTime() const { return t_; } /** * @brief Accessor for x * @return x */ - AmiVector const& getState() const { - return x_; - } + AmiVector const& getState() const { return x_; } /** * @brief Accessor for dx * @return dx */ - AmiVector const& getStateDerivative() const { - return dx_; - } + AmiVector const& getStateDerivative() const { return dx_; } /** * @brief Accessor for sx * @return sx */ - AmiVectorArray const& getStateSensitivity() const { - return sx_; - } + AmiVectorArray const& getStateSensitivity() const { return sx_; } /** * @brief Accessor for x_disc @@ -132,17 +110,13 @@ class ForwardProblem { * @brief Accessor for nroots * @return nroots */ - std::vector const& getNumberOfRoots() const { - return nroots_; - } + std::vector const& getNumberOfRoots() const { return nroots_; } /** * @brief Accessor for discs * @return discs */ - std::vector const& getDiscontinuities() const { - return discs_; - } + std::vector const& getDiscontinuities() const { return discs_; } /** * @brief Accessor for rootidx @@ -156,81 +130,63 @@ class ForwardProblem { * @brief Accessor for dJydx * @return dJydx */ - std::vector const& getDJydx() const { - return dJydx_; - } + std::vector const& getDJydx() const { return dJydx_; } /** * @brief Accessor for dJzdx * @return dJzdx */ - std::vector const& getDJzdx() const { - return dJzdx_; - } + std::vector const& getDJzdx() const { return dJzdx_; } /** * @brief Accessor for pointer to x * @return &x */ - AmiVector *getStatePointer() { - return &x_; - } + AmiVector* getStatePointer() { return &x_; } /** * @brief Accessor for pointer to dx * @return &dx */ - AmiVector *getStateDerivativePointer() { - return &dx_; - } + AmiVector* getStateDerivativePointer() { return &dx_; } /** * @brief accessor for pointer to sx * @return &sx */ - AmiVectorArray *getStateSensitivityPointer() { - return &sx_; - } + AmiVectorArray* getStateSensitivityPointer() { return &sx_; } /** * @brief Accessor for pointer to sdx * @return &sdx */ - AmiVectorArray *getStateDerivativeSensitivityPointer() { - return &sdx_; - } + AmiVectorArray* getStateDerivativeSensitivityPointer() { return &sdx_; } /** * @brief Accessor for it * @return it */ - int getCurrentTimeIteration() const { - return it_; - } + int getCurrentTimeIteration() const { return it_; } /** * @brief Returns final time point for which simulations are available * @return time point */ - realtype getFinalTime() const { - return final_state_.t; - } + realtype getFinalTime() const { return final_state_.t; } /** * @brief Returns maximal event index for which simulations are available * @return index */ int getEventCounter() const { - return static_cast(event_states_.size() - 1); + return gsl::narrow(event_states_.size()) - 1; } /** * @brief Returns maximal event index for which the timepoint is available * @return index */ - int getRootCounter() const { - return static_cast(discs_.size() - 1); - } + int getRootCounter() const { return gsl::narrow(discs_.size()) - 1; } /** * @brief Retrieves the carbon copy of the simulation state variables at @@ -238,7 +194,7 @@ class ForwardProblem { * @param it timepoint index * @return state */ - const SimulationState &getSimulationStateTimepoint(int it) const { + SimulationState const& getSimulationStateTimepoint(int it) const { if (model->getTimepoint(it) == initial_state_.t) return getInitialSimulationState(); return timepoint_states_.find(model->getTimepoint(it))->second; @@ -250,7 +206,7 @@ class ForwardProblem { * @param iroot event index * @return SimulationState */ - const SimulationState &getSimulationStateEvent(int iroot) const { + SimulationState const& getSimulationStateEvent(int iroot) const { return event_states_.at(iroot); }; @@ -259,7 +215,7 @@ class ForwardProblem { * initial timepoint * @return SimulationState */ - const SimulationState &getInitialSimulationState() const { + SimulationState const& getInitialSimulationState() const { return initial_state_; }; @@ -268,21 +224,20 @@ class ForwardProblem { * final timepoint (or when simulation failed) * @return SimulationState */ - const SimulationState &getFinalSimulationState() const { + SimulationState const& getFinalSimulationState() const { return final_state_; }; /** pointer to model instance */ - Model *model; + Model* model; /** pointer to solver instance */ - Solver *solver; + Solver* solver; /** pointer to experimental data instance */ - const ExpData *edata; + ExpData const* edata; private: - void handlePresimulation(); /** @@ -290,9 +245,10 @@ class ForwardProblem { * * @param tlastroot pointer to the timepoint of the last event * @param seflag Secondary event flag + * @param initial_event initial event flag */ - void handleEvent(realtype *tlastroot,bool seflag); + void handleEvent(realtype* tlastroot, bool seflag, bool initial_event); /** * @brief Extract output information for events @@ -308,8 +264,6 @@ class ForwardProblem { /** * @brief Applies the event bolus to the current state - * - * @param model pointer to model specification object */ void applyEventBolus(); @@ -324,10 +278,10 @@ class ForwardProblem { * @param nmaxevent maximal number of events */ bool checkEventsToFill(int nmaxevent) const { - return std::any_of(nroots_.cbegin(), nroots_.cend(), - [nmaxevent](int curNRoots) { - return curNRoots < nmaxevent; - }); + return std::any_of( + nroots_.cbegin(), nroots_.cend(), + [nmaxevent](int curNRoots) { return curNRoots < nmaxevent; } + ); }; /** @@ -444,14 +398,14 @@ class ForwardProblem { std::vector stau_; /** storage for last found root */ - realtype tlastroot_ {0.0}; + realtype tlastroot_{0.0}; - /** flag to indicate whether solver was preeinitialized via preequilibration */ - bool preequilibrated_ {false}; + /** flag to indicate whether solver was preeinitialized via preequilibration + */ + bool preequilibrated_{false}; /** current iteration number for time index */ int it_; - }; /** @@ -463,20 +417,21 @@ class FinalStateStorer : public ContextManager { * @brief constructor, attaches problem pointer * @param fwd problem from which the simulation state is to be stored */ - explicit FinalStateStorer(ForwardProblem *fwd) : fwd_(fwd) { - } + explicit FinalStateStorer(ForwardProblem* fwd) + : fwd_(fwd) {} - FinalStateStorer &operator=(const FinalStateStorer &other) = delete; + FinalStateStorer& operator=(FinalStateStorer const& other) = delete; /** * @brief destructor, stores simulation state */ ~FinalStateStorer() { - if(fwd_) + if (fwd_) fwd_->final_state_ = fwd_->getSimulationState(); } + private: - ForwardProblem *fwd_; + ForwardProblem* fwd_; }; } // namespace amici diff --git a/deps/AMICI/include/amici/hdf5.h b/deps/AMICI/include/amici/hdf5.h index 8ed91da0e..32cd4c925 100644 --- a/deps/AMICI/include/amici/hdf5.h +++ b/deps/AMICI/include/amici/hdf5.h @@ -9,13 +9,12 @@ #include - /* Macros for enabling/disabling HDF5 error auto-printing * AMICI_H5_SAVE_ERROR_HANDLER and AMICI_H5_RESTORE_ERROR_HANDLER must be called * within the same context, otherwise the stack handler is lost. */ #define AMICI_H5_SAVE_ERROR_HANDLER \ - herr_t (*old_func)(void *); \ - void *old_client_data; \ + herr_t (*old_func)(void*); \ + void* old_client_data; \ H5Eget_auto1(&old_func, &old_client_data); \ H5Eset_auto1(NULL, NULL) @@ -39,7 +38,7 @@ namespace hdf5 { * @param hdf5filename File to open * @return File object */ -H5::H5File createOrOpenForWriting(std::string const &hdf5filename); +H5::H5File createOrOpenForWriting(std::string const& hdf5filename); /** * @brief Read solver options from HDF5 file. @@ -47,8 +46,9 @@ H5::H5File createOrOpenForWriting(std::string const &hdf5filename); * @param solver Solver to set options on * @param datasetPath Path inside the HDF5 file */ -void readSolverSettingsFromHDF5(const H5::H5File &file, Solver &solver, - std::string const &datasetPath); +void readSolverSettingsFromHDF5( + const H5::H5File& file, Solver& solver, std::string const& datasetPath +); /** * @brief Write solver options to HDF5 file. @@ -56,9 +56,10 @@ void readSolverSettingsFromHDF5(const H5::H5File &file, Solver &solver, * @param solver Solver to write options from * @param hdf5Location Path inside the HDF5 file */ -void writeSolverSettingsToHDF5(Solver const& solver, - std::string const& hdf5Filename, - std::string const& hdf5Location); +void writeSolverSettingsToHDF5( + Solver const& solver, std::string const& hdf5Filename, + std::string const& hdf5Location +); /** * @brief Write solver options to HDF5 file. @@ -66,9 +67,10 @@ void writeSolverSettingsToHDF5(Solver const& solver, * @param solver Solver to write options from * @param hdf5Location Path inside the HDF5 file */ -void writeSolverSettingsToHDF5(Solver const& solver, - H5::H5File const& file, - std::string const& hdf5Location); +void writeSolverSettingsToHDF5( + Solver const& solver, H5::H5File const& file, + std::string const& hdf5Location +); /** * @brief Read solver options from HDF5 file. @@ -76,8 +78,9 @@ void writeSolverSettingsToHDF5(Solver const& solver, * @param solver Solver to set options on * @param datasetPath Path inside the HDF5 file */ -void readSolverSettingsFromHDF5(std::string const &hdffile, Solver &solver, - std::string const &datasetPath); +void readSolverSettingsFromHDF5( + std::string const& hdffile, Solver& solver, std::string const& datasetPath +); /** * @brief Read model data from HDF5 file. @@ -85,8 +88,9 @@ void readSolverSettingsFromHDF5(std::string const &hdffile, Solver &solver, * @param model Model to set data on * @param datasetPath Path inside the HDF5 file */ -void readModelDataFromHDF5(std::string const &hdffile, Model &model, - std::string const &datasetPath); +void readModelDataFromHDF5( + std::string const& hdffile, Model& model, std::string const& datasetPath +); /** * @brief Read model data from HDF5 file. @@ -94,8 +98,9 @@ void readModelDataFromHDF5(std::string const &hdffile, Model &model, * @param model Model to set data on * @param datasetPath Path inside the HDF5 file */ -void readModelDataFromHDF5(H5::H5File const &file, Model &model, - std::string const &datasetPath); +void readModelDataFromHDF5( + H5::H5File const& file, Model& model, std::string const& datasetPath +); /** * @brief Write ReturnData to HDF5 file. @@ -104,8 +109,10 @@ void readModelDataFromHDF5(H5::H5File const &file, Model &model, * @param hdf5Location Full dataset path inside the HDF5 file (will be created) */ -void writeReturnData(const ReturnData &rdata, H5::H5File const &file, - const std::string &hdf5Location); +void writeReturnData( + ReturnData const& rdata, H5::H5File const& file, + std::string const& hdf5Location +); /** * @brief Write ReturnData to HDF5 file. @@ -114,8 +121,10 @@ void writeReturnData(const ReturnData &rdata, H5::H5File const &file, * @param hdf5Location Full dataset path inside the HDF5 file (will be created) */ -void writeReturnData(const ReturnData &rdata, std::string const &hdf5Filename, - const std::string &hdf5Location); +void writeReturnData( + ReturnData const& rdata, std::string const& hdf5Filename, + std::string const& hdf5Location +); /** * @brief Write ReturnData diagnosis data to HDF5 file. @@ -123,8 +132,10 @@ void writeReturnData(const ReturnData &rdata, std::string const &hdf5Filename, * @param file HDF5 file to write to * @param hdf5Location Full dataset path inside the HDF5 file (will be created) */ -void writeReturnDataDiagnosis(const ReturnData &rdata, H5::H5File const &file, - const std::string &hdf5Location); +void writeReturnDataDiagnosis( + ReturnData const& rdata, H5::H5File const& file, + std::string const& hdf5Location +); /** * @brief Create the given group and possibly parents. @@ -132,8 +143,10 @@ void writeReturnDataDiagnosis(const ReturnData &rdata, H5::H5File const &file, * @param groupPath Path to the group to be created * @param recursively Create intermediary groups */ -void createGroup(const H5::H5File &file, std::string const &groupPath, - bool recursively = true); +void createGroup( + const H5::H5File& file, std::string const& groupPath, + bool recursively = true +); /** * @brief Read AMICI ExpData data from HDF5 file. @@ -143,9 +156,10 @@ void createGroup(const H5::H5File &file, std::string const &groupPath, * @return ExpData created from data in the given location */ -std::unique_ptr readSimulationExpData(const std::string &hdf5Filename, - const std::string &hdf5Root, - const Model &model); +std::unique_ptr readSimulationExpData( + std::string const& hdf5Filename, std::string const& hdf5Root, + Model const& model +); /** * @brief Write AMICI experimental data to HDF5 file. @@ -154,8 +168,10 @@ std::unique_ptr readSimulationExpData(const std::string &hdf5Filename, * @param hdf5Location Path inside the HDF5 file to object having ExpData */ -void writeSimulationExpData(const ExpData &edata, H5::H5File const &file, - const std::string &hdf5Location); +void writeSimulationExpData( + ExpData const& edata, H5::H5File const& file, + std::string const& hdf5Location +); /** * @brief Check whether an attribute with the given name exists @@ -165,8 +181,10 @@ void writeSimulationExpData(const ExpData &edata, H5::H5File const &file, * @param attributeName Name of the attribute of interest * @return `true` if attribute exists, `false` otherwise */ -bool attributeExists(H5::H5File const &file, const std::string &optionsObject, - const std::string &attributeName); +bool attributeExists( + H5::H5File const& file, std::string const& optionsObject, + std::string const& attributeName +); /** * @brief Check whether an attribute with the given name exists @@ -175,8 +193,9 @@ bool attributeExists(H5::H5File const &file, const std::string &optionsObject, * @param attributeName Name of the attribute of interest * @return `true` if attribute exists, `false` otherwise */ -bool attributeExists(H5::H5Object const &object, - const std::string &attributeName); +bool attributeExists( + H5::H5Object const& object, std::string const& attributeName +); /** * @brief Create and write to 1-dimensional native integer dataset. @@ -184,9 +203,10 @@ bool attributeExists(H5::H5Object const &object, * @param datasetName Name of dataset to create * @param buffer Data to write to dataset */ -void createAndWriteInt1DDataset(H5::H5File const &file, - std::string const &datasetName, - gsl::span buffer); +void createAndWriteInt1DDataset( + H5::H5File const& file, std::string const& datasetName, + gsl::span buffer +); /** * @brief Create and write to 2-dimensional native integer dataset. @@ -196,10 +216,10 @@ void createAndWriteInt1DDataset(H5::H5File const &file, * @param m Number of rows in buffer * @param n Number of columns buffer */ -void createAndWriteInt2DDataset(H5::H5File const &file, - std::string const &datasetName, - gsl::span buffer, hsize_t m, - hsize_t n); +void createAndWriteInt2DDataset( + H5::H5File const& file, std::string const& datasetName, + gsl::span buffer, hsize_t m, hsize_t n +); /** * @brief Create and write to 1-dimensional native double dataset. @@ -207,9 +227,10 @@ void createAndWriteInt2DDataset(H5::H5File const &file, * @param datasetName Name of dataset to create * @param buffer Data to write to dataset */ -void createAndWriteDouble1DDataset(H5::H5File const &file, - std::string const &datasetName, - gsl::span buffer); +void createAndWriteDouble1DDataset( + H5::H5File const& file, std::string const& datasetName, + gsl::span buffer +); /** * @brief Create and write to 2-dimensional native double dataset. @@ -220,10 +241,10 @@ void createAndWriteDouble1DDataset(H5::H5File const &file, * @param n Number of columns buffer */ -void createAndWriteDouble2DDataset(H5::H5File const &file, - std::string const &datasetName, - gsl::span buffer, hsize_t m, - hsize_t n); +void createAndWriteDouble2DDataset( + H5::H5File const& file, std::string const& datasetName, + gsl::span buffer, hsize_t m, hsize_t n +); /** * @brief Create and write to 3-dimensional native double dataset. @@ -235,10 +256,10 @@ void createAndWriteDouble2DDataset(H5::H5File const &file, * @param o Length of first dimension in buffer */ -void createAndWriteDouble3DDataset(H5::H5File const &file, - std::string const &datasetName, - gsl::span buffer, hsize_t m, - hsize_t n, hsize_t o); +void createAndWriteDouble3DDataset( + H5::H5File const& file, std::string const& datasetName, + gsl::span buffer, hsize_t m, hsize_t n, hsize_t o +); /** * @brief Read string attribute from HDF5 object. @@ -247,9 +268,10 @@ void createAndWriteDouble3DDataset(H5::H5File const &file, * @param attributeName Name of attribute to read * @return Attribute value */ -std::string getStringAttribute(H5::H5File const& file, - std::string const& optionsObject, - std::string const& attributeName); +std::string getStringAttribute( + H5::H5File const& file, std::string const& optionsObject, + std::string const& attributeName +); /** * @brief Read scalar native double attribute from HDF5 object. @@ -258,9 +280,10 @@ std::string getStringAttribute(H5::H5File const& file, * @param attributeName Name of attribute to read * @return Attribute value */ -double getDoubleScalarAttribute(const H5::H5File &file, - const std::string &optionsObject, - const std::string &attributeName); +double getDoubleScalarAttribute( + const H5::H5File& file, std::string const& optionsObject, + std::string const& attributeName +); /** * @brief Read scalar native integer attribute from HDF5 object. @@ -270,9 +293,10 @@ double getDoubleScalarAttribute(const H5::H5File &file, * @return Attribute value */ -int getIntScalarAttribute(const H5::H5File &file, - const std::string &optionsObject, - const std::string &attributeName); +int getIntScalarAttribute( + const H5::H5File& file, std::string const& optionsObject, + std::string const& attributeName +); /** * @brief Read 1-dimensional native integer dataset from HDF5 file. @@ -280,8 +304,8 @@ int getIntScalarAttribute(const H5::H5File &file, * @param name Name of dataset to read * @return Data read */ -std::vector getIntDataset1D(const H5::H5File &file, - std::string const &name); +std::vector +getIntDataset1D(const H5::H5File& file, std::string const& name); /** * @brief Read 1-dimensional native double dataset from HDF5 file. @@ -290,8 +314,8 @@ std::vector getIntDataset1D(const H5::H5File &file, * @return Data read */ -std::vector getDoubleDataset1D(const H5::H5File &file, - std::string const &name); +std::vector +getDoubleDataset1D(const H5::H5File& file, std::string const& name); /** * @brief Read 2-dimensional native double dataset from HDF5 file. @@ -302,9 +326,9 @@ std::vector getDoubleDataset1D(const H5::H5File &file, * @return Flattened data (row-major) */ -std::vector getDoubleDataset2D(const H5::H5File &file, - std::string const &name, hsize_t &m, - hsize_t &n); +std::vector getDoubleDataset2D( + const H5::H5File& file, std::string const& name, hsize_t& m, hsize_t& n +); /** * @brief Read 3-dimensional native double dataset from HDF5 file. @@ -316,9 +340,10 @@ std::vector getDoubleDataset2D(const H5::H5File &file, * @return Flattened data (row-major) */ -std::vector getDoubleDataset3D(const H5::H5File &file, - std::string const &name, hsize_t &m, - hsize_t &n, hsize_t &o); +std::vector getDoubleDataset3D( + const H5::H5File& file, std::string const& name, hsize_t& m, hsize_t& n, + hsize_t& o +); /** * @brief Check if the given location (group, link or dataset) exists in the @@ -327,7 +352,7 @@ std::vector getDoubleDataset3D(const H5::H5File &file, * @param location Location to test for * @return `true` if exists, `false` otherwise */ -bool locationExists(std::string const &filename, std::string const &location); +bool locationExists(std::string const& filename, std::string const& location); /** * @brief Check if the given location (group, link or dataset) exists in the @@ -337,7 +362,7 @@ bool locationExists(std::string const &filename, std::string const &location); * @return `true` if exists, `false` otherwise */ -bool locationExists(H5::H5File const &file, std::string const &location); +bool locationExists(H5::H5File const& file, std::string const& location); } // namespace hdf5 } // namespace amici diff --git a/deps/AMICI/include/amici/interface_matlab.h b/deps/AMICI/include/amici/interface_matlab.h index 1f1846188..21fd89c41 100644 --- a/deps/AMICI/include/amici/interface_matlab.h +++ b/deps/AMICI/include/amici/interface_matlab.h @@ -1,21 +1,21 @@ #ifndef AMICI_INTERFACE_MATLAB_H #define AMICI_INTERFACE_MATLAB_H -#include "amici/amici.h" +#include -#include #include - +#include namespace amici { -namespace generic_model { -extern std::unique_ptr getModel(); -} // namespace generic_model - class Model; +class Solver; class ReturnDataMatlab; +class ExpData; +namespace generic_model { +extern std::unique_ptr getModel(); +} // namespace generic_model /** * @brief setModelData sets data from the matlab call to the model object @@ -23,7 +23,7 @@ class ReturnDataMatlab; * @param nrhs: number of elements in prhs * @param model: model to update */ -void setModelData(const mxArray *prhs[], int nrhs, Model& model); +void setModelData(mxArray const* prhs[], int nrhs, Model& model); /** * @brief setSolverOptions solver options from the matlab call to a solver @@ -32,7 +32,7 @@ void setModelData(const mxArray *prhs[], int nrhs, Model& model); * @param nrhs: number of elements in prhs * @param solver: solver to update */ -void setSolverOptions(const mxArray *prhs[], int nrhs, Solver& solver); +void setSolverOptions(mxArray const* prhs[], int nrhs, Solver& solver); /** * @brief setupReturnData initialises the return data struct @@ -40,8 +40,7 @@ void setSolverOptions(const mxArray *prhs[], int nrhs, Solver& solver); * @param nlhs number of elements in plhs * @return rdata: return data struct */ -ReturnDataMatlab *setupReturnData(mxArray *plhs[], int nlhs); - +ReturnDataMatlab* setupReturnData(mxArray* plhs[], int nlhs); /*! * @brief expDataFromMatlabCall parses the experimental data from the matlab @@ -52,19 +51,21 @@ ReturnDataMatlab *setupReturnData(mxArray *plhs[], int nlhs); * dimension checks * @return edata pointer to experimental data object */ -std::unique_ptr expDataFromMatlabCall(const mxArray *prhs[], - const Model &model); - -void amici_dgemv(BLASLayout layout, BLASTranspose TransA, - const int M, const int N, const double alpha, const double *A, - const int lda, const double *X, const int incX, - const double beta, double *Y, const int incY); - -void amici_dgemm(BLASLayout layout, BLASTranspose TransA, - BLASTranspose TransB, const int M, const int N, - const int K, const double alpha, const double *A, - const int lda, const double *B, const int ldb, - const double beta, double *C, const int ldc); +std::unique_ptr +expDataFromMatlabCall(mxArray const* prhs[], Model const& model); + +void amici_dgemv( + BLASLayout layout, BLASTranspose TransA, int const M, int const N, + double const alpha, double const* A, int const lda, double const* X, + int const incX, double const beta, double* Y, int const incY +); + +void amici_dgemm( + BLASLayout layout, BLASTranspose TransA, BLASTranspose TransB, int const M, + int const N, int const K, double const alpha, double const* A, + int const lda, double const* B, int const ldb, double const beta, double* C, + int const ldc +); } // namespace amici diff --git a/deps/AMICI/include/amici/logging.h b/deps/AMICI/include/amici/logging.h new file mode 100644 index 000000000..0118bedd2 --- /dev/null +++ b/deps/AMICI/include/amici/logging.h @@ -0,0 +1,96 @@ +#ifndef AMICI_LOGGER_H +#define AMICI_LOGGER_H + +#include +#include + +namespace amici { + +struct LogItem; + +/** + * @brief Severity levels for logging. + */ +enum class LogSeverity { + error, + warning, + debug, +}; + +/** + * @brief A logger, holding a list of error messages. + */ +class Logger { + public: + Logger() = default; + /** + * @brief Add a log entry + * @param severity Severity level + * @param identifier Short identifier for the logged event + * @param message A more detailed message + */ + void + log(LogSeverity severity, std::string const& identifier, + std::string const& message); + +#if SWIG_VERSION >= 0x040002 + /** + * @brief Add a log entry with printf-like message formatting + * @param severity Severity level + * @param identifier Short identifier for the logged event + * @param format printf format string + * @param ... arguments to be formatted + */ +#else + // swig 4.0.1 segfaults on "@param ..." + // see https://github.com/swig/swig/issues/1643 + /** + * @brief Add a log entry with printf-like message formatting + * @param severity Severity level + * @param identifier Short identifier for the logged event + * @param format printf format string + */ +#endif + void + log(LogSeverity severity, std::string const& identifier, char const* format, + ...); + + /** The log items */ + std::vector items; +}; + +/** + * @brief A log item. + */ +struct LogItem { + /** + * @brief Default ctor. + */ + LogItem() = default; + + /** + * @brief Construct a LogItem + * @param severity + * @param identifier + * @param message + */ + LogItem( + LogSeverity severity, std::string const& identifier, + std::string const& message + ) + : severity(severity) + , identifier(identifier) + , message(message){}; + + /** Severity level */ + LogSeverity severity; + + /** Short identifier for the logged event */ + std::string identifier; + + /** A more detailed and readable message */ + std::string message; +}; + +} // namespace amici +#endif // AMICI_LOGGER_H diff --git a/deps/AMICI/include/amici/misc.h b/deps/AMICI/include/amici/misc.h index ee069b522..c18606e3e 100644 --- a/deps/AMICI/include/amici/misc.h +++ b/deps/AMICI/include/amici/misc.h @@ -7,9 +7,15 @@ #include // SUNMatrixContent_Sparse #include -#include +#include +#include #include #include +#include + +#ifdef HAS_BOOST_CHRONO +#include +#endif #include @@ -25,11 +31,11 @@ namespace amici { */ template -gsl::span slice(std::vector &data, int index, unsigned size) { +gsl::span slice(std::vector& data, int index, unsigned size) { if ((index + 1) * size > data.size()) throw std::out_of_range("requested slice is out of data range"); if (size > 0) - return gsl::make_span(&data.at(index*size), size); + return gsl::make_span(&data.at(index * size), size); return gsl::make_span(static_cast(nullptr), 0); } @@ -44,12 +50,11 @@ gsl::span slice(std::vector &data, int index, unsigned size) { */ template -gsl::span slice(const std::vector &data, - int index, unsigned size) { +gsl::span slice(std::vector const& data, int index, unsigned size) { if ((index + 1) * size > data.size()) throw std::out_of_range("requested slice is out of data range"); if (size > 0) - return gsl::make_span(&data.at(index*size), size); + return gsl::make_span(&data.at(index * size), size); return gsl::make_span(static_cast(nullptr), 0); } @@ -61,80 +66,110 @@ gsl::span slice(const std::vector &data, * @param expected_size expected size of the buffer */ template -void checkBufferSize(gsl::span buffer, - typename gsl::span::index_type expected_size) { +void checkBufferSize( + gsl::span buffer, typename gsl::span::index_type expected_size +) { if (buffer.size() != expected_size) - throw AmiException("Incorrect buffer size! Was %u, expected %u.", - buffer.size(), expected_size); + throw AmiException( + "Incorrect buffer size! Was %u, expected %u.", buffer.size(), + expected_size + ); } /* TODO: templating writeSlice breaks implicit conversion between vector & span not sure whether this is fixable */ /** - * @brief local helper function to write computed slice to provided buffer (span) + * @brief local helper function to write computed slice to provided buffer + * (span) * @param slice computed value * @param buffer buffer to which values are to be written */ template -void writeSlice(const gsl::span slice, gsl::span buffer) { +void writeSlice(const gsl::span slice, gsl::span buffer) { checkBufferSize(buffer, slice.size()); std::copy(slice.begin(), slice.end(), buffer.data()); }; /** - * @brief local helper function to write computed slice to provided buffer (vector) + * @brief local helper function to add the computed slice to provided buffer + * (span) + * @param slice computed value + * @param buffer buffer to which values are to be added + */ +template +void addSlice(const gsl::span slice, gsl::span buffer) { + checkBufferSize(buffer, slice.size()); + std::transform( + slice.begin(), slice.end(), buffer.begin(), buffer.begin(), + std::plus() + ); +}; + +/** + * @brief local helper function to write computed slice to provided buffer + * (vector) * @param s computed value * @param b buffer to which values are to be written */ -template -void writeSlice(const std::vector &s, std::vector &b) { - writeSlice(gsl::make_span(s.data(), s.size()), - gsl::make_span(b.data(), b.size())); +template void writeSlice(std::vector const& s, std::vector& b) { + writeSlice( + gsl::make_span(s.data(), s.size()), gsl::make_span(b.data(), b.size()) + ); }; /** - * @brief local helper function to write computed slice to provided buffer (vector/span) + * @brief local helper function to write computed slice to provided buffer + * (vector/span) * @param s computed value * @param b buffer to which values are to be written */ -template -void writeSlice(const std::vector &s, gsl::span b) { +template void writeSlice(std::vector const& s, gsl::span b) { writeSlice(gsl::make_span(s.data(), s.size()), b); }; /** - * @brief local helper function to write computed slice to provided buffer (AmiVector/span) + * @brief local helper function to add the computed slice to provided buffer + * (vector/span) * @param s computed value * @param b buffer to which values are to be written */ -void writeSlice(const AmiVector &s, gsl::span b); +template void addSlice(std::vector const& s, gsl::span b) { + addSlice(gsl::make_span(s.data(), s.size()), b); +}; +/** + * @brief local helper function to write computed slice to provided buffer + * (AmiVector/span) + * @param s computed value + * @param b buffer to which values are to be written + */ +void writeSlice(AmiVector const& s, gsl::span b); /** - * @brief Remove parameter scaling according to the parameter scaling in pscale - * - * All vectors must be of same length. - * - * @param bufferScaled scaled parameters - * @param pscale parameter scaling - * @param bufferUnscaled unscaled parameters are written to the array - */ -void unscaleParameters(gsl::span bufferScaled, - gsl::span pscale, - gsl::span bufferUnscaled); + * @brief Remove parameter scaling according to the parameter scaling in pscale + * + * All vectors must be of same length. + * + * @param bufferScaled scaled parameters + * @param pscale parameter scaling + * @param bufferUnscaled unscaled parameters are written to the array + */ +void unscaleParameters( + gsl::span bufferScaled, + gsl::span pscale, gsl::span bufferUnscaled +); /** - * @brief Remove parameter scaling according to `scaling` - * - * @param scaledParameter scaled parameter - * @param scaling parameter scaling - * - * @return Unscaled parameter - */ + * @brief Remove parameter scaling according to `scaling` + * + * @param scaledParameter scaled parameter + * @param scaling parameter scaling + * + * @return Unscaled parameter + */ double getUnscaledParameter(double scaledParameter, ParameterScaling scaling); - /** * @brief Apply parameter scaling according to `scaling` * @param unscaledParameter @@ -143,23 +178,24 @@ double getUnscaledParameter(double scaledParameter, ParameterScaling scaling); */ double getScaledParameter(double unscaledParameter, ParameterScaling scaling); - /** * @brief Apply parameter scaling according to `scaling` * @param bufferUnscaled * @param pscale parameter scaling * @param bufferScaled destination */ -void scaleParameters(gsl::span bufferUnscaled, - gsl::span pscale, - gsl::span bufferScaled); +void scaleParameters( + gsl::span bufferUnscaled, + gsl::span pscale, gsl::span bufferScaled +); /** * @brief Returns the current backtrace as std::string * @param maxFrames Number of frames to include + * @param first_frame Index of first frame to include * @return Backtrace */ -std::string backtraceString(int maxFrames); +std::string backtraceString(int maxFrames, int const first_frame = 0); /** * @brief Convert std::regex_constants::error_type to string @@ -174,18 +210,133 @@ std::string regexErrorToString(std::regex_constants::error_type err_type); * @param ap Argument list pointer * @return Formatted String */ -std::string printfToString(const char *fmt, va_list ap); +std::string printfToString(char const* fmt, va_list ap); /** * @brief Generic implementation for a context manager, explicitly deletes copy * and move operators for derived classes */ -class ContextManager{ +class ContextManager { public: ContextManager() = default; - ContextManager(ContextManager &other) = delete; - ContextManager(ContextManager &&other) = delete; + ContextManager(ContextManager& other) = delete; + ContextManager(ContextManager&& other) = delete; +}; + +/** + * @brief Convert a flat index to a pair of row/column indices, + * assuming row-major order. + * @param flat_idx flat index + * @param num_cols number of columns of referred to matrix + * @return row index, column index + */ +auto unravel_index(size_t flat_idx, size_t num_cols) + -> std::pair; + +/** + * @brief Check if two spans are equal, treating NaNs in the same position as + * equal. + * @param a + * @param b + * @return Whether the contents of the two spans are equal. + */ +template bool is_equal(T const& a, T const& b) { + if (a.size() != b.size()) + return false; + + auto a_data = a.data(); + auto b_data = b.data(); + for (typename T::size_type i = 0; i < a.size(); ++i) { + if (a_data[i] != b_data[i] + && !(std::isnan(a_data[i]) && std::isnan(b_data[i]))) + return false; + } + return true; +} + +#ifdef BOOST_CHRONO_HAS_THREAD_CLOCK +/** Tracks elapsed CPU time using boost::chrono::thread_clock. */ +class CpuTimer { + using clock = boost::chrono::thread_clock; + using time_point = clock::time_point; + using d_seconds = boost::chrono::duration; + using d_milliseconds = boost::chrono::duration; + + public: + /** + * @brief Constructor + */ + CpuTimer() + : start_(clock::now()) {} + + /** + * @brief Reset the timer + */ + void reset() { start_ = clock::now(); } + + /** + * @brief Get elapsed CPU time in seconds since initialization or last reset + * @return CPU time in seconds + */ + double elapsed_seconds() const { + return d_seconds(clock::now() - start_).count(); + } + + /** + * @brief Get elapsed CPU time in milliseconds since initialization or last + * reset + * @return CPU time in milliseconds + */ + double elapsed_milliseconds() const { + return d_milliseconds(clock::now() - start_).count(); + } + + static const bool uses_thread_clock = true; + + private: + /** Start time */ + time_point start_; +}; +#else +/** Tracks elapsed CPU time using std::clock. */ +class CpuTimer { + public: + /** + * @brief Constructor + */ + CpuTimer() + : start_(std::clock()) {} + + /** + * @brief Reset the timer + */ + void reset() { start_ = std::clock(); } + + /** + * @brief Get elapsed CPU time in seconds since initialization or last reset + * @return CPU time in seconds + */ + double elapsed_seconds() const { + return static_cast(std::clock() - start_) / CLOCKS_PER_SEC; + } + + /** + * @brief Get elapsed CPU time in milliseconds since initialization or last + * reset + * @return CPU time in milliseconds + */ + double elapsed_milliseconds() const { + return static_cast(std::clock() - start_) * 1000.0 + / CLOCKS_PER_SEC; + } + + static const bool uses_thread_clock = false; + + private: + /** Start time */ + std::clock_t start_; }; +#endif } // namespace amici diff --git a/deps/AMICI/include/amici/model.h b/deps/AMICI/include/amici/model.h index 8cb1e3be4..72f733e6c 100644 --- a/deps/AMICI/include/amici/model.h +++ b/deps/AMICI/include/amici/model.h @@ -3,11 +3,13 @@ #include "amici/abstract_model.h" #include "amici/defines.h" -#include "amici/sundials_matrix_wrapper.h" -#include "amici/vector.h" -#include "amici/simulation_parameters.h" +#include "amici/logging.h" #include "amici/model_dimensions.h" #include "amici/model_state.h" +#include "amici/simulation_parameters.h" +#include "amici/splinefunctions.h" +#include "amici/sundials_matrix_wrapper.h" +#include "amici/vector.h" #include #include @@ -18,9 +20,6 @@ namespace amici { class ExpData; class Model; class Solver; -class AmiciApplication; - -extern AmiciApplication defaultContext; } // namespace amici @@ -28,12 +27,74 @@ extern AmiciApplication defaultContext; namespace boost { namespace serialization { template -void serialize(Archive &ar, amici::Model &m, unsigned int version); +void serialize(Archive& ar, amici::Model& m, unsigned int version); } } // namespace boost namespace amici { +/** + * @brief Describes the various model quantities + */ +enum class ModelQuantity { + J, + JB, + Jv, + JvB, + JDiag, + sx, + sy, + sz, + srz, + ssigmay, + ssigmaz, + xdot, + sxdot, + xBdot, + x0_rdata, + x0, + x_rdata, + x, + dwdw, + dwdx, + dwdp, + y, + dydp, + dydx, + w, + root, + qBdot, + qBdot_ss, + xBdot_ss, + JSparseB_ss, + deltax, + deltasx, + deltaxB, + k, + p, + ts, + dJydy, + dJydy_matlab, + deltaqB, + dsigmaydp, + dsigmaydy, + dsigmazdp, + dJydsigma, + dJydx, + dzdx, + dzdp, + dJrzdsigma, + dJrzdz, + dJrzdx, + dJzdsigma, + dJzdz, + dJzdx, + drzdp, + drzdx, +}; + +extern const std::map model_quantity_to_str; + /** * @brief The Model class represents an AMICI ODE/DAE model. * @@ -57,13 +118,14 @@ class Model : public AbstractModel, public ModelDimensions { * @param ndxdotdx_explicit Number of nonzero elements in `dxdotdx_explicit` * @param w_recursion_depth Recursion depth of fw */ - Model(ModelDimensions const& model_dimensions, - SimulationParameters simulation_parameters, - 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); + Model( + ModelDimensions const& model_dimensions, + SimulationParameters simulation_parameters, + 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 + ); /** Destructor. */ ~Model() override = default; @@ -73,13 +135,13 @@ class Model : public AbstractModel, public ModelDimensions { * @param other Object to copy from * @return */ - Model &operator=(Model const &other) = delete; + Model& operator=(Model const& other) = delete; /** * @brief Clone this instance. * @return The clone */ - virtual Model *clone() const = 0; + virtual Model* clone() const = 0; /** * @brief Serialize Model (see `boost::serialization::serialize`). @@ -88,8 +150,9 @@ class Model : public AbstractModel, public ModelDimensions { * @param version Version number */ template - friend void boost::serialization::serialize(Archive &ar, Model &m, - unsigned int version); + friend void boost::serialization::serialize( + Archive& ar, Model& m, unsigned int version + ); /** * @brief Check equality of data members. @@ -97,7 +160,7 @@ class Model : public AbstractModel, public ModelDimensions { * @param b Second model instance * @return Equality */ - friend bool operator==(const Model &a, const Model &b); + friend bool operator==(Model const& a, Model const& b); // Overloaded base class methods using AbstractModel::fdeltaqB; @@ -115,16 +178,28 @@ class Model : public AbstractModel, public ModelDimensions { using AbstractModel::fdrzdp; using AbstractModel::fdrzdx; using AbstractModel::fdsigmaydp; + using AbstractModel::fdsigmaydy; using AbstractModel::fdsigmazdp; + using AbstractModel::fdtotal_cldp; + using AbstractModel::fdtotal_cldx_rdata; + using AbstractModel::fdtotal_cldx_rdata_colptrs; + using AbstractModel::fdtotal_cldx_rdata_rowvals; using AbstractModel::fdwdp; using AbstractModel::fdwdp_colptrs; using AbstractModel::fdwdp_rowvals; - using AbstractModel::fdwdx; - using AbstractModel::fdwdx_colptrs; - using AbstractModel::fdwdx_rowvals; using AbstractModel::fdwdw; using AbstractModel::fdwdw_colptrs; using AbstractModel::fdwdw_rowvals; + using AbstractModel::fdwdx; + using AbstractModel::fdwdx_colptrs; + using AbstractModel::fdwdx_rowvals; + using AbstractModel::fdx_rdatadp; + using AbstractModel::fdx_rdatadtcl; + using AbstractModel::fdx_rdatadtcl_colptrs; + using AbstractModel::fdx_rdatadtcl_rowvals; + using AbstractModel::fdx_rdatadx_solver; + using AbstractModel::fdx_rdatadx_solver_colptrs; + using AbstractModel::fdx_rdatadx_solver_rowvals; using AbstractModel::fdydp; using AbstractModel::fdydx; using AbstractModel::fdzdp; @@ -154,9 +229,13 @@ class Model : public AbstractModel, public ModelDimensions { * @param sdx Reference to time derivative of state sensitivities (DAE only) * @param computeSensitivities Flag indicating whether sensitivities are to * be computed + * @param roots_found boolean indicators indicating whether roots were found + * at t0 by this fun */ - void initialize(AmiVector &x, AmiVector &dx, AmiVectorArray &sx, - AmiVectorArray &sdx, bool computeSensitivities); + void initialize( + AmiVector& x, AmiVector& dx, AmiVectorArray& sx, AmiVectorArray& sdx, + bool computeSensitivities, std::vector& roots_found + ); /** * @brief Initialize model properties. @@ -165,21 +244,31 @@ class Model : public AbstractModel, public ModelDimensions { * @param xQB Adjoint quadratures * @param posteq Flag indicating whether postequilibration was performed */ - void initializeB(AmiVector &xB, AmiVector &dxB, AmiVector &xQB, - bool posteq) const; + void initializeB(AmiVector& xB, AmiVector& dxB, AmiVector& xQB, bool posteq) + const; /** * @brief Initialize initial states. * @param x State vector to be initialized */ - void initializeStates(AmiVector &x); + void initializeStates(AmiVector& x); /** * @brief Initialize initial state sensitivities. * @param sx Reference to state variable sensitivities * @param x Reference to state variables */ - void initializeStateSensitivities(AmiVectorArray &sx, const AmiVector &x); + void initializeStateSensitivities(AmiVectorArray& sx, AmiVector const& x); + + /** + * @brief Initialization of spline functions + */ + void initializeSplines(); + + /** + * @brief Initialization of spline sensitivity functions + */ + void initializeSplineSensitivities(); /** * @brief Initialize the Heaviside variables `h` at the initial time `t0`. @@ -188,8 +277,12 @@ class Model : public AbstractModel, public ModelDimensions { * * @param x Reference to state variables * @param dx Reference to time derivative of states (DAE only) + * @param roots_found boolean indicators indicating whether roots were found + * at t0 by this fun */ - void initHeaviside(const AmiVector &x, const AmiVector &dx); + void initEvents( + AmiVector const& x, AmiVector const& dx, std::vector& roots_found + ); /** * @brief Get number of parameters wrt to which sensitivities are computed. @@ -226,7 +319,7 @@ class Model : public AbstractModel, public ModelDimensions { * @brief Get fixed parameters. * @return Pointer to constants array */ - const double *k() const; + double const* k() const; /** * @brief Get maximum number of events that may occur for each type. @@ -250,7 +343,7 @@ class Model : public AbstractModel, public ModelDimensions { * @brief Get parameter scale for each parameter. * @return Vector of parameter scales */ - std::vector const &getParameterScale() const; + std::vector const& getParameterScale() const; /** * @brief Set parameter scale for each parameter. @@ -268,40 +361,40 @@ class Model : public AbstractModel, public ModelDimensions { * * @param pscaleVec Vector of parameter scales */ - void setParameterScale(const std::vector &pscaleVec); + void setParameterScale(std::vector const& pscaleVec); /** * @brief Get parameters with transformation according to parameter scale * applied. * @return Unscaled parameters */ - std::vector const &getUnscaledParameters() const; + std::vector const& getUnscaledParameters() const; /** * @brief Get parameter vector. * @return The user-set parameters (see also `Model::getUnscaledParameters`) */ - std::vector const &getParameters() const; + std::vector const& getParameters() const; /** * @brief Get value of first model parameter with the specified ID. * @param par_id Parameter ID * @return Parameter value */ - realtype getParameterById(std::string const &par_id) const; + realtype getParameterById(std::string const& par_id) const; /** * @brief Get value of first model parameter with the specified name. * @param par_name Parameter name * @return Parameter value */ - realtype getParameterByName(std::string const &par_name) const; + realtype getParameterByName(std::string const& par_name) const; /** * @brief Set the parameter vector. * @param p Vector of parameters */ - void setParameters(std::vector const &p); + void setParameters(std::vector const& p); /** * @brief Set model parameters according to the parameter IDs and mapped @@ -310,15 +403,16 @@ class Model : public AbstractModel, public ModelDimensions { * @param ignoreErrors Ignore errors such as parameter IDs in p which are * not model parameters */ - void setParameterById(std::map const &p, - bool ignoreErrors = false); + void setParameterById( + std::map const& p, bool ignoreErrors = false + ); /** * @brief Set value of first model parameter with the specified ID. * @param par_id Parameter ID * @param value Parameter value */ - void setParameterById(std::string const &par_id, realtype value); + void setParameterById(std::string const& par_id, realtype value); /** * @brief Set all values of model parameters with IDs matching the specified @@ -327,14 +421,14 @@ class Model : public AbstractModel, public ModelDimensions { * @param value Parameter value * @return Number of parameter IDs that matched the regex */ - int setParametersByIdRegex(std::string const &par_id_regex, realtype value); + int setParametersByIdRegex(std::string const& par_id_regex, realtype value); /** * @brief Set value of first model parameter with the specified name. * @param par_name Parameter name * @param value Parameter value */ - void setParameterByName(std::string const &par_name, realtype value); + void setParameterByName(std::string const& par_name, realtype value); /** * @brief Set model parameters according to the parameter name and mapped @@ -343,8 +437,9 @@ class Model : public AbstractModel, public ModelDimensions { * @param ignoreErrors Ignore errors such as parameter names in p which are * not model parameters */ - void setParameterByName(std::map const &p, - bool ignoreErrors = false); + void setParameterByName( + std::map const& p, bool ignoreErrors = false + ); /** * @brief Set all values of all model parameters with names matching the @@ -353,22 +448,22 @@ class Model : public AbstractModel, public ModelDimensions { * @param value Parameter value * @return Number of fixed parameter names that matched the regex */ - int setParametersByNameRegex(std::string const &par_name_regex, - realtype value); + int + setParametersByNameRegex(std::string const& par_name_regex, realtype value); /** * @brief Get values of fixed parameters. * @return Vector of fixed parameters with same ordering as in * Model::getFixedParameterIds */ - std::vector const &getFixedParameters() const; + std::vector const& getFixedParameters() const; /** * @brief Get value of fixed parameter with the specified ID. * @param par_id Parameter ID * @return Parameter value */ - realtype getFixedParameterById(std::string const &par_id) const; + realtype getFixedParameterById(std::string const& par_id) const; /** * @brief Get value of fixed parameter with the specified name. @@ -379,20 +474,20 @@ class Model : public AbstractModel, public ModelDimensions { * @param par_name Parameter name * @return Parameter value */ - realtype getFixedParameterByName(std::string const &par_name) const; + realtype getFixedParameterByName(std::string const& par_name) const; /** * @brief Set values for constants. * @param k Vector of fixed parameters */ - void setFixedParameters(std::vector const &k); + void setFixedParameters(std::vector const& k); /** * @brief Set value of first fixed parameter with the specified ID. * @param par_id Fixed parameter id * @param value Fixed parameter value */ - void setFixedParameterById(std::string const &par_id, realtype value); + void setFixedParameterById(std::string const& par_id, realtype value); /** * @brief Set values of all fixed parameters with the ID matching the @@ -401,15 +496,16 @@ class Model : public AbstractModel, public ModelDimensions { * @param value Fixed parameter value * @return Number of fixed parameter IDs that matched the regex */ - int setFixedParametersByIdRegex(std::string const &par_id_regex, - realtype value); + int setFixedParametersByIdRegex( + std::string const& par_id_regex, realtype value + ); /** * @brief Set value of first fixed parameter with the specified name. * @param par_name Fixed parameter ID * @param value Fixed parameter value */ - void setFixedParameterByName(std::string const &par_name, realtype value); + void setFixedParameterByName(std::string const& par_name, realtype value); /** * @brief Set value of all fixed parameters with name matching the specified @@ -418,8 +514,9 @@ class Model : public AbstractModel, public ModelDimensions { * @param value Fixed parameter value * @return Number of fixed parameter names that matched the regex */ - int setFixedParametersByNameRegex(std::string const &par_name_regex, - realtype value); + int setFixedParametersByNameRegex( + std::string const& par_name_regex, realtype value + ); /** * @brief Get the model name. @@ -455,6 +552,12 @@ class Model : public AbstractModel, public ModelDimensions { */ virtual std::vector getStateNames() const; + /** + * @brief Get names of the solver states. + * @return State names + */ + virtual std::vector getStateNamesSolver() const; + /** * @brief Report whether the model has fixed parameter names set. * @return Boolean indicating whether fixed parameter names were set. Also @@ -516,10 +619,16 @@ class Model : public AbstractModel, public ModelDimensions { /** * @brief Get IDs of the model states. - * @return Sate IDs + * @return State IDs */ virtual std::vector getStateIds() const; + /** + * @brief Get IDs of the solver states. + * @return State IDs + */ + virtual std::vector getStateIdsSolver() const; + /** * @brief Report whether the model has fixed parameter IDs set. * @return Boolean indicating whether fixed parameter IDs were set. Also @@ -570,7 +679,7 @@ class Model : public AbstractModel, public ModelDimensions { * @brief Get the timepoint vector. * @return Timepoint vector */ - std::vector const &getTimepoints() const; + std::vector const& getTimepoints() const; /** * @brief Get simulation timepoint for time index `it`. @@ -583,7 +692,7 @@ class Model : public AbstractModel, public ModelDimensions { * @brief Set the timepoint vector. * @param ts New timepoint vector */ - void setTimepoints(std::vector const &ts); + void setTimepoints(std::vector const& ts); /** * @brief Get simulation start time. @@ -602,14 +711,14 @@ class Model : public AbstractModel, public ModelDimensions { * non-negative. * @return Vector of flags */ - std::vector const &getStateIsNonNegative() const; + std::vector const& getStateIsNonNegative() const; /** * @brief Set flags indicating whether states should be treated as * non-negative. * @param stateIsNonNegative Vector of flags */ - void setStateIsNonNegative(std::vector const &stateIsNonNegative); + void setStateIsNonNegative(std::vector const& stateIsNonNegative); /** * @brief Set flags indicating that all states should be treated as @@ -621,72 +730,66 @@ class Model : public AbstractModel, public ModelDimensions { * @brief Get the current model state. * @return Current model state */ - ModelState const &getModelState() const { - return state_; - }; + ModelState const& getModelState() const { return state_; }; /** * @brief Set the current model state. * @param state Model state */ - void setModelState(ModelState const &state) { - if (static_cast(state.unscaledParameters.size()) != np()) + void setModelState(ModelState const& state) { + if (gsl::narrow(state.unscaledParameters.size()) != np()) throw AmiException("Mismatch in parameter size"); - if (static_cast(state.fixedParameters.size()) != nk()) + if (gsl::narrow(state.fixedParameters.size()) != nk()) throw AmiException("Mismatch in fixed parameter size"); - if (static_cast(state.h.size()) != ne) + if (gsl::narrow(state.h.size()) != ne) throw AmiException("Mismatch in Heaviside size"); - if (static_cast(state.total_cl.size()) != ncl()) + if (gsl::narrow(state.total_cl.size()) != ncl()) throw AmiException("Mismatch in conservation law size"); - if (static_cast(state.stotal_cl.size()) != ncl() * np() ) + if (gsl::narrow(state.stotal_cl.size()) != ncl() * np()) throw AmiException("Mismatch in conservation law sensitivity size"); state_ = state; }; /** - * @brief Sets the estimated lower boundary for sigma_y. When :meth:`setAddSigmaResiduals` is - * activated, this lower boundary must ensure that log(sigma) + min_sigma > 0. + * @brief Sets the estimated lower boundary for sigma_y. When + * :meth:`setAddSigmaResiduals` is activated, this lower boundary must + * ensure that log(sigma) + min_sigma > 0. * @param min_sigma lower boundary */ - void setMinimumSigmaResiduals(double min_sigma) { - min_sigma_ = min_sigma; - } + void setMinimumSigmaResiduals(double min_sigma) { min_sigma_ = min_sigma; } /** * @brief Gets the specified estimated lower boundary for sigma_y. * @return lower boundary */ - realtype getMinimumSigmaResiduals() const { - return min_sigma_; - } + realtype getMinimumSigmaResiduals() const { return min_sigma_; } /** - * @brief Specifies whether residuals should be added to account for parameter dependent sigma. + * @brief Specifies whether residuals should be added to account for + * parameter dependent sigma. * - * If set to true, additional residuals of the form \f$ \sqrt{\log(\sigma) + C} \f$ will be added. - * This enables least-squares optimization for variables with Gaussian noise assumption and parameter - * dependent standard deviation sigma. The constant \f$ C \f$ can be set via + * If set to true, additional residuals of the form \f$ \sqrt{\log(\sigma) + + * C} \f$ will be added. This enables least-squares optimization for + * variables with Gaussian noise assumption and parameter dependent standard + * deviation sigma. The constant \f$ C \f$ can be set via * :meth:`setMinimumSigmaResiduals`. * * @param sigma_res if true, additional residuals are added */ - void setAddSigmaResiduals(bool sigma_res) { - sigma_res_ = sigma_res; - } + void setAddSigmaResiduals(bool sigma_res) { sigma_res_ = sigma_res; } /** - * @brief Checks whether residuals should be added to account for parameter dependent sigma. + * @brief Checks whether residuals should be added to account for parameter + * dependent sigma. * @return sigma_res */ - bool getAddSigmaResiduals() const { - return sigma_res_; - } + bool getAddSigmaResiduals() const { return sigma_res_; } /** * @brief Get the list of parameters for which sensitivities are computed. * @return List of parameter indices */ - std::vector const &getParameterList() const; + std::vector const& getParameterList() const; /** * @brief Get entry in parameter list by index. @@ -703,7 +806,7 @@ class Model : public AbstractModel, public ModelDimensions { * * @param plist List of parameter indices */ - void setParameterList(std::vector const &plist); + void setParameterList(std::vector const& plist); /** * @brief Get the initial states. @@ -715,7 +818,7 @@ class Model : public AbstractModel, public ModelDimensions { * @brief Set the initial states. * @param x0 Initial state vector */ - void setInitialStates(std::vector const &x0); + void setInitialStates(std::vector const& x0); /** * @brief Return whether custom initial states have been set. @@ -734,7 +837,7 @@ class Model : public AbstractModel, public ModelDimensions { * @param sx0 vector of initial state sensitivities with chainrule applied. * This could be a slice of ReturnData::sx or ReturnData::sx0 */ - void setInitialStateSensitivities(std::vector const &sx0); + void setInitialStateSensitivities(std::vector const& sx0); /** * @brief Return whether custom initial state sensitivities have been set. @@ -748,7 +851,21 @@ class Model : public AbstractModel, public ModelDimensions { * @param sx0 Vector of initial state sensitivities without chainrule * applied. This could be the readin from a `model.sx0data` saved to HDF5. */ - void setUnscaledInitialStateSensitivities(std::vector const &sx0); + void setUnscaledInitialStateSensitivities(std::vector const& sx0); + + /** + * @brief Set the mode how steady state is computed in the steadystate + * simulation. + * @param mode Steadystate computation mode + */ + void setSteadyStateComputationMode(SteadyStateComputationMode mode); + + /** + * @brief Gets the mode how steady state is computed in the steadystate + * simulation. + * @return Mode + */ + SteadyStateComputationMode getSteadyStateComputationMode() const; /** * @brief Set the mode how sensitivities are computed in the steadystate @@ -792,7 +909,8 @@ class Model : public AbstractModel, public ModelDimensions { * @param t Current timepoint * @param x Current state */ - void getExpression(gsl::span w, const realtype t, const AmiVector &x); + void + getExpression(gsl::span w, const realtype t, AmiVector const& x); /** * @brief Get time-resolved observables. @@ -800,8 +918,8 @@ class Model : public AbstractModel, public ModelDimensions { * @param t Current timepoint * @param x Current state */ - void getObservable(gsl::span y, const realtype t, - const AmiVector &x); + void + getObservable(gsl::span y, const realtype t, AmiVector const& x); /** * @brief Get scaling type for observable @@ -820,8 +938,10 @@ class Model : public AbstractModel, public ModelDimensions { * @param x State variables * @param sx State sensitivities */ - void getObservableSensitivity(gsl::span sy, const realtype t, - const AmiVector &x, const AmiVectorArray &sx); + void getObservableSensitivity( + gsl::span sy, const realtype t, AmiVector const& x, + AmiVectorArray const& sx + ); /** * @brief Get time-resolved observable standard deviations @@ -830,8 +950,9 @@ class Model : public AbstractModel, public ModelDimensions { * @param edata Pointer to experimental data instance (optional, pass * `nullptr` to ignore) */ - void getObservableSigma(gsl::span sigmay, const int it, - const ExpData *edata); + void getObservableSigma( + gsl::span sigmay, int const it, ExpData const* edata + ); /** * @brief Sensitivity of time-resolved observable standard deviation. @@ -839,12 +960,15 @@ class Model : public AbstractModel, public ModelDimensions { * Total derivative (can be used with both adjoint and forward sensitivity). * * @param ssigmay Buffer (shape `ny` x `nplist`, row-major) + * @param sy Sensitivity of time-resolved observables for current timepoint * @param it Timepoint index * @param edata Pointer to experimental data instance (optional, pass * `nullptr` to ignore) */ - void getObservableSigmaSensitivity(gsl::span ssigmay, - const int it, const ExpData *edata); + void getObservableSigmaSensitivity( + gsl::span ssigmay, gsl::span sy, int const it, + ExpData const* edata + ); /** * @brief Add time-resolved measurement negative log-likelihood \f$ Jy \f$. @@ -853,12 +977,13 @@ class Model : public AbstractModel, public ModelDimensions { * @param x State variables * @param edata Experimental data */ - void addObservableObjective(realtype &Jy, const int it, const AmiVector &x, - const ExpData &edata); + void addObservableObjective( + realtype& Jy, int const it, AmiVector const& x, ExpData const& edata + ); /** - * @brief Add sensitivity of time-resolved measurement negative log-likelihood - * \f$ Jy \f$. + * @brief Add sensitivity of time-resolved measurement negative + * log-likelihood \f$ Jy \f$. * * @param sllh First-order buffer (shape `nplist`) * @param s2llh Second-order buffer (shape `nJ - 1` x `nplist`, row-major) @@ -867,11 +992,10 @@ class Model : public AbstractModel, public ModelDimensions { * @param sx State sensitivities * @param edata Experimental data */ - void addObservableObjectiveSensitivity(std::vector &sllh, - std::vector &s2llh, - const int it, const AmiVector &x, - const AmiVectorArray &sx, - const ExpData &edata); + void addObservableObjectiveSensitivity( + std::vector& sllh, std::vector& s2llh, int const it, + AmiVector const& x, AmiVectorArray const& sx, ExpData const& edata + ); /** * @brief Add sensitivity of time-resolved measurement negative @@ -886,11 +1010,10 @@ class Model : public AbstractModel, public ModelDimensions { * @param x State variables * @param edata Experimental data */ - void addPartialObservableObjectiveSensitivity(std::vector &sllh, - std::vector &s2llh, - const int it, - const AmiVector &x, - const ExpData &edata); + void addPartialObservableObjectiveSensitivity( + std::vector& sllh, std::vector& s2llh, int const it, + AmiVector const& x, ExpData const& edata + ); /** * @brief Get state sensitivity of the negative loglikelihood \f$ Jy \f$, @@ -901,9 +1024,10 @@ class Model : public AbstractModel, public ModelDimensions { * @param x State variables * @param edata Experimental data instance */ - void getAdjointStateObservableUpdate(gsl::span dJydx, - const int it, const AmiVector &x, - const ExpData &edata); + void getAdjointStateObservableUpdate( + gsl::span dJydx, int const it, AmiVector const& x, + ExpData const& edata + ); /** * @brief Get event-resolved observables. @@ -912,8 +1036,10 @@ class Model : public AbstractModel, public ModelDimensions { * @param t Timepoint * @param x State variables */ - void getEvent(gsl::span z, const int ie, const realtype t, - const AmiVector &x); + void getEvent( + gsl::span z, int const ie, const realtype t, + AmiVector const& x + ); /** * @brief Get sensitivities of event-resolved observables. * @@ -925,9 +1051,10 @@ class Model : public AbstractModel, public ModelDimensions { * @param x State variables * @param sx State sensitivities */ - void getEventSensitivity(gsl::span sz, const int ie, - const realtype t, const AmiVector &x, - const AmiVectorArray &sx); + void getEventSensitivity( + gsl::span sz, int const ie, const realtype t, + AmiVector const& x, AmiVectorArray const& sx + ); /** * @brief Get sensitivity of `z` at final timepoint. @@ -937,7 +1064,7 @@ class Model : public AbstractModel, public ModelDimensions { * @param sz Output buffer (shape `nz x nplist`, row-major) * @param ie Event index */ - void getUnobservedEventSensitivity(gsl::span sz, const int ie); + void getUnobservedEventSensitivity(gsl::span sz, int const ie); /** * @brief Get regularization for event-resolved observables. @@ -946,8 +1073,10 @@ class Model : public AbstractModel, public ModelDimensions { * @param t Timepoint * @param x State variables */ - void getEventRegularization(gsl::span rz, const int ie, - const realtype t, const AmiVector &x); + void getEventRegularization( + gsl::span rz, int const ie, const realtype t, + AmiVector const& x + ); /** * @brief Get sensitivities of regularization for event-resolved @@ -961,10 +1090,10 @@ class Model : public AbstractModel, public ModelDimensions { * @param x State variables * @param sx State sensitivities */ - void getEventRegularizationSensitivity(gsl::span srz, - const int ie, const realtype t, - const AmiVector &x, - const AmiVectorArray &sx); + void getEventRegularizationSensitivity( + gsl::span srz, int const ie, const realtype t, + AmiVector const& x, AmiVectorArray const& sx + ); /** * @brief Get event-resolved observable standard deviations. * @param sigmaz Output buffer (shape `nz`) @@ -974,9 +1103,10 @@ class Model : public AbstractModel, public ModelDimensions { * @param edata Pointer to experimental data (optional, pass * `nullptr` to ignore) */ - void getEventSigma(gsl::span sigmaz, const int ie, - const int nroots, const realtype t, - const ExpData *edata); + void getEventSigma( + gsl::span sigmaz, int const ie, int const nroots, + const realtype t, ExpData const* edata + ); /** * @brief Get sensitivities of event-resolved observable standard @@ -991,9 +1121,10 @@ class Model : public AbstractModel, public ModelDimensions { * @param edata Pointer to experimental data (optional, pass * `nullptr` to ignore) */ - void getEventSigmaSensitivity(gsl::span ssigmaz, const int ie, - const int nroots, const realtype t, - const ExpData *edata); + void getEventSigmaSensitivity( + gsl::span ssigmaz, int const ie, int const nroots, + const realtype t, ExpData const* edata + ); /** * @brief Add event-resolved observable negative log-likelihood. @@ -1004,9 +1135,10 @@ class Model : public AbstractModel, public ModelDimensions { * @param x State variables * @param edata Experimental data */ - void addEventObjective(realtype &Jz, const int ie, const int nroots, - const realtype t, const AmiVector &x, - const ExpData &edata); + void addEventObjective( + realtype& Jz, int const ie, int const nroots, const realtype t, + AmiVector const& x, ExpData const& edata + ); /** * @brief Add event-resolved observable negative log-likelihood. @@ -1017,10 +1149,10 @@ class Model : public AbstractModel, public ModelDimensions { * @param x State variables * @param edata Experimental data */ - void addEventObjectiveRegularization(realtype &Jrz, const int ie, - const int nroots, const realtype t, - const AmiVector &x, - const ExpData &edata); + void addEventObjectiveRegularization( + realtype& Jrz, int const ie, int const nroots, const realtype t, + AmiVector const& x, ExpData const& edata + ); /** * @brief Add sensitivity of time-resolved measurement negative @@ -1038,12 +1170,11 @@ class Model : public AbstractModel, public ModelDimensions { * @param sx State sensitivities * @param edata Experimental data */ - void addEventObjectiveSensitivity(std::vector &sllh, - std::vector &s2llh, - const int ie, const int nroots, - const realtype t, const AmiVector &x, - const AmiVectorArray &sx, - const ExpData &edata); + void addEventObjectiveSensitivity( + std::vector& sllh, std::vector& s2llh, int const ie, + int const nroots, const realtype t, AmiVector const& x, + AmiVectorArray const& sx, ExpData const& edata + ); /** * @brief Add sensitivity of time-resolved measurement negative @@ -1060,12 +1191,11 @@ class Model : public AbstractModel, public ModelDimensions { * @param x State variables * @param edata Experimental data */ - void addPartialEventObjectiveSensitivity(std::vector &sllh, - std::vector &s2llh, - const int ie, const int nroots, - const realtype t, - const AmiVector &x, - const ExpData &edata); + void addPartialEventObjectiveSensitivity( + std::vector& sllh, std::vector& s2llh, int const ie, + int const nroots, const realtype t, AmiVector const& x, + ExpData const& edata + ); /** * @brief State sensitivity of the negative loglikelihood \f$ Jz \f$. @@ -1079,9 +1209,10 @@ class Model : public AbstractModel, public ModelDimensions { * @param x State variables * @param edata Experimental data */ - void getAdjointStateEventUpdate(gsl::span dJzdx, const int ie, - const int nroots, const realtype t, - const AmiVector &x, const ExpData &edata); + void getAdjointStateEventUpdate( + gsl::span dJzdx, int const ie, int const nroots, + const realtype t, AmiVector const& x, ExpData const& edata + ); /** * @brief Sensitivity of event timepoint, total derivative. @@ -1094,9 +1225,10 @@ class Model : public AbstractModel, public ModelDimensions { * @param x State variables * @param sx State sensitivities */ - void getEventTimeSensitivity(std::vector &stau, const realtype t, - const int ie, const AmiVector &x, - const AmiVectorArray &sx); + void getEventTimeSensitivity( + std::vector& stau, const realtype t, int const ie, + AmiVector const& x, AmiVectorArray const& sx + ); /** * @brief Update state variables after event. @@ -1106,8 +1238,10 @@ class Model : public AbstractModel, public ModelDimensions { * @param xdot Current residual function values * @param xdot_old Value of residual function before event */ - void addStateEventUpdate(AmiVector &x, const int ie, const realtype t, - const AmiVector &xdot, const AmiVector &xdot_old); + void addStateEventUpdate( + AmiVector& x, int const ie, const realtype t, AmiVector const& xdot, + AmiVector const& xdot_old + ); /** * @brief Update state sensitivity after event. @@ -1120,12 +1254,11 @@ class Model : public AbstractModel, public ModelDimensions { * @param stau Timepoint sensitivity, to be computed with * `Model::getEventTimeSensitivity` */ - void addStateSensitivityEventUpdate(AmiVectorArray &sx, const int ie, - const realtype t, - const AmiVector &x_old, - const AmiVector &xdot, - const AmiVector &xdot_old, - const std::vector &stau); + void addStateSensitivityEventUpdate( + AmiVectorArray& sx, int const ie, const realtype t, + AmiVector const& x_old, AmiVector const& xdot, + AmiVector const& xdot_old, std::vector const& stau + ); /** * @brief Update adjoint state after event. @@ -1136,10 +1269,10 @@ class Model : public AbstractModel, public ModelDimensions { * @param xdot Current residual function values * @param xdot_old Value of residual function before event */ - void addAdjointStateEventUpdate(AmiVector &xB, const int ie, - const realtype t, const AmiVector &x, - const AmiVector &xdot, - const AmiVector &xdot_old); + void addAdjointStateEventUpdate( + AmiVector& xB, int const ie, const realtype t, AmiVector const& x, + AmiVector const& xdot, AmiVector const& xdot_old + ); /** * @brief Update adjoint quadratures after event. @@ -1151,11 +1284,10 @@ class Model : public AbstractModel, public ModelDimensions { * @param xdot Current residual function values * @param xdot_old Value of residual function before event */ - void addAdjointQuadratureEventUpdate(AmiVector xQB, const int ie, - const realtype t, const AmiVector &x, - const AmiVector &xB, - const AmiVector &xdot, - const AmiVector &xdot_old); + void addAdjointQuadratureEventUpdate( + AmiVector xQB, int const ie, const realtype t, AmiVector const& x, + AmiVector const& xB, AmiVector const& xdot, AmiVector const& xdot_old + ); /** * @brief Update the Heaviside variables `h` on event occurrences. @@ -1164,7 +1296,7 @@ class Model : public AbstractModel, public ModelDimensions { * it will give the right update to the Heaviside variables (zero if no root * was found) */ - void updateHeaviside(const std::vector &rootsfound); + void updateHeaviside(std::vector const& rootsfound); /** * @brief Updates the Heaviside variables `h` on event occurrences in the @@ -1173,20 +1305,47 @@ class Model : public AbstractModel, public ModelDimensions { * it will give the right update to the Heaviside variables (zero if no root * was found) */ - void updateHeavisideB(const int *rootsfound); + void updateHeavisideB(int const* rootsfound); + + /** + * @brief Check if the given array has only finite elements. + * + * For (1D) spans. + * + * @param array + * @param model_quantity The model quantity `array` corresponds to + * @return + */ + int checkFinite( + gsl::span array, ModelQuantity model_quantity + ) const; + /** + * @brief Check if the given array has only finite elements. + * + * For flattened 2D arrays. + * + * @param array Flattened matrix + * @param model_quantity The model quantity `array` corresponds to + * @param num_cols Number of columns of the non-flattened matrix + * @return + */ + int checkFinite( + gsl::span array, ModelQuantity model_quantity, + size_t num_cols + ) const; /** * @brief Check if the given array has only finite elements. * - * If not, try to give hints by which other fields this could be caused. + * For SUNMatrix. * - * @param array Array to check - * @param fun Name of the function that generated the values (for more - * informative messages). - * @return `amici::AMICI_RECOVERABLE_ERROR` if a NaN/Inf value was found, - * `amici::AMICI_SUCCESS` otherwise + * @param m Matrix to check + * @param model_quantity The model quantity `m` corresponds to + * @param t current timepoint + * @return */ - int checkFinite(gsl::span array, const char *fun) const; + int + checkFinite(SUNMatrix m, ModelQuantity model_quantity, realtype t) const; /** * @brief Set whether the result of every call to `Model::f*` should be @@ -1206,21 +1365,21 @@ class Model : public AbstractModel, public ModelDimensions { * @brief Compute/get initial states. * @param x Output buffer. */ - void fx0(AmiVector &x); + void fx0(AmiVector& x); /** * @brief Set only those initial states that are specified via * fixed parameters. * @param x Output buffer. */ - void fx0_fixedParameters(AmiVector &x); + void fx0_fixedParameters(AmiVector& x); /** * @brief Compute/get initial value for initial state sensitivities. * @param sx Output buffer for state sensitivities * @param x State variables */ - void fsx0(AmiVectorArray &sx, const AmiVector &x); + void fsx0(AmiVectorArray& sx, AmiVector const& x); /** * @brief Get only those initial states sensitivities that are affected @@ -1228,7 +1387,7 @@ class Model : public AbstractModel, public ModelDimensions { * @param sx Output buffer for state sensitivities * @param x State variables */ - void fsx0_fixedParameters(AmiVectorArray &sx, const AmiVector &x); + void fsx0_fixedParameters(AmiVectorArray& sx, AmiVector const& x); /** * @brief Compute sensitivity of derivative initial states sensitivities @@ -1245,7 +1404,7 @@ class Model : public AbstractModel, public ModelDimensions { * @param x_solver State variables with conservation laws applied * (solver returns this) */ - void fx_rdata(AmiVector &x_rdata, const AmiVector &x_solver); + void fx_rdata(AmiVector& x_rdata, AmiVector const& x_solver); /** * @brief Expand conservation law for state sensitivities. @@ -1253,15 +1412,20 @@ class Model : public AbstractModel, public ModelDimensions { * conservation laws expanded (stored in `amici::ReturnData`). * @param sx_solver State variables sensitivities with conservation laws * applied (solver returns this) + * @param x_solver State variables with conservation laws + * applied (solver returns this) */ - void fsx_rdata(AmiVectorArray &sx_rdata, const AmiVectorArray &sx_solver); + void fsx_rdata( + AmiVectorArray& sx_rdata, AmiVectorArray const& sx_solver, + AmiVector const& x_solver + ); /** * @brief Set indices of states to be reinitialized based on provided * constants / fixed parameters * @param idxs Array of state indices */ - void setReinitializationStateIdxs(const std::vector &idxs); + void setReinitializationStateIdxs(std::vector const& idxs); /** * @brief Return indices of states to be reinitialized based on provided @@ -1277,13 +1441,13 @@ class Model : public AbstractModel, public ModelDimensions { * @brief getter for dxdotdp (matlab generated) * @return dxdotdp */ - const AmiVectorArray &get_dxdotdp() const; + AmiVectorArray const& get_dxdotdp() const; /** * @brief getter for dxdotdp (python generated) * @return dxdotdp */ - const SUNMatrixWrapper &get_dxdotdp_full() const; + SUNMatrixWrapper const& get_dxdotdp_full() const; /** * Flag indicating whether for @@ -1295,8 +1459,8 @@ class Model : public AbstractModel, public ModelDimensions { /** Flag array for DAE equations */ std::vector idlist; - /** AMICI application context */ - AmiciApplication *app = &defaultContext; + /** Logger */ + Logger* logger = nullptr; protected: /** @@ -1306,8 +1470,10 @@ class Model : public AbstractModel, public ModelDimensions { * @param buffer Output data slice * @param ie Event index */ - void writeSliceEvent(gsl::span slice, - gsl::span buffer, const int ie); + void writeSliceEvent( + gsl::span slice, gsl::span buffer, + int const ie + ); /** * @brief Write part of a sensitivity slice to a buffer according to @@ -1316,8 +1482,10 @@ class Model : public AbstractModel, public ModelDimensions { * @param buffer output data slice * @param ie event index */ - void writeSensitivitySliceEvent(gsl::span slice, - gsl::span buffer, const int ie); + void writeSensitivitySliceEvent( + gsl::span slice, gsl::span buffer, + int const ie + ); /** * @brief Separate first and second order objective sensitivity information @@ -1326,17 +1494,19 @@ class Model : public AbstractModel, public ModelDimensions { * @param sllh First order buffer * @param s2llh Second order buffer */ - void writeLLHSensitivitySlice(const std::vector &dLLhdp, - std::vector &sllh, - std::vector &s2llh); + void writeLLHSensitivitySlice( + std::vector const& dLLhdp, std::vector& sllh, + std::vector& s2llh + ); /** * @brief Verify that the provided buffers have the expected size. * @param sllh first order buffer * @param s2llh second order buffer */ - void checkLLHBufferSize(const std::vector &sllh, - const std::vector &s2llh) const; + void checkLLHBufferSize( + std::vector const& sllh, std::vector const& s2llh + ) const; /** * @brief Set the nplist-dependent vectors to their proper sizes. @@ -1348,7 +1518,7 @@ class Model : public AbstractModel, public ModelDimensions { * @param t Current timepoint * @param x Current state */ - void fy(realtype t, const AmiVector &x); + void fy(realtype t, AmiVector const& x); /** * @brief Compute partial derivative of observables \f$ y \f$ w.r.t. model @@ -1356,7 +1526,7 @@ class Model : public AbstractModel, public ModelDimensions { * @param t Current timepoint * @param x Current state */ - void fdydp(realtype t, const AmiVector &x); + void fdydp(realtype t, AmiVector const& x); /** * @brief Compute partial derivative of observables \f$ y \f$ w.r.t. state @@ -1364,22 +1534,32 @@ class Model : public AbstractModel, public ModelDimensions { * @param t Current timepoint * @param x Current state */ - void fdydx(realtype t, const AmiVector &x); + void fdydx(realtype t, AmiVector const& x); /** * @brief Compute standard deviation of measurements. * @param it Timepoint index * @param edata Experimental data */ - void fsigmay(int it, const ExpData *edata); + void fsigmay(int it, ExpData const* edata); /** * @brief Compute partial derivative of standard deviation of measurements * w.r.t. model parameters. * @param it Timepoint index - * @param edata pointer to `amici::ExpData` data instance holding sigma values + * @param edata pointer to `amici::ExpData` data instance holding sigma + * values */ - void fdsigmaydp(int it, const ExpData *edata); + void fdsigmaydp(int it, ExpData const* edata); + + /** + * @brief Compute partial derivative of standard deviation of measurements + * w.r.t. model outputs. + * @param it Timepoint index + * @param edata pointer to `amici::ExpData` data instance holding sigma + * values + */ + void fdsigmaydy(int it, ExpData const* edata); /** * @brief Compute negative log-likelihood of measurements \f$ y \f$. @@ -1389,7 +1569,7 @@ class Model : public AbstractModel, public ModelDimensions { * @param y Simulated observable * @param edata Pointer to experimental data instance */ - void fJy(realtype &Jy, int it, const AmiVector &y, const ExpData &edata); + void fJy(realtype& Jy, int it, AmiVector const& y, ExpData const& edata); /** * @brief Compute partial derivative of time-resolved measurement negative @@ -1398,7 +1578,7 @@ class Model : public AbstractModel, public ModelDimensions { * @param x state variables * @param edata Pointer to experimental data */ - void fdJydy(int it, const AmiVector &x, const ExpData &edata); + void fdJydy(int it, AmiVector const& x, ExpData const& edata); /** * @brief Sensitivity of time-resolved measurement negative log-likelihood @@ -1407,7 +1587,7 @@ class Model : public AbstractModel, public ModelDimensions { * @param x state variables * @param edata pointer to experimental data instance */ - void fdJydsigma(int it, const AmiVector &x, const ExpData &edata); + void fdJydsigma(int it, AmiVector const& x, ExpData const& edata); /** * @brief Compute sensitivity of time-resolved measurement negative @@ -1416,7 +1596,7 @@ class Model : public AbstractModel, public ModelDimensions { * @param x state variables * @param edata pointer to experimental data instance */ - void fdJydp(const int it, const AmiVector &x, const ExpData &edata); + void fdJydp(int const it, AmiVector const& x, ExpData const& edata); /** * @brief Sensitivity of time-resolved measurement negative log-likelihood @@ -1425,7 +1605,7 @@ class Model : public AbstractModel, public ModelDimensions { * @param x State variables * @param edata Pointer to experimental data instance */ - void fdJydx(const int it, const AmiVector &x, const ExpData &edata); + void fdJydx(int const it, AmiVector const& x, ExpData const& edata); /** * @brief Compute event-resolved output. @@ -1433,7 +1613,7 @@ class Model : public AbstractModel, public ModelDimensions { * @param t Current timepoint * @param x Current state */ - void fz(int ie, realtype t, const AmiVector &x); + void fz(int ie, realtype t, AmiVector const& x); /** * @brief Compute partial derivative of event-resolved output `z` w.r.t. @@ -1442,7 +1622,7 @@ class Model : public AbstractModel, public ModelDimensions { * @param t current timepoint * @param x current state */ - void fdzdp(int ie, realtype t, const AmiVector &x); + void fdzdp(int ie, realtype t, AmiVector const& x); /** * @brief Compute partial derivative of event-resolved output `z` w.r.t. @@ -1451,7 +1631,7 @@ class Model : public AbstractModel, public ModelDimensions { * @param t Current timepoint * @param x Current state */ - void fdzdx(int ie, realtype t, const AmiVector &x); + void fdzdx(int ie, realtype t, AmiVector const& x); /** * @brief Compute event root function of events. @@ -1462,7 +1642,7 @@ class Model : public AbstractModel, public ModelDimensions { * @param t Current timepoint * @param x Current state */ - void frz(int ie, realtype t, const AmiVector &x); + void frz(int ie, realtype t, AmiVector const& x); /** * @brief Compute sensitivity of event-resolved root output w.r.t. model @@ -1471,16 +1651,16 @@ class Model : public AbstractModel, public ModelDimensions { * @param t Current timepoint * @param x Current state */ - void fdrzdp(int ie, realtype t, const AmiVector &x); + void fdrzdp(int ie, realtype t, AmiVector const& x); /** - * @brief Compute sensitivity of event-resolved measurements \f$ rz \f$ w.r.t. - * model states `x`. + * @brief Compute sensitivity of event-resolved measurements \f$ rz \f$ + * w.r.t. model states `x`. * @param ie Event index * @param t Current timepoint * @param x Current state */ - void fdrzdx(int ie, realtype t, const AmiVector &x); + void fdrzdx(int ie, realtype t, AmiVector const& x); /** * @brief Compute standard deviation of events. @@ -1489,8 +1669,9 @@ class Model : public AbstractModel, public ModelDimensions { * @param t Current timepoint * @param edata Experimental data */ - void fsigmaz(const int ie, const int nroots, const realtype t, - const ExpData *edata); + void fsigmaz( + int const ie, int const nroots, const realtype t, ExpData const* edata + ); /** * @brief Compute sensitivity of standard deviation of events measurements @@ -1500,7 +1681,7 @@ class Model : public AbstractModel, public ModelDimensions { * @param t Current timepoint * @param edata Pointer to experimental data instance */ - void fdsigmazdp(int ie, int nroots, realtype t, const ExpData *edata); + void fdsigmazdp(int ie, int nroots, realtype t, ExpData const* edata); /** * @brief Compute negative log-likelihood of event-resolved measurements @@ -1510,8 +1691,8 @@ class Model : public AbstractModel, public ModelDimensions { * @param z Simulated event * @param edata Experimental data */ - void fJz(realtype &Jz, int nroots, const AmiVector &z, - const ExpData &edata); + void + fJz(realtype& Jz, int nroots, AmiVector const& z, ExpData const& edata); /** * @brief Compute partial derivative of event measurement negative @@ -1522,8 +1703,10 @@ class Model : public AbstractModel, public ModelDimensions { * @param x State variables * @param edata Experimental data */ - void fdJzdz(const int ie, const int nroots, const realtype t, - const AmiVector &x, const ExpData &edata); + void fdJzdz( + int const ie, int const nroots, const realtype t, AmiVector const& x, + ExpData const& edata + ); /** * @brief Compute sensitivity of event measurement negative log-likelihood @@ -1534,8 +1717,10 @@ class Model : public AbstractModel, public ModelDimensions { * @param x State variables * @param edata Pointer to experimental data instance */ - void fdJzdsigma(const int ie, const int nroots, const realtype t, - const AmiVector &x, const ExpData &edata); + void fdJzdsigma( + int const ie, int const nroots, const realtype t, AmiVector const& x, + ExpData const& edata + ); /** * @brief Compute sensitivity of event-resolved measurement negative @@ -1546,8 +1731,10 @@ class Model : public AbstractModel, public ModelDimensions { * @param x State variables * @param edata Pointer to experimental data instance */ - void fdJzdp(const int ie, const int nroots, realtype t, const AmiVector &x, - const ExpData &edata); + void fdJzdp( + int const ie, int const nroots, realtype t, AmiVector const& x, + ExpData const& edata + ); /** * @brief Compute sensitivity of event-resolved measurement negative @@ -1558,8 +1745,10 @@ class Model : public AbstractModel, public ModelDimensions { * @param x State variables * @param edata Experimental data */ - void fdJzdx(const int ie, const int nroots, realtype t, const AmiVector &x, - const ExpData &edata); + void fdJzdx( + int const ie, int const nroots, realtype t, AmiVector const& x, + ExpData const& edata + ); /** * @brief Compute regularization of negative log-likelihood with roots of @@ -1569,8 +1758,8 @@ class Model : public AbstractModel, public ModelDimensions { * @param rz Regularization variable * @param edata Experimental data */ - void fJrz(realtype &Jrz, int nroots, const AmiVector &rz, - const ExpData &edata); + void + fJrz(realtype& Jrz, int nroots, AmiVector const& rz, ExpData const& edata); /** * @brief Compute partial derivative of event measurement negative @@ -1581,8 +1770,10 @@ class Model : public AbstractModel, public ModelDimensions { * @param x State variables * @param edata Experimental data */ - void fdJrzdz(const int ie, const int nroots, const realtype t, - const AmiVector &x, const ExpData &edata); + void fdJrzdz( + int const ie, int const nroots, const realtype t, AmiVector const& x, + ExpData const& edata + ); /** * @brief Compute sensitivity of event measurement negative log-likelihood @@ -1593,36 +1784,50 @@ class Model : public AbstractModel, public ModelDimensions { * @param x state variables * @param edata pointer to experimental data instance */ - void fdJrzdsigma(const int ie, const int nroots, const realtype t, - const AmiVector &x, const ExpData &edata); + void fdJrzdsigma( + int const ie, int const nroots, const realtype t, AmiVector const& x, + ExpData const& edata + ); + + /** + * @brief Spline functions + * @param t timepoint + */ + void fspl(realtype t); + + /** + * @brief Parametric derivatives of splines functions + * @param t timepoint + */ + void fsspl(realtype t); /** * @brief Compute recurring terms in xdot. * @param t Timepoint * @param x Array with the states */ - void fw(realtype t, const realtype *x); + void fw(realtype t, realtype const* x); /** * @brief Compute parameter derivative for recurring terms in xdot. * @param t Timepoint * @param x Array with the states */ - void fdwdp(realtype t, const realtype *x); + void fdwdp(realtype t, realtype const* x); /** * @brief Compute state derivative for recurring terms in xdot. * @param t Timepoint * @param x Array with the states */ - void fdwdx(realtype t, const realtype *x); + void fdwdx(realtype t, realtype const* x); /** * @brief Compute self derivative for recurring terms in xdot. * @param t Timepoint * @param x Array with the states */ - void fdwdw(realtype t, const realtype *x); + void fdwdw(realtype t, realtype const* x); /** * @brief Compute fx_rdata. @@ -1632,9 +1837,13 @@ class Model : public AbstractModel, public ModelDimensions { * @param x_rdata State variables with conservation laws expanded * @param x_solver State variables with conservation laws applied * @param tcl Total abundances for conservation laws + * @param p parameter vector + * @param k constant vector */ - virtual void fx_rdata(realtype *x_rdata, const realtype *x_solver, - const realtype *tcl); + virtual void fx_rdata( + realtype* x_rdata, realtype const* x_solver, realtype const* tcl, + realtype const* p, realtype const* k + ); /** * @brief Compute fsx_solver. @@ -1646,10 +1855,17 @@ class Model : public AbstractModel, public ModelDimensions { * @param sx_solver State sensitivity variables with conservation laws * applied * @param stcl Sensitivities of total abundances for conservation laws + * @param p parameter vector + * @param k constant vector + * @param x_solver State variables with conservation laws applied + * @param tcl Total abundances for conservation laws * @param ip Sensitivity index */ - virtual void fsx_rdata(realtype *sx_rdata, const realtype *sx_solver, - const realtype *stcl, int ip); + virtual void fsx_rdata( + realtype* sx_rdata, realtype const* sx_solver, realtype const* stcl, + realtype const* p, realtype const* k, realtype const* x_solver, + realtype const* tcl, int const ip + ); /** * @brief Compute fx_solver. @@ -1659,7 +1875,7 @@ class Model : public AbstractModel, public ModelDimensions { * @param x_solver State variables with conservation laws applied * @param x_rdata State variables with conservation laws expanded */ - virtual void fx_solver(realtype *x_solver, const realtype *x_rdata); + virtual void fx_solver(realtype* x_solver, realtype const* x_rdata); /** * @brief Compute fsx_solver. @@ -1671,7 +1887,7 @@ class Model : public AbstractModel, public ModelDimensions { * @param sx_solver State sensitivity variables with conservation laws * applied */ - virtual void fsx_solver(realtype *sx_solver, const realtype *sx_rdata); + virtual void fsx_solver(realtype* sx_solver, realtype const* sx_rdata); /** * @brief Compute ftotal_cl. @@ -1680,8 +1896,13 @@ class Model : public AbstractModel, public ModelDimensions { * * @param total_cl Total abundances of conservation laws * @param x_rdata State variables with conservation laws expanded + * @param p parameter vector + * @param k constant vector */ - virtual void ftotal_cl(realtype *total_cl, const realtype *x_rdata); + virtual void ftotal_cl( + realtype* total_cl, realtype const* x_rdata, realtype const* p, + realtype const* k + ); /** * @brief Compute fstotal_cl @@ -1693,9 +1914,16 @@ class Model : public AbstractModel, public ModelDimensions { * @param sx_rdata State sensitivity variables with conservation laws * expanded * @param ip Sensitivity index + * @param x_rdata State variables with conservation laws expanded + * @param p parameter vector + * @param k constant vector + * @param tcl Total abundances for conservation laws */ - virtual void fstotal_cl(realtype *stotal_cl, const realtype *sx_rdata, - int ip); + virtual void fstotal_cl( + realtype* stotal_cl, realtype const* sx_rdata, int const ip, + realtype const* x_rdata, realtype const* p, realtype const* k, + realtype const* tcl + ); /** * @brief Compute non-negative state vector. @@ -1712,6 +1940,21 @@ class Model : public AbstractModel, public ModelDimensions { */ const_N_Vector computeX_pos(const_N_Vector x); + /** + * @brief Compute non-negative state vector. + * + * Compute non-negative state vector according to stateIsNonNegative. + * If anyStateNonNegative is set to `false`, i.e., all entries in + * stateIsNonNegative are `false`, this function directly returns `x`, + * otherwise all entries of x are copied in to `amici::Model::x_pos_tmp_` + * and negative values are replaced by `0` where applicable. + * + * @param x State vector possibly containing negative values + * @return State vector with negative values replaced by `0` according to + * stateIsNonNegative + */ + realtype const* computeX_pos(AmiVector const& x); + /** All variables necessary for function evaluation */ ModelState state_; @@ -1720,6 +1963,9 @@ class Model : public AbstractModel, public ModelDimensions { */ ModelStateDerived derived_state_; + /** Storage for splines of the model */ + std::vector splines_; + /** index indicating to which event an event output belongs */ std::vector z2event_; @@ -1733,29 +1979,38 @@ class Model : public AbstractModel, public ModelDimensions { * be positive */ std::vector state_is_non_negative_; + /** Vector of booleans indicating the initial boolean value for every event + * trigger function. Events at t0 can only trigger if the initial value is + * set to `false`. Must be specified during model compilation by setting the + * `initialValue` attribute of an event trigger. */ + std::vector root_initial_values_; + /** boolean indicating whether any entry in stateIsNonNegative is `true` */ - bool any_state_non_negative_ {false}; + bool any_state_non_negative_{false}; /** maximal number of events to track */ - int nmaxevent_ {10}; + int nmaxevent_{10}; - /** - * flag indicating whether steadystate sensitivities are to be computed - * via FSA when steadyStateSimulation is used - */ - SteadyStateSensitivityMode steadystate_sensitivity_mode_ {SteadyStateSensitivityMode::newtonOnly}; + /** method for steady-state computation */ + SteadyStateComputationMode steadystate_computation_mode_{ + SteadyStateComputationMode::integrateIfNewtonFails}; + + /** method for steadystate sensitivities computation */ + SteadyStateSensitivityMode steadystate_sensitivity_mode_{ + SteadyStateSensitivityMode::integrateIfNewtonFails}; /** * Indicates whether the result of every call to `Model::f*` should be * checked for finiteness */ - bool always_check_finite_ {false}; + bool always_check_finite_{false}; - /** indicates whether sigma residuals are to be added for every datapoint */ - bool sigma_res_ {false}; + /** indicates whether sigma residuals are to be added for every datapoint */ + bool sigma_res_{false}; - /** offset to ensure positivity of sigma residuals, only has an effect when `sigma_res_` is `true` */ - realtype min_sigma_ {50.0}; + /** offset to ensure positivity of sigma residuals, only has an effect when + * `sigma_res_` is `true` */ + realtype min_sigma_{50.0}; private: /** Sparse dwdp implicit temporary storage (shape `ndwdp`) */ @@ -1768,14 +2023,14 @@ class Model : public AbstractModel, public ModelDimensions { mutable std::vector dwdx_hierarchical_; /** Recursion */ - int w_recursion_depth_ {0}; + int w_recursion_depth_{0}; /** Simulation parameters, initial state, etc. */ SimulationParameters simulation_parameters_; }; -bool operator==(const Model &a, const Model &b); -bool operator==(const ModelDimensions &a, const ModelDimensions &b); +bool operator==(Model const& a, Model const& b); +bool operator==(ModelDimensions const& a, ModelDimensions const& b); } // namespace amici diff --git a/deps/AMICI/include/amici/model_dae.h b/deps/AMICI/include/amici/model_dae.h index 9c10738bb..dd16e7466 100644 --- a/deps/AMICI/include/amici/model_dae.h +++ b/deps/AMICI/include/amici/model_dae.h @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -38,21 +39,35 @@ class Model_DAE : public Model { * @param z2event mapping of event outputs to events * @param pythonGenerated flag indicating matlab or python wrapping * @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 */ - Model_DAE(const ModelDimensions &model_dimensions, - SimulationParameters simulation_parameters, - const SecondOrderMode o2mode, - std::vector const &idlist, - std::vector const &z2event, const bool pythonGenerated=false, - const int ndxdotdp_explicit=0) - : Model(model_dimensions, simulation_parameters, - o2mode, idlist, z2event, pythonGenerated, - ndxdotdp_explicit) { + Model_DAE( + ModelDimensions const& model_dimensions, + SimulationParameters simulation_parameters, + const SecondOrderMode 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 + ) + : Model( + model_dimensions, simulation_parameters, o2mode, idlist, z2event, + pythonGenerated, ndxdotdp_explicit, ndxdotdx_explicit, + w_recursion_depth + ) { derived_state_.M_ = SUNMatrixWrapper(nx_solver, nx_solver); + auto M_nnz = static_cast( + std::reduce(idlist.begin(), idlist.end()) + ); + derived_state_.MSparse_ + = SUNMatrixWrapper(nx_solver, nx_solver, M_nnz, CSC_MAT); + derived_state_.dfdx_ + = SUNMatrixWrapper(nx_solver, nx_solver, 0, CSC_MAT); } - void fJ(realtype t, realtype cj, const AmiVector &x, const AmiVector &dx, - const AmiVector &xdot, SUNMatrix J) override; + void + fJ(realtype t, realtype cj, AmiVector const& x, AmiVector const& dx, + AmiVector const& xdot, SUNMatrix J) override; /** * @brief Jacobian of xdot with respect to states x @@ -63,12 +78,14 @@ class Model_DAE : public Model { * @param xdot Vector with the right hand side * @param J Matrix to which the Jacobian will be written **/ - void fJ(realtype t, realtype cj, const_N_Vector x, const_N_Vector dx, - const_N_Vector xdot, SUNMatrix J); + void + fJ(realtype t, realtype cj, const_N_Vector x, const_N_Vector dx, + const_N_Vector xdot, SUNMatrix J); - void fJB(const realtype t, realtype cj, const AmiVector &x, - const AmiVector &dx, const AmiVector &xB, const AmiVector &dxB, - const AmiVector &xBdot, SUNMatrix JB) override; + void + fJB(const realtype t, realtype cj, AmiVector const& x, AmiVector const& dx, + AmiVector const& xB, AmiVector const& dxB, AmiVector const& xBdot, + SUNMatrix JB) override; /** * @brief Jacobian of xBdot with respect to adjoint state xB @@ -80,12 +97,14 @@ class Model_DAE : public Model { * @param dxB Vector with the adjoint derivative states * @param JB Matrix to which the Jacobian will be written **/ - void fJB(realtype t, realtype cj, const_N_Vector x, const_N_Vector dx, - const_N_Vector xB, const_N_Vector dxB, SUNMatrix JB); + void + fJB(realtype t, realtype cj, const_N_Vector x, const_N_Vector dx, + const_N_Vector xB, const_N_Vector dxB, SUNMatrix JB); - void fJSparse(realtype t, realtype cj, const AmiVector &x, - const AmiVector &dx, const AmiVector &xdot, - SUNMatrix J) override; + void fJSparse( + realtype t, realtype cj, AmiVector const& x, AmiVector const& dx, + AmiVector const& xdot, SUNMatrix J + ) override; /** * @brief J in sparse form (for sparse solvers from the SuiteSparse Package) @@ -95,16 +114,20 @@ class Model_DAE : public Model { * @param dx Vector with the derivative states * @param J Matrix to which the Jacobian will be written */ - void fJSparse(realtype t, realtype cj, const_N_Vector x, const_N_Vector dx, - SUNMatrix J); + void fJSparse( + realtype t, realtype cj, const_N_Vector x, const_N_Vector dx, + SUNMatrix J + ); - void fJSparseB(const realtype t, realtype cj, const AmiVector &x, - const AmiVector &dx, const AmiVector &xB, - const AmiVector &dxB, const AmiVector &xBdot, - SUNMatrix JB) override; + void fJSparseB( + const realtype t, realtype cj, AmiVector const& x, AmiVector const& dx, + AmiVector const& xB, AmiVector const& dxB, AmiVector const& xBdot, + SUNMatrix JB + ) override; /** - * @brief JB in sparse form (for sparse solvers from the SuiteSparse Package) + * @brief JB in sparse form (for sparse solvers from the SuiteSparse + * Package) * @param t timepoint * @param cj scalar in Jacobian * @param x Vector with the states @@ -113,8 +136,10 @@ class Model_DAE : public Model { * @param dxB Vector with the adjoint derivative states * @param JB Matrix to which the Jacobian will be written */ - void fJSparseB(realtype t, realtype cj, const_N_Vector x, const_N_Vector dx, - const_N_Vector xB, const_N_Vector dxB, SUNMatrix JB); + void fJSparseB( + realtype t, realtype cj, const_N_Vector x, const_N_Vector dx, + const_N_Vector xB, const_N_Vector dxB, SUNMatrix JB + ); /** * @brief Diagonal of the Jacobian (for preconditioning) @@ -125,12 +150,15 @@ class Model_DAE : public Model { * @param dx Vector with the derivative states **/ - void fJDiag(realtype t, AmiVector &JDiag, realtype cj, const AmiVector &x, - const AmiVector &dx) override; + void fJDiag( + realtype t, AmiVector& JDiag, realtype cj, AmiVector const& x, + AmiVector const& dx + ) override; - void fJv(realtype t, const AmiVector &x, const AmiVector &dx, - const AmiVector &xdot, const AmiVector &v, AmiVector &nJv, - realtype cj) override; + void + fJv(realtype t, AmiVector const& x, AmiVector const& dx, + AmiVector const& xdot, AmiVector const& v, AmiVector& nJv, + realtype cj) override; /** * @brief Matrix vector product of J with a vector v (for iterative solvers) @@ -142,11 +170,13 @@ class Model_DAE : public Model { * @param Jv Vector to which the Jacobian vector product will be * written **/ - void fJv(realtype t, const_N_Vector x, const_N_Vector dx, const_N_Vector v, - N_Vector Jv, realtype cj); + void + fJv(realtype t, const_N_Vector x, const_N_Vector dx, const_N_Vector v, + N_Vector Jv, realtype cj); /** - * @brief Matrix vector product of JB with a vector v (for iterative solvers) + * @brief Matrix vector product of JB with a vector v (for iterative + *solvers) * @param t timepoint * @param x Vector with the states * @param dx Vector with the derivative states @@ -157,12 +187,15 @@ class Model_DAE : public Model { * @param cj scalar in Jacobian (inverse stepsize) **/ - void fJvB(realtype t, const_N_Vector x, const_N_Vector dx, - const_N_Vector xB, const_N_Vector dxB, - const_N_Vector vB, N_Vector JvB, realtype cj); + void fJvB( + realtype t, const_N_Vector x, const_N_Vector dx, const_N_Vector xB, + const_N_Vector dxB, const_N_Vector vB, N_Vector JvB, realtype cj + ); - void froot(realtype t, const AmiVector &x, const AmiVector &dx, - gsl::span root) override; + void froot( + realtype t, AmiVector const& x, AmiVector const& dx, + gsl::span root + ) override; /** * @brief Event trigger function for events @@ -171,10 +204,14 @@ class Model_DAE : public Model { * @param dx Vector with the derivative states * @param root array with root function values */ - void froot(realtype t, const_N_Vector x, const_N_Vector dx, gsl::span root); + void froot( + realtype t, const_N_Vector x, const_N_Vector dx, + gsl::span root + ); - void fxdot(realtype t, const AmiVector &x, const AmiVector &dx, - AmiVector &xdot) override; + void fxdot( + realtype t, AmiVector const& x, AmiVector const& dx, AmiVector& xdot + ) override; /** * @brief Residual function of the DAE @@ -194,8 +231,10 @@ class Model_DAE : public Model { * @param dxB Vector with the adjoint derivative states * @param xBdot Vector with the adjoint right hand side */ - void fxBdot(realtype t, const_N_Vector x, const_N_Vector dx, - const_N_Vector xB, const_N_Vector dxB, N_Vector xBdot); + void fxBdot( + realtype t, const_N_Vector x, const_N_Vector dx, const_N_Vector xB, + const_N_Vector dxB, N_Vector xBdot + ); /** * @brief Right hand side of integral equation for quadrature states qB @@ -206,22 +245,27 @@ class Model_DAE : public Model { * @param dxB Vector with the adjoint derivative states * @param qBdot Vector with the adjoint quadrature right hand side */ - void fqBdot(realtype t, const_N_Vector x, const_N_Vector dx, - const_N_Vector xB, const_N_Vector dxB, - N_Vector qBdot); + void fqBdot( + realtype t, const_N_Vector x, const_N_Vector dx, const_N_Vector xB, + const_N_Vector dxB, N_Vector qBdot + ); - void fxBdot_ss(const realtype t, const AmiVector &xB, - const AmiVector &dxB, AmiVector &xBdot) override; + void fxBdot_ss( + const realtype t, AmiVector const& xB, AmiVector const& dxB, + AmiVector& xBdot + ) override; /** - * @brief Implementation of fxBdot for steady state case at the N_Vector level + * @brief Implementation of fxBdot for steady state case at the N_Vector + * level * @param t timepoint * @param xB Vector with the adjoint state * @param dxB Vector with the adjoint derivative states * @param xBdot Vector with the adjoint right hand side */ - void fxBdot_ss(realtype t, const_N_Vector xB, const_N_Vector dxB, - N_Vector xBdot) const; + void fxBdot_ss( + realtype t, const_N_Vector xB, const_N_Vector dxB, N_Vector xBdot + ) const; /** * @brief Implementation of fqBdot for steady state at the N_Vector level @@ -230,8 +274,9 @@ class Model_DAE : public Model { * @param dxB Vector with the adjoint derivative states * @param qBdot Vector with the adjoint quadrature right hand side */ - void fqBdot_ss(realtype t, const_N_Vector xB, const_N_Vector dxB, - N_Vector qBdot) const; + void fqBdot_ss( + realtype t, const_N_Vector xB, const_N_Vector dxB, N_Vector qBdot + ) const; /** * @brief Sparse Jacobian function backward, steady state case @@ -250,10 +295,10 @@ class Model_DAE : public Model { * @param dxB Vector with the adjoint derivative states * @param xBdot Vector with the adjoint state right hand side */ - void writeSteadystateJB(const realtype t, realtype cj, - const AmiVector &x, const AmiVector &dx, - const AmiVector &xB, const AmiVector &dxB, - const AmiVector &xBdot) override; + void writeSteadystateJB( + const realtype t, realtype cj, AmiVector const& x, AmiVector const& dx, + AmiVector const& xB, AmiVector const& dxB, AmiVector const& xBdot + ) override; /** * @brief Sensitivity of dx/dt wrt model parameters p @@ -262,16 +307,18 @@ class Model_DAE : public Model { * @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, const AmiVector &x, - const AmiVector &dx) override { + void fdxdotdp(const realtype t, AmiVector const& x, AmiVector const& dx) + override { fdxdotdp(t, x.getNVector(), dx.getNVector()); }; - void fsxdot(realtype t, const AmiVector &x, const AmiVector &dx, int ip, - const AmiVector &sx, const AmiVector &sdx, - AmiVector &sxdot) override; + void fsxdot( + realtype t, AmiVector const& x, AmiVector const& dx, int ip, + AmiVector const& sx, AmiVector const& sdx, AmiVector& sxdot + ) override; /** - * @brief Right hand side of differential equation for state sensitivities sx + * @brief Right hand side of differential equation for state sensitivities + * sx * @param t timepoint * @param x Vector with the states * @param dx Vector with the derivative states @@ -280,8 +327,10 @@ class Model_DAE : public Model { * @param sdx Vector with the derivative state sensitivities * @param sxdot Vector with the sensitivity right hand side */ - void fsxdot(realtype t, const_N_Vector x, const_N_Vector dx, int ip, - const_N_Vector sx, const_N_Vector sdx, N_Vector sxdot); + void fsxdot( + realtype t, const_N_Vector x, const_N_Vector dx, int ip, + const_N_Vector sx, const_N_Vector sdx, N_Vector sxdot + ); /** * @brief Mass matrix for DAE systems @@ -306,10 +355,11 @@ class Model_DAE : public Model { * @param w vector with helper variables * @param dwdx derivative of w wrt x **/ - virtual void fJSparse(SUNMatrixContent_Sparse JSparse, realtype t, - const realtype *x, const double *p, const double *k, - const realtype *h, realtype cj, const realtype *dx, - const realtype *w, const realtype *dwdx) = 0; + virtual void fJSparse( + SUNMatrixContent_Sparse JSparse, realtype t, realtype const* x, + double const* p, double const* k, realtype const* h, realtype cj, + realtype const* dx, realtype const* w, realtype const* dwdx + ); /** * @brief Model specific implementation for froot @@ -321,9 +371,10 @@ class Model_DAE : public Model { * @param h Heaviside vector * @param dx Vector with the derivative states **/ - virtual void froot(realtype *root, realtype t, const realtype *x, - const double *p, const double *k, const realtype *h, - const realtype *dx); + virtual void froot( + realtype* root, realtype t, realtype const* x, double const* p, + double const* k, realtype const* h, realtype const* dx + ); /** * @brief Model specific implementation for fxdot @@ -336,9 +387,11 @@ class Model_DAE : public Model { * @param w vector with helper variables * @param dx Vector with the derivative states **/ - virtual void fxdot(realtype *xdot, realtype t, const realtype *x, - const double *p, const double *k, const realtype *h, - const realtype *dx, const realtype *w) = 0; + virtual void fxdot( + realtype* xdot, realtype t, realtype const* x, double const* p, + double const* k, realtype const* h, realtype const* dx, + realtype const* w + ) = 0; /** * @brief Model specific implementation of fdxdotdp @@ -353,10 +406,114 @@ class Model_DAE : public Model { * @param w vector with helper variables * @param dwdp derivative of w wrt p */ - virtual void fdxdotdp(realtype *dxdotdp, realtype t, const realtype *x, - const realtype *p, const realtype *k, - const realtype *h, int ip, const realtype *dx, - const realtype *w, const realtype *dwdp); + virtual void fdxdotdp( + realtype* dxdotdp, realtype t, realtype const* x, realtype const* p, + realtype const* k, realtype const* h, int ip, realtype const* dx, + realtype const* w, realtype const* dwdp + ); + + /** + * @brief Model specific implementation of fdxdotdp_explicit, no w chainrule + * (Py) + * + * @param dxdotdp_explicit partial derivative xdot wrt p + * @param t timepoint + * @param x Vector with the states + * @param p parameter vector + * @param k constants vector + * @param h Heaviside vector + * @param dx Vector with the derivative states + * @param w vector with helper variables + */ + virtual void fdxdotdp_explicit( + realtype* dxdotdp_explicit, realtype t, realtype const* x, + realtype const* p, realtype const* k, realtype const* h, + realtype const* dx, realtype const* w + ); + + /** + * @brief Model specific implementation of fdxdotdp_explicit, colptrs part + * + * @param dxdotdp sparse matrix to which colptrs will be written + */ + virtual void fdxdotdp_explicit_colptrs(SUNMatrixWrapper& dxdotdp); + + /** + * @brief Model specific implementation of fdxdotdp_explicit, rowvals part + * + * @param dxdotdp sparse matrix to which rowvals will be written + */ + virtual void fdxdotdp_explicit_rowvals(SUNMatrixWrapper& dxdotdp); + + /** + * @brief Model specific implementation of fdxdotdx_explicit, no w chainrule + * (Py) + * + * @param dxdotdx_explicit partial derivative xdot wrt x + * @param t timepoint + * @param x Vector with the states + * @param p parameter vector + * @param k constants vector + * @param h heavyside vector + * @param dx Vector with the derivative states + * @param w vector with helper variables + */ + virtual void fdxdotdx_explicit( + realtype* dxdotdx_explicit, realtype t, realtype const* x, + realtype const* p, realtype const* k, realtype const* h, + realtype const* dx, realtype const* w + ); + + /** + * @brief Model specific implementation of fdxdotdx_explicit, colptrs part + * + * @param dxdotdx sparse matrix to which colptrs will be written + */ + virtual void fdxdotdx_explicit_colptrs(SUNMatrixWrapper& dxdotdx); + + /** + * @brief Model specific implementation of fdxdotdx_explicit, rowvals part + * + * @param dxdotdx sparse matrix to which rowvals will be written + */ + virtual void fdxdotdx_explicit_rowvals(SUNMatrixWrapper& dxdotdx); + + /** + * @brief Model specific implementation of fdxdotdw, data part + * @param dxdotdw partial derivative xdot wrt w + * @param t timepoint + * @param x Vector with the states + * @param p parameter vector + * @param k constants vector + * @param h Heaviside vector + * @param dx Vector with the derivative states + * @param w vector with helper variables + */ + virtual void fdxdotdw( + realtype* dxdotdw, realtype t, realtype const* x, realtype const* p, + realtype const* k, realtype const* h, realtype const* dx, + realtype const* w + ); + + /** + * @brief Model specific implementation of fdxdotdw, colptrs part + * @param dxdotdw sparse matrix to which colptrs will be written + */ + virtual void fdxdotdw_colptrs(SUNMatrixWrapper& dxdotdw); + + /** + * @brief Model specific implementation of fdxdotdw, rowvals part + * @param dxdotdw sparse matrix to which rowvals will be written + */ + virtual void fdxdotdw_rowvals(SUNMatrixWrapper& dxdotdw); + + /** + * @brief Sensitivity of dx/dt wrt model parameters w + * @param t timepoint + * @param x Vector with the states + * @param dx Vector with the derivative states + */ + void fdxdotdw(realtype t, const_N_Vector x, const_N_Vector dx); /** * @brief Model specific implementation of fM @@ -366,8 +523,9 @@ class Model_DAE : public Model { * @param p parameter vector * @param k constants vector */ - virtual void fM(realtype *M, const realtype t, const realtype *x, - const realtype *p, const realtype *k); + virtual void + fM(realtype* M, const realtype 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 ca7f0f1af..f0679dbe3 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 nspl Number of splines * @param nJ Number of objective functions * @param nw Number of repeating elements * @param ndwdx Number of nonzero elements in the `x` derivative of the @@ -39,27 +40,56 @@ struct ModelDimensions { * repeating elements * @param ndwdw Number of nonzero elements in the `w` derivative of the * repeating elements - * @param ndxdotdw Number of nonzero elements in the \f$ w\f$ derivative of \f$ xdot\f$ - * @param ndJydy Number of nonzero elements in the \f$ y\f$ derivative of \f$ dJy\f$ (shape `nytrue`) + * @param ndxdotdw Number of nonzero elements in the \f$ w\f$ derivative of + * \f$ xdot\f$ + * @param ndJydy Number of nonzero elements in the \f$ y\f$ derivative of + * \f$ dJy\f$ (shape `nytrue`) + * @param ndxrdatadxsolver Number of nonzero elements in the \f$ x\f$ + * derivative of \f$ x_rdata\f$ + * @param ndxrdatadtcl Number of nonzero elements in the \f$ tcl\f$ + * derivative of \f$ x_rdata\f$ + * @param ndtotal_cldx_rdata Number of nonzero elements in the + * \f$ x_rdata \f$ derivative of \f$ total_cl \f$ * @param nnz Number of nonzero elements in Jacobian * @param ubw Upper matrix bandwidth in the Jacobian * @param lbw Lower matrix bandwidth in the Jacobian */ ModelDimensions( - const int nx_rdata, const int nxtrue_rdata, const int nx_solver, - const int nxtrue_solver, const int nx_solver_reinit, const int np, - const int nk, const int ny, - const int nytrue, const int nz, const int nztrue, const int ne, - const int nJ, const int nw, const int ndwdx, const int ndwdp, - const int ndwdw, const int ndxdotdw, std::vector ndJydy, - const int nnz, const int ubw, const int lbw) - : nx_rdata(nx_rdata), nxtrue_rdata(nxtrue_rdata), nx_solver(nx_solver), - nxtrue_solver(nxtrue_solver), nx_solver_reinit(nx_solver_reinit), - np(np), nk(nk), - ny(ny), nytrue(nytrue), nz(nz), nztrue(nztrue), - ne(ne), nw(nw), ndwdx(ndwdx), ndwdp(ndwdp), ndwdw(ndwdw), - ndxdotdw(ndxdotdw), ndJydy(std::move(ndJydy)), - nnz(nnz), nJ(nJ), ubw(ubw), lbw(lbw) { + 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 + ) + : nx_rdata(nx_rdata) + , nxtrue_rdata(nxtrue_rdata) + , nx_solver(nx_solver) + , nxtrue_solver(nxtrue_solver) + , nx_solver_reinit(nx_solver_reinit) + , np(np) + , nk(nk) + , ny(ny) + , nytrue(nytrue) + , nz(nz) + , nztrue(nztrue) + , ne(ne) + , nspl(nspl) + , nw(nw) + , ndwdx(ndwdx) + , ndwdp(ndwdp) + , ndwdw(ndwdw) + , ndxdotdw(ndxdotdw) + , ndJydy(std::move(ndJydy)) + , ndxrdatadxsolver(ndxrdatadxsolver) + , ndxrdatadtcl(ndxrdatadtcl) + , ndtotal_cldx_rdata(ndtotal_cldx_rdata) + , nnz(nnz) + , nJ(nJ) + , ubw(ubw) + , lbw(lbw) { Expects(nxtrue_rdata >= 0); Expects(nxtrue_rdata <= nx_rdata); Expects(nxtrue_solver >= 0); @@ -74,6 +104,7 @@ struct ModelDimensions { Expects(nztrue >= 0); Expects(nztrue <= nz); Expects(ne >= 0); + Expects(nspl >= 0); Expects(nw >= 0); Expects(ndwdx >= 0); Expects(ndwdx <= nw * nx_solver); @@ -82,6 +113,12 @@ struct ModelDimensions { Expects(ndwdw >= 0); Expects(ndwdw <= nw * nw); Expects(ndxdotdw >= 0); + Expects(ndxrdatadxsolver >= 0); + Expects(ndxrdatadxsolver <= nx_rdata * nx_solver); + Expects(ndxrdatadtcl >= 0); + Expects(ndxrdatadtcl <= nx_rdata * (nx_rdata - nx_solver)); + Expects(ndtotal_cldx_rdata >= 0); + Expects(ndtotal_cldx_rdata <= (nx_rdata - nx_solver) * nx_rdata); Expects(nnz >= 0); Expects(nJ >= 0); Expects(ubw >= 0); @@ -127,6 +164,9 @@ struct ModelDimensions { /** Number of events */ int ne{0}; + /** numer of spline functions in the model */ + int nspl{0}; + /** Number of common expressions */ int nw{0}; @@ -134,22 +174,23 @@ struct ModelDimensions { * Number of nonzero elements in the `x` derivative of the * repeating elements */ - int ndwdx {0}; + int ndwdx{0}; /** * Number of nonzero elements in the `p` derivative of the * repeating elements */ - int ndwdp {0}; + int ndwdp{0}; /** * Number of nonzero elements in the `w` derivative of the * repeating elements */ - int ndwdw {0}; + int ndwdw{0}; - /** Number of nonzero elements in the \f$ w \f$ derivative of \f$ xdot \f$ */ - int ndxdotdw {0}; + /** Number of nonzero elements in the \f$ w \f$ derivative of \f$ xdot \f$ + */ + int ndxdotdw{0}; /** * Number of nonzero elements in the \f$ y \f$ derivative of @@ -157,6 +198,18 @@ struct ModelDimensions { */ std::vector ndJydy; + /** Number of nonzero elements in the \f$ x \f$ derivative of \f$ x_rdata + * \f$ */ + int ndxrdatadxsolver{0}; + + /** Number of nonzero elements in the \f$ tcl\f$ derivative of \f$ x_rdata + * \f$ */ + int ndxrdatadtcl{0}; + + /** Number of nonzero elements in the \f$ x_rdata\f$ derivative of + * \f$ total_cl \f$ */ + int ndtotal_cldx_rdata{0}; + /** Number of nonzero entries in Jacobian */ int nnz{0}; diff --git a/deps/AMICI/include/amici/model_ode.h b/deps/AMICI/include/amici/model_ode.h index 4a11ab410..91e0c9cd4 100644 --- a/deps/AMICI/include/amici/model_ode.h +++ b/deps/AMICI/include/amici/model_ode.h @@ -41,19 +41,23 @@ class Model_ODE : public Model { * @param ndxdotdx_explicit number of nonzero elements dxdotdx_explicit * @param w_recursion_depth Recursion depth of fw */ - Model_ODE(ModelDimensions const& model_dimensions, - SimulationParameters simulation_parameters, - const SecondOrderMode o2mode, - std::vector const &idlist, - std::vector const &z2event, const bool pythonGenerated=false, - const int ndxdotdp_explicit=0, const int ndxdotdx_explicit=0, - const int w_recursion_depth=0) - : Model(model_dimensions, simulation_parameters, - o2mode, idlist, z2event, pythonGenerated, - ndxdotdp_explicit, ndxdotdx_explicit, w_recursion_depth) {} - - void fJ(realtype t, realtype cj, const AmiVector &x, const AmiVector &dx, - const AmiVector &xdot, SUNMatrix J) override; + Model_ODE( + ModelDimensions const& model_dimensions, + SimulationParameters simulation_parameters, + const SecondOrderMode 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 + ) + : Model( + model_dimensions, simulation_parameters, o2mode, idlist, z2event, + pythonGenerated, ndxdotdp_explicit, ndxdotdx_explicit, + w_recursion_depth + ) {} + + void + fJ(realtype t, realtype cj, AmiVector const& x, AmiVector const& dx, + AmiVector const& xdot, SUNMatrix J) override; /** * @brief Implementation of fJ at the N_Vector level @@ -68,25 +72,29 @@ 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, const AmiVector &x, - const AmiVector &dx, const AmiVector &xB, const AmiVector &dxB, - const AmiVector &xBdot, SUNMatrix JB) override; + void + fJB(const realtype t, realtype cj, AmiVector const& x, AmiVector const& dx, + AmiVector const& xB, AmiVector const& dxB, AmiVector const& xBdot, + SUNMatrix JB) override; /** - * @brief Implementation of fJB at the N_Vector level, this function provides - * an interface to the model specific routines for the solver implementation + * @brief Implementation of fJB at the N_Vector level, this function + *provides an interface to the model specific routines for the solver + *implementation * @param t timepoint * @param x Vector with the states * @param xB Vector with the adjoint states * @param xBdot Vector with the adjoint right hand side * @param JB Matrix to which the Jacobian will be written **/ - void fJB(realtype t, const_N_Vector x, const_N_Vector xB, - const_N_Vector xBdot, SUNMatrix JB); + void + fJB(realtype t, const_N_Vector x, const_N_Vector xB, const_N_Vector xBdot, + SUNMatrix JB); - void fJSparse(realtype t, realtype cj, const AmiVector &x, - const AmiVector &dx, const AmiVector &xdot, - SUNMatrix J) override; + void fJSparse( + realtype t, realtype cj, AmiVector const& x, AmiVector const& dx, + AmiVector const& xdot, SUNMatrix J + ) override; /** * @brief Implementation of fJSparse at the N_Vector level, this function @@ -98,10 +106,11 @@ class Model_ODE : public Model { */ void fJSparse(realtype t, const_N_Vector x, SUNMatrix J); - void fJSparseB(const realtype t, realtype cj, const AmiVector &x, - const AmiVector &dx, const AmiVector &xB, - const AmiVector &dxB, const AmiVector &xBdot, - SUNMatrix JB) override; + void fJSparseB( + const realtype t, realtype cj, AmiVector const& x, AmiVector const& dx, + AmiVector const& xB, AmiVector const& dxB, AmiVector const& xBdot, + SUNMatrix JB + ) override; /** * @brief Implementation of fJSparseB at the N_Vector level, this function @@ -113,12 +122,15 @@ class Model_ODE : public Model { * @param xBdot Vector with the adjoint right hand side * @param JB Matrix to which the Jacobian will be written */ - void fJSparseB(realtype t, const_N_Vector x, const_N_Vector xB, - const_N_Vector xBdot, SUNMatrix JB); + void fJSparseB( + realtype t, const_N_Vector x, const_N_Vector xB, const_N_Vector xBdot, + SUNMatrix JB + ); /** - * @brief Implementation of fJDiag at the N_Vector level, this function provides - * an interface to the model specific routines for the solver implementation + * @brief Implementation of fJDiag at the N_Vector level, this function + *provides an interface to the model specific routines for the solver + *implementation * @param t timepoint * @param JDiag Vector to which the Jacobian diagonal will be written * @param x Vector with the states @@ -133,12 +145,15 @@ class Model_ODE : public Model { * @param x Vector with the states * @param dx Vector with the derivative states **/ - void fJDiag(realtype t, AmiVector &JDiag, realtype cj, const AmiVector &x, - const AmiVector &dx) override; + void fJDiag( + realtype t, AmiVector& JDiag, realtype cj, AmiVector const& x, + AmiVector const& dx + ) override; - void fJv(realtype t, const AmiVector &x, const AmiVector &dx, - const AmiVector &xdot, const AmiVector &v, AmiVector &nJv, - realtype cj) override; + void + fJv(realtype t, AmiVector const& x, AmiVector const& dx, + AmiVector const& xdot, AmiVector const& v, AmiVector& nJv, + realtype cj) override; /** * @brief Implementation of fJv at the N_Vector level. @@ -158,11 +173,15 @@ class Model_ODE : public Model { * @param vB Vector with which the Jacobian is multiplied * @param JvB Vector to which the Jacobian vector product will be written **/ - void fJvB(const_N_Vector vB, N_Vector JvB, realtype t, const_N_Vector x, - const_N_Vector xB); + void fJvB( + const_N_Vector vB, N_Vector JvB, realtype t, const_N_Vector x, + const_N_Vector xB + ); - void froot(realtype t, const AmiVector &x, const AmiVector &dx, - gsl::span root) override; + void froot( + realtype t, AmiVector const& x, AmiVector const& dx, + gsl::span root + ) override; /** * @brief Implementation of froot at the N_Vector level @@ -174,8 +193,9 @@ class Model_ODE : public Model { */ void froot(realtype t, const_N_Vector x, gsl::span root); - void fxdot(realtype t, const AmiVector &x, const AmiVector &dx, - AmiVector &xdot) override; + void fxdot( + realtype t, AmiVector const& x, AmiVector const& dx, AmiVector& xdot + ) override; /** * @brief Implementation of fxdot at the N_Vector level, this function @@ -203,10 +223,13 @@ class Model_ODE : public Model { * @param xB Vector with the adjoint states * @param qBdot Vector with the adjoint quadrature right hand side */ - void fqBdot(realtype t, const_N_Vector x, const_N_Vector xB, N_Vector qBdot); + void + fqBdot(realtype t, const_N_Vector x, const_N_Vector xB, N_Vector qBdot); - void fxBdot_ss(const realtype t, const AmiVector &xB, - const AmiVector & /*dxB*/, AmiVector &xBdot) override; + void fxBdot_ss( + const realtype t, AmiVector const& xB, AmiVector const& /*dxB*/, + AmiVector& xBdot + ) override; /** * @brief Implementation of fxBdot for steady state at the N_Vector level @@ -217,7 +240,8 @@ class Model_ODE : public Model { void fxBdot_ss(realtype t, const_N_Vector xB, N_Vector xBdot) const; /** - * @brief Implementation of fqBdot for steady state case at the N_Vector level + * @brief Implementation of fqBdot for steady state case at the N_Vector + * level * @param t timepoint * @param xB Vector with the adjoint states * @param qBdot Vector with the adjoint quadrature right hand side @@ -241,14 +265,15 @@ class Model_ODE : public Model { * @param dxB Vector with the adjoint derivative states * @param xBdot Vector with the adjoint state right hand side */ - void writeSteadystateJB(const realtype t, realtype cj, - const AmiVector &x, const AmiVector &dx, - const AmiVector &xB, const AmiVector &dxB, - const AmiVector &xBdot) override; + void writeSteadystateJB( + const realtype t, realtype cj, AmiVector const& x, AmiVector const& dx, + AmiVector const& xB, AmiVector const& dxB, AmiVector const& xBdot + ) override; - void fsxdot(realtype t, const AmiVector &x, const AmiVector &dx, int ip, - const AmiVector &sx, const AmiVector &sdx, - AmiVector &sxdot) override; + void fsxdot( + realtype t, AmiVector const& x, AmiVector const& dx, int ip, + AmiVector const& sx, AmiVector const& sdx, AmiVector& sxdot + ) override; /** * @brief Implementation of fsxdot at the N_Vector level @@ -258,13 +283,13 @@ class Model_ODE : public Model { * @param sx Vector with the state sensitivities * @param sxdot Vector with the sensitivity right hand side */ - void fsxdot(realtype t, const_N_Vector x, int ip, const_N_Vector sx, - N_Vector sxdot); + void fsxdot( + realtype t, const_N_Vector x, int ip, const_N_Vector sx, N_Vector sxdot + ); std::unique_ptr getSolver() override; protected: - /** * @brief Model specific implementation for fJSparse (Matlab) * @param JSparse Matrix to which the Jacobian will be written @@ -276,10 +301,11 @@ class Model_ODE : public Model { * @param w vector with helper variables * @param dwdx derivative of w wrt x **/ - virtual void fJSparse(SUNMatrixContent_Sparse JSparse, realtype t, - const realtype *x, const realtype *p, - const realtype *k, const realtype *h, - const realtype *w, const realtype *dwdx); + virtual void fJSparse( + SUNMatrixContent_Sparse JSparse, realtype t, realtype const* x, + realtype const* p, realtype const* k, realtype const* h, + realtype const* w, realtype const* dwdx + ); /** * @brief Model specific implementation for fJSparse, data only (Py) @@ -292,22 +318,23 @@ class Model_ODE : public Model { * @param w vector with helper variables * @param dwdx derivative of w wrt x **/ - virtual void fJSparse(realtype *JSparse, realtype t, const realtype *x, - const realtype *p, const realtype *k, - const realtype *h, const realtype *w, - const realtype *dwdx); + virtual void fJSparse( + realtype* JSparse, realtype t, realtype const* x, realtype const* p, + realtype const* k, realtype const* h, realtype const* w, + realtype const* dwdx + ); /** * @brief Model specific implementation for fJSparse, column pointers * @param JSparse sparse matrix to which colptrs will be written **/ - virtual void fJSparse_colptrs(SUNMatrixWrapper &JSparse); + virtual void fJSparse_colptrs(SUNMatrixWrapper& JSparse); /** * @brief Model specific implementation for fJSparse, row values * @param JSparse sparse matrix to which rowvals will be written **/ - virtual void fJSparse_rowvals(SUNMatrixWrapper &JSparse); + virtual void fJSparse_rowvals(SUNMatrixWrapper& JSparse); /** * @brief Model specific implementation for froot @@ -317,9 +344,12 @@ class Model_ODE : public Model { * @param p parameter vector * @param k constants vector * @param h Heaviside vector + * @param tcl total abundances for conservation laws **/ - virtual void froot(realtype *root, realtype t, const realtype *x, - const realtype *p, const realtype *k, const realtype *h); + virtual void froot( + realtype* root, realtype t, realtype const* x, realtype const* p, + realtype const* k, realtype const* h, realtype const* tcl + ); /** * @brief Model specific implementation for fxdot @@ -331,12 +361,14 @@ class Model_ODE : public Model { * @param h Heaviside vector * @param w vector with helper variables **/ - virtual void fxdot(realtype *xdot, realtype t, const realtype *x, - const realtype *p, const realtype *k, const realtype *h, - const realtype *w) = 0; + virtual void fxdot( + realtype* xdot, realtype t, realtype const* x, realtype const* p, + realtype const* k, realtype const* h, realtype const* w + ) = 0; /** - * @brief Model specific implementation of fdxdotdp, with w chainrule (Matlab) + * @brief Model specific implementation of fdxdotdp, with w chainrule + * (Matlab) * @param dxdotdp partial derivative xdot wrt p * @param t timepoint * @param x Vector with the states @@ -347,13 +379,15 @@ class Model_ODE : public Model { * @param w vector with helper variables * @param dwdp derivative of w wrt p */ - virtual void fdxdotdp(realtype *dxdotdp, realtype t, const realtype *x, - const realtype *p, const realtype *k, - const realtype *h, int ip, const realtype *w, - const realtype *dwdp); + virtual void fdxdotdp( + realtype* dxdotdp, realtype t, realtype const* x, realtype const* p, + realtype const* k, realtype const* h, int ip, realtype const* w, + realtype const* dwdp + ); /** - * @brief Model specific implementation of fdxdotdp_explicit, no w chainrule (Py) + * @brief Model specific implementation of fdxdotdp_explicit, no w chainrule + * (Py) * @param dxdotdp_explicit partial derivative xdot wrt p * @param t timepoint * @param x Vector with the states @@ -362,25 +396,27 @@ class Model_ODE : public Model { * @param h Heaviside vector * @param w vector with helper variables */ - virtual void fdxdotdp_explicit(realtype *dxdotdp_explicit, realtype t, - const realtype *x, const realtype *p, - const realtype *k, const realtype *h, - const realtype *w); + virtual void fdxdotdp_explicit( + realtype* dxdotdp_explicit, realtype t, realtype const* x, + realtype const* p, realtype const* k, realtype const* h, + realtype const* w + ); /** * @brief Model specific implementation of fdxdotdp_explicit, colptrs part * @param dxdotdp sparse matrix to which colptrs will be written */ - virtual void fdxdotdp_explicit_colptrs(SUNMatrixWrapper &dxdotdp); + virtual void fdxdotdp_explicit_colptrs(SUNMatrixWrapper& dxdotdp); /** * @brief Model specific implementation of fdxdotdp_explicit, rowvals part * @param dxdotdp sparse matrix to which rowvals will be written */ - virtual void fdxdotdp_explicit_rowvals(SUNMatrixWrapper &dxdotdp); + virtual void fdxdotdp_explicit_rowvals(SUNMatrixWrapper& dxdotdp); /** - * @brief Model specific implementation of fdxdotdx_explicit, no w chainrule (Py) + * @brief Model specific implementation of fdxdotdx_explicit, no w chainrule + * (Py) * @param dxdotdx_explicit partial derivative xdot wrt x * @param t timepoint * @param x Vector with the states @@ -389,22 +425,23 @@ class Model_ODE : public Model { * @param h heavyside vector * @param w vector with helper variables */ - virtual void fdxdotdx_explicit(realtype *dxdotdx_explicit, realtype t, - const realtype *x, const realtype *p, - const realtype *k, const realtype *h, - const realtype *w); + virtual void fdxdotdx_explicit( + realtype* dxdotdx_explicit, realtype t, realtype const* x, + realtype const* p, realtype const* k, realtype const* h, + realtype const* w + ); /** * @brief Model specific implementation of fdxdotdx_explicit, colptrs part * @param dxdotdx sparse matrix to which colptrs will be written */ - virtual void fdxdotdx_explicit_colptrs(SUNMatrixWrapper &dxdotdx); + virtual void fdxdotdx_explicit_colptrs(SUNMatrixWrapper& dxdotdx); /** * @brief Model specific implementation of fdxdotdx_explicit, rowvals part * @param dxdotdx sparse matrix to which rowvals will be written */ - virtual void fdxdotdx_explicit_rowvals(SUNMatrixWrapper &dxdotdx); + virtual void fdxdotdx_explicit_rowvals(SUNMatrixWrapper& dxdotdx); /** * @brief Model specific implementation of fdxdotdw, data part @@ -416,21 +453,22 @@ class Model_ODE : public Model { * @param h Heaviside vector * @param w vector with helper variables */ - virtual void fdxdotdw(realtype *dxdotdw, realtype t, const realtype *x, - const realtype *p, const realtype *k, - const realtype *h, const realtype *w); + virtual void fdxdotdw( + realtype* dxdotdw, realtype t, realtype const* x, realtype const* p, + realtype const* k, realtype const* h, realtype const* w + ); /** * @brief Model specific implementation of fdxdotdw, colptrs part * @param dxdotdw sparse matrix to which colptrs will be written */ - virtual void fdxdotdw_colptrs(SUNMatrixWrapper &dxdotdw); + virtual void fdxdotdw_colptrs(SUNMatrixWrapper& dxdotdw); /** * @brief Model specific implementation of fdxdotdw, rowvals part * @param dxdotdw sparse matrix to which rowvals will be written */ - virtual void fdxdotdw_rowvals(SUNMatrixWrapper &dxdotdw); + virtual void fdxdotdw_rowvals(SUNMatrixWrapper& dxdotdw); /** * @brief Sensitivity of dx/dt wrt model parameters w @@ -445,7 +483,7 @@ class Model_ODE : public Model { */ void fdxdotdp(realtype t, const_N_Vector x); - void fdxdotdp(realtype t, const AmiVector &x, const AmiVector &dx) override; + void fdxdotdp(realtype t, AmiVector const& x, AmiVector const& dx) override; }; } // namespace amici diff --git a/deps/AMICI/include/amici/model_state.h b/deps/AMICI/include/amici/model_state.h index 578835283..c6c46df3d 100644 --- a/deps/AMICI/include/amici/model_state.h +++ b/deps/AMICI/include/amici/model_state.h @@ -2,14 +2,14 @@ #define AMICI_MODEL_STATE_H #include "amici/defines.h" -#include "amici/sundials_matrix_wrapper.h" +#include "amici/misc.h" #include "amici/model_dimensions.h" +#include "amici/sundials_matrix_wrapper.h" #include namespace amici { - /** * @brief Exchange format to store and transfer the state of the * model at a specific timepoint. @@ -43,8 +43,18 @@ struct ModelState { * (dimension: nplist) */ std::vector plist; + + /** temporary storage for spline values */ + std::vector spl_; }; +inline bool operator==(ModelState const& a, ModelState const& b) { + return is_equal(a.h, b.h) && is_equal(a.total_cl, b.total_cl) + && is_equal(a.stotal_cl, b.stotal_cl) + && is_equal(a.unscaledParameters, b.unscaledParameters) + && is_equal(a.fixedParameters, b.fixedParameters) + && a.plist == b.plist; +} /** * @brief Storage for `amici::Model` quantities computed based on @@ -61,24 +71,36 @@ struct ModelStateDerived { */ explicit ModelStateDerived(ModelDimensions const& dim); - /** Sparse Jacobian (dimension: `amici::Model::nnz`) */ + /** Sparse Jacobian (dimension: `nx_solver` x `nx_solver`, nnz: + * `amici::Model::nnz`) */ SUNMatrixWrapper J_; - /** Sparse Backwards Jacobian (dimension: `amici::Model::nnz`) */ + /** Sparse Backwards Jacobian (dimension: `nx_solver` x `nx_solver`, + * nnz:`amici::Model::nnz`) */ SUNMatrixWrapper JB_; - /** Sparse dxdotdw temporary storage (dimension: `ndxdotdw`) */ + /** Sparse dxdotdw temporary storage (dimension: `nx_solver` x `nw`, nnz: + * `ndxdotdw`) */ SUNMatrixWrapper dxdotdw_; - /** Sparse dwdx temporary storage (dimension: `ndwdx`) */ + /** Sparse dwdx temporary storage (dimension: `nw` x `nx_solver`, + * nnz:`ndwdx`) */ SUNMatrixWrapper dwdx_; - /** Sparse dwdp temporary storage (dimension: `ndwdp`) */ + /** Sparse dwdp temporary storage (dimension: `nw` x `np`, nnz: `ndwdp`) */ SUNMatrixWrapper dwdp_; /** Dense Mass matrix (dimension: `nx_solver` x `nx_solver`) */ SUNMatrixWrapper M_; + /** Sparse Mass matrix (dimension: `nx_solver` x `nx_solver`, nnz: + * `sum(amici::Model::idlist)`) */ + SUNMatrixWrapper MSparse_; + + /** JSparse intermediate matrix (dimension: `nx_solver` x `nx_solver`, nnz: + * dynamic) */ + SUNMatrixWrapper dfdx_; + /** * Temporary storage of `dxdotdp_full` data across functions (Python only) * (dimension: `nplist` x `nx_solver`, nnz: dynamic, @@ -87,9 +109,9 @@ struct ModelStateDerived { SUNMatrixWrapper dxdotdp_full; /** - * Temporary storage of `dxdotdp_explicit` data across functions (Python only) - * (dimension: `nplist` x `nx_solver`, nnz: `ndxdotdp_explicit`, - * type `CSC_MAT`) + * Temporary storage of `dxdotdp_explicit` data across functions (Python + * only) (dimension: `nplist` x `nx_solver`, nnz: `ndxdotdp_explicit`, type + * `CSC_MAT`) */ SUNMatrixWrapper dxdotdp_explicit; @@ -102,8 +124,8 @@ struct ModelStateDerived { SUNMatrixWrapper dxdotdp_implicit; /** - * Temporary storage of `dxdotdx_explicit` data across functions (Python only) - * (dimension: `nplist` x `nx_solver`, nnz: `nxdotdotdx_explicit`, + * Temporary storage of `dxdotdx_explicit` data across functions (Python + * only) (dimension: `nplist` x `nx_solver`, nnz: `nxdotdotdx_explicit`, * type `CSC_MAT`) */ SUNMatrixWrapper dxdotdx_explicit; @@ -116,11 +138,31 @@ struct ModelStateDerived { */ SUNMatrixWrapper dxdotdx_implicit; + /** + * Temporary storage for `dx_rdatadx_solver` + * (dimension: `nx_rdata` x `nx_solver`, nnz: `ndxrdatadxsolver`, type: + * `CSC_MAT`) + */ + SUNMatrixWrapper dx_rdatadx_solver; + + /** + * Temporary storage for `dx_rdatadtcl` + * (dimension: `nx_rdata` x `ncl`, nnz: `ndxrdatadtclr`, type: `CSC_MAT`) + */ + SUNMatrixWrapper dx_rdatadtcl; + + /** + * Temporary storage for `dtotal_cldx_rdata` + * (dimension: `ncl` x `nx_rdata`, nnz: `ndtotal_cldx_rdata`, + * type: `CSC_MAT`) + */ + SUNMatrixWrapper dtotal_cldx_rdata; + /** * Temporary storage of `dxdotdp` data across functions, Matlab only * (dimension: `nplist` x `nx_solver` , row-major) */ - AmiVectorArray dxdotdp {0, 0}; + AmiVectorArray dxdotdp{0, 0}; /** Sparse observable derivative of data likelihood, only used if * `pythonGenerated` == `true` (dimension `nytrue`, `nJ` x `ny`, row-major) @@ -216,6 +258,11 @@ struct ModelStateDerived { */ std::vector sx_; + /** temporary storage for sy, + * (dimension: `ny` x `nplist`, row-major) + */ + std::vector sy_; + /** temporary storage for `x_rdata` (dimension: `nx_rdata`) */ std::vector x_rdata_; @@ -228,11 +275,16 @@ struct ModelStateDerived { /** data standard deviation for current timepoint (dimension: ny) */ std::vector sigmay_; - /** temporary storage for parameter derivative of data standard deviation, + /** temporary storage for parameter derivative of data standard deviation, * (dimension: ny x nplist, row-major) */ std::vector dsigmaydp_; + /** temporary storage for observable derivative of data standard deviation, + * (dimension: ny x ny, row-major) + */ + std::vector dsigmaydy_; + /** temporary storage for event-resolved observable (dimension: nz) */ std::vector z_; @@ -247,7 +299,8 @@ struct ModelStateDerived { */ std::vector dsigmazdp_; - /** temporary storage for change in x after event (dimension: `nx_solver`) */ + /** temporary storage for change in x after event (dimension: `nx_solver`) + */ std::vector deltax_; /** temporary storage for change in sx after event @@ -255,7 +308,8 @@ struct ModelStateDerived { */ std::vector deltasx_; - /** temporary storage for change in xB after event (dimension: `nx_solver`) */ + /** temporary storage for change in xB after event (dimension: `nx_solver`) + */ std::vector deltaxB_; /** temporary storage for change in qB after event @@ -263,11 +317,30 @@ struct ModelStateDerived { */ std::vector deltaqB_; + /** temporary storage for sensitivity values of splines */ + SUNMatrixWrapper sspl_; + /** temporary storage of positified state variables according to * stateIsNonNegative (dimension: `nx_solver`) */ - AmiVector x_pos_tmp_ {0}; + AmiVector x_pos_tmp_{0}; }; +/** + * @brief implements an exchange format to store and transfer the state of a + * simulation at a specific timepoint. + */ +struct SimulationState { + /** timepoint */ + realtype t; + /** state variables */ + AmiVector x; + /** state variables */ + AmiVector dx; + /** state variable sensitivity */ + AmiVectorArray sx; + /** state of the model that was used for simulation */ + ModelState state; +}; } // namespace amici diff --git a/deps/AMICI/include/amici/newton_solver.h b/deps/AMICI/include/amici/newton_solver.h index 01c033aeb..2e8b2f657 100644 --- a/deps/AMICI/include/amici/newton_solver.h +++ b/deps/AMICI/include/amici/newton_solver.h @@ -1,10 +1,9 @@ #ifndef amici_newton_solver_h #define amici_newton_solver_h -#include "amici/vector.h" -#include "amici/defines.h" +#include "amici/solver.h" #include "amici/sundials_matrix_wrapper.h" -#include "amici/sundials_linsol_wrapper.h" +#include "amici/vector.h" #include @@ -13,6 +12,7 @@ namespace amici { class Model; class Solver; class AmiVector; +struct SimulationState; /** * @brief The NewtonSolver class sets up the linear solver for the Newton @@ -23,72 +23,64 @@ class NewtonSolver { public: /** - * @brief Initializes all members with the provided objects + * @brief Initializes solver according to the dimensions in the provided + * model * - * @param t pointer to time variable - * @param x pointer to state variables * @param model pointer to the model object */ - NewtonSolver(realtype *t, AmiVector *x, Model *model); + explicit NewtonSolver(Model const& model); /** * @brief Factory method to create a NewtonSolver based on linsolType * - * @param t pointer to time variable - * @param x pointer to state variables * @param simulationSolver solver with settings - * @param model pointer to the model object + * @param model pointer to the model instance * @return solver NewtonSolver according to the specified linsolType */ - static std::unique_ptr getSolver( - realtype *t, AmiVector *x, Solver &simulationSolver, Model *model); + static std::unique_ptr + getSolver(Solver const& simulationSolver, Model const& model); /** * @brief Computes the solution of one Newton iteration * - * @param ntry integer newton_try integer start number of Newton solver - * (1 or 2) - * @param nnewt integer number of current Newton step * @param delta containing the RHS of the linear system, will be * overwritten by solution to the linear system + * @param model pointer to the model instance + * @param state current simulation state */ - void getStep(int ntry, int nnewt, AmiVector &delta); + void getStep(AmiVector& delta, Model& model, SimulationState const& state); /** * @brief Computes steady state sensitivities * * @param sx pointer to state variable sensitivities + * @param model pointer to the model instance + * @param state current simulation state */ - void computeNewtonSensis(AmiVectorArray &sx); - - /** - * @brief Accessor for numlinsteps - * - * @return numlinsteps - */ - const std::vector &getNumLinSteps() const { - return num_lin_steps_; - } + void computeNewtonSensis( + AmiVectorArray& sx, Model& model, SimulationState const& state + ); /** * @brief Writes the Jacobian for the Newton iteration and passes it to the * linear solver * - * @param ntry integer newton_try integer start number of Newton solver - * (1 or 2) - * @param nnewt integer number of current Newton step + * @param model pointer to the model instance + * @param state current simulation state */ - virtual void prepareLinearSystem(int ntry, int nnewt) = 0; + virtual void prepareLinearSystem(Model& model, SimulationState const& state) + = 0; /** - * Writes the Jacobian (JB) for the Newton iteration and passes it to the linear - * solver + * Writes the Jacobian (JB) for the Newton iteration and passes it to the + * linear solver * - * @param ntry integer newton_try integer start number of Newton solver - * (1 or 2) - * @param nnewt integer number of current Newton step + * @param model pointer to the model instance + * @param state current simulation state */ - virtual void prepareLinearSystemB(int ntry, int nnewt) = 0; + virtual void + prepareLinearSystemB(Model& model, SimulationState const& state) + = 0; /** * @brief Solves the linear system for the Newton step @@ -96,40 +88,36 @@ class NewtonSolver { * @param rhs containing the RHS of the linear system, will be * overwritten by solution to the linear system */ - virtual void solveLinearSystem(AmiVector &rhs) = 0; + virtual void solveLinearSystem(AmiVector& rhs) = 0; - virtual ~NewtonSolver() = default; + /** + * @brief Reinitialize the linear solver + * + */ + virtual void reinitialize() = 0; + + /** + * @brief Checks whether linear system is singular + * + * @param model pointer to the model instance + * @param state current simulation state + * @return boolean indicating whether the linear system is singular + * (condition number < 1/machine precision) + */ + virtual bool is_singular(Model& model, SimulationState const& state) const + = 0; - /** maximum number of allowed linear steps per Newton step for steady state - * computation */ - int max_lin_steps_ {0}; - /** maximum number of allowed Newton steps for steady state computation */ - int max_steps {0}; - /** absolute tolerance */ - realtype atol_ {1e-16}; - /** relative tolerance */ - realtype rtol_ {1e-8}; - /** damping factor flag */ - NewtonDampingFactorMode damping_factor_mode_ {NewtonDampingFactorMode::on}; - /** damping factor lower bound */ - realtype damping_factor_lower_bound {1e-8}; + virtual ~NewtonSolver() = default; protected: - /** time variable */ - realtype *t_; - /** pointer to the model object */ - Model *model_; - /** right hand side AmiVector */ + /** dummy rhs, used as dummy argument when computing J and JB */ AmiVector xdot_; - /** current state */ - AmiVector *x_; - /** current state time derivative (DAE) */ - AmiVector dx_; - /** history of number of linear steps */ - std::vector num_lin_steps_; - /** current adjoint state */ + /** dummy state, attached to linear solver */ + AmiVector x_; + /** dummy adjoint state, used as dummy argument when computing JB */ AmiVector xB_; - /** current adjoint state time derivative (DAE) */ + /** dummy differential adjoint state, used as dummy argument when computing + * JB */ AmiVector dxB_; }; @@ -142,56 +130,36 @@ class NewtonSolverDense : public NewtonSolver { public: /** - * @brief Constructor, initializes all members with the provided objects - * and initializes temporary storage objects + * @brief constructor for sparse solver * - * @param t pointer to time variable - * @param x pointer to state variables - * @param model pointer to the model object + * @param model model instance that provides problem dimensions */ + explicit NewtonSolverDense(Model const& model); - NewtonSolverDense(realtype *t, AmiVector *x, Model *model); - - NewtonSolverDense(const NewtonSolverDense&) = delete; + NewtonSolverDense(NewtonSolverDense const&) = delete; - NewtonSolverDense& operator=(const NewtonSolverDense& other) = delete; + NewtonSolverDense& operator=(NewtonSolverDense const& other) = delete; ~NewtonSolverDense() override; - /** - * @brief Solves the linear system for the Newton step - * - * @param rhs containing the RHS of the linear system, will be - * overwritten by solution to the linear system - */ - void solveLinearSystem(AmiVector &rhs) override; + void solveLinearSystem(AmiVector& rhs) override; - /** - * @brief Writes the Jacobian for the Newton iteration and passes it to the - * linear solver - * - * @param ntry integer newton_try integer start number of Newton solver - * (1 or 2) - * @param nnewt integer number of current Newton step - */ - void prepareLinearSystem(int ntry, int nnewt) override; + void + prepareLinearSystem(Model& model, SimulationState const& state) override; - /** - * Writes the Jacobian (JB) for the Newton iteration and passes it to the linear - * solver - * - * @param ntry integer newton_try integer start number of Newton solver - * (1 or 2) - * @param nnewt integer number of current Newton step - */ - void prepareLinearSystemB(int ntry, int nnewt) override; + void + prepareLinearSystemB(Model& model, SimulationState const& state) override; + + void reinitialize() override; + + bool is_singular(Model& model, SimulationState const& state) const override; private: /** temporary storage of Jacobian */ SUNMatrixWrapper Jtmp_; /** dense linear solver */ - SUNLinearSolver linsol_ {nullptr}; + SUNLinearSolver linsol_{nullptr}; }; /** @@ -203,148 +171,38 @@ class NewtonSolverSparse : public NewtonSolver { public: /** - * @brief Constructor, initializes all members with the provided objects, - * initializes temporary storage objects and the klu solver + * @brief constructor for dense solver * - * @param t pointer to time variable - * @param x pointer to state variables - * @param model pointer to the model object + * @param model model instance that provides problem dimensions */ - NewtonSolverSparse(realtype *t, AmiVector *x, Model *model); + explicit NewtonSolverSparse(Model const& model); - NewtonSolverSparse(const NewtonSolverSparse&) = delete; + NewtonSolverSparse(NewtonSolverSparse const&) = delete; - NewtonSolverSparse& operator=(const NewtonSolverSparse& other) = delete; + NewtonSolverSparse& operator=(NewtonSolverSparse const& other) = delete; ~NewtonSolverSparse() override; - /** - * @brief Solves the linear system for the Newton step - * - * @param rhs containing the RHS of the linear system, will be - * overwritten by solution to the linear system - */ - void solveLinearSystem(AmiVector &rhs) override; + void solveLinearSystem(AmiVector& rhs) override; - /** - * @brief Writes the Jacobian for the Newton iteration and passes it to the - * linear solver - * - * @param ntry integer newton_try integer start number of Newton solver - * (1 or 2) - * @param nnewt integer number of current Newton step - */ - void prepareLinearSystem(int ntry, int nnewt) override; + void + prepareLinearSystem(Model& model, SimulationState const& state) override; - /** - * Writes the Jacobian (JB) for the Newton iteration and passes it to the linear - * solver - * - * @param ntry integer newton_try integer start number of Newton solver - * (1 or 2) - * @param nnewt integer number of current Newton step - */ - void prepareLinearSystemB(int ntry, int nnewt) override; + void + prepareLinearSystemB(Model& model, SimulationState const& state) override; + + bool is_singular(Model& model, SimulationState const& state) const override; + + void reinitialize() override; private: /** temporary storage of Jacobian */ SUNMatrixWrapper Jtmp_; /** sparse linear solver */ - SUNLinearSolver linsol_ {nullptr}; -}; - -/** - * @brief The NewtonSolverIterative provides access to the iterative linear - * solver for the Newton method. - */ - -class NewtonSolverIterative : public NewtonSolver { - - public: - /** - * @brief Constructor, initializes all members with the provided objects - * @param t pointer to time variable - * @param x pointer to state variables - * @param model pointer to the model object - */ - NewtonSolverIterative(realtype *t, AmiVector *x, Model *model); - - ~NewtonSolverIterative() override = default; - - /** - * @brief Solves the linear system for the Newton step by passing it to - * linsolveSPBCG - * - * @param rhs containing the RHS of the linear system, will be - * overwritten by solution to the linear system - */ - void solveLinearSystem(AmiVector &rhs) override; - - /** - * Writes the Jacobian (J) for the Newton iteration and passes it to the linear - * solver. - * Also wraps around getSensis for iterative linear solver. - * - * @param ntry integer newton_try integer start number of Newton solver - * (1 or 2) - * @param nnewt integer number of current Newton step - */ - void prepareLinearSystem(int ntry, int nnewt) override; - - /** - * Writes the Jacobian (JB) for the Newton iteration and passes it to the linear - * solver. - * Also wraps around getSensis for iterative linear solver. - * - * @param ntry integer newton_try integer start number of Newton solver - * (1 or 2) - * @param nnewt integer number of current Newton step - */ - void prepareLinearSystemB(int ntry, int nnewt) override; - - /** - * Iterative linear solver created from SPILS BiCG-Stab. - * Solves the linear system within each Newton step if iterative solver is - * chosen. - * - * @param ntry integer newton_try integer start number of Newton solver - * (1 or 2) - * @param nnewt integer number of current Newton step - * @param ns_delta Newton step - */ - void linsolveSPBCG(int ntry, int nnewt, AmiVector &ns_delta); - - private: - /** number of tries */ - int newton_try_ {0}; - /** number of iterations */ - int i_newton_ {0}; - /** ??? */ - AmiVector ns_p_; - /** ??? */ - AmiVector ns_h_; - /** ??? */ - AmiVector ns_t_; - /** ??? */ - AmiVector ns_s_; - /** ??? */ - AmiVector ns_r_; - /** ??? */ - AmiVector ns_rt_; - /** ??? */ - AmiVector ns_v_; - /** ??? */ - AmiVector ns_Jv_; - /** ??? */ - AmiVector ns_tmp_; - /** ??? */ - AmiVector ns_Jdiag_; - /** temporary storage of Jacobian */ - SUNMatrixWrapper ns_J_; + SUNLinearSolver linsol_{nullptr}; }; - } // namespace amici #endif // NEWTON_SOLVER diff --git a/deps/AMICI/include/amici/rdata.h b/deps/AMICI/include/amici/rdata.h index d83c775b6..3b9ea01b9 100644 --- a/deps/AMICI/include/amici/rdata.h +++ b/deps/AMICI/include/amici/rdata.h @@ -2,10 +2,11 @@ #define AMICI_RDATA_H #include "amici/defines.h" -#include "amici/vector.h" -#include "amici/model.h" +#include "amici/logging.h" #include "amici/misc.h" -#include "amici/forwardproblem.h" +#include "amici/model.h" +#include "amici/vector.h" + #include namespace amici { @@ -20,7 +21,7 @@ class SteadystateProblem; namespace boost { namespace serialization { template -void serialize(Archive &ar, amici::ReturnData &r, unsigned int version); +void serialize(Archive& ar, amici::ReturnData& r, unsigned int version); } } // namespace boost @@ -31,7 +32,7 @@ namespace amici { * * NOTE: multi-dimensional arrays are stored in row-major order (C-style) */ -class ReturnData: public ModelDimensions { +class ReturnData : public ModelDimensions { public: /** * @brief Default constructor @@ -53,17 +54,18 @@ class ReturnData: public ModelDimensions { * @param rdrm see amici::Solver::rdata_reporting * @param quadratic_llh whether model defines a quadratic nllh and * computing res, sres and FIM makes sense - * @param sigma_res indicates whether additional residuals are to be added for each sigma + * @param sigma_res indicates whether additional residuals are to be added + * for each sigma * @param sigma_offset offset to ensure real-valuedness of sigma residuals */ - ReturnData(std::vector ts, - ModelDimensions const& model_dimensions, - int nplist, int nmaxevent, int nt, - int newton_maxsteps, - std::vector pscale, SecondOrderMode o2mode, - SensitivityOrder sensi, SensitivityMethod sensi_meth, - RDataReporting rdrm, bool quadratic_llh, bool sigma_res, - realtype sigma_offset); + ReturnData( + std::vector ts, ModelDimensions const& model_dimensions, + int nplist, int nmaxevent, int nt, int newton_maxsteps, + std::vector pscale, SecondOrderMode o2mode, + SensitivityOrder sensi, SensitivityMethod sensi_meth, + RDataReporting rdrm, bool quadratic_llh, bool sigma_res, + realtype sigma_offset + ); /** * @brief constructor that uses information from model and solver to @@ -71,7 +73,7 @@ class ReturnData: public ModelDimensions { * @param solver solver instance * @param model model instance */ - ReturnData(Solver const &solver, const Model &model); + ReturnData(Solver const& solver, Model const& model); ~ReturnData() = default; @@ -81,17 +83,17 @@ class ReturnData: public ModelDimensions { * @param preeq simulated preequilibration problem, pass `nullptr` to ignore * @param fwd simulated forward problem, pass `nullptr` to ignore * @param bwd simulated backward problem, pass `nullptr` to ignore - * @param posteq simulated postequilibration problem, pass `nullptr` to ignore + * @param posteq simulated postequilibration problem, pass `nullptr` to + * ignore * @param model matching model instance * @param solver matching solver instance * @param edata matching experimental data */ - void processSimulationObjects(SteadystateProblem const *preeq, - ForwardProblem const *fwd, - BackwardProblem const *bwd, - SteadystateProblem const *posteq, - Model &model, Solver const &solver, - ExpData const *edata); + void processSimulationObjects( + SteadystateProblem const* preeq, ForwardProblem const* fwd, + BackwardProblem const* bwd, SteadystateProblem const* posteq, + Model& model, Solver const& solver, ExpData const* edata + ); /** * @brief Arbitrary (not necessarily unique) identifier. */ @@ -106,13 +108,15 @@ class ReturnData: public ModelDimensions { std::vector xdot; /** - * Jacobian of differential equation right hand side (shape `nx` x `nx`, row-major) + * Jacobian of differential equation right hand side (shape `nx` x `nx`, + * row-major) */ std::vector J; /** * w data from the model (recurring terms in xdot, for imported SBML models - * from python, this contains the flux vector) (shape `nt` x `nw`, row major) + * from python, this contains the flux vector) (shape `nt` x `nw`, row + * major) */ std::vector w; @@ -120,17 +124,20 @@ class ReturnData: public ModelDimensions { std::vector z; /** - * event output sigma standard deviation (shape `nmaxevent` x `nz`, row-major) + * event output sigma standard deviation (shape `nmaxevent` x `nz`, + * row-major) */ std::vector sigmaz; /** - * parameter derivative of event output (shape `nmaxevent` x `nz`, row-major) + * parameter derivative of event output + * (shape `nmaxevent` x `nplist` x `nz`, row-major) */ std::vector sz; /** - * parameter derivative of event output standard deviation (shape `nmaxevent` x `nz`, row-major) + * parameter derivative of event output standard deviation + * (shape `nmaxevent` x `nplist` x `nz`, row-major) */ std::vector ssigmaz; @@ -138,7 +145,8 @@ class ReturnData: public ModelDimensions { std::vector rz; /** - * parameter derivative of event trigger output (shape `nmaxevent` x `nz` x `nplist`, row-major) + * parameter derivative of event trigger output + * (shape `nmaxevent` x `nplist` x `nz`, row-major) */ std::vector srz; @@ -169,7 +177,8 @@ class ReturnData: public ModelDimensions { std::vector sy; /** - * parameter derivative of observable standard deviation (shape `nt` x `nplist` x `ny`, row-major) + * parameter derivative of observable standard deviation + * (shape `nt` x `nplist` x `ny`, row-major) */ std::vector ssigmay; @@ -208,37 +217,99 @@ class ReturnData: public ModelDimensions { std::vector numnonlinsolvconvfails; /** - * number of linear solver convergence failures backward problem (shape `nt`) + * number of linear solver convergence failures backward problem (shape + * `nt`) */ std::vector numnonlinsolvconvfailsB; /** employed order forward problem (shape `nt`) */ std::vector order; - /** computation time of forward solve [ms] */ + /** + * @brief computation time of forward solve [ms] + * + * .. warning:: + * If AMICI was built without boost, this tracks the CPU-time of the + * current process. Therefore, in a multi-threaded context, this value + * may be incorrect. + * + */ double cpu_time = 0.0; - /** computation time of backward solve [ms] */ + /** + * @brief computation time of backward solve [ms] + * + * .. warning:: + * If AMICI was built without boost, this tracks the CPU-time of the + * current process. Therefore, in a multi-threaded context, this value + * may be incorrect. + * + */ double cpu_timeB = 0.0; + /** + * @brief total CPU time from entering runAmiciSimulation until exiting [ms] + * + * .. warning:: + * If AMICI was built without boost, this tracks the CPU-time of the + * current process. Therefore, in a multi-threaded context, this value + * may be incorrect. + * + */ + double cpu_time_total = 0.0; + /** flags indicating success of steady state solver (preequilibration) */ std::vector preeq_status; - /** computation time of the steady state solver [ms] (preequilibration) */ + /** + * @brief computation time of the steady state solver [ms] + * (preequilibration) + * + * .. warning:: + * If AMICI was built without boost, this tracks the CPU-time of the + * current process. Therefore, in a multi-threaded context, this value + * may be incorrect. + * + */ double preeq_cpu_time = 0.0; - /** computation time of the steady state solver of the backward problem [ms] - * (preequilibration) */ + /** + * @brief computation time of the steady state solver of the backward + * problem [ms] (preequilibration) + * + * .. warning:: + * If AMICI was built without boost, this tracks the CPU-time of the + * current process. Therefore, in a multi-threaded context, this value + * may be incorrect. + * + */ double preeq_cpu_timeB = 0.0; /** flags indicating success of steady state solver (postequilibration) */ std::vector posteq_status; - /** computation time of the steady state solver [ms] (postequilibration) */ + /** + * @brief computation time of the steady state solver [ms] + * (postequilibration) + * + * .. warning:: + * If AMICI was built without boost, this tracks the CPU-time of the + * current process. Therefore, in a multi-threaded context, this value + * may be incorrect. + * + */ double posteq_cpu_time = 0.0; - /** computation time of the steady state solver of the backward problem [ms] - * (postequilibration) */ + /** + * @brief computation time of the steady state solver of the backward + * problem [ms] (postequilibration) + * + * .. warning:: + * If AMICI was built without boost, this tracks the CPU-time of the + * current process. Therefore, in a multi-threaded context, this value + * may be incorrect. + * + */ double posteq_cpu_timeB = 0.0; /** @@ -247,13 +318,6 @@ class ReturnData: public ModelDimensions { */ std::vector preeq_numsteps; - /** - * number of linear steps by Newton step for steady state problem. this - * will only be filled for iterative solvers (preequilibration) - * (shape `newton_maxsteps * 2`) - */ - std::vector preeq_numlinsteps; - /** * number of simulation steps for adjoint steady state problem * (preequilibration) [== 0 if analytical solution worked, > 0 otherwise] @@ -266,13 +330,6 @@ class ReturnData: public ModelDimensions { */ std::vector posteq_numsteps; - /** - * number of linear steps by Newton step for steady state problem. this - * will only be filled for iterative solvers (postequilibration) - * (shape `newton_maxsteps * 2`) - */ - std::vector posteq_numlinsteps; - /** * number of simulation steps for adjoint steady state problem * (postequilibration) [== 0 if analytical solution worked, > 0 otherwise] @@ -304,14 +361,14 @@ class ReturnData: public ModelDimensions { /** initial state (shape `nx`) */ std::vector x0; - /** preequilibration steady state found by Newton solver (shape `nx`) */ + /** preequilibration steady state (shape `nx`) */ std::vector x_ss; /** initial sensitivities (shape `nplist` x `nx`, row-major) */ std::vector sx0; /** - * preequilibration sensitivities found by Newton solver + * preequilibration sensitivities * (shape `nplist` x `nx`, row-major) */ std::vector sx_ss; @@ -331,8 +388,19 @@ class ReturnData: public ModelDimensions { */ std::vector s2llh; - /** status code */ - int status = 0; + /** + * @brief Simulation status code. + * + * One of: + * + * * AMICI_SUCCESS, indicating successful simulation + * * AMICI_MAX_TIME_EXCEEDED, indicating that the simulation did not finish + * within the allowed time (see Solver.{set,get}MaxTime) + * * AMICI_ERROR, indicating that some error occurred during simulation + * (a more detailed error message will have been printed). + * * AMICI_NOT_RUN, if no simulation was started + */ + int status = AMICI_NOT_RUN; /** number of states (alias `nx_rdata`, kept for backward compatibility) */ int nx{0}; @@ -377,24 +445,30 @@ class ReturnData: public ModelDimensions { * @param version Version number */ template - friend void boost::serialization::serialize(Archive &ar, ReturnData &r, - unsigned int version); + friend void boost::serialization::serialize( + Archive& ar, ReturnData& r, unsigned int version + ); - /** boolean indicating whether residuals for standard deviations have been added */ + /** boolean indicating whether residuals for standard deviations have been + * added */ bool sigma_res; - protected: + /** log messages */ + std::vector messages; + protected: /** offset for sigma_residuals */ realtype sigma_offset; /** timepoint for model evaluation*/ realtype t_; - /** partial state vector, excluding states eliminated from conservation laws */ + /** partial state vector, excluding states eliminated from conservation laws + */ AmiVector x_solver_; - /** partial time derivative of state vector, excluding states eliminated from conservation laws */ + /** partial time derivative of state vector, excluding states eliminated + * from conservation laws */ AmiVector dx_solver_; /** partial sensitivity state vector array, excluding states eliminated from @@ -414,8 +488,8 @@ class ReturnData: public ModelDimensions { /** * @brief initializes storage for likelihood reporting mode - * @param quadratic_llh whether model defines a quadratic nllh and computing res, sres and FIM - * makes sense. + * @param quadratic_llh whether model defines a quadratic nllh and computing + * res, sres and FIM makes sense. */ void initializeLikelihoodReporting(bool quadratic_llh); @@ -431,7 +505,6 @@ class ReturnData: public ModelDimensions { */ void initializeFullReporting(bool enable_fim); - /** * @brief initialize values for chi2 and llh and derivatives * @param enable_chi2 whether chi2 values are to be computed @@ -443,8 +516,7 @@ class ReturnData: public ModelDimensions { * @param preeq SteadystateProblem for preequilibration * @param model Model instance to compute return values */ - void processPreEquilibration(SteadystateProblem const &preeq, - Model &model); + void processPreEquilibration(SteadystateProblem const& preeq, Model& model); /** * @brief extracts data from a preequilibration SteadystateProblem @@ -452,9 +524,9 @@ class ReturnData: public ModelDimensions { * @param model Model instance to compute return values * @param edata ExpData instance containing observable data */ - void processPostEquilibration(SteadystateProblem const &posteq, - Model &model, - ExpData const *edata); + void processPostEquilibration( + SteadystateProblem const& posteq, Model& model, ExpData const* edata + ); /** * @brief extracts results from forward problem @@ -462,10 +534,9 @@ class ReturnData: public ModelDimensions { * @param model model that was used for forward simulation * @param edata ExpData instance containing observable data */ - void processForwardProblem(ForwardProblem const &fwd, - Model &model, - ExpData const *edata); - + void processForwardProblem( + ForwardProblem const& fwd, Model& model, ExpData const* edata + ); /** * @brief extracts results from backward problem @@ -474,25 +545,26 @@ class ReturnData: public ModelDimensions { * @param preeq SteadystateProblem for preequilibration * @param model model that was used for forward/backward simulation */ - void processBackwardProblem(ForwardProblem const &fwd, - BackwardProblem const &bwd, - SteadystateProblem const *preeq, - Model &model); + void processBackwardProblem( + ForwardProblem const& fwd, BackwardProblem const& bwd, + SteadystateProblem const* preeq, Model& model + ); /** * @brief extracts results from solver * @param solver solver that was used for forward/backward simulation */ - void processSolver(Solver const &solver); + void processSolver(Solver const& solver); /** - * @brief Evaluates and stores the Jacobian and right hand side at final timepoint + * @brief Evaluates and stores the Jacobian and right hand side at final + * timepoint * @param problem forward problem or steadystate problem * @param model model that was used for forward/backward simulation */ template - void storeJacobianAndDerivativeInReturnData(T const &problem, Model &model) - { + void + storeJacobianAndDerivativeInReturnData(T const& problem, Model& model) { readSimulationState(problem.getFinalSimulationState(), model); AmiVector xdot(nx_solver); @@ -508,16 +580,17 @@ class ReturnData: public ModelDimensions { // 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++) - this->J.at(ix * model.nx_solver + jx) = - J.data()[ix + model.nx_solver * jx]; + this->J.at(ix * model.nx_solver + jx) + = J.data()[ix + model.nx_solver * jx]; } } /** - * @brief sets member variables and model state according to provided simulation state + * @brief sets member variables and model state according to provided + * simulation state * @param state simulation state provided by Problem * @param model model that was used for forward/backward simulation */ - void readSimulationState(SimulationState const &state, Model &model); + void readSimulationState(SimulationState const& state, Model& model); /** * @brief Residual function @@ -525,14 +598,14 @@ class ReturnData: public ModelDimensions { * @param model model that was used for forward/backward simulation * @param edata ExpData instance containing observable data */ - void fres(int it, Model &model, const ExpData &edata); + void fres(int it, Model& model, ExpData const& edata); /** * @brief Chi-squared function * @param it time index * @param edata ExpData instance containing observable data */ - void fchi2(int it, const ExpData &edata); + void fchi2(int it, ExpData const& edata); /** * @brief Residual sensitivity function @@ -540,7 +613,7 @@ class ReturnData: public ModelDimensions { * @param model model that was used for forward/backward simulation * @param edata ExpData instance containing observable data */ - void fsres(int it, Model &model, const ExpData &edata); + void fsres(int it, Model& model, ExpData const& edata); /** * @brief Fisher information matrix function @@ -548,7 +621,7 @@ class ReturnData: public ModelDimensions { * @param model model that was used for forward/backward simulation * @param edata ExpData instance containing observable data */ - void fFIM(int it, Model &model, const ExpData &edata); + void fFIM(int it, Model& model, ExpData const& edata); /** * @brief Set likelihood, state variables, outputs and respective @@ -574,16 +647,17 @@ class ReturnData: public ModelDimensions { * the sensitivities of simulation results * @param model Model from which the ReturnData was obtained */ - void applyChainRuleFactorToSimulationResults(const Model &model); - + void applyChainRuleFactorToSimulationResults(Model const& model); /** * @brief Checks whether forward sensitivity analysis is performed * @return boolean indicator */ bool computingFSA() const { - return (sensi_meth == SensitivityMethod::forward && - sensi >= SensitivityOrder::first); + return ( + sensi_meth == SensitivityMethod::forward + && sensi >= SensitivityOrder::first + ); } /** @@ -593,7 +667,7 @@ class ReturnData: public ModelDimensions { * @param model model that was used in forward solve * @param edata ExpData instance carrying experimental data */ - void getDataOutput(int it, Model &model, ExpData const *edata); + void getDataOutput(int it, Model& model, ExpData const* edata); /** * @brief Extracts data information for forward sensitivity analysis, @@ -602,7 +676,7 @@ class ReturnData: public ModelDimensions { * @param model model that was used in forward solve * @param edata ExpData instance carrying experimental data */ - void getDataSensisFSA(int it, Model &model, ExpData const *edata); + void getDataSensisFSA(int it, Model& model, ExpData const* edata); /** * @brief Extracts output information for events, expects that x_solver_ @@ -613,8 +687,10 @@ class ReturnData: public ModelDimensions { * @param model model that was used in forward solve * @param edata ExpData instance carrying experimental data */ - void getEventOutput(realtype t, const std::vector rootidx, - Model &model, ExpData const *edata); + void getEventOutput( + realtype t, const std::vector rootidx, Model& model, + ExpData const* edata + ); /** * @brief Extracts event information for forward sensitivity analysis, @@ -624,8 +700,8 @@ class ReturnData: public ModelDimensions { * @param model model that was used in forward solve * @param edata ExpData instance carrying experimental data */ - void getEventSensisFSA(int ie, realtype t, Model &model, - ExpData const *edata); + void + getEventSensisFSA(int ie, realtype t, Model& model, ExpData const* edata); /** * @brief Updates contribution to likelihood from quadratures (xQB), @@ -636,20 +712,23 @@ class ReturnData: public ModelDimensions { * of preequilibration * @param xQB vector with quadratures from adjoint computation */ - void handleSx0Backward(const Model &model, SteadystateProblem const &preeq, - std::vector &llhS0, AmiVector &xQB) const; + void handleSx0Backward( + Model const& model, SteadystateProblem const& preeq, + std::vector& llhS0, AmiVector& xQB + ) const; /** * @brief Updates contribution to likelihood for initial state sensitivities - * (llhS0), if no preequilibration was run or if forward sensitivities were used + * (llhS0), if no preequilibration was run or if forward sensitivities were + * used * @param model model that was used for forward/backward simulation * @param llhS0 contribution to likelihood for initial state sensitivities * @param xB vector with final adjoint state * (excluding conservation laws) */ - void handleSx0Forward(const Model &model, - std::vector &llhS0, - AmiVector &xB) const; + void handleSx0Forward( + Model const& model, std::vector& llhS0, AmiVector& xB + ) const; }; /** @@ -663,9 +742,9 @@ class ModelContext : public ContextManager { * * @param model */ - explicit ModelContext(Model *model); + explicit ModelContext(Model* model); - ModelContext &operator=(const ModelContext &other) = delete; + ModelContext& operator=(ModelContext const& other) = delete; ~ModelContext(); @@ -677,11 +756,10 @@ class ModelContext : public ContextManager { void restore(); private: - Model *model_ {nullptr}; + Model* model_{nullptr}; ModelState original_state_; }; - } // namespace amici #endif /* _MY_RDATA */ diff --git a/deps/AMICI/include/amici/returndata_matlab.h b/deps/AMICI/include/amici/returndata_matlab.h index ae9b05885..6a51db526 100644 --- a/deps/AMICI/include/amici/returndata_matlab.h +++ b/deps/AMICI/include/amici/returndata_matlab.h @@ -14,7 +14,7 @@ namespace amici { * @param rdata ReturnDataObject * @return rdatamatlab ReturnDataObject stored as matlab compatible data */ -mxArray *getReturnDataMatlabFromAmiciCall(ReturnData const *rdata); +mxArray* getReturnDataMatlabFromAmiciCall(ReturnData const* rdata); /** * @brief allocates and initializes solution mxArray with the corresponding @@ -22,7 +22,7 @@ mxArray *getReturnDataMatlabFromAmiciCall(ReturnData const *rdata); * @param rdata ReturnDataObject * @return Solution mxArray */ -mxArray *initMatlabReturnFields(ReturnData const *rdata); +mxArray* initMatlabReturnFields(ReturnData const* rdata); /** * @brief allocates and initializes diagnosis mxArray with the corresponding @@ -30,7 +30,7 @@ mxArray *initMatlabReturnFields(ReturnData const *rdata); * @param rdata ReturnDataObject * @return Diagnosis mxArray */ -mxArray *initMatlabDiagnosisFields(ReturnData const *rdata); +mxArray* initMatlabDiagnosisFields(ReturnData const* rdata); /** * @brief initialize vector and attach to the field @@ -40,8 +40,9 @@ mxArray *initMatlabDiagnosisFields(ReturnData const *rdata); * @param fieldData Data which will be stored in the field */ template -void writeMatlabField0(mxArray *matlabStruct, const char *fieldName, - T fieldData); +void writeMatlabField0( + mxArray* matlabStruct, char const* fieldName, T fieldData +); /** * @brief initialize vector and attach to the field @@ -52,8 +53,10 @@ void writeMatlabField0(mxArray *matlabStruct, const char *fieldName, * @param dim0 Number of elements in the vector */ template -void writeMatlabField1(mxArray *matlabStruct, const char *fieldName, - gsl::span const &fieldData, const int dim0); +void writeMatlabField1( + mxArray* matlabStruct, char const* fieldName, + gsl::span const& fieldData, const mwSize dim0 +); /** * @brief initialize matrix, attach to the field and write data @@ -65,9 +68,11 @@ void writeMatlabField1(mxArray *matlabStruct, const char *fieldName, * @param perm reordering of dimensions (i.e., transposition) */ template -void writeMatlabField2(mxArray *matlabStruct, const char *fieldName, - std::vector const &fieldData, int dim0, int dim1, - std::vector perm); +void writeMatlabField2( + mxArray* matlabStruct, char const* fieldName, + std::vector const& fieldData, mwSize dim0, mwSize dim1, + std::vector perm +); /** * @brief initialize 3D tensor, attach to the field and write data @@ -80,9 +85,11 @@ void writeMatlabField2(mxArray *matlabStruct, const char *fieldName, * @param perm reordering of dimensions */ template -void writeMatlabField3(mxArray *matlabStruct, const char *fieldName, - std::vector const &fieldData, int dim0, int dim1, - int dim2, std::vector perm); +void writeMatlabField3( + mxArray* matlabStruct, char const* fieldName, + std::vector const& fieldData, mwSize dim0, mwSize dim1, mwSize dim2, + std::vector perm +); /** * @brief initialize 4D tensor, attach to the field and write data @@ -96,9 +103,11 @@ void writeMatlabField3(mxArray *matlabStruct, const char *fieldName, * @param perm reordering of dimensions */ template -void writeMatlabField4(mxArray *matlabStruct, const char *fieldName, - std::vector const &fieldData, int dim0, int dim1, - int dim2, int dim3, std::vector perm); +void writeMatlabField4( + mxArray* matlabStruct, char const* fieldName, + std::vector const& fieldData, mwSize dim0, mwSize dim1, mwSize dim2, + mwSize dim3, std::vector perm +); /** * @brief initializes the field fieldName in matlabStruct with dimension dim @@ -108,15 +117,16 @@ void writeMatlabField4(mxArray *matlabStruct, const char *fieldName, * * @return pointer to field data */ -double *initAndAttachArray(mxArray *matlabStruct, const char *fieldName, - std::vector dim); +double* initAndAttachArray( + mxArray* matlabStruct, char const* fieldName, std::vector dim +); /** * @brief checks whether fieldNames was properly allocated * @param fieldNames array of field names * @param fieldCount expected number of fields in fieldNames */ -void checkFieldNames(const char **fieldNames, const int fieldCount); +void checkFieldNames(char const** fieldNames, int const fieldCount); /** * @brief template function that reorders elements in a std::vector @@ -127,8 +137,8 @@ void checkFieldNames(const char **fieldNames, const int fieldCount); * @return Reordered vector */ template -std::vector reorder(std::vector const& input, - std::vector const& order); +std::vector +reorder(std::vector const& input, std::vector const& order); } // namespace amici diff --git a/deps/AMICI/include/amici/serialization.h b/deps/AMICI/include/amici/serialization.h index b62f0a4ff..7d0428f71 100644 --- a/deps/AMICI/include/amici/serialization.h +++ b/deps/AMICI/include/amici/serialization.h @@ -1,24 +1,24 @@ #ifndef AMICI_SERIALIZATION_H #define AMICI_SERIALIZATION_H -#include "amici/rdata.h" #include "amici/model.h" +#include "amici/rdata.h" #include "amici/solver.h" #include "amici/solver_cvodes.h" #include +#include #include #include -#include -#include -#include #include #include #include #include #include #include +#include +#include /** @file serialization.h Helper functions and forward declarations for * boost::serialization */ @@ -32,17 +32,17 @@ namespace serialization { * @param size Size of p */ template -void archiveVector(Archive &ar, T **p, int size) { +void archiveVector(Archive& ar, T** p, int size) { if (Archive::is_loading::value) { - if(*p != nullptr) - delete[] *p; - ar &size; + if (*p != nullptr) + 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); + ar& make_array(*p, size); } #ifndef EXHALE_DOXYGEN_SHOULD_SKIP_THIS @@ -52,39 +52,41 @@ void archiveVector(Archive &ar, T **p, int size) { * @param s Solver instance to serialize */ template -void serialize(Archive &ar, amici::Solver &s, const unsigned int /*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_atol_; - ar &s.ss_rtol_; - ar &s.ss_atol_sensi_; - ar &s.ss_rtol_sensi_; - ar &s.maxsteps_; - ar &s.maxstepsB_; - ar &s.requires_preequilibration_; - ar &s.newton_maxsteps_; - ar &s.newton_maxlinsteps_; - 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.rdata_mode_; - ar &s.maxtime_; +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_; } /** @@ -93,14 +95,17 @@ void serialize(Archive &ar, amici::Solver &s, const unsigned int /*version*/) { * @param d Duration */ template -void serialize(Archive &ar, std::chrono::duration &d, const unsigned int /*version*/) { +void serialize( + Archive& ar, std::chrono::duration& d, + unsigned int const /*version*/ +) { 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; } } @@ -110,8 +115,10 @@ void serialize(Archive &ar, std::chrono::duration &d, const unsigne * @param s Solver instance to serialize */ template -void serialize(Archive &ar, amici::CVodeSolver &s, const unsigned int /*version*/) { - ar & static_cast(s); +void serialize( + Archive& ar, amici::CVodeSolver& s, unsigned int const /*version*/ +) { + ar& static_cast(s); } /** @@ -120,45 +127,48 @@ void serialize(Archive &ar, amici::CVodeSolver &s, const unsigned int /*version* * @param m Model instance to serialize */ template -void serialize(Archive &ar, amici::Model &m, const unsigned int /*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_; +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_; } - /** * @brief Serialize amici::SimulationParameters to boost archive * @param ar Archive * @param s amici::SimulationParameters instance to serialize */ template -void serialize(Archive &ar, amici::SimulationParameters &s, const unsigned int /*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; +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; } /** @@ -168,69 +178,69 @@ void serialize(Archive &ar, amici::SimulationParameters &s, const unsigned int / */ template -void serialize(Archive &ar, amici::ReturnData &r, const unsigned int /*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.preeq_cpu_time; - ar &r.preeq_cpu_timeB; - ar &r.preeq_status; - ar &r.preeq_numsteps; - ar &r.preeq_numlinsteps; - 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_numlinsteps; - 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; +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; } - /** * @brief Serialize amici::ModelDimensions to boost archive * @param ar Archive @@ -238,29 +248,32 @@ void serialize(Archive &ar, amici::ReturnData &r, const unsigned int /*version*/ */ template -void serialize(Archive &ar, amici::ModelDimensions &m, const unsigned int /*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.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; +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; } #endif } // namespace serialization @@ -276,31 +289,31 @@ namespace amici { * * @return The object serialized as char */ -template -char *serializeToChar(T const& data, int *size) { +template char* serializeToChar(T const& data, int* size) { try { std::string serialized; - ::boost::iostreams::back_insert_device inserter(serialized); - ::boost::iostreams::stream<::boost::iostreams::back_insert_device> + ::boost::iostreams::back_insert_device inserter(serialized + ); + ::boost::iostreams::stream< + ::boost::iostreams::back_insert_device> s(inserter); ::boost::archive::binary_oarchive oar(s); oar << data; s.flush(); - char *charBuffer = new char[serialized.size()]; + char* charBuffer = new char[serialized.size()]; memcpy(charBuffer, serialized.data(), serialized.size()); if (size) *size = serialized.size(); return charBuffer; - } catch(boost::archive::archive_exception const& e) { + } catch (boost::archive::archive_exception const& e) { throw AmiException("Serialization to char failed: %s", e.what()); } } - /** * @brief Deserialize object that has been serialized using serializeToChar * @@ -310,8 +323,7 @@ char *serializeToChar(T const& data, int *size) { * @return The deserialized object */ -template -T deserializeFromChar(const char *buffer, int size) { +template T deserializeFromChar(char const* buffer, int size) { namespace ba = ::boost::archive; namespace bio = ::boost::iostreams; @@ -324,7 +336,7 @@ T deserializeFromChar(const char *buffer, int size) { // archive must be destroyed BEFORE returning ba::binary_iarchive iar(s); iar >> data; - } catch(ba::archive_exception const& e) { + } catch (ba::archive_exception const& e) { throw AmiException("Deserialization from char failed: %s", e.what()); } return data; @@ -338,8 +350,7 @@ T deserializeFromChar(const char *buffer, int size) { * @return The object serialized as string */ -template -std::string serializeToString(T const& data) { +template std::string serializeToString(T const& data) { namespace ba = ::boost::archive; namespace bio = ::boost::iostreams; @@ -351,7 +362,7 @@ std::string serializeToString(T const& data) { // archive must be destroyed BEFORE returning ba::binary_oarchive oar(os); oar << data; - } catch(ba::archive_exception const& e) { + } catch (ba::archive_exception const& e) { throw AmiException("Serialization to string failed: %s", e.what()); } @@ -366,21 +377,18 @@ std::string serializeToString(T const& data) { * @return The object serialized as std::vector */ -template -std::vector serializeToStdVec(T const& data) { +template std::vector serializeToStdVec(T const& data) { namespace ba = ::boost::archive; namespace bio = ::boost::iostreams; std::vector buffer; - bio::stream< - bio::back_insert_device< - std::vector>> os(buffer); + bio::stream>> os(buffer); - try{ + try { // archive must be destroyed BEFORE returning ba::binary_oarchive oar(os); oar << data; - } catch(ba::archive_exception const& e) { + } catch (ba::archive_exception const& e) { throw AmiException("Serialization to std::vector failed: %s", e.what()); } @@ -395,8 +403,7 @@ std::vector serializeToStdVec(T const& data) { * @return The deserialized object */ -template -T deserializeFromString(std::string const& serialized) { +template T deserializeFromString(std::string const& serialized) { namespace ba = ::boost::archive; namespace bio = ::boost::iostreams; @@ -404,18 +411,18 @@ T deserializeFromString(std::string const& serialized) { bio::stream> os(device); T deserialized; - try{ + try { // archive must be destroyed BEFORE returning ba::binary_iarchive iar(os); iar >> deserialized; - } catch(ba::archive_exception const& e) { - throw AmiException("Deserialization from std::string failed: %s", - e.what()); + } catch (ba::archive_exception const& e) { + throw AmiException( + "Deserialization from std::string failed: %s", e.what() + ); } return deserialized; } - } // namespace amici #endif // AMICI_SERIALIZATION_H diff --git a/deps/AMICI/include/amici/simulation_parameters.h b/deps/AMICI/include/amici/simulation_parameters.h index bc5a110e6..ca0e127c5 100644 --- a/deps/AMICI/include/amici/simulation_parameters.h +++ b/deps/AMICI/include/amici/simulation_parameters.h @@ -11,7 +11,7 @@ namespace amici { * @brief Container for various simulation parameters. */ class SimulationParameters { -public: + public: SimulationParameters() = default; /** @@ -19,23 +19,21 @@ class SimulationParameters { * @param timepoints Timepoints for which simulation results are requested */ explicit SimulationParameters(std::vector timepoints) - : ts_(std::move(timepoints)) - { - } + : ts_(std::move(timepoints)) {} /** * @brief Constructor * @param fixedParameters Model constants * @param parameters Model parameters */ - SimulationParameters(std::vector fixedParameters, - std::vector parameters) - : fixedParameters(std::move(fixedParameters)), - parameters(std::move(parameters)), - pscale(std::vector(this->parameters.size(), - ParameterScaling::none)) - { - } + SimulationParameters( + std::vector fixedParameters, std::vector parameters + ) + : fixedParameters(std::move(fixedParameters)) + , parameters(std::move(parameters)) + , pscale(std::vector( + this->parameters.size(), ParameterScaling::none + )) {} /** * @brief Constructor @@ -44,17 +42,16 @@ class SimulationParameters { * @param plist Model parameter indices w.r.t. which sensitivities are to be * computed */ - SimulationParameters(std::vector fixedParameters, - std::vector parameters, - std::vector plist - ) - : fixedParameters(std::move(fixedParameters)), - parameters(std::move(parameters)), - pscale(std::vector(this->parameters.size(), - ParameterScaling::none)), - plist(std::move(plist)) - { - } + SimulationParameters( + std::vector fixedParameters, std::vector parameters, + std::vector plist + ) + : fixedParameters(std::move(fixedParameters)) + , parameters(std::move(parameters)) + , pscale(std::vector( + this->parameters.size(), ParameterScaling::none + )) + , plist(std::move(plist)) {} /** * @brief Constructor @@ -62,16 +59,16 @@ class SimulationParameters { * @param fixedParameters Model constants * @param parameters Model parameters */ - SimulationParameters(std::vector timepoints, - std::vector fixedParameters, - std::vector parameters) - : fixedParameters(std::move(fixedParameters)), - parameters(std::move(parameters)), - pscale(std::vector(this->parameters.size(), - ParameterScaling::none)), - ts_(std::move(timepoints)) - { - } + SimulationParameters( + std::vector timepoints, std::vector fixedParameters, + std::vector parameters + ) + : fixedParameters(std::move(fixedParameters)) + , parameters(std::move(parameters)) + , pscale(std::vector( + this->parameters.size(), ParameterScaling::none + )) + , ts_(std::move(timepoints)) {} /** * @brief Set reinitialization of all states based on model constants for @@ -83,7 +80,9 @@ class SimulationParameters { * * @param nx_rdata Number of states (Model::nx_rdata) */ - void reinitializeAllFixedParameterDependentInitialStatesForPresimulation(int nx_rdata); + void reinitializeAllFixedParameterDependentInitialStatesForPresimulation( + int nx_rdata + ); /** * @brief Set reinitialization of all states based on model constants for @@ -96,7 +95,9 @@ class SimulationParameters { * * @param nx_rdata Number of states (Model::nx_rdata) */ - void reinitializeAllFixedParameterDependentInitialStatesForSimulation(int nx_rdata); + void reinitializeAllFixedParameterDependentInitialStatesForSimulation( + int nx_rdata + ); /** * @brief Set reinitialization of all states based on model constants for @@ -120,7 +121,7 @@ class SimulationParameters { /** * @brief Model constants for pre-equilibration * - * Vector of size Model::nk() or empty. Overrides Solver::newton_preeq + * Vector of size Model::nk() or empty. */ std::vector fixedParametersPreequilibration; @@ -169,7 +170,7 @@ class SimulationParameters { std::vector plist; /** starting time */ - realtype tstart_ {0.0}; + realtype tstart_{0.0}; /** * @brief Duration of pre-simulation. @@ -178,7 +179,7 @@ class SimulationParameters { * (model->t0 - t_presim) to model->t0 using the fixedParameters in * fixedParametersPresimulation */ - realtype t_presim {0.0}; + realtype t_presim{0.0}; /** * @brief Timepoints for which model state/outputs/... are requested @@ -191,7 +192,7 @@ class SimulationParameters { * @brief Flag indicating whether reinitialization of states depending on * fixed parameters is activated */ - bool reinitializeFixedParameterInitialStates {false}; + bool reinitializeFixedParameterInitialStates{false}; /** * @brief Indices of states to be reinitialized based on provided @@ -206,7 +207,7 @@ class SimulationParameters { std::vector reinitialization_state_idxs_sim; }; -bool operator==(const SimulationParameters &a, const SimulationParameters &b); +bool operator==(SimulationParameters const& a, SimulationParameters const& b); } // namespace amici diff --git a/deps/AMICI/include/amici/solver.h b/deps/AMICI/include/amici/solver.h index 1c15a1afb..120a963ba 100644 --- a/deps/AMICI/include/amici/solver.h +++ b/deps/AMICI/include/amici/solver.h @@ -1,16 +1,16 @@ #ifndef AMICI_SOLVER_H #define AMICI_SOLVER_H -#include "amici/amici.h" #include "amici/defines.h" +#include "amici/logging.h" +#include "amici/misc.h" #include "amici/sundials_linsol_wrapper.h" -#include "amici/symbolic_functions.h" #include "amici/vector.h" +#include #include #include #include -#include namespace amici { @@ -19,18 +19,16 @@ class ForwardProblem; class BackwardProblem; class Model; class Solver; -class AmiciApplication; -extern AmiciApplication defaultContext; } // namespace amici // for serialization friend in Solver namespace boost { namespace serialization { template -void serialize(Archive &ar, amici::Solver &s, unsigned int version); +void serialize(Archive& ar, amici::Solver& s, unsigned int version); } -} // namespace boost::serialization +} // namespace boost namespace amici { @@ -44,29 +42,23 @@ namespace amici { * * NOTE: Any changes in data members here must be propagated to copy ctor, * equality operator, serialization functions in serialization.h, and - * amici::hdf5::readSolverSettingsFromHDF5 in hdf5.cpp. + * amici::hdf5::(read/write)SolverSettings(From/To)HDF5 in hdf5.cpp. */ class Solver { public: /** Type of what is passed to Sundials solvers as user_data */ - using user_data_type = std::pair; + using user_data_type = std::pair; /** * @brief Default constructor */ Solver() = default; - /** - * @brief Constructor - * @param app AMICI application context - */ - Solver(AmiciApplication *app); - /** * @brief Solver copy constructor * @param other */ - Solver(const Solver &other); + Solver(Solver const& other); virtual ~Solver() = default; @@ -74,7 +66,7 @@ class Solver { * @brief Clone this instance * @return The clone */ - virtual Solver *clone() const = 0; + virtual Solver* clone() const = 0; /** * @brief runs a forward simulation until the specified timepoint @@ -109,9 +101,10 @@ class Solver { * @param sdx0 initial derivative state sensitivities */ - void setup(realtype t0, Model *model, const AmiVector &x0, - const AmiVector &dx0, const AmiVectorArray &sx0, - const AmiVectorArray &sdx0) const; + void setup( + realtype t0, Model* model, AmiVector const& x0, AmiVector const& dx0, + AmiVectorArray const& sx0, AmiVectorArray const& sdx0 + ) const; /** * @brief Initializes the AMI memory object for the backwards problem @@ -123,8 +116,10 @@ class Solver { * @param xQB0 initial adjoint quadratures */ - void setupB(int *which, realtype tf, Model *model, const AmiVector &xB0, - const AmiVector &dxB0, const AmiVector &xQB0) const; + void setupB( + int* which, realtype tf, Model* model, AmiVector const& xB0, + AmiVector const& dxB0, AmiVector const& xQB0 + ) const; /** * @brief Initializes the ami memory for quadrature computation @@ -137,17 +132,19 @@ class Solver { * @param xQ0 initial quadrature vector */ - void setupSteadystate(const realtype t0, Model *model, const AmiVector &x0, - const AmiVector &dx0, const AmiVector &xB0, - const AmiVector &dxB0, const AmiVector &xQ0) const; + void setupSteadystate( + const realtype t0, Model* model, AmiVector const& x0, + AmiVector const& dx0, AmiVector const& xB0, AmiVector const& dxB0, + AmiVector const& xQ0 + ) const; /** - * @brief Reinitializes state and respective sensitivities (if necessary) according - * to changes in fixedParameters + * @brief Reinitializes state and respective sensitivities (if necessary) + * according to changes in fixedParameters * * @param model pointer to the model instance */ - void updateAndReinitStatesAndSensitivities(Model *model); + void updateAndReinitStatesAndSensitivities(Model* model) const; /** * getRootInfo extracts information which event occurred @@ -155,7 +152,7 @@ class Solver { * @param rootsfound array with flags indicating whether the respective * event occurred */ - virtual void getRootInfo(int *rootsfound) const = 0; + virtual void getRootInfo(int* rootsfound) const = 0; /** * @brief Calculates consistent initial conditions, assumes initial @@ -210,7 +207,8 @@ class Solver { * @brief Set sensitivity method for preequilibration * @param sensi_meth_preeq */ - void setSensitivityMethodPreequilibration(SensitivityMethod sensi_meth_preeq); + void setSensitivityMethodPreequilibration(SensitivityMethod sensi_meth_preeq + ); /** * @brief Disable forward sensitivity integration (used in steady state sim) @@ -231,32 +229,6 @@ class Solver { */ void setNewtonMaxSteps(int newton_maxsteps); - /** - * @brief Get if model preequilibration is enabled - * @return - */ - bool getPreequilibration() const; - - /** - * @brief Enable/disable model preequilibration - * @param require_preequilibration - */ - void setPreequilibration(bool require_preequilibration); - - /** - * @brief Get maximum number of allowed linear steps per Newton step for - * steady state computation - * @return - */ - int getNewtonMaxLinearSteps() const; - - /** - * @brief Set maximum number of allowed linear steps per Newton step for - * steady state computation - * @param newton_maxlinsteps - */ - void setNewtonMaxLinearSteps(int newton_maxlinsteps); - /** * @brief Get a state of the damping factor used in the Newton solver * @return @@ -410,6 +382,26 @@ class Solver { */ void setAbsoluteToleranceQuadratures(double atol); + /** + * @brief returns the steady state simulation tolerance factor. + * + * Steady state simulation tolerances are the product of the simulation + * tolerances and this factor, unless manually set with + * `set(Absolute/Relative)ToleranceSteadyState()`. + * @return steady state simulation tolerance factor + */ + double getSteadyStateToleranceFactor() const; + + /** + * @brief set the steady state simulation tolerance factor. + * + * Steady state simulation tolerances are the product of the simulation + * tolerances and this factor, unless manually set with + * `set(Absolute/Relative)ToleranceSteadyState()`. + * @param factor tolerance factor (non-negative number) + */ + void setSteadyStateToleranceFactor(double factor); + /** * @brief returns the relative tolerance for the steady state problem * @return relative tolerance @@ -434,6 +426,26 @@ class Solver { */ void setAbsoluteToleranceSteadyState(double atol); + /** + * @brief returns the steady state sensitivity simulation tolerance factor. + * + * Steady state sensitivity simulation tolerances are the product of the + * sensitivity simulation tolerances and this factor, unless manually set + * with `set(Absolute/Relative)ToleranceSteadyStateSensi()`. + * @return steady state simulation tolerance factor + */ + double getSteadyStateSensiToleranceFactor() const; + + /** + * @brief set the steady state sensitivity simulation tolerance factor. + * + * Steady state sensitivity simulation tolerances are the product of the + * sensitivity simulation tolerances and this factor, unless manually set + * with `set(Absolute/Relative)ToleranceSteadyStateSensi()`. + * @param factor tolerance factor (non-negative number) + */ + void setSteadyStateSensiToleranceFactor(double factor); + /** * @brief returns the relative tolerance for the sensitivities of the * steady state problem @@ -482,8 +494,8 @@ class Solver { double getMaxTime() const; /** - * @brief Set the maximum time allowed for integration - * @param maxtime Time in seconds + * @brief Set the maximum CPU time allowed for integration + * @param maxtime Time in seconds. Zero means infinite time. */ void setMaxTime(double maxtime); @@ -494,10 +506,13 @@ class Solver { /** * @brief Check whether maximum integration time was exceeded + * @param interval Only check the time every ``interval`` ths call to avoid + * potentially relatively expensive syscalls + * @return True if the maximum integration time was exceeded, * false otherwise. */ - bool timeExceeded() const; + bool timeExceeded(int interval = 1) const; /** * @brief returns the maximum number of solver steps for the backward @@ -628,40 +643,43 @@ class Solver { * @param sx state sensitivity * @param xQ quadrature */ - void writeSolution(realtype *t, AmiVector &x, AmiVector &dx, - AmiVectorArray &sx, AmiVector &xQ) const; + void writeSolution( + realtype* t, AmiVector& x, AmiVector& dx, AmiVectorArray& sx, + AmiVector& xQ + ) const; /** - * @brief write solution from forward simulation + * @brief write solution from backward simulation * @param t time * @param xB adjoint state * @param dxB adjoint derivative state * @param xQB adjoint quadrature * @param which index of adjoint problem */ - void writeSolutionB(realtype *t, AmiVector &xB, AmiVector &dxB, - AmiVector &xQB, int which) const; + void writeSolutionB( + realtype* t, AmiVector& xB, AmiVector& dxB, AmiVector& xQB, int which + ) const; /** * @brief Access state solution at time t * @param t time * @return x or interpolated solution dky */ - const AmiVector &getState(realtype t) const; + AmiVector const& getState(realtype t) const; /** * @brief Access derivative state solution at time t * @param t time * @return dx or interpolated solution dky */ - const AmiVector &getDerivativeState(realtype t) const; + AmiVector const& getDerivativeState(realtype t) const; /** * @brief Access state sensitivity solution at time t * @param t time * @return (interpolated) solution sx */ - const AmiVectorArray &getStateSensitivity(realtype t) const; + AmiVectorArray const& getStateSensitivity(realtype t) const; /** * @brief Access adjoint solution at time t @@ -669,7 +687,7 @@ class Solver { * @param t time * @return (interpolated) solution xB */ - const AmiVector &getAdjointState(int which, realtype t) const; + AmiVector const& getAdjointState(int which, realtype t) const; /** * @brief Access adjoint derivative solution at time t @@ -677,7 +695,7 @@ class Solver { * @param t time * @return (interpolated) solution dxB */ - const AmiVector &getAdjointDerivativeState(int which, realtype t) const; + AmiVector const& getAdjointDerivativeState(int which, realtype t) const; /** * @brief Access adjoint quadrature solution at time t @@ -685,14 +703,14 @@ class Solver { * @param t time * @return (interpolated) solution xQB */ - const AmiVector &getAdjointQuadrature(int which, realtype t) const; + AmiVector const& getAdjointQuadrature(int which, realtype t) const; /** * @brief Access quadrature solution at time t * @param t time * @return (interpolated) solution xQ */ - const AmiVector &getQuadrature(realtype t) const; + AmiVector const& getQuadrature(realtype t) const; /** * @brief Reinitializes the states in the solver after an event occurrence @@ -701,8 +719,9 @@ class Solver { * @param yy0 initial state variables * @param yp0 initial derivative state variables (DAE only) */ - virtual void reInit(realtype t0, const AmiVector &yy0, - const AmiVector &yp0) const = 0; + virtual void + reInit(realtype t0, AmiVector const& yy0, AmiVector const& yp0) const + = 0; /** * @brief Reinitializes the state sensitivities in the solver after an @@ -711,8 +730,9 @@ class Solver { * @param yyS0 new state sensitivity * @param ypS0 new derivative state sensitivities (DAE only) */ - virtual void sensReInit(const AmiVectorArray &yyS0, - const AmiVectorArray &ypS0) const = 0; + virtual void + sensReInit(AmiVectorArray const& yyS0, AmiVectorArray const& ypS0) const + = 0; /** * @brief Switches off computation of state sensitivities without @@ -728,8 +748,10 @@ class Solver { * @param yyB0 new adjoint state * @param ypB0 new adjoint derivative state */ - virtual void reInitB(int which, realtype tB0, const AmiVector &yyB0, - const AmiVector &ypB0) const = 0; + virtual void reInitB( + int which, realtype tB0, AmiVector const& yyB0, AmiVector const& ypB0 + ) const + = 0; /** * @brief Reinitialize the adjoint states after an event occurrence @@ -737,7 +759,7 @@ class Solver { * @param which identifier of the backwards problem * @param yQB0 new adjoint quadrature state */ - virtual void quadReInitB(int which, const AmiVector &yQB0) const = 0; + virtual void quadReInitB(int which, AmiVector const& yQB0) const = 0; /** * @brief current solver timepoint @@ -780,8 +802,9 @@ class Solver { * @return flag */ bool computingFSA() const { - return getSensitivityOrder() >= SensitivityOrder::first && - getSensitivityMethod() == SensitivityMethod::forward && nplist() > 0; + return getSensitivityOrder() >= SensitivityOrder::first + && getSensitivityMethod() == SensitivityMethod::forward + && nplist() > 0; } /** @@ -789,8 +812,9 @@ class Solver { * @return flag */ bool computingASA() const { - return getSensitivityOrder() >= SensitivityOrder::first && - getSensitivityMethod() == SensitivityMethod::adjoint && nplist() > 0; + return getSensitivityOrder() >= SensitivityOrder::first + && getSensitivityMethod() == SensitivityMethod::adjoint + && nplist() > 0; } /** @@ -799,12 +823,14 @@ class Solver { void resetDiagnosis() const; /** - * @brief Stores diagnosis information from solver memory block for forward problem + * @brief Stores diagnosis information from solver memory block for forward + * problem */ void storeDiagnosis() const; /** - * @brief Stores diagnosis information from solver memory block for backward problem + * @brief Stores diagnosis information from solver memory block for backward + * problem * * @param which identifier of the backwards problem */ @@ -814,49 +840,37 @@ class Solver { * @brief Accessor ns * @return ns */ - std::vector const& getNumSteps() const { - return ns_; - } + std::vector const& getNumSteps() const { return ns_; } /** * @brief Accessor nsB * @return nsB */ - std::vector const& getNumStepsB() const { - return nsB_; - } + std::vector const& getNumStepsB() const { return nsB_; } /** * @brief Accessor nrhs * @return nrhs */ - std::vector const& getNumRhsEvals() const { - return nrhs_; - } + std::vector const& getNumRhsEvals() const { return nrhs_; } /** * @brief Accessor nrhsB * @return nrhsB */ - std::vector const& getNumRhsEvalsB() const { - return nrhsB_; - } + std::vector const& getNumRhsEvalsB() const { return nrhsB_; } /** * @brief Accessor netf * @return netf */ - std::vector const& getNumErrTestFails() const { - return netf_; - } + std::vector const& getNumErrTestFails() const { return netf_; } /** * @brief Accessor netfB * @return netfB */ - std::vector const& getNumErrTestFailsB() const { - return netfB_; - } + std::vector const& getNumErrTestFailsB() const { return netfB_; } /** * @brief Accessor nnlscf @@ -878,8 +892,47 @@ class Solver { * @brief Accessor order * @return order */ - std::vector const& getLastOrder() const { - return order_; + std::vector const& getLastOrder() const { return order_; } + + /** + * @brief Returns how convergence checks for steadystate computation are + * performed. If activated, convergence checks are limited to every 25 steps + * in the simulation solver to limit performance impact. + * @return boolean flag indicating newton step (true) or the right hand side + * (false) + */ + bool getNewtonStepSteadyStateCheck() const { + return newton_step_steadystate_conv_; + } + + /** + * @brief Returns how convergence checks for steadystate computation are + * performed. + * @return boolean flag indicating state and sensitivity equations (true) or + * only state variables (false). + */ + bool getSensiSteadyStateCheck() const { + return check_sensi_steadystate_conv_; + } + + /** + * @brief Sets how convergence checks for steadystate computation are + * performed. + * @param flag boolean flag to pick newton step (true) or the right hand + * side (false, default) + */ + void setNewtonStepSteadyStateCheck(bool flag) { + newton_step_steadystate_conv_ = flag; + } + + /** + * @brief Sets for which variables convergence checks for steadystate + * computation are performed. + * @param flag boolean flag to pick state and sensitivity equations (true, + * default) or only state variables (false). + */ + void setSensiSteadyStateCheck(bool flag) { + check_sensi_steadystate_conv_ = flag; } /** @@ -889,8 +942,9 @@ class Solver { * @param version Version number */ template - friend void boost::serialization::serialize(Archive &ar, Solver &s, - unsigned int version); + friend void boost::serialization::serialize( + Archive& ar, Solver& s, unsigned int version + ); /** * @brief Check equality of data members excluding solver memory @@ -898,10 +952,10 @@ class Solver { * @param b * @return */ - friend bool operator==(const Solver &a, const Solver &b); + friend bool operator==(Solver const& a, Solver const& b); - /** AMICI context */ - AmiciApplication *app = &defaultContext; + /** logger */ + Logger* logger = nullptr; protected: /** @@ -930,7 +984,7 @@ class Solver { * checkpoints * @return status flag indicating success of execution */ - virtual int solveF(realtype tout, int itask, int *ncheckPtr) const = 0; + virtual int solveF(realtype tout, int itask, int* ncheckPtr) const = 0; /** * @brief reInitPostProcessF postprocessing of the solver memory after a @@ -972,7 +1026,7 @@ class Solver { * * @param t timepoint for quadrature extraction */ - virtual void getQuad(realtype &t) const = 0; + virtual void getQuad(realtype& t) const = 0; /** * @brief Initializes the states at the specified initial timepoint @@ -981,8 +1035,9 @@ class Solver { * @param x0 initial states * @param dx0 initial derivative states */ - virtual void init(realtype t0, const AmiVector &x0, - const AmiVector &dx0) const = 0; + virtual void + init(realtype t0, AmiVector const& x0, AmiVector const& dx0) const + = 0; /** * @brief Initializes the states at the specified initial timepoint @@ -991,16 +1046,19 @@ class Solver { * @param x0 initial states * @param dx0 initial derivative states */ - virtual void initSteadystate(realtype t0, const AmiVector &x0, - const AmiVector &dx0) const = 0; + virtual void initSteadystate( + realtype t0, AmiVector const& x0, AmiVector const& dx0 + ) const + = 0; /** * @brief Initializes the forward sensitivities * @param sx0 initial states sensitivities * @param sdx0 initial derivative states sensitivities */ - virtual void sensInit1(const AmiVectorArray &sx0, - const AmiVectorArray &sdx0) const = 0; + virtual void + sensInit1(AmiVectorArray const& sx0, AmiVectorArray const& sdx0) const + = 0; /** * @brief Initialize the adjoint states at the specified final timepoint @@ -1010,8 +1068,10 @@ class Solver { * @param xB0 initial adjoint state * @param dxB0 initial adjoint derivative state */ - virtual void binit(int which, realtype tf, const AmiVector &xB0, - const AmiVector &dxB0) const = 0; + virtual void binit( + int which, realtype tf, AmiVector const& xB0, AmiVector const& dxB0 + ) const + = 0; /** * @brief Initialize the quadrature states at the specified final timepoint @@ -1019,7 +1079,7 @@ class Solver { * @param which identifier of the backwards problem * @param xQB0 initial adjoint quadrature state */ - virtual void qbinit(int which, const AmiVector &xQB0) const = 0; + virtual void qbinit(int which, AmiVector const& xQB0) const = 0; /** * @brief Initializes the rootfinding for events @@ -1032,7 +1092,7 @@ class Solver { * @brief Initalize non-linear solver for sensitivities * @param model Model instance */ - void initializeNonLinearSolverSens(const Model *model) const; + void initializeNonLinearSolverSens(Model const* model) const; /** * @brief Set the dense Jacobian function @@ -1109,7 +1169,7 @@ class Solver { * @param rtol relative tolerances * @param atol array of absolute tolerances for every sensitivity variable */ - virtual void setSensSStolerances(double rtol, const double *atol) const = 0; + virtual void setSensSStolerances(double rtol, double const* atol) const = 0; /** * SetSensErrCon specifies whether error control is also enforced for @@ -1160,7 +1220,8 @@ class Solver { * problem * * @param mxsteps number of steps - * @note in contrast to the SUNDIALS method, this sets the overall maximum, not the maximum between output times. + * @note in contrast to the SUNDIALS method, this sets the overall maximum, + * not the maximum between output times. */ virtual void setMaxNumSteps(long int mxsteps) const = 0; @@ -1170,7 +1231,8 @@ class Solver { * * @param which identifier of the backwards problem * @param mxstepsB number of steps - * @note in contrast to the SUNDIALS method, this sets the overall maximum, not the maximum between output times. + * @note in contrast to the SUNDIALS method, this sets the overall maximum, + * not the maximum between output times. */ virtual void setMaxNumStepsB(int which, long int mxstepsB) const = 0; @@ -1198,7 +1260,7 @@ class Solver { * * @param model model specification */ - virtual void setId(const Model *model) const = 0; + virtual void setId(Model const* model) const = 0; /** * @brief deactivates error control for algebraic components (DAE only) @@ -1215,8 +1277,10 @@ class Solver { * @param pbar parameter scaling constants * @param plist parameter index list */ - virtual void setSensParams(const realtype *p, const realtype *pbar, - const int *plist) const = 0; + virtual void setSensParams( + realtype const* p, realtype const* pbar, int const* plist + ) const + = 0; /** * @brief interpolates the (derivative of the) solution at the requested @@ -1274,7 +1338,7 @@ class Solver { * @brief initializes the quadratures * @param xQ0 vector with initial values for xQ */ - virtual void quadInit(const AmiVector &xQ0) const = 0; + virtual void quadInit(AmiVector const& xQ0) const = 0; /** * @brief Specifies solver method and initializes solver memory for the @@ -1282,7 +1346,7 @@ class Solver { * * @param which identifier of the backwards problem */ - virtual void allocateSolverB(int *which) const = 0; + virtual void allocateSolverB(int* which) const = 0; /** * @brief sets relative and absolute tolerances for the backward @@ -1292,8 +1356,9 @@ class Solver { * @param relTolB relative tolerances * @param absTolB absolute tolerances */ - virtual void setSStolerancesB(int which, realtype relTolB, - realtype absTolB) const = 0; + virtual void + setSStolerancesB(int which, realtype relTolB, realtype absTolB) const + = 0; /** * @brief sets relative and absolute tolerances for the quadrature @@ -1303,8 +1368,9 @@ class Solver { * @param reltolQB relative tolerances * @param abstolQB absolute tolerances */ - virtual void quadSStolerancesB(int which, realtype reltolQB, - realtype abstolQB) const = 0; + virtual void + quadSStolerancesB(int which, realtype reltolQB, realtype abstolQB) const + = 0; /** * @brief sets relative and absolute tolerances for the quadrature problem @@ -1312,8 +1378,8 @@ class Solver { * @param reltolQB relative tolerances * @param abstolQB absolute tolerances */ - virtual void quadSStolerances(realtype reltolQB, - realtype abstolQB) const = 0; + virtual void quadSStolerances(realtype reltolQB, realtype abstolQB) const + = 0; /** * @brief reports the number of solver steps @@ -1322,7 +1388,7 @@ class Solver { * forward or backward problem) * @param numsteps output array */ - virtual void getNumSteps(const void *ami_mem, long int *numsteps) const = 0; + virtual void getNumSteps(void const* ami_mem, long int* numsteps) const = 0; /** * @brief reports the number of right hand evaluations @@ -1331,8 +1397,9 @@ class Solver { * forward or backward problem) * @param numrhsevals output array */ - virtual void getNumRhsEvals(const void *ami_mem, - long int *numrhsevals) const = 0; + virtual void + getNumRhsEvals(void const* ami_mem, long int* numrhsevals) const + = 0; /** * @brief reports the number of local error test failures @@ -1341,8 +1408,9 @@ class Solver { * forward or backward problem) * @param numerrtestfails output array */ - virtual void getNumErrTestFails(const void *ami_mem, - long int *numerrtestfails) const = 0; + virtual void + getNumErrTestFails(void const* ami_mem, long int* numerrtestfails) const + = 0; /** * @brief reports the number of nonlinear convergence failures @@ -1351,9 +1419,10 @@ class Solver { * forward or backward problem) * @param numnonlinsolvconvfails output array */ - virtual void - getNumNonlinSolvConvFails(const void *ami_mem, - long int *numnonlinsolvconvfails) const = 0; + virtual void getNumNonlinSolvConvFails( + void const* ami_mem, long int* numnonlinsolvconvfails + ) const + = 0; /** * @brief Reports the order of the integration method during the @@ -1363,14 +1432,14 @@ class Solver { * forward or backward problem) * @param order output array */ - virtual void getLastOrder(const void *ami_mem, int *order) const = 0; + virtual void getLastOrder(void const* ami_mem, int* order) const = 0; /** * @brief Initializes and sets the linear solver for the forward problem * * @param model pointer to the model object */ - void initializeLinearSolver(const Model *model) const; + void initializeLinearSolver(Model const* model) const; /** * @brief Sets the non-linear solver @@ -1411,7 +1480,7 @@ class Solver { * @param which index of the backward problem */ - void initializeLinearSolverB(const Model *model, int which) const; + void initializeLinearSolverB(Model const* model, int which) const; /** * @brief Initializes the non-linear solver for the backward problem @@ -1424,7 +1493,7 @@ class Solver { * * @return user data model */ - virtual const Model *getModel() const = 0; + virtual Model const* getModel() const = 0; /** * @brief checks whether memory for the forward problem has been allocated @@ -1496,7 +1565,7 @@ class Solver { * @return A (void *) pointer to the CVODES memory allocated for the * backward problem. */ - virtual void *getAdjBmem(void *ami_mem, int which) const = 0; + virtual void* getAdjBmem(void* ami_mem, int which) const = 0; /** * @brief updates solver tolerances according to the currently specified @@ -1539,10 +1608,10 @@ class Solver { void applySensitivityTolerances() 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 */ @@ -1550,30 +1619,30 @@ class Solver { /** internal sensitivity method flag used to select the sensitivity solution * method. Only applies for Forward Sensitivities. */ - InternalSensitivityMethod ism_ {InternalSensitivityMethod::simultaneous}; + InternalSensitivityMethod ism_{InternalSensitivityMethod::simultaneous}; /** specifies the linear multistep method. */ - LinearMultistepMethod lmm_ {LinearMultistepMethod::BDF}; + LinearMultistepMethod lmm_{LinearMultistepMethod::BDF}; /** * specifies the type of nonlinear solver iteration */ - NonlinearSolverIteration iter_ {NonlinearSolverIteration::newton}; + NonlinearSolverIteration iter_{NonlinearSolverIteration::newton}; /** interpolation type for the forward problem solution which * is then used for the backwards problem. */ - InterpolationType interp_type_ {InterpolationType::hermite}; + InterpolationType interp_type_{InterpolationType::polynomial}; /** maximum number of allowed integration steps */ - long int maxsteps_ {10000}; + long int maxsteps_{10000}; - /** Maximum wall-time for integration in seconds */ - std::chrono::duration> maxtime_ {std::chrono::duration::max()}; + /** Maximum CPU-time for integration in seconds */ + std::chrono::duration> maxtime_{0}; /** Time at which solver timer was started */ - mutable std::chrono::time_point starttime_; + mutable CpuTimer simulation_timer_; /** linear solver for the forward problem */ mutable std::unique_ptr linear_solver_; @@ -1591,10 +1660,10 @@ class Solver { mutable std::unique_ptr non_linear_solver_sens_; /** flag indicating whether the forward solver has been called */ - mutable bool solver_was_called_F_ {false}; + mutable bool solver_was_called_F_{false}; /** flag indicating whether the backward solver has been called */ - mutable bool solver_was_called_B_ {false}; + mutable bool solver_was_called_B_{false}; /** * @brief sets that memory for the forward problem has been allocated @@ -1638,48 +1707,51 @@ class Solver { * @param sensi_meth new value for sensi_meth[_preeq] * @param preequilibration flag indicating preequilibration or simulation */ - void checkSensitivityMethod(const SensitivityMethod sensi_meth, - bool preequilibration) const; + void checkSensitivityMethod( + const SensitivityMethod sensi_meth, bool preequilibration + ) const; /** state (dimension: nx_solver) */ - mutable AmiVector x_ {0}; + mutable AmiVector x_{0}; /** state interface variable (dimension: nx_solver) */ - mutable AmiVector dky_ {0}; + mutable AmiVector dky_{0}; /** state derivative dummy (dimension: nx_solver) */ - mutable AmiVector dx_ {0}; + mutable AmiVector dx_{0}; /** state sensitivities interface variable (dimension: nx_solver x nplist) */ - mutable AmiVectorArray sx_ {0, 0}; + mutable AmiVectorArray sx_{0, 0}; /** state derivative sensitivities dummy (dimension: nx_solver x nplist) */ - mutable AmiVectorArray sdx_ {0, 0}; + mutable AmiVectorArray sdx_{0, 0}; /** adjoint state interface variable (dimension: nx_solver) */ - mutable AmiVector xB_ {0}; + mutable AmiVector xB_{0}; /** adjoint derivative dummy variable (dimension: nx_solver) */ - mutable AmiVector dxB_ {0}; + mutable AmiVector dxB_{0}; /** adjoint quadrature interface variable (dimension: nJ x nplist) */ - mutable AmiVector xQB_ {0}; + mutable AmiVector xQB_{0}; /** forward quadrature interface variable (dimension: nx_solver) */ - mutable AmiVector xQ_ {0}; + mutable AmiVector xQ_{0}; /** integration time of the forward problem */ - mutable realtype t_ {std::nan("")}; + mutable realtype t_{std::nan("")}; /** flag to force reInitPostProcessF before next call to solve */ - mutable bool force_reinit_postprocess_F_ {false}; + mutable bool force_reinit_postprocess_F_{false}; /** flag to force reInitPostProcessB before next call to solveB */ - mutable bool force_reinit_postprocess_B_ {false}; + mutable bool force_reinit_postprocess_B_{false}; - private: + /** flag indicating whether sensInit1 was called */ + mutable bool sens_initialized_{false}; + private: /** * @brief applies total number of steps for next solver call */ @@ -1690,100 +1762,106 @@ class Solver { */ void apply_max_num_steps_B() const; - /** method for sensitivity computation */ - SensitivityMethod sensi_meth_ {SensitivityMethod::forward}; + SensitivityMethod sensi_meth_{SensitivityMethod::forward}; /** method for sensitivity computation in preequilibration */ - SensitivityMethod sensi_meth_preeq_ {SensitivityMethod::forward}; + SensitivityMethod sensi_meth_preeq_{SensitivityMethod::forward}; /** flag controlling stability limit detection */ - booleantype stldet_ {true}; + booleantype stldet_{true}; /** state ordering */ - int ordering_ {static_cast(SUNLinSolKLU::StateOrdering::AMD)}; + int ordering_{static_cast(SUNLinSolKLU::StateOrdering::AMD)}; /** maximum number of allowed Newton steps for steady state computation */ - long int newton_maxsteps_ {0L}; + long int newton_maxsteps_{0L}; /** maximum number of allowed linear steps per Newton step for steady state * computation */ - long int newton_maxlinsteps_ {0L}; + long int newton_maxlinsteps_{0L}; /** Damping factor state used int the Newton method */ - NewtonDampingFactorMode newton_damping_factor_mode_ - {NewtonDampingFactorMode::on}; + NewtonDampingFactorMode newton_damping_factor_mode_{ + NewtonDampingFactorMode::on}; /** Lower bound of the damping factor. */ - realtype newton_damping_factor_lower_bound_ {1e-8}; - - /** Enable model preequilibration */ - bool requires_preequilibration_ {false}; + realtype newton_damping_factor_lower_bound_{1e-8}; /** linear solver specification */ - LinearSolver linsol_ {LinearSolver::KLU}; + LinearSolver linsol_{LinearSolver::KLU}; /** absolute tolerances for integration */ - realtype atol_ {1e-16}; + realtype atol_{1e-16}; /** relative tolerances for integration */ - realtype rtol_ {1e-8}; + realtype rtol_{1e-8}; /** absolute tolerances for forward sensitivity integration */ - realtype atol_fsa_ {NAN}; + realtype atol_fsa_{NAN}; /** relative tolerances for forward sensitivity integration */ - realtype rtol_fsa_ {NAN}; + realtype rtol_fsa_{NAN}; /** absolute tolerances for adjoint sensitivity integration */ - realtype atolB_ {NAN}; + realtype atolB_{NAN}; /** relative tolerances for adjoint sensitivity integration */ - realtype rtolB_ {NAN}; + realtype rtolB_{NAN}; /** absolute tolerances for backward quadratures */ - realtype quad_atol_ {1e-12}; + realtype quad_atol_{1e-12}; /** relative tolerances for backward quadratures */ - realtype quad_rtol_ {1e-8}; + realtype quad_rtol_{1e-8}; + + /** steady state simulation tolerance factor */ + realtype ss_tol_factor_{1e2}; /** absolute tolerances for steadystate computation */ - realtype ss_atol_ {NAN}; + realtype ss_atol_{NAN}; /** relative tolerances for steadystate computation */ - realtype ss_rtol_ {NAN}; + realtype ss_rtol_{NAN}; - /** absolute tolerances for steadystate computation */ - realtype ss_atol_sensi_ {NAN}; + /** steady state sensitivity simulation tolerance factor */ + realtype ss_tol_sensi_factor_{1e2}; - /** relative tolerances for steadystate computation */ - realtype ss_rtol_sensi_ {NAN}; + /** absolute tolerances for steadystate sensitivity computation */ + realtype ss_atol_sensi_{NAN}; + + /** relative tolerances for steadystate sensitivity computation */ + realtype ss_rtol_sensi_{NAN}; + + RDataReporting rdata_mode_{RDataReporting::full}; - RDataReporting rdata_mode_ {RDataReporting::full}; + /** whether newton step should be used for convergence steps */ + bool newton_step_steadystate_conv_{false}; + + /** whether sensitivities should be checked for convergence to steadystate + */ + bool check_sensi_steadystate_conv_{true}; /** CPU time, forward solve */ - mutable realtype cpu_time_ {0.0}; + mutable realtype cpu_time_{0.0}; /** CPU time, backward solve */ - mutable realtype cpu_timeB_ {0.0}; + mutable realtype cpu_timeB_{0.0}; /** maximum number of allowed integration steps for backward problem */ - long int maxstepsB_ {0L}; + long int maxstepsB_{0L}; /** flag indicating whether sensitivities are supposed to be computed */ - SensitivityOrder sensi_ {SensitivityOrder::none}; + SensitivityOrder sensi_{SensitivityOrder::none}; /** flag indicating whether init was called */ - mutable bool initialized_ {false}; - - /** flag indicating whether sensInit1 was called */ - mutable bool sens_initialized_ {false}; + mutable bool initialized_{false}; /** flag indicating whether adjInit was called */ - mutable bool adj_initialized_ {false}; + mutable bool adj_initialized_{false}; /** flag indicating whether (forward) quadInit was called */ - mutable bool quad_initialized_ {false}; + mutable bool quad_initialized_{false}; /** vector of flags indicating whether binit was called for respective which */ @@ -1794,7 +1872,7 @@ class Solver { mutable std::vector initializedQB_{false}; /** number of checkpoints in the forward problem */ - mutable int ncheckPtr_ {0}; + mutable int ncheckPtr_{0}; /** number of integration steps forward problem (dimension: nt) */ mutable std::vector ns_; @@ -1805,7 +1883,8 @@ class Solver { /** number of right hand side evaluations forward problem (dimension: nt) */ mutable std::vector nrhs_; - /** number of right hand side evaluations backward problem (dimension: nt) */ + /** number of right hand side evaluations backward problem (dimension: nt) + */ mutable std::vector nrhsB_; /** number of error test failures forward problem (dimension: nt) */ @@ -1828,7 +1907,7 @@ class Solver { mutable std::vector order_; }; -bool operator==(const Solver &a, const Solver &b); +bool operator==(Solver const& a, Solver const& b); /** * @brief Extracts diagnosis information from solver memory block and @@ -1840,8 +1919,10 @@ bool operator==(const Solver &a, const Solver &b); * @param msg error message * @param eh_data amici::Solver as void* */ -void wrapErrHandlerFn(int error_code, const char *module, const char *function, - char *msg, void *eh_data); +void wrapErrHandlerFn( + int error_code, char const* module, char const* function, char* msg, + void* eh_data +); } // namespace amici diff --git a/deps/AMICI/include/amici/solver_cvodes.h b/deps/AMICI/include/amici/solver_cvodes.h index 1f457418d..d6d1dcea2 100644 --- a/deps/AMICI/include/amici/solver_cvodes.h +++ b/deps/AMICI/include/amici/solver_cvodes.h @@ -18,9 +18,9 @@ class CVodeSolver; namespace boost { namespace serialization { template -void serialize(Archive &ar, amici::CVodeSolver &s, unsigned int version); +void serialize(Archive& ar, amici::CVodeSolver& s, unsigned int version); } -} // namespace boost::serialization +} // namespace boost namespace amici { @@ -38,25 +38,25 @@ class CVodeSolver : public Solver { * @brief Clone this instance * @return The clone */ - Solver *clone() const override; + Solver* clone() const override; - void reInit(realtype t0, const AmiVector &yy0, - const AmiVector &yp0) const override; + void reInit(realtype t0, AmiVector const& yy0, AmiVector const& yp0) + const override; - void sensReInit(const AmiVectorArray &yyS0, - const AmiVectorArray &ypS0) const override; + void sensReInit(AmiVectorArray const& yyS0, AmiVectorArray const& ypS0) + const override; void sensToggleOff() const override; - void reInitB(int which, realtype tB0, - const AmiVector &yyB0, const AmiVector &ypB0) const override; + void reInitB( + int which, realtype tB0, AmiVector const& yyB0, AmiVector const& ypB0 + ) const override; - void quadReInitB(int which, const AmiVector &yQB0) const override; + void quadReInitB(int which, AmiVector const& yQB0) const override; int solve(realtype tout, int itask) const override; - int solveF(realtype tout, int itask, - int *ncheckPtr) const override; + int solveF(realtype tout, int itask, int* ncheckPtr) const override; void solveB(realtype tBout, int itaskB) const override; @@ -64,18 +64,17 @@ class CVodeSolver : public Solver { void getSensDky(realtype t, int k) const override; - void getQuadDkyB(realtype t, int k, - int which) const override; + void getQuadDkyB(realtype t, int k, int which) const override; void getDkyB(realtype t, int k, int which) const override; - void getRootInfo(int *rootsfound) const override; + void getRootInfo(int* rootsfound) const override; void setStopTime(realtype tstop) const override; void turnOffRootFinding() const override; - const Model *getModel() const override; + Model const* getModel() const override; #if !defined(EXHALE_DOXYGEN_SHOULD_SKIP_THIS) using Solver::setLinearSolver; @@ -93,7 +92,6 @@ class CVodeSolver : public Solver { void setNonLinearSolverB(int which) const override; protected: - void calcIC(realtype tout1) const override; void calcICB(int which, realtype tout1) const override; @@ -104,7 +102,7 @@ class CVodeSolver : public Solver { void getQuadB(int which) const override; - void getQuad(realtype &t) const override; + void getQuad(realtype& t) const override; void getQuadDky(realtype t, int k) const override; @@ -119,15 +117,15 @@ class CVodeSolver : public Solver { * @param yout new state vector * @param tout anticipated next integration timepoint. */ - void reInitPostProcess(void *cv_mem, realtype *t, AmiVector *yout, - realtype tout) const; + void reInitPostProcess( + void* cv_mem, realtype* t, AmiVector* yout, realtype tout + ) const; void allocateSolver() const override; void setSStolerances(double rtol, double atol) const override; - void setSensSStolerances(double rtol, - const double *atol) const override; + void setSensSStolerances(double rtol, double const* atol) const override; void setSensErrCon(bool error_corr) const override; @@ -147,31 +145,33 @@ class CVodeSolver : public Solver { void setStabLimDetB(int which, int stldet) const override; - void setId(const Model *model) const override; + void setId(Model const* model) const override; void setSuppressAlg(bool flag) const override; /** - * @brief resetState reset the CVODES solver to restart integration after a rhs discontinuity. + * @brief resetState reset the CVODES solver to restart integration after a + * rhs discontinuity. * @param cv_mem pointer to CVODES solver memory object * @param y0 new state vector */ - void resetState(void *cv_mem, const_N_Vector y0) const; + void resetState(void* cv_mem, const_N_Vector y0) const; - void setSensParams(const realtype *p, const realtype *pbar, - const int *plist) const override; + void setSensParams( + realtype const* p, realtype const* pbar, int const* plist + ) const override; void adjInit() const override; - void quadInit(const AmiVector &xQ0) const override; + void quadInit(AmiVector const& xQ0) const override; - void allocateSolverB(int *which) const override; + void allocateSolverB(int* which) const override; - void setSStolerancesB(int which, realtype relTolB, - realtype absTolB) const override; + void setSStolerancesB(int which, realtype relTolB, realtype absTolB) + const override; - void quadSStolerancesB(int which, realtype reltolQB, - realtype abstolQB) const override; + void quadSStolerancesB(int which, realtype reltolQB, realtype abstolQB) + const override; void quadSStolerances(realtype reltolQ, realtype abstolQ) const override; @@ -181,21 +181,21 @@ class CVodeSolver : public Solver { void diagB(int which) const override; - void getNumSteps(const void *ami_mem, long int *numsteps) const override; + void getNumSteps(void const* ami_mem, long int* numsteps) const override; - void getNumRhsEvals(const void *ami_mem, - long int *numrhsevals) const override; + void + getNumRhsEvals(void const* ami_mem, long int* numrhsevals) const override; - void getNumErrTestFails(const void *ami_mem, - long int *numerrtestfails) const override; + void getNumErrTestFails(void const* ami_mem, long int* numerrtestfails) + const override; - void - getNumNonlinSolvConvFails(const void *ami_mem, - long int *numnonlinsolvconvfails) const override; + void getNumNonlinSolvConvFails( + void const* ami_mem, long int* numnonlinsolvconvfails + ) const override; - void getLastOrder(const void *ami_ami_mem, int *order) const override; + void getLastOrder(void const* ami_ami_mem, int* order) const override; - void *getAdjBmem(void *ami_mem, int which) const override; + void* getAdjBmem(void* ami_mem, int which) const override; /** * @brief Serialize amici::CVodeSolver to boost archive @@ -203,8 +203,8 @@ class CVodeSolver : public Solver { * @param s Solver instance to serialize */ template - friend void boost::serialization::serialize(Archive &ar, CVodeSolver &s, - unsigned int /*version*/); + friend void boost::serialization:: + serialize(Archive& ar, CVodeSolver& s, unsigned int /*version*/); /** * @brief Equality operator @@ -212,21 +212,23 @@ class CVodeSolver : public Solver { * @param b * @return Whether a and b are equal */ - friend bool operator==(const CVodeSolver &a, const CVodeSolver &b); + friend bool operator==(CVodeSolver const& a, CVodeSolver const& b); - void init(realtype t0, const AmiVector &x0, - const AmiVector &dx0) const override; + void + init(realtype t0, AmiVector const& x0, AmiVector const& dx0) const override; - void initSteadystate(const realtype t0, const AmiVector &x0, - const AmiVector &dx0) const override; + void initSteadystate( + const realtype t0, AmiVector const& x0, AmiVector const& dx0 + ) const override; - void sensInit1(const AmiVectorArray &sx0, const AmiVectorArray &sdx0) - const override; + void sensInit1(AmiVectorArray const& sx0, AmiVectorArray const& sdx0) + const override; - void binit(int which, realtype tf, const AmiVector &xB0, - const AmiVector &dxB0) const override; + void binit( + int which, realtype tf, AmiVector const& xB0, AmiVector const& dxB0 + ) const override; - void qbinit(int which, const AmiVector &xQB0) const override; + void qbinit(int which, AmiVector const& xQB0) const override; void rootInit(int ne) const override; diff --git a/deps/AMICI/include/amici/solver_idas.h b/deps/AMICI/include/amici/solver_idas.h index 331a1a620..0dba1a950 100644 --- a/deps/AMICI/include/amici/solver_idas.h +++ b/deps/AMICI/include/amici/solver_idas.h @@ -16,9 +16,9 @@ class IDASolver; namespace boost { namespace serialization { template -void serialize(Archive &ar, amici::IDASolver &s, unsigned int version); +void serialize(Archive& ar, amici::IDASolver& s, unsigned int version); } -} // namespace boost::serialization +} // namespace boost namespace amici { @@ -35,38 +35,38 @@ class IDASolver : public Solver { * @brief Clone this instance * @return The clone */ - Solver *clone() const override; + Solver* clone() const override; void reInitPostProcessF(realtype tnext) const override; void reInitPostProcessB(realtype tnext) const override; - void reInit(realtype t0, const AmiVector &yy0, - const AmiVector &yp0) const override; + void reInit(realtype t0, AmiVector const& yy0, AmiVector const& yp0) + const override; - void sensReInit(const AmiVectorArray &yyS0, - const AmiVectorArray &ypS0) const override; + void sensReInit(AmiVectorArray const& yyS0, AmiVectorArray const& ypS0) + const override; void sensToggleOff() const override; - void reInitB(int which, realtype tB0, - const AmiVector &yyB0, const AmiVector &ypB0) const override; + void reInitB( + int which, realtype tB0, AmiVector const& yyB0, AmiVector const& ypB0 + ) const override; - void quadReInitB(int which, const AmiVector &yQB0) const override; + void quadReInitB(int which, AmiVector const& yQB0) const override; - void quadSStolerancesB(int which, realtype reltolQB, - realtype abstolQB) const override; + void quadSStolerancesB(int which, realtype reltolQB, realtype abstolQB) + const override; void quadSStolerances(realtype reltolQ, realtype abstolQ) const override; int solve(realtype tout, int itask) const override; - int solveF(realtype tout, int itask, - int *ncheckPtr) const override; + int solveF(realtype tout, int itask, int* ncheckPtr) const override; void solveB(realtype tBout, int itaskB) const override; - void getRootInfo(int *rootsfound) const override; + void getRootInfo(int* rootsfound) const override; void getDky(realtype t, int k) const override; @@ -82,7 +82,7 @@ class IDASolver : public Solver { void getQuadDkyB(realtype t, int k, int which) const override; - void getQuad(realtype &t) const override; + void getQuad(realtype& t) const override; void getQuadDky(realtype t, int k) const override; @@ -94,7 +94,7 @@ class IDASolver : public Solver { void turnOffRootFinding() const override; - const Model *getModel() const override; + Model const* getModel() const override; void setLinearSolver() const override; @@ -115,16 +115,17 @@ class IDASolver : public Solver { * @param ypout new state derivative vector * @param tout anticipated next integration timepoint. */ - void reInitPostProcess(void *ida_mem, realtype *t, AmiVector *yout, - AmiVector *ypout, realtype tout) const; + void reInitPostProcess( + void* ida_mem, realtype* t, AmiVector* yout, AmiVector* ypout, + realtype tout + ) const; void allocateSolver() const override; - void setSStolerances(realtype rtol, - realtype atol) const override; + void setSStolerances(realtype rtol, realtype atol) const override; - void setSensSStolerances(realtype rtol, - const realtype *atol) const override; + void + setSensSStolerances(realtype rtol, realtype const* atol) const override; void setSensErrCon(bool error_corr) const override; @@ -144,66 +145,70 @@ class IDASolver : public Solver { void setStabLimDetB(int which, int stldet) const override; - void setId(const Model *model) const override; + void setId(Model const* model) const override; void setSuppressAlg(bool flag) const override; /** - * @brief resetState reset the IDAS solver to restart integration after a rhs discontinuity. + * @brief resetState reset the IDAS solver to restart integration after a + * rhs discontinuity. * @param ida_mem pointer to IDAS solver memory object * @param yy0 new state vector * @param yp0 new state derivative vector */ - void resetState(void *ida_mem, const_N_Vector yy0, - const_N_Vector yp0) const; + void + resetState(void* ida_mem, const_N_Vector yy0, const_N_Vector yp0) const; - void setSensParams(const realtype *p, const realtype *pbar, - const int *plist) const override; + void setSensParams( + realtype const* p, realtype const* pbar, int const* plist + ) const override; void adjInit() const override; - void quadInit(const AmiVector &xQ0) const override; + void quadInit(AmiVector const& xQ0) const override; - void allocateSolverB(int *which) const override; + void allocateSolverB(int* which) const override; - void setMaxNumStepsB(int which, - long int mxstepsB) const override; + void setMaxNumStepsB(int which, long int mxstepsB) const override; - void setSStolerancesB(int which, realtype relTolB, - realtype absTolB) const override; + void setSStolerancesB(int which, realtype relTolB, realtype absTolB) + const override; void diag() const override; void diagB(int which) const override; - void getNumSteps(const void *ami_mem, long int *numsteps) const override; + void getNumSteps(void const* ami_mem, long int* numsteps) const override; - void getNumRhsEvals(const void *ami_mem, - long int *numrhsevals) const override; + void + getNumRhsEvals(void const* ami_mem, long int* numrhsevals) const override; - void getNumErrTestFails(const void *ami_mem, - long int *numerrtestfails) const override; + void getNumErrTestFails(void const* ami_mem, long int* numerrtestfails) + const override; - void - getNumNonlinSolvConvFails(const void *ami_mem, - long int *numnonlinsolvconvfails) const override; + void getNumNonlinSolvConvFails( + void const* ami_mem, long int* numnonlinsolvconvfails + ) const override; - void getLastOrder(const void *ami_mem, int *order) const override; + void getLastOrder(void const* ami_mem, int* order) const override; - void *getAdjBmem(void *ami_mem, int which) const override; + void* getAdjBmem(void* ami_mem, int which) const override; - void init(realtype t0, const AmiVector &x0, - const AmiVector &dx0) const override; + void + init(realtype t0, AmiVector const& x0, AmiVector const& dx0) const override; - void initSteadystate(const realtype t0, const AmiVector &x0, - const AmiVector &dx0) const override; + void initSteadystate( + const realtype t0, AmiVector const& x0, AmiVector const& dx0 + ) const override; - void sensInit1(const AmiVectorArray &sx0, const AmiVectorArray &sdx0) const override; + void sensInit1(AmiVectorArray const& sx0, AmiVectorArray const& sdx0) + const override; - void binit(int which, realtype tf, - const AmiVector &xB0, const AmiVector &dxB0) const override; + void binit( + int which, realtype tf, AmiVector const& xB0, AmiVector const& dxB0 + ) const override; - void qbinit(int which, const AmiVector &xQB0) const override; + void qbinit(int which, AmiVector const& xQB0) const override; void rootInit(int ne) const override; diff --git a/deps/AMICI/include/amici/spline.h b/deps/AMICI/include/amici/spline.h index e0587ceac..07a436e38 100644 --- a/deps/AMICI/include/amici/spline.h +++ b/deps/AMICI/include/amici/spline.h @@ -5,15 +5,19 @@ namespace amici { #ifndef EXHALE_DOXYGEN_SHOULD_SKIP_THIS -int spline(int n, int end1, int end2, double slope1, double slope2, double x[], - double y[], double b[], double c[], double d[]); +int spline( + int n, int end1, int end2, double slope1, double slope2, double x[], + double y[], double b[], double c[], double d[] +); #endif -double seval(int n, double u, double x[], double y[], double b[], double c[], - double d[]); +double seval( + int n, double u, double x[], double y[], double b[], double c[], double d[] +); -double sinteg(int n, double u, double x[], double y[], double b[], double c[], - double d[]); +double sinteg( + int n, double u, double x[], double y[], double b[], double c[], double d[] +); } // namespace amici diff --git a/deps/AMICI/include/amici/splinefunctions.h b/deps/AMICI/include/amici/splinefunctions.h new file mode 100644 index 000000000..db4410de9 --- /dev/null +++ b/deps/AMICI/include/amici/splinefunctions.h @@ -0,0 +1,398 @@ +#ifndef AMICI_SPLINEFUNCTIONS_H +#define AMICI_SPLINEFUNCTIONS_H + +#include "amici/defines.h" + +#include + +#include + +namespace amici { + +class Model; +/** + * @brief AMICI spline base class. + * + * Instances of this class are created upon solver setup and the needed splines + * are set up (e.g., interpolation of the nodes is performed). + * Upon call to a spline function, only the evaluation of the spline polynomial + * is carried out. + */ +class AbstractSpline { + public: + /** default constructor */ + AbstractSpline() = default; + + /** + * @brief Common constructor for `AbstractSpline` instances. + * @param nodes the nodes defining the position at which the value of + * the spline is known + * (if `equidistant_spacing` is true, it must contain only the first and + * the last node; the other nodes will be automatically inserted, + * assuming they are uniformly spaced) + * @param node_values the values assumed by the spline at the nodes + * @param equidistant_spacing whether equidistant nodes are to be computed + * @param logarithmic_parametrization if true, the spline interpolation + * will occur in log-space in order to ensure positivity of the interpolant + * (which strictly speaking will no longer be a spline) + */ + AbstractSpline( + std::vector nodes, std::vector node_values, + bool equidistant_spacing, bool logarithmic_parametrization + ); + + virtual ~AbstractSpline() = default; + + /** + * @brief Compute the coefficients for all polynomial segments of this + * spline + */ + virtual void compute_coefficients() = 0; + + /** + * @brief Compute the coefficients for all polynomial segments of + * the derivatives of this spline with respect to the parameters + * @param nplist number of parameters + * @param spline_offset offset of this spline inside `dvaluesdp` + * and `dslopesdp` + * @param dvaluesdp derivatives of the spline values with respect to the + * parameters (for all splines in the model, not just this one) + * @param dslopesdp derivatives of the spline derivatives with respect + * to the parameters (for all splines in the model, not just this one) + * @remark The contents of `dvaluesdp` and `dslopesdp` may be modified + * by this function. + */ + virtual void compute_coefficients_sensi( + int nplist, int spline_offset, gsl::span dvaluesdp, + gsl::span dslopesdp + ) = 0; + + /** + * @brief Get the value of this spline at a given point + * @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; + + /** + * @brief Get the value of this spline at a given point + * in the scale in which interpolation is carried out (e.g., log-scale) + * @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; + + /** + * @brief Get the value of this spline at a given node + * @param i index of the node at which the spline is to be evaluated + * @return value of the spline at the `i`-th node + */ + realtype get_node_value(int const i) const; + + /** + * @brief Get the value of this spline at a given node + * in the scale in which interpolation is carried out (e.g., log-scale) + * @param i index of the node at which the spline is to be evaluated + * @return scaled value of the spline at the `i`-th node + */ + realtype get_node_value_scaled(int const i) const; + + /** + * @brief Get the derivative of this spline with respect to a given + * parameter at a given point + * @param t point at which the sensitivity is to be evaluated + * @param ip index of the parameter + * @return sensitivity of the spline with respect to the `ip`th parameter + * at `t` + */ + realtype get_sensitivity(const realtype t, int const ip) const; + + /** + * @brief Get the derivative of this spline with respect to a given + * parameter at a given point + * @param t point at which the sensitivity is to be evaluated + * @param ip index of the parameter + * @param value value of the spline at the given time point. + * It is used e.g. when interpolation is carried out in log-space. + * If omitted it will be computed. + * @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 value) const; + + /** + * @brief Get the derivative of this spline with respect to a given + * parameter at a given point + * in the scale in which interpolation is carried out (e.g., log-scale) + * @param t point at which the sensitivity is to be evaluated + * @param ip index of the parameter + * @return scaled sensitivity of the spline with respect to the `ip`th + * parameter at `t` + */ + virtual realtype + get_sensitivity_scaled(const realtype t, int const ip) const + = 0; + + /** + * @brief Compute the limit value of the spline + * as the evaluation point tends to positive infinity. + */ + virtual void compute_final_value() = 0; + + /** + * @brief Compute the limit of the value of the sensitivity + * as the evaluation point tends to positive infinity. + * @param nplist number of parameters + * @param spline_offset offset of this spline inside `dspline_valuesdp` + * and `dspline_slopesdp` + * @param dspline_valuesdp derivatives of the spline values with respect to + * the parameters (for all splines in the model, not just this one) + * @param dspline_slopesdp derivatives of the spline derivatives with + * respect to the parameters (for all splines in the model, not just this + * one) + */ + virtual void compute_final_sensitivity( + int nplist, int spline_offset, gsl::span dspline_valuesdp, + gsl::span dspline_slopesdp + ) = 0; + + /** + * @brief Get the limit value of the spline + * as the evaluation point tends to positive infinity. + * @return limit value + */ + realtype get_final_value() const; + + /** + * @brief Get the limit value of the spline + * (in the scale in which interpolation is carried out) + * as the evaluation point tends to positive infinity. + * @return limit value + */ + realtype get_final_value_scaled() const; + + /** + * @brief Get the limit value of the sensitivity + * with respect to the given parameter + * as the evaluation point tends to positive infinity. + * @param ip parameter index + * @return limit value + */ + realtype get_final_sensitivity(int const ip) const; + + /** + * @brief Get the limit value of the sensitivity + * with respect to the given parameter + * (in the scale in which interpolation is carried out) + * as the evaluation point tends to positive infinity. + * @param ip parameter index + * @return limit value + */ + realtype get_final_sensitivity_scaled(int const ip) const; + + /** + * @brief Whether nodes are uniformly spaced + * @return boolean flag + */ + bool get_equidistant_spacing() const; + + /** + * @brief Whether spline interpolation is carried out in log-space + * @return boolean flag + */ + bool get_logarithmic_parametrization() const; + + /** + * @brief The number of interpolation nodes for this spline + * @return number of nodes + */ + int n_nodes() const { return static_cast(nodes_.size()); } + + protected: + /** + * @brief The nodes at which this spline is interpolated + */ + std::vector nodes_; + + /** + * @brief The values the spline assumes at the nodes + */ + std::vector node_values_; + + /** + * @brief Coefficients for each polynomial segment of the spline + */ + std::vector coefficients; + + /** + * @brief Polynomial coefficients for the extrapolating the spline values + */ + std::vector coefficients_extrapolate; + + /** + * @brief Coefficients for each polynomial segment of the sensitivities + * with respect to the parameters + */ + std::vector coefficients_sensi; + + /** + * @brief Polynomial coefficients for the extrapolating the sensitivities + */ + std::vector coefficients_extrapolate_sensi; + + /** + * @brief Set the limit value of the spline + * (in the scale in which interpolation is carried out) + * as the evaluation point tends to positive infinity. + * @param finalValue final value + */ + void set_final_value_scaled(realtype finalValue); + + /** + * @brief Set the limit value of the sensitivity + * (in the scale in which interpolation is carried out) + * as the evaluation point tends to positive infinity. + * @param finalSensitivity final value of the sensitivity + * for each parameter + */ + void set_final_sensitivity_scaled(std::vector finalSensitivity); + + private: + realtype final_value_scaled_; + + std::vector final_sensitivity_scaled_; + + bool equidistant_spacing_ = false; + + bool logarithmic_parametrization_ = false; + +}; // class SplineFunction + +/** + * @brief AMICI Hermite spline class. + * + * Instances of this class represent Hermite splines, + * which are uniquely determined by their nodes, + * the values at their nodes, the derivatives at their nodes + * (defaulting to finite difference approximations from the node values), + * boundary conditions and extrapolation conditions. + * Optionally, the spline can be defined in log-space in order + * to ensure positivity. + */ +class HermiteSpline : public AbstractSpline { + public: + HermiteSpline() = default; + + /** + * @brief Construct a `HermiteSpline`. + * @param nodes the nodes defining the position at which the value of + * the spline is known + * (if `equidistant_spacing` is true, it must contain only the first and + * the last node; the other nodes will be automatically inserted, + * assuming they are uniformly spaced) + * @param node_values the values assumed by the spline at the nodes + * @param node_values_derivative the derivatives of the spline at the nodes + * (if `node_derivative_by_FD` is true, it will resized and filled with + * finite difference approximations computed from `node_values`) + * @param firstNodeBC boundary condition at the first node + * @param lastNodeBC boundary condition at the last node + * @param firstNodeExtrapol extrapolation method on the left side + * @param lastNodeExtrapol extrapolation method on the right side + * @param node_derivative_by_FD whether derivatives are to be computed by + * finite differences + * @param equidistant_spacing whether equidistant nodes are to be computed + * @param logarithmic_parametrization if true, the spline interpolation + * will occur in log-space in order to ensure positivity of the interpolant + * (which strictly speaking will no longer be a spline) + */ + HermiteSpline( + std::vector nodes, std::vector node_values, + std::vector node_values_derivative, + SplineBoundaryCondition firstNodeBC, SplineBoundaryCondition lastNodeBC, + SplineExtrapolation firstNodeExtrapol, + SplineExtrapolation lastNodeExtrapol, bool node_derivative_by_FD, + bool equidistant_spacing, bool logarithmic_parametrization + ); + + void compute_coefficients() override; + + void compute_coefficients_sensi( + int nplist, int spline_offset, gsl::span dvaluesdp, + gsl::span dslopesdp + ) override; + + void compute_final_value() override; + + void compute_final_sensitivity( + int nplist, int spline_offset, gsl::span dspline_valuesdp, + gsl::span dspline_slopesdp + ) override; + + realtype get_value_scaled(const realtype t) const override; + + /** + * @brief Get the derivative of the spline at a given node + * @param i index of the node at which the spline is to be evaluated + * @return value of the derivative at the `i`-th node + */ + realtype get_node_derivative(int const i) const; + + /** + * @brief Get the derivative of the spline at a given node + * in the scale in which interpolation is carried out (e.g., log-scale) + * @param i index of the node at which the spline is to be evaluated + * @return scaled value of the derivative at the `i`-th node + */ + realtype get_node_derivative_scaled(int const i) const; + + realtype + get_sensitivity_scaled(const realtype t, int const ip) const override; + + /** + * @brief Whether derivatives of this spline are computed + * by finite differences + * @return boolean flag + */ + bool get_node_derivative_by_fd() const { return node_derivative_by_FD_; } + + private: + void compute_slope_sensitivities_by_fd( + int nplist, int spline_offset, int ip, gsl::span dvaluesdp, + gsl::span dslopesdp + ); + + void get_coeffs_sensi_lowlevel( + int ip, int i_node, int nplist, int n_spline_coefficients, + int spline_offset, realtype len, gsl::span dnodesdp, + gsl::span dslopesdp, gsl::span coeffs + ) const; + + void handle_inner_derivatives(); + + void handle_boundary_conditions(); + + void compute_coefficients_extrapolation(); + + void compute_coefficients_extrapolation_sensi( + int nplist, int spline_offset, gsl::span dspline_valuesdp, + gsl::span dspline_slopesdp + ); + + std::vector node_values_derivative_; + + SplineBoundaryCondition first_node_bc_ = SplineBoundaryCondition::given; + + SplineBoundaryCondition last_node_bc_ = SplineBoundaryCondition::given; + + SplineExtrapolation first_node_ep_ = SplineExtrapolation::linear; + + SplineExtrapolation last_node_ep_ = SplineExtrapolation::linear; + + bool node_derivative_by_FD_ = false; + +}; // class HermiteSpline + +} // namespace amici + +#endif /* AMICI_SPLINEFUNCTIONS_H */ diff --git a/deps/AMICI/include/amici/steadystateproblem.h b/deps/AMICI/include/amici/steadystateproblem.h index 550433e79..dc19c014c 100644 --- a/deps/AMICI/include/amici/steadystateproblem.h +++ b/deps/AMICI/include/amici/steadystateproblem.h @@ -1,11 +1,10 @@ #ifndef AMICI_STEADYSTATEPROBLEM_H #define AMICI_STEADYSTATEPROBLEM_H -#include "amici/defines.h" -#include "amici/vector.h" -#include "amici/solver_cvodes.h" -#include "amici/forwardproblem.h" +#include +#include #include +#include #include @@ -29,8 +28,7 @@ class SteadystateProblem { * @param solver Solver instance * @param model Model instance */ - explicit SteadystateProblem(const Solver &solver, - const Model &model); + explicit SteadystateProblem(Solver const& solver, Model const& model); /** * @brief Handles steady state computation in the forward case: @@ -40,8 +38,7 @@ class SteadystateProblem { * @param model pointer to the model object * @param it integer with the index of the current time step */ - void workSteadyStateProblem(Solver *solver, Model *model, int it); - + void workSteadyStateProblem(Solver const& solver, Model& model, int it); /** * Integrates over the adjoint state backward in time by solving a linear @@ -51,30 +48,132 @@ class SteadystateProblem { * @param model pointer to the model object * @param bwd backward problem */ - void workSteadyStateBackwardProblem(Solver *solver, Model *model, - const BackwardProblem *bwd); + void workSteadyStateBackwardProblem( + Solver const& solver, Model& model, BackwardProblem const* bwd + ); + + /** + * @brief Returns the stored SimulationState + * @return stored SimulationState + */ + SimulationState const& getFinalSimulationState() const { return state_; }; + + /** + * @brief Returns the quadratures from pre- or postequilibration + * @return xQB Vector with quadratures + */ + AmiVector const& getEquilibrationQuadratures() const { return xQB_; } + /** + * @brief Returns state at steadystate + * @return x + */ + AmiVector const& getState() const { return state_.x; }; + + /** + * @brief Returns state sensitivity at steadystate + * @return sx + */ + AmiVectorArray const& getStateSensitivity() const { return state_.sx; }; + + /** + * @brief Accessor for dJydx + * @return dJydx + */ + std::vector const& getDJydx() const { return dJydx_; } + + /** + * @brief Accessor for run_time of the forward problem + * @return run_time + */ + double getCPUTime() const { return cpu_time_; } + + /** + * @brief Accessor for run_time of the backward problem + * @return run_time + */ + double getCPUTimeB() const { return cpu_timeB_; } + + /** + * @brief Accessor for steady_state_status + * @return steady_state_status + */ + std::vector const& getSteadyStateStatus() const { + return steady_state_status_; + } + + /** + * @brief Get model time at which steadystate was found through simulation + * @return t + */ + realtype getSteadyStateTime() const { return state_.t; } + + /** + * @brief Accessor for wrms + * @return wrms + */ + realtype getResidualNorm() const { return wrms_; } + + /** + * @brief Accessor for numsteps + * @return numsteps + */ + std::vector const& getNumSteps() const { return numsteps_; } + /** + * @brief Accessor for numstepsB + * @return numstepsB + */ + int getNumStepsB() const { return numstepsB_; } + + /** + * @brief computes adjoint updates dJydx according to provided model and + * expdata + * @param model Model instance + * @param edata experimental data + */ + void getAdjointUpdates(Model& model, ExpData const& edata); + + /** + * @brief Return the adjoint state + * @return xB adjoint state + */ + AmiVector const& getAdjointState() const { return xB_; } + + /** + * @brief Accessor for xQB + * @return xQB + */ + AmiVector const& getAdjointQuadrature() const { return xQB_; } + + /** + * @brief Accessor for hasQuadrature_ + * @return hasQuadrature_ + */ + bool hasQuadrature() const { return hasQuadrature_; } + + /** + * @brief computes adjoint updates dJydx according to provided model and + * expdata + * @return convergence of steady state solver + */ + bool checkSteadyStateSuccess() const; + + private: /** * @brief Handles the computation of the steady state, throws an * AmiException, if no steady state was found * @param solver pointer to the solver object - * @param newtonSolver pointer to the newtonSolver solver object * @param model pointer to the model object * @param it integer with the index of the current time step */ - void findSteadyState(Solver *solver, - NewtonSolver *newtonSolver, - Model *model, int it); + void findSteadyState(Solver const& solver, Model& model, int it); /** * @brief Tries to determine the steady state by using Newton's method - * @param newtonSolver pointer to the newtonSolver solver object * @param model pointer to the model object * @param newton_retry bool flag indicating whether being relaunched */ - void findSteadyStateByNewtonsMethod(NewtonSolver *newtonSolver, - Model *model, - bool newton_retry); + void findSteadyStateByNewtonsMethod(Model& model, bool newton_retry); /** * @brief Tries to determine the steady state by using forward simulation @@ -82,26 +181,22 @@ class SteadystateProblem { * @param model pointer to the model object * @param it integer with the index of the current time step */ - void findSteadyStateBySimulation(const Solver *solver, - Model *model, - int it); + void + findSteadyStateBySimulation(Solver const& solver, Model& model, int it); /** * @brief Handles the computation of quadratures in adjoint mode - * @param newtonSolver pointer to the newtonSolver solver object * @param solver pointer to the solver object * @param model pointer to the model object */ - void computeSteadyStateQuadrature(NewtonSolver *newtonSolver, - const Solver *solver, Model *model); + void computeSteadyStateQuadrature(Solver const& solver, Model& model); /** * @brief Computes the quadrature in steady state backward mode by * solving the linear system defined by the backward Jacobian - * @param newtonSolver pointer to the newtonSolver solver object * @param model pointer to the model object */ - void getQuadratureByLinSolve(NewtonSolver *newtonSolver, Model *model); + void getQuadratureByLinSolve(Model& model); /** * @brief Computes the quadrature in steady state backward mode by @@ -109,23 +204,27 @@ class SteadystateProblem { * @param solver pointer to the solver object * @param model pointer to the model object */ - void getQuadratureBySimulation(const Solver *solver, Model *model); + void getQuadratureBySimulation(Solver const& solver, Model& model); /** * @brief Stores state and throws an exception if equilibration failed - * @param solver pointer to the solver object - * @param model pointer to the model object + * @param tried_newton_1 Whether any Newton step was attempted before + * simulation + * @param tried_simulation Whether simulation was attempted + * @param tried_newton_2 Whether any Newton step was attempted after + * simulation */ - [[noreturn]] void handleSteadyStateFailure(const Solver *solver, - Model *model); + [[noreturn]] void handleSteadyStateFailure( + bool tried_newton_1, bool tried_simulation, bool tried_newton_2 + ); /** * @brief Assembles the error message to be thrown. * @param errorString const pointer to string with error message * @param status Entry of steady_state_status to be processed */ - void writeErrorString(std::string *errorString, SteadyStateStatus - status) const; + void + writeErrorString(std::string* errorString, SteadyStateStatus status) const; /** * @brief Checks depending on the status of the Newton solver, @@ -137,8 +236,10 @@ class SteadystateProblem { * @param context SteadyStateContext giving the situation for the flag * @return flag telling how to process state sensitivities */ - bool getSensitivityFlag(const Model *model, const Solver *solver, int it, - SteadyStateContext context); + bool getSensitivityFlag( + Model const& model, Solver const& solver, int it, + SteadyStateContext context + ); /** * @brief Computes the weighted root mean square of xdot @@ -151,39 +252,44 @@ class SteadystateProblem { * @param ewt error weight vector * @return root-mean-square norm */ - realtype getWrmsNorm(AmiVector const &x, - AmiVector const &xdot, - realtype atol, - realtype rtol, - AmiVector &ewt) const; + realtype getWrmsNorm( + AmiVector const& x, AmiVector const& xdot, realtype atol, realtype rtol, + AmiVector& ewt + ) const; /** - * @brief Checks convergence for state and respective sensitivities - * @param solver Solver instance - * @param model instance - * @param checkSensitivities flag whether sensitivities should be checked - * @return boolean indicating convergence + * @brief Checks convergence for state or adjoint quadratures, depending on + * sensi method + * @param model Model instance + * @param sensi_method sensitivity method + * @return weighted root mean squared residuals of the RHS + */ + realtype getWrms(Model& model, SensitivityMethod sensi_method); + + /** + * @brief Checks convergence for state sensitivities + * @param model Model instance + * @return weighted root mean squared residuals of the RHS */ - bool checkConvergence(const Solver *solver, Model *model, - SensitivityMethod checkSensitivities); + realtype getWrmsFSA(Model& model); /** * @brief Runs the Newton solver iterations and checks for convergence * to steady state * @param model pointer to the model object - * @param newtonSolver pointer to the NewtonSolver object * @param newton_retry flag indicating if Newton solver is rerun */ - void applyNewtonsMethod(Model *model, NewtonSolver *newtonSolver, - bool newton_retry); + void applyNewtonsMethod(Model& model, bool newton_retry); /** - * @brief Simulation is launched, if Newton solver or linear system solve fails + * @brief Simulation is launched, if Newton solver or linear system solve + * fails * @param solver pointer to the solver object * @param model pointer to the model object * @param backward flag indicating adjoint mode (including quadrature) */ - void runSteadystateSimulation(const Solver *solver, Model *model, bool backward); + void + runSteadystateSimulation(Solver const& solver, Model& model, bool backward); /** * @brief Initialize CVodeSolver instance for preequilibration simulation @@ -193,21 +299,28 @@ class SteadystateProblem { * @param backward flag switching on quadratures computation * @return solver instance */ - std::unique_ptr createSteadystateSimSolver(const Solver *solver, - Model *model, - bool forwardSensis, - bool backward) const; + std::unique_ptr createSteadystateSimSolver( + Solver const& solver, Model& model, bool forwardSensis, bool backward + ) const; /** - * @brief Initialize backward computation by setting state, time, adjoint - * state and checking for preequilibration mode + * @brief Initialize forward computation + * @param it integer with the index of the current time step + * @param solver pointer to the solver object + * @param model pointer to the model object + */ + void initializeForwardProblem(int it, Solver const& solver, Model& model); + + /** + * @brief Initialize backward computation * @param solver pointer to the solver object * @param model pointer to the model object * @param bwd pointer to backward problem * @return flag indicating whether backward computation to be carried out */ - bool initializeBackwardProblem(Solver *solver, Model *model, - const BackwardProblem *bwd); + bool initializeBackwardProblem( + Solver const& solver, Model& model, BackwardProblem const* bwd + ); /** * @brief Compute the backward quadratures, which contribute to the @@ -216,159 +329,66 @@ class SteadystateProblem { * @param yQ vector to be multiplied with dxdotdp * @param yQB resulting vector after multiplication */ - void computeQBfromQ(Model *model, const AmiVector &yQ, AmiVector &yQB) const; - - /** - * @brief Store carbon copy of current simulation state variables as SimulationState - * @param model model carrying the ModelState to be used - * @param storesensi flag to enable storage of sensitivities - */ - void storeSimulationState(Model *model, bool storesensi); - - /** - * @brief Returns the stored SimulationState - * @return stored SimulationState - */ - const SimulationState &getFinalSimulationState() const { - return state_; - }; - - /** - * @brief Returns the quadratures from pre- or postequilibration - * @return xQB Vector with quadratures - */ - const AmiVector &getEquilibrationQuadratures() const { - return xQB_; - } - /** - * @brief Returns state at steadystate - * @return x - */ - const AmiVector &getState() const { - return x_; - }; - - /** - * @brief Returns state sensitivity at steadystate - * @return sx - */ - const AmiVectorArray &getStateSensitivity() const { - return sx_; - }; - - /** - * @brief Accessor for dJydx - * @return dJydx - */ - std::vector const& getDJydx() const { - return dJydx_; - } - - /** - * @brief Accessor for run_time of the forward problem - * @return run_time - */ - double getCPUTime() const { return cpu_time_; } - - /** - * @brief Accessor for run_time of the backward problem - * @return run_time - */ - double getCPUTimeB() const { return cpu_timeB_; } - - /** - * @brief Accessor for steady_state_status - * @return steady_state_status - */ - std::vector const& getSteadyStateStatus() const - { return steady_state_status_; } + void + computeQBfromQ(Model& model, AmiVector const& yQ, AmiVector& yQB) const; /** - * @brief Accessor for t - * @return t - */ - realtype getSteadyStateTime() const { return t_; } - - /** - * @brief Accessor for wrms - * @return wrms - */ - realtype getResidualNorm() const { return wrms_; } - - /** - * @brief Accessor for numsteps - * @return numsteps - */ - const std::vector &getNumSteps() const { return numsteps_; } - - /** - * @brief Accessor for numstepsB - * @return numstepsB + * @brief Ensures state positivity, if requested and repeats convergence + * check, if necessary + * @param model pointer to the model object */ - int getNumStepsB() const { return numstepsB_; } + bool makePositiveAndCheckConvergence(Model& model); /** - * @brief Accessor for numlinsteps - * @return numlinsteps + * @brief Updates the damping factor gamma that determines step size + * + * @param step_successful flag indicating whether the previous step was + * successful + * @return boolean flag indicating whether search direction should be + * updated (true) or the same direction should be retried with the updated + * dampening (false) */ - const std::vector &getNumLinSteps() const { return numlinsteps_; } + bool updateDampingFactor(bool step_successful); /** - * @brief computes adjoint updates dJydx according to provided model and expdata - * @param model Model instance - * @param edata experimental data + * @brief Updates member variables to indicate that state_.x has been + * updated and xdot_, delta_, etc. need to be recomputed. */ - void getAdjointUpdates(Model &model, const ExpData &edata); + void flagUpdatedState(); /** - * @brief Return the adjoint state - * @return xB adjoint state - */ - AmiVector const& getAdjointState() const { return xB_; } - - /** - * @brief Accessor for xQB - * @return xQB + * @brief Retrieves simulation sensitivities from the provided solver and + * sets the corresponding flag to indicate they are up to date + * @param solver simulation solver instance */ - AmiVector const& getAdjointQuadrature() const { return xQB_; } + void updateSensiSimulation(Solver const& solver); /** - * @brief Accessor for hasQuadrature_ - * @return hasQuadrature_ + * @brief Computes the right hand side for the current state_.x and sets the + * corresponding flag to indicate xdot_ is up to date. + * @param model model instance */ - bool hasQuadrature() const { return hasQuadrature_; } + void updateRightHandSide(Model& model); /** - * @brief computes adjoint updates dJydx according to provided model and expdata - * @return convergence of steady state solver + * @brief Computes the newton step for the current state_.x and sets the + * corresponding flag to indicate delta_ is up to date. + * @param model model instance */ - bool checkSteadyStateSuccess() const; + void getNewtonStep(Model& model); - private: - /** time variable for simulation steadystate finding */ - realtype t_; /** newton step */ AmiVector delta_; + /** previous newton step */ + AmiVector delta_old_; /** error weights for solver state, dimension nx_solver */ AmiVector ewt_; /** error weights for backward quadratures, dimension nplist() */ AmiVector ewtQB_; - /** container for relative error calculation? */ - AmiVector rel_x_newton_; - /** container for absolute error calculation? */ - AmiVector x_newton_; - /** state vector */ - AmiVector x_; /** old state vector */ AmiVector x_old_; - /** differential state vector */ - AmiVector dx_; /** time derivative state vector */ AmiVector xdot_; - /** old time derivative state vector */ - AmiVector xdot_old_; - /** state sensitivities */ - AmiVectorArray sx_; /** state differential sensitivities */ AmiVectorArray sdx_; /** adjoint state vector */ @@ -381,10 +401,10 @@ class SteadystateProblem { AmiVector xQBdot_; /** maximum number of steps for Newton solver for allocating numlinsteps */ - int max_steps_ {0}; + int max_steps_{0}; /** weighted root-mean-square error */ - realtype wrms_ {NAN}; + realtype wrms_{NAN}; /** state derivative of data likelihood * (dimension nJ x nx x nt, ordering =?) */ @@ -393,27 +413,62 @@ class SteadystateProblem { SimulationState state_; /** stores diagnostic information about employed number of steps */ - std::vector numsteps_ {std::vector(3, 0)}; - - /** stores diagnostic information about employed number of linear steps */ - std::vector numlinsteps_; + std::vector numsteps_{std::vector(3, 0)}; /** stores information about employed number of backward steps */ - int numstepsB_ {0}; + int numstepsB_{0}; /** stores diagnostic information about runtime */ - double cpu_time_ {0.0}; + double cpu_time_{0.0}; /** stores diagnostic information about runtime backward */ - double cpu_timeB_ {0.0}; + double cpu_timeB_{0.0}; /** flag indicating whether backward mode was run */ - bool hasQuadrature_ {false}; + bool hasQuadrature_{false}; + + /** stepsize for newton step */ + double gamma_{1.0}; /** stores diagnostic information about execution success of the different * approaches [newton, simulation, newton] (length = 3) */ std::vector steady_state_status_; + + /** absolute tolerance for convergence check (state)*/ + realtype atol_{NAN}; + /** relative tolerance for convergence check (state)*/ + realtype rtol_{NAN}; + /** absolute tolerance for convergence check (state sensi)*/ + realtype atol_sensi_{NAN}; + /** relative tolerance for convergence check (state sensi)*/ + realtype rtol_sensi_{NAN}; + /** absolute tolerance for convergence check (quadratures)*/ + realtype atol_quad_{NAN}; + /** relative tolerance for convergence check (quadratures)*/ + realtype rtol_quad_{NAN}; + + /** newton solver */ + std::unique_ptr newton_solver_{nullptr}; + + /** damping factor flag */ + NewtonDampingFactorMode damping_factor_mode_{NewtonDampingFactorMode::on}; + /** damping factor lower bound */ + realtype damping_factor_lower_bound_{1e-8}; + /** whether newton step should be used for convergence steps */ + bool newton_step_conv_{false}; + /** whether sensitivities should be checked for convergence to steadystate + */ + bool check_sensi_conv_{true}; + + /** flag indicating whether xdot_ has been computed for the current state */ + bool xdot_updated_{false}; + /** flag indicating whether delta_ has been computed for the current state + */ + bool delta_updated_{false}; + /** flag indicating whether simulation sensitivities have been retrieved for + * the current state */ + bool sensis_updated_{false}; }; } // namespace amici diff --git a/deps/AMICI/include/amici/sundials_linsol_wrapper.h b/deps/AMICI/include/amici/sundials_linsol_wrapper.h index 86b67fed2..613eb2215 100644 --- a/deps/AMICI/include/amici/sundials_linsol_wrapper.h +++ b/deps/AMICI/include/amici/sundials_linsol_wrapper.h @@ -1,7 +1,6 @@ #ifndef AMICI_SUNDIALS_LINSOL_WRAPPER_H #define AMICI_SUNDIALS_LINSOL_WRAPPER_H -#include "amici/exception.h" #include "amici/sundials_matrix_wrapper.h" #include "amici/vector.h" @@ -44,27 +43,27 @@ class SUNLinSolWrapper { * @brief Copy constructor * @param other */ - SUNLinSolWrapper(const SUNLinSolWrapper &other) = delete; + SUNLinSolWrapper(SUNLinSolWrapper const& other) = delete; /** * @brief Move constructor * @param other */ - SUNLinSolWrapper(SUNLinSolWrapper &&other) noexcept; + SUNLinSolWrapper(SUNLinSolWrapper&& other) noexcept; /** * @brief Copy assignment * @param other * @return */ - SUNLinSolWrapper &operator=(const SUNLinSolWrapper &other) = delete; + SUNLinSolWrapper& operator=(SUNLinSolWrapper const& other) = delete; /** * @brief Move assignment * @param other * @return */ - SUNLinSolWrapper &operator=(SUNLinSolWrapper &&other) noexcept; + SUNLinSolWrapper& operator=(SUNLinSolWrapper&& other) noexcept; /** * @brief Returns the wrapped SUNLinSol. @@ -90,7 +89,7 @@ class SUNLinSolWrapper { * system matrix A. * @param A */ - void setup(const SUNMatrixWrapper& A) const; + void setup(SUNMatrixWrapper const& A) const; /** * @brief Solves a linear system A*x = b @@ -114,7 +113,7 @@ class SUNLinSolWrapper { * @param leniwLS output argument for size of integer workspace * @return workspace size */ - int space(long int *lenrwLS, long int *leniwLS) const; + int space(long int* lenrwLS, long int* leniwLS) const; /** * @brief Get the matrix A (matrix solvers only). @@ -131,10 +130,9 @@ class SUNLinSolWrapper { int initialize(); /** Wrapped solver */ - SUNLinearSolver solver_ {nullptr}; + SUNLinearSolver solver_{nullptr}; }; - /** * @brief SUNDIALS band direct solver. */ @@ -154,7 +152,7 @@ class SUNLinSolBand : public SUNLinSolWrapper { * @param ubw upper bandwidth of band matrix A * @param lbw lower bandwidth of band matrix A */ - SUNLinSolBand(AmiVector const &x, int ubw, int lbw); + SUNLinSolBand(AmiVector const& x, int ubw, int lbw); SUNMatrix getMatrix() const override; @@ -163,7 +161,6 @@ class SUNLinSolBand : public SUNLinSolWrapper { SUNMatrixWrapper A_; }; - /** * @brief SUNDIALS dense direct solver. */ @@ -173,7 +170,7 @@ class SUNLinSolDense : public SUNLinSolWrapper { * @brief Create dense solver * @param x A template for cloning vectors needed within the solver. */ - explicit SUNLinSolDense(AmiVector const &x); + explicit SUNLinSolDense(AmiVector const& x); SUNMatrix getMatrix() const override; @@ -182,18 +179,13 @@ class SUNLinSolDense : public SUNLinSolWrapper { SUNMatrixWrapper A_; }; - /** * @brief SUNDIALS KLU sparse direct solver. */ class SUNLinSolKLU : public SUNLinSolWrapper { public: /** KLU state reordering (different from SuperLUMT ordering!) */ - enum class StateOrdering { - AMD, - COLAMD, - natural - }; + enum class StateOrdering { AMD, COLAMD, natural }; /** * @brief Create KLU solver with given matrix @@ -209,8 +201,9 @@ class SUNLinSolKLU : public SUNLinSolWrapper { * @param sparsetype Sparse matrix type (CSC_MAT, CSR_MAT) * @param ordering */ - SUNLinSolKLU(AmiVector const &x, int nnz, int sparsetype, - StateOrdering ordering); + SUNLinSolKLU( + AmiVector const& x, int nnz, int sparsetype, StateOrdering ordering + ); SUNMatrix getMatrix() const override; @@ -240,7 +233,7 @@ class SUNLinSolKLU : public SUNLinSolWrapper { /** * @brief SUNDIALS SuperLUMT sparse direct solver. */ -class SUNLinSolSuperLUMT : public SUNLinSolWrapper { +class SUNLinSolSuperLUMT : public SUNLinSolWrapper { public: /** SuperLUMT ordering (different from KLU ordering!) */ enum class StateOrdering { @@ -269,8 +262,9 @@ class SUNLinSolSuperLUMT : public SUNLinSolWrapper { * @param sparsetype Sparse matrix type (CSC_MAT, CSR_MAT) * @param ordering */ - SUNLinSolSuperLUMT(AmiVector const &x, int nnz, int sparsetype, - StateOrdering ordering); + SUNLinSolSuperLUMT( + AmiVector const& x, int nnz, int sparsetype, StateOrdering ordering + ); /** * @brief Create SuperLUMT solver and matrix to operate on @@ -280,8 +274,10 @@ class SUNLinSolSuperLUMT : public SUNLinSolWrapper { * @param ordering * @param numThreads Number of threads to be used by SuperLUMT */ - SUNLinSolSuperLUMT(AmiVector const &x, int nnz, int sparsetype, - StateOrdering ordering, int numThreads); + SUNLinSolSuperLUMT( + AmiVector const& x, int nnz, int sparsetype, StateOrdering ordering, + int numThreads + ); SUNMatrix getMatrix() const override; @@ -321,7 +317,7 @@ class SUNLinSolPCG : public SUNLinSolWrapper { * @param ATimes * @return */ - int setATimes(void *A_data, ATimesFn ATimes); + int setATimes(void* A_data, ATimesFn ATimes); /** * @brief Sets function pointers for PSetup and PSolve routines inside @@ -332,7 +328,7 @@ class SUNLinSolPCG : public SUNLinSolWrapper { * @param Psol * @return */ - int setPreconditioner(void *P_data, PSetupFn Pset, PSolveFn Psol); + int setPreconditioner(void* P_data, PSetupFn Pset, PSolveFn Psol); /** * @brief Sets pointers to left/right scaling vectors for the linear @@ -364,7 +360,6 @@ class SUNLinSolPCG : public SUNLinSolWrapper { N_Vector getResid() const; }; - /** * @brief SUNDIALS scaled preconditioned Bi-CGStab (Bi-Conjugate Gradient * Stable method) (SPBCGS) solver. @@ -378,8 +373,9 @@ class SUNLinSolSPBCGS : public SUNLinSolWrapper { * PREC_BOTH) * @param maxl Maximum number of solver iterations */ - explicit SUNLinSolSPBCGS(N_Vector x, int pretype = PREC_NONE, - int maxl = SUNSPBCGS_MAXL_DEFAULT); + explicit SUNLinSolSPBCGS( + N_Vector x, int pretype = PREC_NONE, int maxl = SUNSPBCGS_MAXL_DEFAULT + ); /** * @brief SUNLinSolSPBCGS @@ -388,8 +384,10 @@ class SUNLinSolSPBCGS : public SUNLinSolWrapper { * PREC_BOTH) * @param maxl Maximum number of solver iterations */ - explicit SUNLinSolSPBCGS(AmiVector const &x, int pretype = PREC_NONE, - int maxl = SUNSPBCGS_MAXL_DEFAULT); + explicit SUNLinSolSPBCGS( + AmiVector const& x, int pretype = PREC_NONE, + int maxl = SUNSPBCGS_MAXL_DEFAULT + ); /** * @brief Sets the function pointer for ATimes @@ -398,7 +396,7 @@ class SUNLinSolSPBCGS : public SUNLinSolWrapper { * @param ATimes * @return */ - int setATimes(void *A_data, ATimesFn ATimes); + int setATimes(void* A_data, ATimesFn ATimes); /** * @brief Sets function pointers for PSetup and PSolve routines inside @@ -409,7 +407,7 @@ class SUNLinSolSPBCGS : public SUNLinSolWrapper { * @param Psol * @return */ - int setPreconditioner(void *P_data, PSetupFn Pset, PSolveFn Psol); + int setPreconditioner(void* P_data, PSetupFn Pset, PSolveFn Psol); /** * @brief Sets pointers to left/right scaling vectors for the linear @@ -441,7 +439,6 @@ class SUNLinSolSPBCGS : public SUNLinSolWrapper { N_Vector getResid() const; }; - /** * @brief SUNDIALS scaled preconditioned FGMRES (Flexible Generalized Minimal * Residual method) (SPFGMR) solver. @@ -455,7 +452,7 @@ class SUNLinSolSPFGMR : public SUNLinSolWrapper { * PREC_BOTH) * @param maxl Maximum number of solver iterations */ - SUNLinSolSPFGMR(AmiVector const &x, int pretype, int maxl); + SUNLinSolSPFGMR(AmiVector const& x, int pretype, int maxl); /** * @brief Sets the function pointer for ATimes @@ -464,7 +461,7 @@ class SUNLinSolSPFGMR : public SUNLinSolWrapper { * @param ATimes * @return */ - int setATimes(void *A_data, ATimesFn ATimes); + int setATimes(void* A_data, ATimesFn ATimes); /** * @brief Sets function pointers for PSetup and PSolve routines inside @@ -475,7 +472,7 @@ class SUNLinSolSPFGMR : public SUNLinSolWrapper { * @param Psol * @return */ - int setPreconditioner(void *P_data, PSetupFn Pset, PSolveFn Psol); + int setPreconditioner(void* P_data, PSetupFn Pset, PSolveFn Psol); /** * @brief Sets pointers to left/right scaling vectors for the linear @@ -507,7 +504,6 @@ class SUNLinSolSPFGMR : public SUNLinSolWrapper { N_Vector getResid() const; }; - /** * @brief SUNDIALS scaled preconditioned GMRES (Generalized Minimal Residual * method) solver (SPGMR). @@ -521,8 +517,10 @@ class SUNLinSolSPGMR : public SUNLinSolWrapper { * PREC_BOTH) * @param maxl Maximum number of solver iterations */ - explicit SUNLinSolSPGMR(AmiVector const &x, int pretype = PREC_NONE, - int maxl = SUNSPGMR_MAXL_DEFAULT); + explicit SUNLinSolSPGMR( + AmiVector const& x, int pretype = PREC_NONE, + int maxl = SUNSPGMR_MAXL_DEFAULT + ); /** * @brief Sets the function pointer for ATimes @@ -531,7 +529,7 @@ class SUNLinSolSPGMR : public SUNLinSolWrapper { * @param ATimes * @return */ - int setATimes(void *A_data, ATimesFn ATimes); + int setATimes(void* A_data, ATimesFn ATimes); /** * @brief Sets function pointers for PSetup and PSolve routines inside @@ -542,7 +540,7 @@ class SUNLinSolSPGMR : public SUNLinSolWrapper { * @param Psol * @return */ - int setPreconditioner(void *P_data, PSetupFn Pset, PSolveFn Psol); + int setPreconditioner(void* P_data, PSetupFn Pset, PSolveFn Psol); /** * @brief Sets pointers to left/right scaling vectors for the linear @@ -574,7 +572,6 @@ class SUNLinSolSPGMR : public SUNLinSolWrapper { N_Vector getResid() const; }; - /** * @brief SUNDIALS scaled preconditioned TFQMR (Transpose-Free Quasi-Minimal * Residual method) (SPTFQMR) solver. @@ -588,8 +585,9 @@ class SUNLinSolSPTFQMR : public SUNLinSolWrapper { * PREC_BOTH) * @param maxl Maximum number of solver iterations */ - explicit SUNLinSolSPTFQMR(N_Vector x, int pretype = PREC_NONE, - int maxl = SUNSPTFQMR_MAXL_DEFAULT); + explicit SUNLinSolSPTFQMR( + N_Vector x, int pretype = PREC_NONE, int maxl = SUNSPTFQMR_MAXL_DEFAULT + ); /** * @brief Create SPTFQMR solver @@ -598,8 +596,10 @@ class SUNLinSolSPTFQMR : public SUNLinSolWrapper { * PREC_BOTH) * @param maxl Maximum number of solver iterations */ - explicit SUNLinSolSPTFQMR(AmiVector const &x, int pretype = PREC_NONE, - int maxl = SUNSPTFQMR_MAXL_DEFAULT); + explicit SUNLinSolSPTFQMR( + AmiVector const& x, int pretype = PREC_NONE, + int maxl = SUNSPTFQMR_MAXL_DEFAULT + ); /** * @brief Sets the function pointer for ATimes @@ -608,7 +608,7 @@ class SUNLinSolSPTFQMR : public SUNLinSolWrapper { * @param ATimes * @return */ - int setATimes(void *A_data, ATimesFn ATimes); + int setATimes(void* A_data, ATimesFn ATimes); /** * @brief Sets function pointers for PSetup and PSolve routines inside @@ -619,7 +619,7 @@ class SUNLinSolSPTFQMR : public SUNLinSolWrapper { * @param Psol * @return */ - int setPreconditioner(void *P_data, PSetupFn Pset, PSolveFn Psol); + int setPreconditioner(void* P_data, PSetupFn Pset, PSolveFn Psol); /** * @brief Sets pointers to left/right scaling vectors for the linear @@ -651,7 +651,6 @@ class SUNLinSolSPTFQMR : public SUNLinSolWrapper { N_Vector getResid() const; }; - /** * @brief A RAII wrapper for SUNNonLinearSolver structs which solve the * nonlinear system F (y) = 0 or G(y) = y. @@ -670,27 +669,27 @@ class SUNNonLinSolWrapper { * @brief Copy constructor * @param other */ - SUNNonLinSolWrapper(const SUNNonLinSolWrapper &other) = delete; + SUNNonLinSolWrapper(SUNNonLinSolWrapper const& other) = delete; /** * @brief Move constructor * @param other */ - SUNNonLinSolWrapper(SUNNonLinSolWrapper &&other) noexcept; + SUNNonLinSolWrapper(SUNNonLinSolWrapper&& other) noexcept; /** * @brief Copy assignment * @param other * @return */ - SUNNonLinSolWrapper &operator=(const SUNNonLinSolWrapper &other) = delete; + SUNNonLinSolWrapper& operator=(SUNNonLinSolWrapper const& other) = delete; /** * @brief Move assignment * @param other * @return */ - SUNNonLinSolWrapper &operator=(SUNNonLinSolWrapper &&other) noexcept; + SUNNonLinSolWrapper& operator=(SUNNonLinSolWrapper&& other) noexcept; /** * @brief Get the wrapped SUNNonlinearSolver @@ -710,7 +709,7 @@ class SUNNonLinSolWrapper { * @param mem the sundials integrator memory structure. * @return */ - int setup(N_Vector y, void *mem); + int setup(N_Vector y, void* mem); /** * @brief Solve the nonlinear system F (y) = 0 or G(y) = y. @@ -726,8 +725,10 @@ class SUNNonLinSolWrapper { * @param mem the sundials integrator memory structure. * @return */ - int Solve(N_Vector y0, N_Vector y, N_Vector w, realtype tol, - bool callLSetup, void *mem); + int Solve( + N_Vector y0, N_Vector y, N_Vector w, realtype tol, bool callLSetup, + void* mem + ); /** * @brief Set function to evaluate the nonlinear residual function F(y) = 0 @@ -794,7 +795,6 @@ class SUNNonLinSolWrapper { SUNNonlinearSolver solver = nullptr; }; - /** * @brief SUNDIALS Newton non-linear solver to solve F (y) = 0. */ @@ -821,10 +821,9 @@ class SUNNonLinSolNewton : public SUNNonLinSolWrapper { * @param SysFn * @return */ - int getSysFn(SUNNonlinSolSysFn *SysFn) const; + int getSysFn(SUNNonlinSolSysFn* SysFn) const; }; - /** * @brief SUNDIALS Fixed point non-linear solver to solve G(y) = y. */ @@ -853,7 +852,7 @@ class SUNNonLinSolFixedPoint : public SUNNonLinSolWrapper { * @param SysFn * @return */ - int getSysFn(SUNNonlinSolSysFn *SysFn) const; + int getSysFn(SUNNonlinSolSysFn* SysFn) const; }; } // namespace amici diff --git a/deps/AMICI/include/amici/sundials_matrix_wrapper.h b/deps/AMICI/include/amici/sundials_matrix_wrapper.h index b94667e39..8d63eca5e 100644 --- a/deps/AMICI/include/amici/sundials_matrix_wrapper.h +++ b/deps/AMICI/include/amici/sundials_matrix_wrapper.h @@ -7,8 +7,8 @@ #include -#include #include +#include #include @@ -33,8 +33,9 @@ class SUNMatrixWrapper { * @param NNZ Number of nonzeros * @param sparsetype Sparse type */ - SUNMatrixWrapper(sunindextype M, sunindextype N, sunindextype NNZ, - int sparsetype); + SUNMatrixWrapper( + sunindextype M, sunindextype N, sunindextype NNZ, int sparsetype + ); /** * @brief Create dense matrix. See SUNDenseMatrix in sunmatrix_dense.h @@ -59,8 +60,9 @@ class SUNMatrixWrapper { * @param droptol tolerance for dropping entries * @param sparsetype Sparse type */ - SUNMatrixWrapper(const SUNMatrixWrapper &A, realtype droptol, - int sparsetype); + SUNMatrixWrapper( + SUNMatrixWrapper const& A, realtype droptol, int sparsetype + ); /** * @brief Wrap existing SUNMatrix @@ -74,27 +76,27 @@ class SUNMatrixWrapper { * @brief Copy constructor * @param other */ - SUNMatrixWrapper(const SUNMatrixWrapper &other); + SUNMatrixWrapper(SUNMatrixWrapper const& other); /** * @brief Move constructor * @param other */ - SUNMatrixWrapper(SUNMatrixWrapper &&other); + SUNMatrixWrapper(SUNMatrixWrapper&& other); /** * @brief Copy assignment * @param other * @return */ - SUNMatrixWrapper &operator=(const SUNMatrixWrapper &other); + SUNMatrixWrapper& operator=(SUNMatrixWrapper const& other); /** * @brief Move assignment * @param other * @return */ - SUNMatrixWrapper &operator=(SUNMatrixWrapper &&other); + SUNMatrixWrapper& operator=(SUNMatrixWrapper&& other); /** * @brief Reallocate space for sparse matrix according to specified nnz @@ -103,17 +105,19 @@ class SUNMatrixWrapper { void reallocate(sunindextype nnz); /** - * @brief Reallocate space for sparse matrix to used space according to last entry in indexptrs + * @brief Reallocate space for sparse matrix to used space according to last + * entry in indexptrs */ void realloc(); /** * @brief Get the wrapped SUNMatrix * @return raw SunMatrix object - * @note Even though the returned matrix_ pointer is const qualified, matrix_->content will not be const. - * This is a shortcoming in the underlying C library, which we cannot address and it is not intended that - * any of those values are modified externally. If matrix_->content is manipulated, - * cpp:meth:SUNMatrixWrapper:`refresh` needs to be called. + * @note Even though the returned matrix_ pointer is const qualified, + * matrix_->content will not be const. This is a shortcoming in the + * underlying C library, which we cannot address and it is not intended that + * any of those values are modified externally. If matrix_->content is + * manipulated, cpp:meth:SUNMatrixWrapper:`refresh` needs to be called. */ SUNMatrix get() const; @@ -122,10 +126,12 @@ class SUNMatrixWrapper { * @return number of rows */ sunindextype rows() const { - assert(!matrix_ || - (matrix_id() == SUNMATRIX_SPARSE ? - num_rows_ == SM_ROWS_S(matrix_) : - num_rows_ == SM_ROWS_D(matrix_))); + assert( + !matrix_ + || (matrix_id() == SUNMATRIX_SPARSE + ? num_rows_ == SM_ROWS_S(matrix_) + : num_rows_ == SM_ROWS_D(matrix_)) + ); return num_rows_; } @@ -134,22 +140,26 @@ class SUNMatrixWrapper { * @return number of columns */ sunindextype columns() const { - assert(!matrix_ || - (matrix_id() == SUNMATRIX_SPARSE ? - num_columns_ == SM_COLUMNS_S(matrix_) : - num_columns_ == SM_COLUMNS_D(matrix_))); + assert( + !matrix_ + || (matrix_id() == SUNMATRIX_SPARSE + ? num_columns_ == SM_COLUMNS_S(matrix_) + : num_columns_ == SM_COLUMNS_D(matrix_)) + ); return num_columns_; } /** - * @brief Get the number of specified non-zero elements (sparse matrices only) + * @brief Get the number of specified non-zero elements (sparse matrices + * only) * @note value will be 0 before indexptrs are set. * @return number of nonzero entries */ sunindextype num_nonzeros() const; /** - * @brief Get the number of indexptrs that can be specified (sparse matrices only) + * @brief Get the number of indexptrs that can be specified (sparse matrices + * only) * @return number of indexptrs */ sunindextype num_indexptrs() const; @@ -164,20 +174,20 @@ class SUNMatrixWrapper { * @brief Get raw data of a sparse matrix * @return pointer to first data entry */ - realtype *data(); + realtype* data(); /** * @brief Get const raw data of a sparse matrix * @return pointer to first data entry */ - const realtype *data() const; + realtype const* data() const; /** * @brief Get data of a sparse matrix * @param idx data index * @return idx-th data entry */ - realtype get_data(sunindextype idx) const{ + realtype get_data(sunindextype idx) const { assert(matrix_); assert(matrix_id() == SUNMATRIX_SPARSE); assert(idx < capacity()); @@ -191,7 +201,7 @@ class SUNMatrixWrapper { * @param icol col * @return A(irow,icol) */ - realtype get_data(sunindextype irow, sunindextype icol) const{ + realtype get_data(sunindextype irow, sunindextype icol) const { assert(matrix_); assert(matrix_id() == SUNMATRIX_DENSE); assert(irow < rows()); @@ -256,10 +266,10 @@ 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(const gsl::span vals) { assert(matrix_); assert(matrix_id() == SUNMATRIX_SPARSE); - assert(static_cast(vals.size()) == capacity()); + assert(gsl::narrow(vals.size()) == capacity()); assert(indexvals_ == SM_INDEXVALS_S(matrix_)); std::copy_n(vals.begin(), capacity(), indexvals_); } @@ -280,7 +290,8 @@ class SUNMatrixWrapper { /** * @brief Set the index pointer of a sparse matrix * @param ptr_idx pointer index - * @param ptr data-index where the ptr_idx-th column (CSC) or row (CSR) starts + * @param ptr data-index where the ptr_idx-th column (CSC) or row (CSR) + * starts */ void set_indexptr(sunindextype ptr_idx, sunindextype ptr) { assert(matrix_); @@ -295,12 +306,13 @@ class SUNMatrixWrapper { /** * @brief Set the index pointers of a sparse matrix - * @param ptrs starting data-indices where the columns (CSC) or rows (CSR) start + * @param ptrs starting data-indices where the columns (CSC) or rows (CSR) + * start */ - void set_indexptrs(const gsl::span ptrs) { + void set_indexptrs(const gsl::span ptrs) { assert(matrix_); assert(matrix_id() == SUNMATRIX_SPARSE); - assert(static_cast(ptrs.size()) == num_indexptrs() + 1); + assert(gsl::narrow(ptrs.size()) == num_indexptrs() + 1); assert(indexptrs_ == SM_INDEXPTRS_S(matrix_)); std::copy_n(ptrs.begin(), num_indexptrs() + 1, indexptrs_); num_nonzeros_ = indexptrs_[num_indexptrs()]; @@ -332,19 +344,21 @@ class SUNMatrixWrapper { * @param b multiplication vector * @param alpha scalar coefficient for matrix */ - void multiply(AmiVector& c, AmiVector const& b, realtype alpha = 1.0) const { + void + multiply(AmiVector& c, AmiVector const& b, realtype alpha = 1.0) const { multiply(c.getNVector(), b.getNVector(), alpha); } - /** * @brief Perform matrix vector multiplication c += alpha * A*b * @param c output vector, may already contain values * @param b multiplication vector * @param alpha scalar coefficient */ - void multiply(gsl::span c, gsl::span b, - const realtype alpha = 1.0) const; + void multiply( + gsl::span c, gsl::span b, + const realtype alpha = 1.0 + ) const; /** * @brief Perform reordered matrix vector multiplication c += A[:,cols]*b @@ -353,10 +367,9 @@ class SUNMatrixWrapper { * @param cols int vector for column reordering * @param transpose bool transpose A before multiplication */ - void multiply(N_Vector c, - const_N_Vector b, - gsl::span cols, - bool transpose) const; + void multiply( + N_Vector c, const_N_Vector b, gsl::span cols, bool transpose + ) const; /** * @brief Perform reordered matrix vector multiplication c += A[:,cols]*b @@ -365,19 +378,19 @@ class SUNMatrixWrapper { * @param cols int vector for column reordering * @param transpose bool transpose A before multiplication */ - void multiply(gsl::span c, - gsl::span b, - gsl::span cols, - bool transpose) const; + void multiply( + gsl::span c, gsl::span b, + gsl::span cols, bool transpose + ) const; /** * @brief Perform matrix matrix multiplication C = A * B for sparse A, B, C * @param C output matrix, * @param B multiplication matrix - * @note will overwrite existing data, indexptrs, indexvals for C, but will use preallocated space for these vars + * @note will overwrite existing data, indexptrs, indexvals for C, but will + * use preallocated space for these vars */ - void sparse_multiply(SUNMatrixWrapper &C, - const SUNMatrixWrapper &B) const; + void sparse_multiply(SUNMatrixWrapper& C, SUNMatrixWrapper const& B) const; /** * @brief Perform sparse matrix matrix addition C = alpha * A + beta * B @@ -385,59 +398,72 @@ class SUNMatrixWrapper { * @param alpha scalar A * @param B addition matrix * @param beta scalar B - * @note will overwrite existing data, indexptrs, indexvals for C, but will use preallocated space for these vars + * @note will overwrite existing data, indexptrs, indexvals for C, but will + * use preallocated space for these vars */ - void sparse_add(const SUNMatrixWrapper &A, realtype alpha, - const SUNMatrixWrapper &B, realtype beta); + void sparse_add( + SUNMatrixWrapper const& A, realtype alpha, SUNMatrixWrapper const& B, + realtype beta + ); /** * @brief Perform matrix-matrix addition A = sum(mats(0)...mats(len(mats))) * @param mats vector of sparse matrices - * @note will overwrite existing data, indexptrs, indexvals for A, but will use preallocated space for these vars + * @note will overwrite existing data, indexptrs, indexvals for A, but will + * use preallocated space for these vars */ - void sparse_sum(const std::vector &mats); + void sparse_sum(std::vector const& mats); /** - * @brief Compute x = x + beta * A(:,k), where x is a dense vector and A(:,k) is sparse, and update - * the sparsity pattern for C(:,j) if applicable + * @brief Compute x = x + beta * A(:,k), where x is a dense vector and + * A(:,k) is sparse, and update the sparsity pattern for C(:,j) if + * applicable * * This function currently has two purposes: - * - perform parts of sparse matrix-matrix multiplication C(:,j)=A(:,k)*B(k,j) - * enabled by passing beta=B(k,j), x=C(:,j), C=C, w=sparsity of C(:,j) from B(k,0...j-1), nnz=nnz(C(:,0...j-1) - * - add the k-th column of the sparse matrix A multiplied by beta to the dense vector x. - * enabled by passing beta=*, x=x, C=nullptr, w=nullptr, nnz=* + * - perform parts of sparse matrix-matrix multiplication + * C(:,j)=A(:,k)*B(k,j) enabled by passing beta=B(k,j), x=C(:,j), C=C, + * w=sparsity of C(:,j) from B(k,0...j-1), nnz=nnz(C(:,0...j-1) + * - add the k-th column of the sparse matrix A multiplied by beta to the + * dense vector x. enabled by passing beta=*, x=x, C=nullptr, w=nullptr, + * nnz=* * * @param k column index * @param beta scaling factor - * @param w index workspace, (w[i] x, - const sunindextype mark, - SUNMatrixWrapper *C, sunindextype nnz) const; + sunindextype scatter( + const sunindextype k, const realtype beta, sunindextype* w, + gsl::span x, const sunindextype mark, SUNMatrixWrapper* C, + sunindextype nnz + ) const; /** - * @brief Compute transpose A' of sparse matrix A and writes it to the matrix C = alpha * A' + * @brief Compute transpose A' of sparse matrix A and writes it to the + * matrix C = alpha * A' * * @param C output matrix (sparse or dense) * @param alpha scalar multiplier - * @param blocksize blocksize for transposition. For full matrix transpose set to ncols/nrows + * @param blocksize blocksize for transposition. For full matrix transpose + * set to ncols/nrows */ - void transpose(SUNMatrixWrapper &C, const realtype alpha, - sunindextype blocksize) const; + void transpose( + SUNMatrixWrapper& C, const realtype alpha, sunindextype blocksize + ) const; /** * @brief Writes a sparse matrix A to a dense matrix D. * * @param D dense output matrix */ - void to_dense(SUNMatrixWrapper &D) const; + void to_dense(SUNMatrixWrapper& D) const; /** * @brief Writes the diagonal of sparse matrix A to a dense vector v. @@ -455,64 +481,64 @@ class SUNMatrixWrapper { * @brief Get matrix id * @return SUNMatrix_ID */ - SUNMatrix_ID matrix_id() const {return id_;}; + SUNMatrix_ID matrix_id() const { return id_; }; /** - * @brief Update internal cache, needs to be called after external manipulation of matrix_->content + * @brief Update internal cache, needs to be called after external + * manipulation of matrix_->content */ void refresh(); private: - /** * @brief SUNMatrix to which all methods are applied */ - SUNMatrix matrix_ {nullptr}; + SUNMatrix matrix_{nullptr}; /** * @brief cache for SUNMatrixGetId(matrix_) */ - SUNMatrix_ID id_ {SUNMATRIX_CUSTOM}; + SUNMatrix_ID id_{SUNMATRIX_CUSTOM}; /** * @brief cache for SUNMatrixGetId(matrix_) */ - int sparsetype_ {CSC_MAT}; + int sparsetype_{CSC_MAT}; /** * @brief cache for SM_INDEXPTRS_S(matrix_)[SM_NP_S(matrix_)] */ - sunindextype num_nonzeros_ {0}; + sunindextype num_nonzeros_{0}; /** * @brief cache for SM_NNZ_S(matrix_) */ - sunindextype capacity_ {0}; + sunindextype capacity_{0}; /** * @brief cache for SM_DATA_S(matrix_) */ - realtype *data_ {nullptr}; + realtype* data_{nullptr}; /** * @brief cache for SM_INDEXPTRS_S(matrix_) */ - sunindextype *indexptrs_ {nullptr}; + sunindextype* indexptrs_{nullptr}; /** * @brief cache for SM_INDEXVALS_S(matrix_) */ - sunindextype *indexvals_ {nullptr}; + sunindextype* indexvals_{nullptr}; /** * @brief cache for SM_ROWS_X(matrix_) */ - sunindextype num_rows_ {0}; + sunindextype num_rows_{0}; /** * @brief cache for SM_COLUMS_X(matrix_) */ - sunindextype num_columns_ {0}; + sunindextype num_columns_{0}; /** * @brief cache for SM_NP_S(matrix_) */ - sunindextype num_indexptrs_ {0}; + sunindextype num_indexptrs_{0}; /** * @brief call update_ptrs & update_size @@ -527,11 +553,21 @@ class SUNMatrixWrapper { */ void update_size(); /** - * @brief indicator whether this wrapper allocated matrix_ and is responsible for deallocation + * @brief indicator whether this wrapper allocated matrix_ and is + * responsible for deallocation */ bool ownmat = true; }; +/** + * @brief Convert a flat index to a pair of row/column indices. + * @param i flat index + * @param m referred to matrix + * @return row index, column index + */ +auto unravel_index(sunindextype i, SUNMatrix m) + -> std::pair; + } // namespace amici namespace gsl { @@ -540,8 +576,7 @@ namespace gsl { * @param m SUNMatrix * @return Created span */ -inline span make_span(SUNMatrix m) -{ +inline span make_span(SUNMatrix m) { switch (SUNMatGetID(m)) { case SUNMATRIX_DENSE: return span(SM_DATA_D(m), SM_LDATA_D(m)); diff --git a/deps/AMICI/include/amici/symbolic_functions.h b/deps/AMICI/include/amici/symbolic_functions.h index 3cacc823b..0e8f558eb 100644 --- a/deps/AMICI/include/amici/symbolic_functions.h +++ b/deps/AMICI/include/amici/symbolic_functions.h @@ -114,8 +114,6 @@ double getNaN(); */ double sign(double x); - - /* legacy spline implementation in C (MATLAB only) */ /** diff --git a/deps/AMICI/include/amici/vector.h b/deps/AMICI/include/amici/vector.h index 102a75e7f..b1b496c26 100644 --- a/deps/AMICI/include/amici/vector.h +++ b/deps/AMICI/include/amici/vector.h @@ -1,8 +1,8 @@ #ifndef AMICI_VECTOR_H #define AMICI_VECTOR_H -#include #include +#include #include @@ -13,10 +13,10 @@ namespace amici { /** Since const N_Vector is not what we want */ -using const_N_Vector = - std::add_const_t> *; +using const_N_Vector + = std::add_const_t>*; -inline const realtype* N_VGetArrayPointerConst(const_N_Vector x) { +inline realtype const* N_VGetArrayPointerConst(const_N_Vector x) { return N_VGetArrayPointer(const_cast(x)); } @@ -36,9 +36,9 @@ class AmiVector { * @brief empty constructor * @param length number of elements in vector */ - explicit AmiVector(const long int length) - : vec_(static_cast(length), 0.0), - nvec_(N_VMake_Serial(length, vec_.data())) {} + explicit AmiVector(long int const length) + : vec_(static_cast(length), 0.0) + , nvec_(N_VMake_Serial(length, vec_.data())) {} /** Moves data from std::vector and constructs an nvec that points to the * data @@ -46,8 +46,9 @@ class AmiVector { * @param rvec vector from which the data will be moved */ explicit AmiVector(std::vector rvec) - : vec_(std::move(rvec)), - nvec_(N_VMake_Serial(static_cast(vec_.size()), vec_.data())) {} + : vec_(std::move(rvec)) + , nvec_(N_VMake_Serial(gsl::narrow(vec_.size()), vec_.data()) + ) {} /** Copy data from gsl::span and constructs a vector * @brief constructor from gsl::span, @@ -60,16 +61,19 @@ class AmiVector { * @brief copy constructor * @param vold vector from which the data will be copied */ - AmiVector(const AmiVector &vold) : vec_(vold.vec_) { - nvec_ = - N_VMake_Serial(static_cast(vold.vec_.size()), vec_.data()); + AmiVector(AmiVector const& vold) + : vec_(vold.vec_) { + nvec_ = N_VMake_Serial( + gsl::narrow(vold.vec_.size()), vec_.data() + ); } /** * @brief move constructor * @param other vector from which the data will be moved */ - AmiVector(AmiVector&& other) noexcept : nvec_(nullptr) { + AmiVector(AmiVector&& other) noexcept + : nvec_(nullptr) { vec_ = std::move(other.vec_); synchroniseNVector(); } @@ -84,17 +88,18 @@ class AmiVector { * @param other right hand side * @return left hand side */ - AmiVector &operator=(AmiVector const &other); + AmiVector& operator=(AmiVector const& other); /** * @brief operator *= (element-wise multiplication) * @param multiplier multiplier * @return result */ - AmiVector &operator*=(AmiVector const& multiplier) { - N_VProd(getNVector(), - const_cast(multiplier.getNVector()), - getNVector()); + AmiVector& operator*=(AmiVector const& multiplier) { + N_VProd( + getNVector(), const_cast(multiplier.getNVector()), + getNVector() + ); return *this; } @@ -103,10 +108,11 @@ class AmiVector { * @param divisor divisor * @return result */ - AmiVector &operator/=(AmiVector const& divisor) { - N_VDiv(getNVector(), - const_cast(divisor.getNVector()), - getNVector()); + AmiVector& operator/=(AmiVector const& divisor) { + N_VDiv( + getNVector(), const_cast(divisor.getNVector()), + getNVector() + ); return *this; } @@ -128,13 +134,13 @@ class AmiVector { * @brief data accessor * @return pointer to data array */ - realtype *data(); + realtype* data(); /** * @brief const data accessor * @return const pointer to data array */ - const realtype *data() const; + realtype const* data() const; /** * @brief N_Vector accessor @@ -152,7 +158,7 @@ class AmiVector { * @brief Vector accessor * @return Vector */ - std::vector const &getVector() const; + std::vector const& getVector() const; /** * @brief returns the length of the vector @@ -181,40 +187,38 @@ class AmiVector { * @param pos index of element * @return element */ - realtype &operator[](int pos); + realtype& operator[](int pos); /** * @brief accessor to data elements of the vector * @param pos index of element * @return element */ - realtype &at(int pos); + realtype& at(int pos); /** * @brief accessor to data elements of the vector * @param pos index of element * @return element */ - const realtype &at(int pos) const; + realtype const& at(int pos) const; /** * @brief copies data from another AmiVector * @param other data source */ - void copy(const AmiVector &other); + void copy(AmiVector const& other); /** * @brief Take absolute value (in-place) */ - void abs() { - N_VAbs(getNVector(), getNVector()); - }; + void abs() { N_VAbs(getNVector(), getNVector()); }; private: /** main data storage */ std::vector vec_; /** N_Vector, will be synchronized such that it points to data in vec */ - N_Vector nvec_ {nullptr}; + N_Vector nvec_{nullptr}; /** * @brief reconstructs nvec such that data pointer points to vec data array @@ -250,7 +254,7 @@ class AmiVectorArray { * @brief copy constructor * @param vaold object to copy from */ - AmiVectorArray(const AmiVectorArray &vaold); + AmiVectorArray(AmiVectorArray const& vaold); ~AmiVectorArray() = default; @@ -259,21 +263,21 @@ class AmiVectorArray { * @param other right hand side * @return left hand side */ - AmiVectorArray &operator=(AmiVectorArray const &other); + AmiVectorArray& operator=(AmiVectorArray const& other); /** * @brief accessor to data of AmiVector elements * @param pos index of AmiVector * @return pointer to data array */ - realtype *data(int pos); + realtype* data(int pos); /** * @brief const accessor to data of AmiVector elements * @param pos index of AmiVector * @return const pointer to data array */ - const realtype *data(int pos) const; + realtype const* data(int pos) const; /** * @brief accessor to elements of AmiVector elements @@ -281,7 +285,7 @@ class AmiVectorArray { * @param jpos outer index in AmiVectorArray * @return element */ - realtype &at(int ipos, int jpos); + realtype& at(int ipos, int jpos); /** * @brief const accessor to elements of AmiVector elements @@ -289,13 +293,13 @@ class AmiVectorArray { * @param jpos outer index in AmiVectorArray * @return element */ - const realtype &at(int ipos, int jpos) const; + realtype const& at(int ipos, int jpos) const; /** * @brief accessor to NVectorArray * @return N_VectorArray */ - N_Vector *getNVectorArray(); + N_Vector* getNVectorArray(); /** * @brief accessor to NVector element @@ -316,14 +320,14 @@ class AmiVectorArray { * @param pos index of AmiVector * @return AmiVector */ - AmiVector &operator[](int pos); + AmiVector& operator[](int pos); /** * @brief const accessor to AmiVector elements * @param pos index of AmiVector * @return const AmiVector */ - const AmiVector &operator[](int pos) const; + AmiVector const& operator[](int pos) const; /** * @brief length of AmiVectorArray @@ -341,13 +345,13 @@ class AmiVectorArray { * @param vec vector into which the AmiVectorArray will be flattened. Must * have length equal to number of elements. */ - void flatten_to_vector(std::vector &vec) const; + void flatten_to_vector(std::vector& vec) const; /** * @brief copies data from another AmiVectorArray * @param other data source */ - void copy(const AmiVectorArray &other); + void copy(AmiVectorArray const& other); private: /** main data storage */ @@ -368,11 +372,13 @@ class AmiVectorArray { * @param y another vector with same size as x * @param z result vector of same size as x and y */ -inline void linearSum(realtype a, AmiVector const& x, realtype b, - AmiVector const& y, AmiVector& z) { - N_VLinearSum(a, const_cast(x.getNVector()), - b, const_cast(y.getNVector()), - z.getNVector()); +inline void linearSum( + realtype a, AmiVector const& x, realtype b, AmiVector const& y, AmiVector& z +) { + N_VLinearSum( + a, const_cast(x.getNVector()), b, + const_cast(y.getNVector()), z.getNVector() + ); } /** @@ -382,21 +388,21 @@ inline void linearSum(realtype a, AmiVector const& x, realtype b, * @return dot product of x and y */ inline realtype dotProd(AmiVector const& x, AmiVector const& y) { - return N_VDotProd(const_cast(x.getNVector()), - const_cast(y.getNVector())); + return N_VDotProd( + const_cast(x.getNVector()), + const_cast(y.getNVector()) + ); } } // namespace amici - namespace gsl { /** * @brief Create span from N_Vector * @param nv * @return */ -inline span make_span(N_Vector nv) -{ +inline span make_span(N_Vector nv) { return span(N_VGetArrayPointer(nv), N_VGetLength_Serial(nv)); } } // namespace gsl diff --git a/deps/AMICI/matlab/@amidata/amidata.m b/deps/AMICI/matlab/@amidata/amidata.m index b9be294d2..85ff897fe 100644 --- a/deps/AMICI/matlab/@amidata/amidata.m +++ b/deps/AMICI/matlab/@amidata/amidata.m @@ -4,11 +4,11 @@ % classdef amidata < handle % AMIDATA provides a data container to pass experimental data to the - % simulation routine for likelihood computation. - % when any of the properties are updated, the class automatically - % checks consistency of dimension and updates related properties and + % simulation routine for likelihood computation. + % when any of the properties are updated, the class automatically + % checks consistency of dimension and updates related properties and % initialises them with NaNs - + properties % number of timepoints nt=0; @@ -37,12 +37,12 @@ % reinitialize states based on fixed parameters after preeq.? reinitializeStates = false; end - + methods function D = amidata(varargin) - % amidata creates an amidata container for experimental data + % amidata creates an amidata container for experimental data % with specified dimensions amidata. - % + % % AMIDATA(amidata) creates a copy of the input container % % AMIDATA(struct) tries to creates an amidata container from the @@ -62,15 +62,15 @@ % AMIDATA(nt,ny,nz,ne,nk) constructs an empty data container with % in the provided dimensions intialised with NaNs % - % + % % % Parameters: % varargin: % % Return values: % - - + + % initialisation via struct if isa(varargin{1},'amidata') if strcmp(class(varargin{1}),class(D)) @@ -112,7 +112,7 @@ end if(isfield(varargin{1},'Sigma_Z')) D.Sigma_Z = varargin{1}.Sigma_Z; - end + end if(isfield(varargin{1},'condition')) D.nk = numel(varargin{1}.condition); D.condition = varargin{1}.condition; @@ -131,7 +131,7 @@ else error('Assignment error: Value for field reinitializeStates must be logical.'); end - end + end elseif(nargin == 5) D.nt = varargin{1}; D.ny = varargin{2}; @@ -141,62 +141,62 @@ end end - + function set.nt(this,nt) this.nt = nt; this.t = 1:nt; this.Y = NaN; this.Sigma_Y = NaN; end - + function set.ny(this,ny) this.ny = ny; this.Y = NaN; this.Sigma_Y = NaN; end - + function set.nz(this,nz) this.nz = nz; this.Z = NaN; this.Sigma_Z = NaN; end - + function set.ne(this,ne) this.ne = ne; this.Z = NaN; this.Sigma_Z = NaN; end - + function set.nk(this,nk) this.nk = nk; this.condition = NaN(nk,1); end - + function set.t(this,value) assert(isnumeric(value),'AMICI:amimodel:t:numeric','t must have a numeric value!') assert(ismatrix(value),'AMICI:amimodel:t:ndims','t must be a two dimensional matrix!') assert(numel(value)==this.nt,'AMICI:amimodel:t:ndims',['t must have ' num2str(this.nt) ' (D.nt) elements!']) this.t = double(value(:)); end - + function set.condition(this,value) assert(isnumeric(value),'AMICI:amimodel:condition:numeric','condition must have a numeric value!') assert(ismatrix(value),'AMICI:amimodel:condition:ndims','condition must be a two dimensional matrix!') assert(numel(value)==this.nk,'AMICI:amimodel:condition:ndims',['condition must have ' num2str(this.nk) ' (D.nk) elements!']) this.condition = double(value(:)); end - + function set.conditionPreequilibration(this,value) assert(isnumeric(value),'AMICI:amimodel:condition:numeric','condition must have a numeric value!') assert(ismatrix(value),'AMICI:amimodel:condition:ndims','condition must be a two dimensional matrix!') assert(numel(value)==this.nk,'AMICI:amimodel:condition:ndims',['condition must have ' num2str(this.nk) ' (D.nk) elements!']) this.conditionPreequilibration = double(value(:)); end - + function set.Y(this,value) assert(ismatrix(value),'AMICI:amimodel:Y:ndims','Y must be a two dimensional matrix!') assert(all(all(or(isnumeric(value),isnan(value)))),'AMICI:amimodel:Y:numeric','Y must have a numeric value!') - + if(all(size(value)==[this.nt this.ny])) this.Y = double(value); elseif(all(size(value)==[this.nt 1])) @@ -209,7 +209,7 @@ error('AMICI:amimodel:Y:size',['Y must have size [' num2str(this.nt) ',' num2str(this.ny) '] ([D.nt,D.ny])!']) end end - + function set.Sigma_Y(this,value) assert(ismatrix(value),'AMICI:amimodel:Sigma_Y:ndims','Sigma_Y must be a two dimensional matrix!') assert(all(all(or(isnumeric(value),isnan(value)))),'AMICI:amimodel:Sigma_Y:numeric','Sigma_Y must have a numeric value!') @@ -225,7 +225,7 @@ error('AMICI:amimodel:Sigma_Y:size',['Sigma_Y must have size [' num2str(this.nt) ',' num2str(this.ny) '] ([D.nt,D.ny])!']) end end - + function set.Z(this,value) assert(ismatrix(value),'AMICI:amimodel:Z:ndims','Z must be a two dimensional matrix!') assert(all(all(or(isnumeric(value),isnan(value)))),'AMICI:amimodel:Z:numeric','Z must have a numeric value!') @@ -241,7 +241,7 @@ error('AMICI:amimodel:Z:size',['Z must have size [' num2str(this.ne) ',' num2str(this.nz) '] ([D.ne,D.nz])!']) end end - + function set.Sigma_Z(this,value) assert(ismatrix(value),'AMICI:amimodel:Sigma_Z:ndims','Sigma_Z must be a two dimensional matrix!') assert(all(all(or(isnumeric(value),isnan(value)))),'AMICI:amimodel:Sigma_Z:numeric','Sigma_Z must have a numeric value!') @@ -258,6 +258,5 @@ end end end - -end +end diff --git a/deps/AMICI/matlab/@amievent/amievent.m b/deps/AMICI/matlab/@amievent/amievent.m index 098a4e437..1ce5d7f2c 100644 --- a/deps/AMICI/matlab/@amievent/amievent.m +++ b/deps/AMICI/matlab/@amievent/amievent.m @@ -5,7 +5,7 @@ classdef amievent % AMIEVENT defines events which later on will be transformed into appropriate % C code - + properties ( GetAccess = 'public', SetAccess = 'private' ) % the trigger function activates the event on every zero crossing @type symbolic trigger = sym.empty(); @@ -17,7 +17,7 @@ % to speed up symbolic computations hflag = logical.empty(); end - + methods function AE = amievent(trigger,bolus,z) % amievent constructs an amievent object from the provided input. @@ -46,7 +46,7 @@ if(numel(AE.trigger)>1) error('The trigger function must be scalar.') end - + if(~isa(bolus,'sym')) if(isa(bolus,'double')) AE.bolus = sym(bolus(:)); @@ -56,7 +56,7 @@ else AE.bolus = bolus; end - + if(~isa(z,'sym')) if(isa(z,'double')) if(~isempty(z)) @@ -67,13 +67,12 @@ end else error('output function must be a symbolic expression') - end + end else AE.z = z; end end - + this = setHflag(this,hflag); end end - diff --git a/deps/AMICI/matlab/@amifun/amifun.m b/deps/AMICI/matlab/@amifun/amifun.m index 6743434cb..e51c43b31 100644 --- a/deps/AMICI/matlab/@amifun/amifun.m +++ b/deps/AMICI/matlab/@amifun/amifun.m @@ -5,7 +5,7 @@ classdef amifun % AMIFUN defines functions which later on will be transformed into % appropriate C code - + properties ( GetAccess = 'public', SetAccess = 'public' ) % symbolic definition struct @type symbolic sym = sym([]); @@ -29,7 +29,7 @@ % with respect to parameters sensiflag = logical.empty(); end - + methods function AF = amifun(funstr,model) % amievent constructs an amifun object from the provided input. @@ -38,7 +38,7 @@ % funstr: name of the requested function % model: amimodel object which carries all symbolic % definitions to construct the function - % + % % % Return values: % AF: amifun object @@ -50,26 +50,25 @@ AF = AF.getCVar(); AF = AF.getSensiFlag(); end - + writeCcode_sensi(this,model,fid) - + writeCcode(this,model,fid) - + writeMcode(this,model) - + gccode(this,model,fid) - + [ this ] = getDeps(this,model) - + [ this ] = getArgs(this,model) - + [ this ] = getNVecs(this) - + [ this ] = getCVar(this) - + [ this ] = getSensiFlag(this) [ this, model ] = getSyms(this,model) end end - diff --git a/deps/AMICI/matlab/@amifun/gccode.m b/deps/AMICI/matlab/@amifun/gccode.m index a00724b3b..5670f2688 100644 --- a/deps/AMICI/matlab/@amifun/gccode.m +++ b/deps/AMICI/matlab/@amifun/gccode.m @@ -8,17 +8,17 @@ % % Return values: % this: function definition object @type amifun - - + + if(any(any(any(this.sym~=0)))) - + % replace unknown partial derivatives if(model.maxflag) this.sym = subs(this.sym,sym('D([1], am_max)'),sym('D1max')); this.sym = subs(this.sym,sym('D([2], am_max)'),sym('D2max')); this.sym = subs(this.sym,sym('am_max'),sym('max')); end - + % If we have spline, we need to parse them to get derivatives if (model.splineflag) symstr = char(this.sym); @@ -31,7 +31,7 @@ else isDSpline = false; end - + if (isDSpline) [~, nCol] = size(this.sym); for iCol = 1 : nCol @@ -48,7 +48,7 @@ end end end - + cstr = ccode(this.sym); if(~strcmp(cstr(3:4),'t0')) if(any(strcmp(this.funstr,{'J','JB','JDiag','dJydsigma','dJydy','dJzdsigma','dJzdz','dJrzdsigma','dJrzdz','dydx','dzdx','drzdx','M','dfdx'}) )) @@ -60,13 +60,13 @@ else cstr = strrep(cstr,'t0',[this.cvar '_0']); end - + cstr = strrep(cstr,'log','amici::log'); % fix derivatives again (we cant do this before as this would yield % incorrect symbolic expressions cstr = regexprep(regexprep(cstr,'D([0-9]*)([\w]*)\(','D$2\($1,'),'DD([0-9]*)([\w]*)\(','DD$2\($1,'); cstr = strrep(strrep(cstr, 'DDspline', 'DDspline'), 'Dspline', 'Dspline'); - + if (model.splineflag) if (strfind(symstr, 'spline')) % The floating numbers after 't' must be converted to integers @@ -75,11 +75,11 @@ cstr = regexprep(cstr, '([D]*(spline|spline_pos))\((\w+)\,(\w+)\,t\,\w+\.\w+\,', ['amici::$1\($2\,$3\,t\,', num2str(nNodes), '\,']); end end - + if(numel(cstr)>1) - + % fix various function specific variable names/indexes - + cstr = regexprep(cstr,'var_x_([0-9]+)','x[$1]'); cstr = regexprep(cstr,'var_dx_([0-9]+)','dx[$1]'); cstr = regexprep(cstr,'var_sx_([0-9]+)','sx[$1]'); @@ -101,7 +101,7 @@ cstr = regexprep(cstr,'var_sx0_([0-9]+)','sx0[$1]'); cstr = regexprep(cstr,'var_sdx0_([0-9]+)','sdx0[$1]'); cstr = regexprep(cstr,'var_root_([0-9]+)', 'root[$1]'); - + cstr = regexprep(cstr,'var_p_([0-9]+)','p[$1]'); cstr = regexprep(cstr,'var_k_([0-9]+)','k[$1]'); cstr = regexprep(cstr,'h_([0-9]+)','h[$1]'); @@ -114,7 +114,7 @@ cstr = regexprep(cstr,'var_dwdp_([0-9]+)','dwdp[$1]'); cstr = regexprep(cstr,'tmp_J_([0-9]+)','J->data[$1]'); cstr = regexprep(cstr,'tmp_dxdotdp_([0-9]+)','dxdotdp[$1]'); - + cstr = regexprep(cstr,'var_y_([0-9]+)','y[$1]'); cstr = regexprep(cstr,'my_([0-9]+)','my[$1]'); cstr = regexprep(cstr,'var_z_([0-9]+)','z[$1]'); @@ -123,7 +123,7 @@ cstr = regexprep(cstr,'var_srz_([0-9]+)','srz[$1]'); cstr = regexprep(cstr,'var_sy_([0-9]+)','sy[$1]'); cstr = regexprep(cstr,'var_sz_([0-9]+)','sz[$1]'); - + cstr = regexprep(cstr,'var_dydx[_\[]*([0-9\+\*]+)[\]]*','dydx[$1]'); % matches both _... and [...] cstr = regexprep(cstr,'var_dzdx[_\[]*([0-9\+\*]+)[\]]*','dzdx[$1]'); cstr = regexprep(cstr,'var_drzdx[_\[]*([0-9\+\*]+)[\]]*','drzdx[$1]'); @@ -139,7 +139,7 @@ cstr = regexprep(cstr,'var_sigma_z_([0-9]+)','sigmaz[$1]'); cstr = regexprep(cstr,'var_dsigma_zdp_([0-9]+)',['dsigmazdp[$1]']); cstr = regexprep(cstr,'var_dsigma_ydp_([0-9]+)',['dsigmaydp[$1]']); - + cstr = regexprep(cstr,'var_dsdydp_([0-9]+)',['dsigmaydp[ip*' num2str(model.ny) ' + $1]']); cstr = regexprep(cstr,'var_dsdzdp_([0-9]+)',['dsigmazdp[ip*' num2str(model.nz) ' + $1]']); cstr = regexprep(cstr,'var_Jy_([0-9]+)','nllh[$1]'); @@ -153,7 +153,7 @@ cstr = regexprep(cstr,'var_dJrzdsigma[_\[]*([0-9\+\*]+)[\]]*','dJrzdsigma[$1]'); cstr = regexprep(cstr,'var_JDiag[_\[]*([0-9\+\*]+)[\]]*','JDiag[$1]'); end - + %% % print to file fprintf(fid,[cstr '\n']); diff --git a/deps/AMICI/matlab/@amifun/getArgs.m b/deps/AMICI/matlab/@amifun/getArgs.m index 65499d43a..afb50802e 100644 --- a/deps/AMICI/matlab/@amifun/getArgs.m +++ b/deps/AMICI/matlab/@amifun/getArgs.m @@ -10,7 +10,7 @@ % Return values: % this: updated function definition object @type amifun % - + if(strcmp(model.wtype,'iw')) dx = ', const realtype *dx'; sdx = ', const realtype *sdx'; @@ -24,7 +24,7 @@ M = ''; cj = ''; end - + switch(this.funstr) case 'xdot' this.argstr = ['(realtype *xdot, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h' dx ', const realtype *w)']; @@ -42,7 +42,11 @@ case 'sx0' this.argstr = '(realtype *sx0, const realtype t,const realtype *x0, const realtype *p, const realtype *k, const int ip)'; case 'root' - this.argstr = ['(realtype *root, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h' dx ')']; + if(strcmp(model.wtype,'iw')) + this.argstr = ['(realtype *root, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h' dx ')']; + else + this.argstr = ['(realtype *root, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl' dx ')']; + end case 'y' this.argstr = '(double *y, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w)'; case 'z' @@ -72,19 +76,19 @@ case 'deltaqB' this.argstr = '(double *deltaqB, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const int ip, const int ie, const realtype *xdot, const realtype *xdot_old, const realtype *xB)'; case 'deltasx' - this.argstr = '(double *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)'; + this.argstr = '(double *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)'; case 'dxdotdp' this.argstr = ['(realtype *dxdotdp, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const int ip' dx ', const realtype *w, const realtype *dwdp)']; case 'sigma_y' - this.argstr = '(double *sigmay, const realtype t, const realtype *p, const realtype *k)'; + this.argstr = '(double *sigmay, const realtype t, const realtype *p, const realtype *k, const realtype *y)'; case 'dsigma_ydp' - this.argstr = '(double *dsigmaydp, const realtype t, const realtype *p, const realtype *k, const int ip)'; + this.argstr = '(double *dsigmaydp, const realtype t, const realtype *p, const realtype *k, const realtype *y, const int ip)'; case 'sigma_z' this.argstr = '(double *sigmaz, const realtype t, const realtype *p, const realtype *k)'; case 'dsigma_zdp' this.argstr = '(double *dsigmazdp, const realtype t, const realtype *p, const realtype *k, const int ip)'; case 'stau' - this.argstr = '(double *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)'; + this.argstr = '(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)'; case 'Jy' this.argstr = '(double *nllh, const int iy, const realtype *p, const realtype *k, const double *y, const double *sigmay, const double *my)'; case 'dJydy' @@ -104,15 +108,15 @@ 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)'; + 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)'; 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)'; + 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)'; 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)'; + 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)'; case 'M' this.argstr = '(realtype *M, const realtype t, const realtype *x, const realtype *p, const realtype *k)'; otherwise %nothing end - + end diff --git a/deps/AMICI/matlab/@amifun/getCVar.m b/deps/AMICI/matlab/@amifun/getCVar.m index 323303925..43d5ecc2e 100644 --- a/deps/AMICI/matlab/@amifun/getCVar.m +++ b/deps/AMICI/matlab/@amifun/getCVar.m @@ -5,7 +5,7 @@ % % Return values: % this: updated function definition object @type amifun - + switch(this.funstr) case 'JSparse' this.cvar = 'var_JSparse'; @@ -17,4 +17,3 @@ this.cvar = ['var_' this.funstr]; end end - diff --git a/deps/AMICI/matlab/@amifun/getDeps.m b/deps/AMICI/matlab/@amifun/getDeps.m index 800f4ccb9..5249fde31 100644 --- a/deps/AMICI/matlab/@amifun/getDeps.m +++ b/deps/AMICI/matlab/@amifun/getDeps.m @@ -6,7 +6,7 @@ % % Return values: % this: updated function definition object @type amifun - + switch(this.funstr) case 'xdot' if(strcmp(model.wtype,'iw')) @@ -14,205 +14,205 @@ else this.deps = {'p','x','k'}; end - + case 'dfdx' this.deps = {'xdot','x','dwdx'}; - + case 'J' if(strcmp(model.wtype,'iw')) this.deps = {'dfdx','M','x','xdot'}; else this.deps = {'xdot','x','dwdx'}; end - + case 'dxdotdp' this.deps = {'xdot','p','dwdp'}; - + case 'sx0' - this.deps = {'x0','p'}; - + this.deps = {'x0','p'}; + case 'sdx0' - this.deps = {'dx0','p'}; - + this.deps = {'dx0','p'}; + case 'sxdot' if(strcmp(model.wtype,'iw')) this.deps = {'dfdx','M','dxdotdp','sdx','sx'}; else this.deps = {'J','dxdotdp','sx'}; end - + case 'dydx' this.deps = {'y','x'}; - + case 'dydp' this.deps = {'y','p'}; - + case 'sy' this.deps = {'dydp','dydx','sx'}; - + case 'Jv' this.deps = {'J'}; - + case 'JvB' this.deps = {'J'}; - + case 'xBdot' if(strcmp(model.wtype,'iw')) this.deps = {'J','M','xB','dxB'}; else this.deps = {'J','xB'}; end - + case 'qBdot' this.deps = {'dxdotdp','xB'}; - + case 'dsigma_ydp' this.deps = {'sigma_y','p'}; - + case 'dsigma_zdp' this.deps = {'sigma_z','p'}; - + case 'root' this.deps = {'x','k','p'}; - + case 'drootdp' this.deps = {'root','p','drootdx','sx'}; - + case 'drzdp' this.deps = {'rz','p',}; - + case 'drootdx' this.deps = {'root','x'}; - + case 'drzdx' this.deps = {'rz','x',}; - + case 'drootdt' % w is necessary for xdot_noopt this.deps = {'root','drootdx','xdot','w'}; - + case 'deltax' this.deps = {'x','k','p'}; - + case 'ddeltaxdp' this.deps = {'deltax','p'}; - + case 'ddeltaxdx' this.deps = {'deltax','x'}; - + case 'ddeltaxdt' this.deps = {'deltax'}; - + case 'deltasx' this.deps = {'deltax','deltaxdot','ddeltaxdx','ddeltaxdp','ddeltaxdt','dtaudp','xdot','sx','stau'}; - + case 'deltaqB' this.deps = {'ddeltaxdp','xB'}; - + case 'deltaxB' this.deps = {'deltax','dtaudp','xdot','xB','ddeltaxdx'}; - + case 'z' this.deps = {'x','k','p'}; - + case 'rz' this.deps = {'z','root'}; - + case 'srz' this.deps = {'rz','root','drootdx','drootdp','sx'}; - + case 'dzdp' this.deps = {'z','p','dtaudp'}; - + case 'dzdx' this.deps = {'z','x','dtaudx'}; - + case 'dzdt' % w is necessary for xdot_noopt this.deps = {'z','x','xdot','w'}; - + case 'sz' this.deps = {'dzdp','dzdx','dzdt','sx','dtaudp','stau'}; - + case 'sz_tf' this.deps = {'dzdp','dzdx','sx'}; - + case 'dtaudp' this.deps = {'drootdp','drootdt'}; - + case 'dtaudx' this.deps = {'drootdx','drootdt'}; - + case 'stau' this.deps = {'sroot','drootdt'}; - + case 'sroot' this.deps = {'drootdp','drootdx','sx'}; - + case 'x0' this.deps = {'p','k','x'}; - + case 'JBand' this.deps = {'J'}; - + case 'JBandB' this.deps = {'JB'}; - + case 'JSparse' this.deps = {'J'}; - + case 'y' this.deps = {'x','p','k'}; - + case 'sigma_y' this.deps = {'p','k'}; - + case 'sigma_z' this.deps = {'p','k'}; - + case 'rhs' this.deps = {'xdot'}; - + case 'dx0' this.deps = {'x','p','k'}; - + case 'M' this.deps = {'x','p','k'}; - + case 'x' this.deps = {}; - + case 'dx' this.deps = {}; - + case 'xB' this.deps = {}; - + case 'dxB' this.deps = {}; - + case 'k' this.deps = {}; - + case 'p' this.deps = {}; - + case 'sx' this.deps = {}; - + case 'sdx' this.deps = {}; - + case 'deltaxdot' this.deps = {'xdot'}; - + case 'Jy' this.deps = {'y','sigma_y'}; case 'dJydy' this.deps = {'Jy','y'}; case 'dJydsigma' this.deps = {'Jy','sigma_y'}; - + case 'Jz' this.deps = {'z','sigma_z'}; case 'dJzdz' @@ -225,14 +225,14 @@ this.deps = {'Jrz','x'}; case 'dJrzdsigma' this.deps = {'Jrz','sigma_z'}; - + case 'w' this.deps = {'xdot'}; case 'dwdp' this.deps = {'w','p'}; case 'dwdx' this.deps = {'w','x'}; - + case 's2root' this.deps = {'sroot'}; @@ -240,4 +240,3 @@ error(['unknown function string: ' this.funstr ]) end end - diff --git a/deps/AMICI/matlab/@amifun/getNVecs.m b/deps/AMICI/matlab/@amifun/getNVecs.m index 4b75133a0..1eb6670c5 100644 --- a/deps/AMICI/matlab/@amifun/getNVecs.m +++ b/deps/AMICI/matlab/@amifun/getNVecs.m @@ -1,6 +1,6 @@ function this = getNVecs(this) % getfunargs populates the nvecs property with the names of the - % N_Vector elements which are required in the execution of the function + % N_Vector elements which are required in the execution of the function % (if applicable). the information is directly extracted from the % argument string % @@ -9,18 +9,18 @@ % Return values: % this: updated function definition object @type amifun % - + vecs = {'x,','dx,','sx,','*sx,','sdx,','xB,','dxB,',... '*sxdot,','sxdot,','xdot,','xBdot,','qBdot,',... 'x0,','dx0,','*sx0,','*sdx0,',... 'v,','vB,','JDiag,','Jv,','JvB,',... 'xdot_old,'}; - + this.nvecs = {}; for iv = 1:length(vecs) if strfind(this.argstr,['N_Vector ' vecs{iv}]) this.nvecs = [this.nvecs,vecs{iv}(1:(end-1))]; end end - -end \ No newline at end of file + +end diff --git a/deps/AMICI/matlab/@amifun/getSensiFlag.m b/deps/AMICI/matlab/@amifun/getSensiFlag.m index 9d646509c..195f11918 100644 --- a/deps/AMICI/matlab/@amifun/getSensiFlag.m +++ b/deps/AMICI/matlab/@amifun/getSensiFlag.m @@ -5,66 +5,66 @@ % % Return values: % this: updated function definition object @type amifun - + switch(this.funstr) case 'dxdotdp' this.sensiflag = true; - + case 'sx0' - this.sensiflag = true; - + this.sensiflag = true; + case 'sdx0' this.sensiflag = true; - - + + case 'dydp' this.sensiflag = true; - + case 'sy' this.sensiflag = true; - + case 'qBdot' this.sensiflag = true; - + case 'dsigma_ydp' this.sensiflag = true; - + case 'dsigma_zdp' this.sensiflag = true; - + case 'drzdp' this.sensiflag = true; - + case 'ddeltaxdp' this.sensiflag = true; - + case 'deltasx' this.sensiflag = true; - + case 'deltaqB' this.sensiflag = true; - + case 'dzdp' this.sensiflag = true; - + case 'sz' this.sensiflag = true; - + case 'dtaudp' this.sensiflag = true; - + case 'stau' this.sensiflag = true; - + case 'sroot' this.sensiflag = true; - + case 'srz' this.sensiflag = true; - + case 'sx' this.sensiflag = true; - + case 'sdx' this.sensiflag = true; @@ -72,4 +72,3 @@ this.sensiflag = false; end end - diff --git a/deps/AMICI/matlab/@amifun/getSyms.m b/deps/AMICI/matlab/@amifun/getSyms.m index ad741a818..77c23f75a 100644 --- a/deps/AMICI/matlab/@amifun/getSyms.m +++ b/deps/AMICI/matlab/@amifun/getSyms.m @@ -7,19 +7,19 @@ % Return values: % this: updated function definition object @type amifun % model: updated model definition object @type amimodel - + % store often used variables for ease of notation, dependencies should % ensure that these variables are defined - + persistent x p sx w ndw jacw - + nx = model.nx; nevent = model.nevent; np = model.np; nk = model.nk; nz = model.nz; ny = model.ny; - + fprintf([this.funstr ' | ']) switch(this.funstr) case 'x' @@ -32,7 +32,7 @@ % transform into symbolic expression this.sym = sym(xs); x = this.sym; - + case 'dx' % create cell array of same size dxs = cell(nx,1); @@ -42,7 +42,7 @@ end % transform into symbolic expression this.sym = sym(dxs); - + case 'p' % create cell array of same size ps = cell(np,1); @@ -53,7 +53,7 @@ % transform into symbolic expression this.sym = sym(ps); p = this.sym; - + case 'k' % create cell array of same size ks = cell(nk,1); @@ -63,7 +63,7 @@ end % transform into symbolic expression this.sym = sym(ks); - + case 'sx' % create cell array of same size sxs = cell(nx,1); @@ -74,7 +74,7 @@ % transform into symbolic expression this.sym = repmat(sym(sxs),[1,np]); sx = this.sym; - + case 'sdx' % create cell array of same size sdx = cell(nx,np); @@ -86,7 +86,7 @@ end % transform into symbolic expression this.sym = sym(sdx); - + case 'xB' % create cell array of same size xBs = cell(nx,1); @@ -96,7 +96,7 @@ end % transform into symbolic expression this.sym = sym(xBs); - + case 'dxB' % create cell array of same size dxBs = cell(nx,1); @@ -106,14 +106,14 @@ end % transform into symbolic expression this.sym = sym(dxBs); - + case 'y' this.sym = model.sym.y; % replace unify symbolic expression this = unifySyms(this,model); this = makeStrSymsFull(this); - - + + % activate splines for iy = 1:ny if(not(all([model.splineflag,model.minflag,model.maxflag]))) @@ -128,41 +128,41 @@ model.minflag = true; end end - end - + end + case 'x0' this.sym = model.sym.x0; % replace unify symbolic expression this = unifySyms(this,model); - + case 'dx0' this.sym = model.sym.dx0; % replace unify symbolic expression this = unifySyms(this,model); - + case 'sigma_y' this.sym = model.sym.sigma_y; this = makeStrSymsFull(this); % replace unify symbolic expression this = unifySyms(this,model); - + case 'sigma_z' this.sym = model.sym.sigma_z; this = makeStrSymsFull(this); % replace unify symbolic expression this = unifySyms(this,model); - + case 'M' this.sym = sym(model.sym.M); % replace unify symbolic expression this = unifySyms(this,model); this = makeStrSyms(this); - + case 'xdot' this.sym = model.sym.xdot; % replace unify symbolic expression this = unifySyms(this,model); - + if(strcmp(model.wtype,'iw')) if(size(this.sym,2)>size(this.sym,1)) this.sym = -transpose(model.fun.M.sym*model.fun.dx.sym)+this.sym; @@ -170,7 +170,7 @@ this.sym = -model.fun.M.sym*model.fun.dx.sym+this.sym; end end - + % create cell array of same size xdots = cell(nx,1); xdot_olds = cell(nx,1); @@ -181,7 +181,7 @@ end this.strsym = sym(xdots); this.strsym_old = sym(xdot_olds); - + % activate splines for ix = 1:nx if(not(all([model.splineflag,model.minflag,model.maxflag]))) @@ -197,7 +197,7 @@ end end end - + case 'w' optimize = getoptimized(optsym(model.fun.xdot.sym)); tmpxdot = sym(char(optimize(end))); @@ -215,10 +215,10 @@ end % model.nw = 0; % nw = 0; -% this.sym = sym(zeros(0,1)); - +% this.sym = sym(zeros(0,1)); + + - ws = cell(nw,1); ts = cell(nw,1); % fill cell array @@ -249,7 +249,7 @@ jacx = jacobian(model.fun.w.sym,x); this.sym = jacx; for idw = 1:ndw - this.sym = this.sym + (jacw^idw)*jacx; % this part is only to get the right nonzero entries + this.sym = this.sym + (jacw^idw)*jacx; % this part is only to get the right nonzero entries end % fill cell array idx_w = find(this.sym); @@ -270,13 +270,13 @@ this.sym = sym(zeros(0,nx)); this.strsym = sym(zeros(0,nx)); end - + case 'dwdp' if(length(model.fun.w.sym)>0) jacp = jacobian(model.fun.w.sym,p); this.sym = jacp; for idw = 1:ndw - this.sym = this.sym + (jacw^idw)*jacp; % this part is only to get the right nonzero entries + this.sym = this.sym + (jacw^idw)*jacp; % this part is only to get the right nonzero entries end % fill cell array idx_w = find(this.sym); @@ -295,11 +295,11 @@ this.sym = sym(zeros(0,nx)); this.strsym = sym(zeros(0,nx)); end - + case 'dfdx' this.sym=jacobian(model.fun.xdot.sym,x) + jacobian(model.fun.xdot.sym,w)*model.fun.dwdx.strsym; this = makeStrSyms(this); - + case 'J' if(strcmp(model.wtype,'iw')) syms cj @@ -313,15 +313,15 @@ this.sym_noopt = this.sym; end end - + this = makeStrSymsSparse(this); - - - + + + case 'JDiag' this.sym = diag(model.fun.J.sym); this = makeStrSyms(this); - + case 'dxdotdp' if(~isempty(w)) this.sym=jacobian(model.fun.xdot.sym,p) + jacobian(model.fun.xdot.sym,w)*model.fun.dwdp.strsym; @@ -330,7 +330,7 @@ this.sym=jacobian(model.fun.xdot.sym,p); this.sym_noopt = this.sym; end - + %% % build short strings for reuse of dxdotdp % create cell array of same size @@ -341,13 +341,13 @@ end % create full symbolic array this.strsym = sym(dxdotdps); - + case 'sx0' this.sym=jacobian(model.fun.x0.sym,p); - + case 'sdx0' this.sym=jacobian(model.fun.dx0.sym,p); - + case 'sxdot' if(np>0) if(strcmp(model.wtype,'iw')) @@ -358,19 +358,19 @@ else this.sym = sym(zeros(size(sx,1),0)); end - + case 'dydx' this.sym=jacobian(model.fun.y.sym,x); % create cell array of same sizex this.strsym = sym(zeros(ny,nx)); % fill cell array this = makeStrSyms(this); - + case 'dydp' this.sym=jacobian(model.fun.y.sym,p); % create cell array of same size this = makeStrSyms(this); - + case 'xBdot' if(strcmp(model.wtype,'iw')) syms t @@ -378,7 +378,7 @@ else this.sym = model.fun.JB.sym * model.fun.xB.sym; end - + case 'qBdot' % If we do second order adjoints, we have to augment if (model.nxtrue < nx) @@ -388,7 +388,7 @@ this.sym(ig,:) = ... -transpose(model.fun.xB.sym(1:model.nxtrue)) * model.fun.dxdotdp.sym((ig-1)*model.nxtrue+1 : ig*model.nxtrue, :) ... -transpose(model.fun.xB.sym((ig-1)*model.nxtrue+1 : ig*model.nxtrue)) * model.fun.dxdotdp.sym(1:model.nxtrue, :); - end + end else this.sym = -transpose(model.fun.xB.sym)*model.fun.dxdotdp.sym; end @@ -396,7 +396,7 @@ case 'dsigma_ydp' this.sym = jacobian(model.fun.sigma_y.sym,p); this = makeStrSyms(this); - + case 'dsigma_zdp' if(nz>0) this.sym = jacobian(model.fun.sigma_z.sym,p); @@ -404,7 +404,7 @@ this.sym = sym(zeros(model.nz,np)); end this = makeStrSyms(this); - + case 'root' if(nevent>0) this.sym = transpose([model.event.trigger]); @@ -426,20 +426,20 @@ end end end - + case 'drootdp' this.sym = jacobian(model.fun.root.sym,p); - + case 'drootdx' this.sym = jacobian(model.fun.root.sym,x); - + case 'drootdt' % noopt is important here to get derivatives right this.sym = diff(model.fun.root.sym,'t') + model.fun.drootdx.sym*model.fun.xdot.sym_noopt; - + case 'sroot' this.sym = model.fun.drootdp.sym + model.fun.drootdx.sym*sx; - + case 'srz' if(isfield(model.sym,'rz')) % user defined input or from augmentation this.sym = jacobian(model.fun.rz.sym,p) + jacobian(model.fun.rz.sym,x)*sx; @@ -448,7 +448,7 @@ this.sym(iz,:) = model.fun.sroot.sym(model.z2event(iz),:); end end - + case 's2root' switch(model.o2flag) case 1 @@ -459,25 +459,25 @@ vec = model.sym.k((end-np+1):end); end for ievent = 1:nevent - + this.sym(ievent,:,:) = (jacobian(model.fun.sroot.sym(ievent,:),p) + jacobian(model.fun.sroot.sym(ievent,:),x(1:model.nxtrue))*sx(1:model.nxtrue,:) + jacobian(model.fun.sroot.sym(ievent,:),x(1:model.nxtrue))*sx(1:model.nxtrue,:))*vec; for ix = 1:model.nxtrue this.sym(ievent,:,:) = this.sym(ievent,:,:) + model.fun.drootdx.sym(ievent,ix)*s2x(ix,:,:); end end - + case 'dtaudp' this.sym = sym(zeros(nevent,np)); for ievent = 1:nevent this.sym(ievent,:) = - model.fun.drootdp.sym(ievent,:)/model.fun.drootdt.sym(ievent); end - + case 'dtaudx' this.sym = sym(zeros(nevent,nx)); for ievent = 1:nevent this.sym(ievent,:) = - model.fun.drootdx.sym(ievent,:)/model.fun.drootdt.sym(ievent); end - + case 'stau' this.sym = sym(zeros(nevent,np)); for ievent = 1:nevent @@ -493,7 +493,7 @@ staus = sym(staus); % multiply this.strsym = staus; - + case 'deltax' if(nevent>0) this.sym = [model.event.bolus]; @@ -501,16 +501,16 @@ else this.sym = sym(zeros(0,1)); end - + case 'deltaxdot' this.sym = model.fun.xdot.strsym-model.fun.xdot.strsym_old; - + case 'ddeltaxdp' this.sym = sym(zeros(nx,nevent,np)); for ievent = 1:nevent this.sym(:,ievent,:) = jacobian(model.fun.deltax.sym(:,ievent),p); end - + case 'ddeltaxdx' this.sym = sym(zeros(nx,nevent,nx)); if(nx>0) @@ -518,27 +518,27 @@ this.sym(:,ievent,:) = jacobian(model.fun.deltax.sym(:,ievent),x); end end - + case 'ddeltaxdt' this.sym = diff(model.fun.deltax.sym,'t'); - + case 'deltasx' - + if(nevent>0) for ievent = 1:nevent - + % dtdp = (1/drdt)*drdp dtdp = model.fun.stau.strsym; % this 1 here is correct, we explicitely do not want ievent here as the actual stau_tmp will only have dimension np - + % if we are just non-differentiable and but not - % discontinuous we can ignore some of the terms! + % discontinuous we can ignore some of the terms! if(any(logical(model.fun.deltax.sym(:,ievent)~=0))) % dxdp = dx/dt*dt/dp + dx/dp dxdp = sym(zeros(nx,np)); for ix = 1:nx dxdp(ix,:) = model.fun.xdot.sym(ix)*dtdp + sx(ix,:); end - + this.sym(:,:,ievent) = ... + permute(model.fun.ddeltaxdx.sym(:,ievent,:),[1 3 2])*dxdp ... + model.fun.ddeltaxdt.sym(:,ievent)*dtdp ... @@ -553,7 +553,7 @@ end end end - + case 'deltaqB' if (model.nxtrue < nx) ng_tmp = round(nx / model.nxtrue); @@ -561,21 +561,21 @@ else this.sym = sym(zeros(np,nevent)); end - + for ievent = 1:nevent this.sym(1:np,ievent) = -transpose(model.fun.xB.sym)*squeeze(model.fun.ddeltaxdp.sym(:,ievent,:)); % This is just a very quick fix. Events in adjoint systems % have to be implemented in a way more rigorous way later % on... Some day... end - + case 'deltaxB' this.sym = sym(zeros(nx,nevent)); for ievent = 1:nevent this.sym(:,ievent) = -transpose(squeeze(model.fun.ddeltaxdx.sym(:,ievent,:)))*model.fun.xB.sym; end - - + + case 'z' if(nevent>0) this.sym = transpose([model.event.z]); @@ -594,7 +594,7 @@ end end this = makeStrSymsFull(this); - + case 'rz' this.sym = sym(zeros(size(model.fun.z.sym))); if(isfield(model.sym,'rz')) @@ -606,38 +606,38 @@ end this = unifySyms(this,model); this = makeStrSymsFull(this); - + case 'dzdp' this.sym = jacobian(model.fun.z.sym,p); - + for iz = 1:nz this.sym(iz,:) = this.sym(iz,:) + diff(model.fun.z.sym(iz),sym('t'))*model.fun.dtaudp.sym(model.z2event(iz),:); end % create cell array of same size this = makeStrSyms(this); - + case 'drzdp' this.sym = jacobian(model.fun.rz.sym,p); this = makeStrSyms(this); - + case 'dzdx' this.sym = jacobian(model.fun.z.sym,x); for iz = 1:nz this.sym(iz,:) = this.sym(iz,:)+ diff(model.fun.z.sym(iz),sym('t'))*model.fun.dtaudx.sym(model.z2event(iz),:); end this = makeStrSyms(this); - + case 'drzdx' this.sym = jacobian(model.fun.rz.sym,x); this = makeStrSyms(this); - + case 'dzdt' if(nz>0) this.sym = diff(model.fun.z.sym,'t')+jacobian(model.fun.z.sym,x(1:model.nxtrue))*model.fun.xdot.sym_noopt(1:model.nxtrue); else this.sym = sym.empty(); end - + case 'sz' this.sym = sym(zeros(nz,np)); tmpsym = sym(zeros(nz-model.nztrue,np)); @@ -659,15 +659,15 @@ % symmetrise it and add it later on. % also the dzdp part contains second order sensitivities and the drootdt part does not (this leads % to 1:model.nxtrue indexing) - - + + if(model.o2flag==1) tmpsym(iz-model.nztrue,:) = jacobian(1/model.fun.drootdt.sym(model.z2event(iz)),p)*model.fun.z.sym(iz)*model.fun.drootdt.sym(model.z2event(iz)) ... + jacobian(1/model.fun.drootdt.sym(model.z2event(iz)),x(1:model.nxtrue))*sx(1:model.nxtrue,:)*model.fun.z.sym(iz)*model.fun.drootdt.sym(model.z2event(iz)); else - error('sz for directional second order sensis was never implemented and I do not know how to, you are on your own here.'); + error('sz for directional second order sensis was never implemented and I do not know how to, you are on your own here.'); end - + this.sym(iz,:) = ... + jacobian(model.fun.z.sym(iz)*model.fun.drootdt.sym(model.z2event(iz)),p)/model.fun.drootdt.sym(model.z2event(iz)) ... + jacobian(model.fun.z.sym(iz)*model.fun.drootdt.sym(model.z2event(iz)),x)*sx/model.fun.drootdt.sym(model.z2event(iz)) ... @@ -687,7 +687,7 @@ % you might not believe this, but this matrix should (and hopefully will) actually be symmetric ;) end end - + % create cell array of same size szs = cell(nz,np); % fill cell array @@ -698,14 +698,14 @@ end % transform into symbolic expression this.strsym = sym(szs); - + case 'JBand' %do nothing case 'JBandB' %do nothing case 'JSparse' %do nothing - + case 'Jy' this.sym = model.sym.Jy; % replace unify symbolic expression @@ -760,7 +760,7 @@ for iz = 1 : model.nztrue this.sym(iz,:,:) = jacobian(model.fun.Jrz.sym(iz,:),model.fun.sigma_z.strsym); end - + otherwise error('unknown function name') end @@ -821,4 +821,4 @@ else out = in; end -end \ No newline at end of file +end diff --git a/deps/AMICI/matlab/@amifun/writeCcode.m b/deps/AMICI/matlab/@amifun/writeCcode.m index d74709e95..b03232cd9 100644 --- a/deps/AMICI/matlab/@amifun/writeCcode.m +++ b/deps/AMICI/matlab/@amifun/writeCcode.m @@ -94,4 +94,4 @@ function writeCcode(this,model,fid) end -end \ No newline at end of file +end diff --git a/deps/AMICI/matlab/@amifun/writeCcode_sensi.m b/deps/AMICI/matlab/@amifun/writeCcode_sensi.m index b9238d3fc..c660a5e43 100644 --- a/deps/AMICI/matlab/@amifun/writeCcode_sensi.m +++ b/deps/AMICI/matlab/@amifun/writeCcode_sensi.m @@ -5,10 +5,10 @@ function writeCcode_sensi(this,model,fid) % Parameters: % model: model defintion object @type amimodel % fid: file id in which the final expression is written @type fileid -% +% % Return values: % void - + np = model.np; ng = model.ng; @@ -69,7 +69,7 @@ function writeCcode_sensi(this,model,fid) end end end -else +else nonzero = this.sym ~=0; if(any(any(nonzero))) tmpfun = this; @@ -88,4 +88,4 @@ function writeCcode_sensi(this,model,fid) end end end -end \ No newline at end of file +end diff --git a/deps/AMICI/matlab/@amifun/writeMcode.m b/deps/AMICI/matlab/@amifun/writeMcode.m index d78af5e62..4b5fb126c 100644 --- a/deps/AMICI/matlab/@amifun/writeMcode.m +++ b/deps/AMICI/matlab/@amifun/writeMcode.m @@ -20,8 +20,8 @@ function writeMcode(this,model) end this.sym_noopt = subs(this.sym_noopt,h_vars,h_rep); end - + ami_mfun(this.sym_noopt, 'file', fullfile(model.wrap_path,'models',... model.modelname,[ this.funstr '_',model.modelname,'.m']), ... 'vars', {'t',model.fun.x.sym,model.fun.p.sym,model.fun.k.sym},'varnames',{'t','x','p','k'}); -end \ No newline at end of file +end diff --git a/deps/AMICI/matlab/@amimodel/amimodel.m b/deps/AMICI/matlab/@amimodel/amimodel.m index cef415bdf..1aed26355 100644 --- a/deps/AMICI/matlab/@amimodel/amimodel.m +++ b/deps/AMICI/matlab/@amimodel/amimodel.m @@ -4,7 +4,7 @@ % classdef amimodel < handle % AMIMODEL carries all model definitions including functions and events - + properties ( GetAccess = 'public', SetAccess = 'private' ) % symbolic definition struct @type struct sym = struct.empty(); @@ -82,7 +82,7 @@ % storage for flags determining recompilation of individual % functions cfun = struct.empty(); - % flag which identifies augmented models + % flag which identifies augmented models % 0 indicates no augmentation % 1 indicates augmentation by first order sensitivities (yields % second order sensitivities) @@ -90,7 +90,7 @@ % order sensitivities (yields hessian-vector product) o2flag = 0; end - + properties ( GetAccess = 'public', SetAccess = 'public' ) % vector that maps outputs to events z2event = double.empty(); @@ -108,7 +108,7 @@ % number of derivatives of derived variables w, dwdp @type int ndwdp = 0; end - + methods function AM = amimodel(symfun,modelname) % amimodel initializes the model object based on the provided @@ -138,17 +138,17 @@ else error('invalid input symfun') end - + if(isfield(model,'sym')) AM.sym = model.sym; else error('symbolic definitions missing in struct returned by symfun') end - - - + + + props = fields(model); - + for j = 1:length(props) if(~strcmp(props{j},'sym')) % we already checked for the sym field if(isfield(model,props{j})) @@ -174,7 +174,7 @@ end end end - + AM.modelname = modelname; % set path and create folder AM.wrap_path=fileparts(fileparts(fileparts(mfilename('fullpath')))); @@ -192,7 +192,7 @@ AM.nztrue = AM.nz; end AM.nevent = length(AM.event); - + % check whether we have a DAE or ODE if(isfield(AM.sym,'M')) AM.wtype = 'iw'; % DAE @@ -201,7 +201,7 @@ end end end - + function updateRHS(this,xdot) % updateRHS updates the private fun property .fun.xdot.sym % (right hand side of the differential equation) @@ -214,7 +214,7 @@ function updateRHS(this,xdot) this.fun.xdot.sym_noopt = this.fun.xdot.sym; this.fun.xdot.sym = xdot; end - + function updateModelName(this,modelname) % updateModelName updates the modelname % @@ -225,7 +225,7 @@ function updateModelName(this,modelname) % void this.modelname = modelname; end - + function updateWrapPath(this,wrap_path) % updateModelName updates the modelname % @@ -236,38 +236,37 @@ function updateWrapPath(this,wrap_path) % void this.wrap_path = wrap_path; end - + parseModel(this) - + generateC(this) - + generateRebuildM(this) compileC(this) - + generateM(this,amimodelo2) - + getFun(this,HTable,funstr) - + makeEvents(this) - + makeSyms(this) - + cflag = checkDeps(this,HTable,deps) - + HTable = loadOldHashes(this) - + modelo2 = augmento2(this) - + modelo2vec = augmento2vec(this) - + end - + methods(Static) compileAndLinkModel(modelname, modelSourceFolder, coptim, debug, funs, cfun) - + generateMatlabWrapper(nx, ny, np, nk, nz, o2flag, amimodelo2, wrapperFilename, modelname, pscale, forward, adjoint) end end - diff --git a/deps/AMICI/matlab/@amimodel/augmento2.m b/deps/AMICI/matlab/@amimodel/augmento2.m index baf51b70f..e2a0852eb 100644 --- a/deps/AMICI/matlab/@amimodel/augmento2.m +++ b/deps/AMICI/matlab/@amimodel/augmento2.m @@ -8,9 +8,9 @@ % Return values: % this: augmented system which contains symbolic definition of the % original system and its sensitivities @type amimodel - + syms Sx Sdot Sy S0 - + augmodel.nxtrue = length(this.sym.x); % number of states augmodel.nytrue = length(this.sym.y); % number of observables if(this.nevent>0) @@ -19,7 +19,7 @@ augmodel.nztrue = 0; end np = this.np; - + % augment states Sx = sym(zeros(length(this.sym.x),np)); for j = 1:length(this.sym.x) @@ -29,10 +29,10 @@ end end Sdot = jacobian(this.sym.xdot,this.sym.x)*Sx+jacobian(this.sym.xdot,this.sym.p); - + % augment output Sy = jacobian(this.sym.y,this.sym.x)*Sx+jacobian(this.sym.y,this.sym.p); - + % generate deltasx this.getFun([],'deltasx'); this.getFun([],'sz'); @@ -64,7 +64,7 @@ end augmodel.event(ievent) = amievent(this.event(ievent).trigger,bolusnew,znew); end - + % augment likelihood this.getFun([],'dsigma_ydp'); this.getFun([],'y'); @@ -79,7 +79,7 @@ SJy = jacobian(this.fun.Jy.sym,this.sym.p) ... + jacobian(this.fun.Jy.sym,this.fun.sigma_y.strsym)*this.fun.dsigma_ydp.sym ... + jacobian(this.fun.Jy.sym,this.fun.y.strsym)*aug_y_strsym; - + this.getFun([],'dsigma_zdp'); this.getFun([],'rz'); this.getFun([],'Jz'); @@ -92,7 +92,7 @@ SJz = jacobian(this.fun.Jz.sym,this.sym.p); if(~isempty(this.fun.sigma_z.strsym)) SJz = SJz + jacobian(this.fun.Jz.sym,this.fun.sigma_z.strsym)*this.fun.dsigma_zdp.sym ... - + jacobian(this.fun.Jz.sym,this.fun.z.strsym)*aug_z_strsym; + + jacobian(this.fun.Jz.sym,this.fun.z.strsym)*aug_z_strsym; end this.getFun([],'Jrz'); tmp = arrayfun(@(x) sym(['var_rz_' num2str(x)]),0:(augmodel.nztrue*(1+np)-1),'UniformOutput',false); @@ -104,14 +104,14 @@ SJrz = jacobian(this.fun.Jrz.sym,this.sym.p); if(~isempty(this.fun.sigma_z.strsym)) SJrz = SJrz + jacobian(this.fun.Jrz.sym,this.fun.sigma_z.strsym)*this.fun.dsigma_zdp.sym ... - + jacobian(this.fun.Jrz.sym,this.fun.rz.strsym)*aug_rz_strsym; + + jacobian(this.fun.Jrz.sym,this.fun.rz.strsym)*aug_rz_strsym; end - + % augment sigmas this.getFun([],'sigma_y'); this.getFun([],'sigma_z'); S0 = jacobian(this.sym.x0,this.sym.p); - + augmodel.sym.x = [this.sym.x;Sx(:)]; augmodel.sym.xdot = [this.sym.xdot;Sdot(:)]; augmodel.sym.f = augmodel.sym.xdot; @@ -127,7 +127,7 @@ augmodel.sym.k = this.sym.k; augmodel.sym.sigma_y = [transpose(this.sym.sigma_y(:)), reshape(transpose(this.fun.dsigma_ydp.sym), [1,numel(this.fun.dsigma_ydp.sym)])]; augmodel.sym.sigma_z = [transpose(this.sym.sigma_z(:)), reshape(transpose(this.fun.dsigma_zdp.sym), [1,numel(this.fun.dsigma_zdp.sym)])]; - + modelo2 = amimodel(augmodel,[this.modelname '_o2']); modelo2.o2flag = 1; modelo2.debug = this.debug; diff --git a/deps/AMICI/matlab/@amimodel/augmento2vec.m b/deps/AMICI/matlab/@amimodel/augmento2vec.m index 48df87e9c..09ba3000a 100644 --- a/deps/AMICI/matlab/@amimodel/augmento2vec.m +++ b/deps/AMICI/matlab/@amimodel/augmento2vec.m @@ -8,38 +8,38 @@ % Return values: % modelo2vec: augmented system which contains symbolic definition of the % original system and its sensitivities @type amimodel - + syms Sx Sdot Sy S0 - - + + augmodel.nxtrue = length(this.sym.x); % number of states augmodel.nytrue = length(this.sym.y); % number of observables augmodel.nztrue = this.nz; augmodel.coptim = this.coptim; augmodel.debug = this.debug; - + % multiplication vector (extension of kappa vecs = cell([length(this.sym.p),1]); for ivec = 1:length(this.sym.p) vecs{ivec} = sprintf('k_%i', length(this.sym.k) + ivec-1); end vec = sym(vecs); - + if(this.nevent>0) augmodel.nztrue = length([this.event.z]); % number of observables else augmodel.nztrue = 0; end np = this.np; - + % augment states sv = sym('sv',[length(this.sym.x),1]); Sdot = jacobian(this.sym.xdot,this.sym.x)*sv+jacobian(this.sym.xdot,this.sym.p)*vec; - + % augment output Sy = jacobian(this.sym.y,this.sym.x)*sv+jacobian(this.sym.y,this.sym.p)*vec; - + % generate deltasx this.getFun([],'deltasx'); for ievent = 1:this.nevent; @@ -60,7 +60,7 @@ augmodel.event(ievent) = amievent(this.event(ievent).trigger,bolusnew,znew); augmodel.event(ievent) = augmodel.event(ievent).setHflag([hflagold;zeros([numel(sv),1])]); end - + % augment likelihood this.getFun([],'dsigma_ydp'); this.getFun([],'y'); @@ -75,7 +75,7 @@ + jacobian(this.fun.Jy.sym,this.fun.sigma_y.strsym)*this.fun.dsigma_ydp.sym) ... * vec + jacobian(this.fun.Jy.sym,this.fun.y.strsym)*aug_y_strsym; this.getFun([],'dsigma_zdp'); - + this.getFun([],'dzdp'); this.getFun([],'Jz'); SJz = jacobian(this.fun.Jz.sym,this.sym.p); @@ -87,9 +87,9 @@ % augment sigmas this.getFun([],'sigma_y'); this.getFun([],'sigma_z'); - + S0 = jacobian(this.sym.x0,this.sym.p)*vec; - + augmodel.sym.x = [this.sym.x;sv]; augmodel.sym.xdot = [this.sym.xdot;Sdot]; augmodel.sym.f = augmodel.sym.xdot; @@ -101,7 +101,7 @@ augmodel.sym.p = this.sym.p; augmodel.sym.sigma_y = [this.sym.sigma_y, transpose(this.fun.dsigma_ydp.sym * vec)]; augmodel.sym.sigma_z = [this.sym.sigma_z, transpose(this.fun.dsigma_zdp.sym * vec)]; - + modelo2vec = amimodel(augmodel,[this.modelname '_o2vec']); modelo2vec.o2flag = 2; modelo2vec.debug = this.debug; diff --git a/deps/AMICI/matlab/@amimodel/checkDeps.m b/deps/AMICI/matlab/@amimodel/checkDeps.m index caebb514b..77c40b7fb 100644 --- a/deps/AMICI/matlab/@amimodel/checkDeps.m +++ b/deps/AMICI/matlab/@amimodel/checkDeps.m @@ -8,10 +8,10 @@ % deps: cell array with containing a list of dependencies @type cell % % Return values: - % cflag: boolean indicating whether any of the dependencies have + % cflag: boolean indicating whether any of the dependencies have % changed with respect to the hashes stored in HTable @type % bool - + if(~isempty(HTable)) cflags = zeros(length(deps),1); for id = 1:length(deps) @@ -47,4 +47,4 @@ end end end -end \ No newline at end of file +end diff --git a/deps/AMICI/matlab/@amimodel/compileAndLinkModel.m b/deps/AMICI/matlab/@amimodel/compileAndLinkModel.m index 8cfd8e743..c75f57515 100644 --- a/deps/AMICI/matlab/@amimodel/compileAndLinkModel.m +++ b/deps/AMICI/matlab/@amimodel/compileAndLinkModel.m @@ -20,10 +20,12 @@ function compileAndLinkModel(modelname, modelSourceFolder, coptim, debug, funs, % if no list provided, try to determine relevant files from model % folder if(isempty(funs)) - ls = dir(fullfile(modelSourceFolder, [modelname '_*.cpp'])); + ls = dir(fullfile(modelSourceFolder, '*.cpp')); ls = {ls.name}; + % wrapfunctions is handled separately + ls = ls(cellfun(@(x) ~strcmp(x, "wrapfunctions.cpp"), ls)); % extract funs from filename (strip of modelname_ and .cpp - funs = cellfun(@(x) x((length(modelname)+2):(length(x)-4)), ls, 'UniformOutput', false); + funs = cellfun(@(x) x(1:(length(x)-4)), ls, 'UniformOutput', false); end objectFileSuffix = '.o'; @@ -32,9 +34,9 @@ function compileAndLinkModel(modelname, modelSourceFolder, coptim, debug, funs, end % compile flags - COPT = ['COPTIMFLAGS=''' coptim ' -DNDEBUG'' CXXFLAGS=''$CXXFLAGS -std=c++14''']; + COPT = ['COPTIMFLAGS=''' coptim ' -DNDEBUG'' CXXFLAGS=''$CXXFLAGS -std=c++17''']; if(debug) - DEBUG = ' -g CXXFLAGS=''$CXXFLAGS -Wall -std=c++14 -Wno-unused-function -Wno-unused-variable'' '; + DEBUG = ' -g CXXFLAGS=''$CXXFLAGS -Wall -std=c++17 -Wno-unused-function -Wno-unused-variable'' '; COPT = ''; % no optimization with debug flags! else DEBUG = ''; @@ -74,7 +76,7 @@ function compileAndLinkModel(modelname, modelSourceFolder, coptim, debug, funs, %% Model-specific files for j=1:length(funs) - baseFileName = [modelname '_' strrep(funs{j}, 'sigma_', 'sigma')]; + baseFileName = strrep(funs{j}, 'sigma_', 'sigma'); cfun(1).(funs{j}) = sourceNeedsRecompilation(modelSourceFolder, modelObjectFolder, baseFileName, objectFileSuffix); end @@ -124,22 +126,26 @@ function compileAndLinkModel(modelname, modelSourceFolder, coptim, debug, funs, if(numel(funsForRecompile)) fprintf('ffuns | '); - sources = cellfun(@(x) ['"' fullfile(modelSourceFolder,[modelname '_' x '.cpp']) '"'],funsForRecompile,'UniformOutput',false); + sources = cellfun(@(x) ['"' fullfile(modelSourceFolder,[x '.cpp']) '"'],funsForRecompile,'UniformOutput',false); sources = strjoin(sources,' '); - eval(['mex ' DEBUG COPT ... + cmd = ['mex ' DEBUG COPT ... ' -c -outdir "' modelObjectFolder '" ' ... - sources ' ' ... - includesstr ]); - cellfun(@(x) updateFileHashSource(modelSourceFolder, modelObjectFolder, [modelname '_' x]),funsForRecompile,'UniformOutput',false); + sources ' ' includesstr]; + try + eval(cmd); + catch ME + disp(cmd); + rethrow(ME); + end + cellfun(@(x) updateFileHashSource(modelSourceFolder, modelObjectFolder, x),funsForRecompile,'UniformOutput',false); end % append model object files for j=1:length(funs) - filename = fullfile(modelObjectFolder, [modelname '_' strrep(funs{j}, 'sigma_', 'sigma') objectFileSuffix]); + filename = fullfile(modelObjectFolder, [strrep(funs{j}, 'sigma_', 'sigma') objectFileSuffix]); if(exist(filename,'file')) - objectsstr = strcat(objectsstr,... - ' "',filename,'"'); + objectsstr = strcat(objectsstr, ' "',filename,'"'); end end @@ -156,10 +162,17 @@ function compileAndLinkModel(modelname, modelSourceFolder, coptim, debug, funs, % compile the wrapfunctions object fprintf('wrapfunctions | '); - eval(['mex ' DEBUG COPT ... + cmd = ['mex ' DEBUG COPT ... ' -c -outdir "' modelObjectFolder '" "' ... fullfile(modelSourceFolder,'wrapfunctions.cpp') '" ' model_cpp ... - includesstr]); + includesstr]; + try + eval(cmd); + catch ME + disp(cmd); + rethrow(ME); + end + objectsstr = [objectsstr, ' "' fullfile(modelObjectFolder,['wrapfunctions' objectFileSuffix]) '" ' model_cpp_obj]; % now we have compiled everything model-specific, so we can replace hashes.mat to prevent recompilation @@ -186,21 +199,28 @@ function compileAndLinkModel(modelname, modelSourceFolder, coptim, debug, funs, end mexFilename = fullfile(modelSourceFolder,['ami_' modelname]); - eval(['mex ' DEBUG ' ' COPT ' ' CLIBS ... - ' -output "' mexFilename '" ' objectsstr]) + cmd = ['mex ' DEBUG ' ' COPT ' ' CLIBS ... + ' -output "' mexFilename '" ' objectsstr]; + try + eval(cmd); + catch ME + disp(cmd); + rethrow(ME); + end + end function [objectStrAmici] = compileAmiciBase(amiciRootPath, objectFolder, objectFileSuffix, includesstr, DEBUG, COPT) % generate hash for file and append debug string if we have an md5 % file, check this hash against the contained hash cppsrc = {'amici', 'symbolic_functions','spline', ... - 'edata','rdata', 'exception', ... + 'edata','rdata', 'exception', 'logging', ... 'interface_matlab', 'misc', 'simulation_parameters', ... 'solver', 'solver_cvodes', 'solver_idas', 'model_state', ... 'model', 'model_ode', 'model_dae', 'returndata_matlab', ... 'forwardproblem', 'steadystateproblem', 'backwardproblem', 'newton_solver', ... 'abstract_model', 'sundials_matrix_wrapper', 'sundials_linsol_wrapper', ... - 'vector' + 'vector', 'splinefunctions' }; % to be safe, recompile everything if headers have changed. otherwise % would need to check the full include hierarchy @@ -217,8 +237,15 @@ function compileAndLinkModel(modelname, modelSourceFolder, coptim, debug, funs, baseFilename = fullfile(amiciSourcePath, sourcesForRecompile{j}); sourceStr = [sourceStr, ' "', baseFilename, '.cpp"']; end - eval(['mex ' DEBUG COPT ' -c -outdir "' objectFolder '" ' ... - includesstr ' ' sourceStr]); + cmd = ['mex ' DEBUG COPT ' -c -outdir "' objectFolder '" ' ... + includesstr ' ' sourceStr]; + try + eval(cmd); + catch ME + disp(cmd); + rethrow(ME); + end + cellfun(@(x) updateFileHashSource(amiciSourcePath, objectFolder, x), sourcesForRecompile); updateHeaderFileHashes(amiciIncludePath, objectFolder); end @@ -355,5 +382,3 @@ function updateFileHash(fileFolder,hashFolder,filename) str = regexprep(str,'[\s\.\-]','_'); versionstring = genvarname(str); % fix everything else we have missed end - - diff --git a/deps/AMICI/matlab/@amimodel/compileC.m b/deps/AMICI/matlab/@amimodel/compileC.m index 4056b77a3..815e4e065 100644 --- a/deps/AMICI/matlab/@amimodel/compileC.m +++ b/deps/AMICI/matlab/@amimodel/compileC.m @@ -3,6 +3,6 @@ function compileC(this) % % Return values: % void - + amimodel.compileAndLinkModel(this.modelname, fullfile(this.wrap_path,'models',this.modelname), this.coptim, this.debug, this.funs, this.cfun); -end +end diff --git a/deps/AMICI/matlab/@amimodel/generateC.m b/deps/AMICI/matlab/@amimodel/generateC.m index d2d0435a3..59eb5e37b 100644 --- a/deps/AMICI/matlab/@amimodel/generateC.m +++ b/deps/AMICI/matlab/@amimodel/generateC.m @@ -26,7 +26,7 @@ function generateC(this) if(bodyNotEmpty) fprintf([ifun{1} ' | ']); - fid = fopen(fullfile(this.wrap_path,'models',this.modelname,[this.modelname '_' cppFunctionName '.cpp']),'w'); + fid = fopen(fullfile(this.wrap_path,'models',this.modelname,[ cppFunctionName '.cpp']),'w'); fprintf(fid,'\n'); fprintf(fid,'#include "amici/symbolic_functions.h"\n'); fprintf(fid,'#include "amici/defines.h" //realtype definition\n'); @@ -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,[' 0,\n']); fprintf(fid,[' ' num2str(this.ng) ',\n']); fprintf(fid,[' ' num2str(this.nw) ',\n']); fprintf(fid,[' ' num2str(this.ndwdx) ',\n']); @@ -170,6 +171,9 @@ function generateC(this) fprintf(fid,[' 0,\n']); fprintf(fid,[' 0,\n']); fprintf(fid,[' {},\n']); +fprintf(fid,[' 0,\n']); +fprintf(fid,[' 0,\n']); +fprintf(fid,[' 0,\n']); fprintf(fid,[' ' num2str(this.nnz) ',\n']); fprintf(fid,[' ' num2str(this.ubw) ',\n']); fprintf(fid,[' ' num2str(this.lbw) '\n']); @@ -264,7 +268,7 @@ function generateCMakeFile(this) funcName = this.funs{j}; if(checkIfFunctionBodyIsNonEmpty(this,funcName)) cppFunctionName = strrep(funcName, 'sigma_', 'sigma'); - sourceStr = [ sourceStr, sprintf('${MODEL_DIR}/%s_%s.cpp\n', this.modelname, cppFunctionName) ]; + sourceStr = [ sourceStr, sprintf('${MODEL_DIR}/%s.cpp\n', cppFunctionName) ]; end end @@ -312,5 +316,5 @@ function generateMainC(this) % if we don't have symbolic variables, it might have been generated before and symbolic expressions were simply not % regenerated. any() for empty (no generated) variables is always false. cppFunctionName = strrep(ifun, 'sigma_', 'sigma'); - nonempty = or(exist(fullfile(this.wrap_path,'models',this.modelname,[this.modelname '_' cppFunctionName '.cpp']),'file'),any(this.fun.(ifun).sym(:)~=0)); + nonempty = or(exist(fullfile(this.wrap_path,'models',this.modelname,[cppFunctionName '.cpp']),'file'),any(this.fun.(ifun).sym(:)~=0)); end diff --git a/deps/AMICI/matlab/@amimodel/generateM.m b/deps/AMICI/matlab/@amimodel/generateM.m index be85528e8..ea8c531f8 100644 --- a/deps/AMICI/matlab/@amimodel/generateM.m +++ b/deps/AMICI/matlab/@amimodel/generateM.m @@ -23,4 +23,3 @@ function generateM(this, amimodelo2) end - diff --git a/deps/AMICI/matlab/@amimodel/generateMatlabWrapper.m b/deps/AMICI/matlab/@amimodel/generateMatlabWrapper.m index 4ee29db93..ae89270c8 100644 --- a/deps/AMICI/matlab/@amimodel/generateMatlabWrapper.m +++ b/deps/AMICI/matlab/@amimodel/generateMatlabWrapper.m @@ -32,20 +32,20 @@ function generateMatlabWrapper(nx, ny, np, nk, nz, o2flag, amimodelo2, wrapperFi nytrue = ny; o2flag = o2flag; end - + [commit_hash,branch,url] = getCommitHash(amiciRootDir); if(isempty(commit_hash)) commit_hash = '#'; end - + if(o2flag) nxtrue = amimodelo2.nxtrue; nytrue = amimodelo2.nytrue; end - - + + %% Generation of the simulation file - + fid = fopen(wrapperFilename,'w'); fprintf(fid,['%% simulate_' modelname '.m is the matlab interface to the cvodes mex\n'... '%% which simulates the ordinary differential equation and respective\n'... @@ -142,8 +142,6 @@ function generateMatlabWrapper(nx, ny, np, nk, nz, o2flag, amimodelo2, wrapperFi '%% a value of 0 will disable the newton solver\n'... '%% .newton_maxlinsteps ... maximum linear steps\n'... '%% default value is 100\n'... - '%% .newton_preeq ... preequilibration of system via newton solver\n'... - '%% default value is false\n'... '%% .pscale ... parameter scaling\n'... '%% []: (DEFAULT) use value specified in the model (fallback: ''lin'')\n'... '%% 0 or ''lin'': linear\n'... @@ -165,8 +163,8 @@ function generateMatlabWrapper(nx, ny, np, nk, nz, o2flag, amimodelo2, wrapperFi '%% sol.z ... event output\n'... '%% sol.sz ... sensitivity of event output\n'... ]); - - + + fprintf(fid,['function varargout = simulate_' modelname '(varargin)\n\n']); fprintf(fid,'%% DO NOT CHANGE ANYTHING IN THIS FILE UNLESS YOU ARE VERY SURE ABOUT WHAT YOU ARE DOING\n'); fprintf(fid,'%% MANUAL CHANGES TO THIS FILE CAN RESULT IN WRONG SOLUTIONS AND CRASHING OF MATLAB\n'); @@ -176,7 +174,7 @@ function generateMatlabWrapper(nx, ny, np, nk, nz, o2flag, amimodelo2, wrapperFi fprintf(fid,' tout=varargin{1};\n'); fprintf(fid,' theta=varargin{2};\n'); fprintf(fid,'end\n'); - + fprintf(fid,'if(nargin>=3)\n'); fprintf(fid,' kappa=varargin{3};\n'); fprintf(fid,'else\n'); @@ -188,12 +186,12 @@ function generateMatlabWrapper(nx, ny, np, nk, nz, o2flag, amimodelo2, wrapperFi fprintf(fid,' kappa = [];\n'); fprintf(fid,'end\n'); end - + fprintf(fid,['if(length(theta)<' num2str(np) ')\n']); fprintf(fid,' error(''provided parameter vector is too short'');\n'); fprintf(fid,'end\n'); fprintf(fid,'\n'); - + fprintf(fid,'\n'); fprintf(fid,'xscale = [];\n'); fprintf(fid,'if(nargin>=5)\n'); @@ -214,12 +212,12 @@ function generateMatlabWrapper(nx, ny, np, nk, nz, o2flag, amimodelo2, wrapperFi fprintf(fid,'end\n'); end fprintf(fid,'\n'); - - + + fprintf(fid,'if(isempty(options_ami.pscale))\n'); fprintf(fid,[' options_ami.pscale = ''' pscale ''' ;\n']); fprintf(fid,'end\n'); - + if(o2flag == 2) fprintf(fid,'if(nargin>=6)\n'); fprintf(fid,' chainRuleFactor = getChainRuleFactors(options_ami.pscale, theta, options_ami.sens_ind);\n'); @@ -231,7 +229,7 @@ function generateMatlabWrapper(nx, ny, np, nk, nz, o2flag, amimodelo2, wrapperFi fprintf(fid,' end\n'); fprintf(fid,'end\n'); end - + if(o2flag) fprintf(fid,'if(nargout>1)\n'); fprintf(fid,' if(nargout>6)\n'); @@ -315,13 +313,13 @@ function generateMatlabWrapper(nx, ny, np, nk, nz, o2flag, amimodelo2, wrapperFi fprintf(fid,['if(length(kappa)<' num2str(nk) ')\n']); fprintf(fid,' error(''provided condition vector is too short'');\n'); fprintf(fid,'end\n'); - + if(o2flag == 2) fprintf(fid,'if(nargin>=6)\n'); fprintf(fid,' kappa = [kappa(:);v(:)];\n'); fprintf(fid,'end\n'); end - + fprintf(fid,'init = struct();\n'); fprintf(fid,'if(~isempty(options_ami.x0))\n'); fprintf(fid,' if(size(options_ami.x0,2)~=1)\n'); @@ -340,8 +338,8 @@ function generateMatlabWrapper(nx, ny, np, nk, nz, o2flag, amimodelo2, wrapperFi fprintf(fid,' error(''Number of rows in sx0 field does not agree with number of states!'');\n'); fprintf(fid,' end\n'); fprintf(fid,'end\n'); - - + + if(o2flag) fprintf(fid,'if(options_ami.sensi<2)\n'); fprintf(fid,[' sol = ami_' modelname '(tout,theta(1:' num2str(np) '),kappa(1:' num2str(nk) '),options_ami,plist,xscale,init,data);\n']); @@ -356,11 +354,11 @@ function generateMatlabWrapper(nx, ny, np, nk, nz, o2flag, amimodelo2, wrapperFi else fprintf(fid,['sol = ami_' modelname '(tout,theta(1:' num2str(np) '),kappa(1:' num2str(nk) '),options_ami,plist,xscale,init,data);\n']); end - + if(o2flag) fprintf(fid,'if(options_ami.sensi == 2)\n'); fprintf(fid, ' if(~(options_ami.sensi_meth==2))\n'); - + fprintf(fid,[' sz = zeros(size(sol.z,1),' num2str(nztrue) ',length(theta(options_ami.sens_ind)));\n']); fprintf(fid,[' ssigmaz = zeros(size(sol.z,1),' num2str(nztrue) ',length(theta(options_ami.sens_ind)));\n']); fprintf(fid,[' srz = zeros(size(sol.z,1),' num2str(nztrue) ',length(theta(options_ami.sens_ind)));\n']); @@ -418,7 +416,7 @@ function generateMatlabWrapper(nx, ny, np, nk, nz, o2flag, amimodelo2, wrapperFi fprintf(fid,[' sol.sigmaz = sol.sigmaz(:,1:' num2str(nztrue) ');\n']); fprintf(fid,'end\n'); end - + fprintf(fid,'if(nargout>1)\n'); fprintf(fid,' varargout{1} = sol.status;\n'); fprintf(fid,' varargout{2} = sol.t;\n'); @@ -437,7 +435,7 @@ function generateMatlabWrapper(nx, ny, np, nk, nz, o2flag, amimodelo2, wrapperFi fprintf(fid,'else\n'); fprintf(fid,' varargout{1} = sol;\n'); fprintf(fid,'end\n'); - + fprintf(fid,'function chainRuleFactors = getChainRuleFactors(pscale, theta, sens_ind)\n'); fprintf(fid,' if(length(pscale) == 1 && length(sens_ind) ~= length(pscale))\n'); fprintf(fid,' chainRuleFactors = arrayfun(@(x, ip) getChainRuleFactor(x, theta(ip)), repmat(pscale, 1, length(sens_ind)), sens_ind);\n'); @@ -460,8 +458,7 @@ function generateMatlabWrapper(nx, ny, np, nk, nz, o2flag, amimodelo2, wrapperFi fprintf(fid,'\n'); fprintf(fid,'end\n'); - + fclose(fid); - -end +end diff --git a/deps/AMICI/matlab/@amimodel/generateRebuildM.m b/deps/AMICI/matlab/@amimodel/generateRebuildM.m index 56b213af7..07bf5bf02 100644 --- a/deps/AMICI/matlab/@amimodel/generateRebuildM.m +++ b/deps/AMICI/matlab/@amimodel/generateRebuildM.m @@ -23,4 +23,3 @@ function generateRebuildM(this) fclose(fid); end - diff --git a/deps/AMICI/matlab/@amimodel/getFun.m b/deps/AMICI/matlab/@amimodel/getFun.m index 4d126c585..12f405b32 100644 --- a/deps/AMICI/matlab/@amimodel/getFun.m +++ b/deps/AMICI/matlab/@amimodel/getFun.m @@ -8,11 +8,11 @@ function getFun(this,HTable,funstr) % % Return values: % void - + [wrap_path,~,~]=fileparts(fileparts(which('amiwrap.m'))); - + fun = amifun(funstr,this); - + if(~isfield(this.fun,funstr)) % check whether we already computed the respective fun if(~all(strcmp(fun.deps,funstr))) % prevent infinite loops @@ -35,12 +35,12 @@ function getFun(this,HTable,funstr) else cflag = 0; end - - + + if(cflag) fun = amifun(funstr,this); [fun,this] = fun.getSyms(this); this.fun(1).(funstr) = fun; end - -end \ No newline at end of file + +end diff --git a/deps/AMICI/matlab/@amimodel/loadOldHashes.m b/deps/AMICI/matlab/@amimodel/loadOldHashes.m index 088aa826e..fd56c3361 100644 --- a/deps/AMICI/matlab/@amimodel/loadOldHashes.m +++ b/deps/AMICI/matlab/@amimodel/loadOldHashes.m @@ -4,7 +4,7 @@ % Return values: % HTable: struct with hashes of symbolic definition from the previous % compilation @type struct - + [wrap_path,~,~]=fileparts(fileparts(which('amiwrap.m'))); try load(fullfile(wrap_path,'models',this.modelname,['hashes.mat'])) @@ -25,7 +25,7 @@ this.sparseidxB = sparseidxB; catch err end - + catch HTable = struct(); end @@ -47,7 +47,7 @@ end DHTable.Jy = ''; DHTable.Jz = ''; - + DHTable.generateC = ''; DHTable.makeSyms = ''; DHTable.makeEvents = ''; @@ -62,6 +62,6 @@ DHTable.writeCcode_sensi = ''; DHTable.tdata = ''; - + HTable = am_setdefault(HTable,DHTable); end diff --git a/deps/AMICI/matlab/@amimodel/makeEvents.m b/deps/AMICI/matlab/@amimodel/makeEvents.m index f283ff920..db6025403 100644 --- a/deps/AMICI/matlab/@amimodel/makeEvents.m +++ b/deps/AMICI/matlab/@amimodel/makeEvents.m @@ -69,10 +69,10 @@ function makeEvents( this ) tmp_bolus{ievent} = sym(zeros([nx,1])); end syms polydirac - + % initialise hflag hflags = zeros([nx,nevent]); - + % heaviside event_dependency = zeros(nevent); for ievent = 1:nevent @@ -91,14 +91,14 @@ function makeEvents( this ) end end end - + % check for loops if(any(any(event_dependency^(size(event_dependency,1))))) error('Found loop in trigger dependency. This can lead to the simulation getting stuck and is thus currently not supported. Please check your model definition!') end - + P = 1:size(event_dependency,1); - + % make matrix upper triangular, this is to ensure that we dont end % up with partially replaced trigger functions that we no longer recognise while(~isempty(find(triu(event_dependency(P,P))-event_dependency(P,P)))) @@ -115,9 +115,9 @@ function makeEvents( this ) trigger = trigger(P); bolus = bolus(P); z = z(P); - - - + + + for ix = 1:nx symchar = char(this.sym.xdot(ix)); symvariable = this.sym.xdot(ix); @@ -140,18 +140,18 @@ function makeEvents( this ) end end if(strfind(symchar,'heaviside')) - + for ievent = 1:nevent % remove the heaviside function and replace by h % variable which is updated upon event occurrence in the % solver - + % h variables only change for one sign change but heaviside - % needs updating for both, thus we should + % needs updating for both, thus we should symvariable = subs(symvariable,heaviside( trigger{ievent}),betterSym(['h_' num2str(ievent-1)'])); symvariable = subs(symvariable,heaviside(-trigger{ievent}),betterSym(['(1-h_' num2str(ievent-1) ')'])); % set hflag - + % we can check whether dividing cfp(2) by % trigger{ievent} reduced the length of the symbolic % expression. If it does, this suggests that @@ -172,7 +172,7 @@ function makeEvents( this ) % update xdot this.sym.xdot(ix) = symvariable; end - + % loop until we no longer found any dynamic heaviside functions in the triggers in the previous loop nheavy = 1; while nheavy>0 @@ -196,13 +196,13 @@ function makeEvents( this ) trigger{ievent} = betterSym(symchar); end end - + % compute dtriggerdt and constant trigger functions for ievent = 1:nevent dtriggerdt(ievent) = diff(trigger{ievent},sym('t')) + jacobian(trigger{ievent},this.sym.x)*this.sym.xdot(:); end triggeridx = logical(dtriggerdt~=0); - + % multiply by the dtriggerdt factor, this should stay here as we % want the xdot to be cleaned of any dirac functions ievent = 1; @@ -221,7 +221,7 @@ function makeEvents( this ) ievent = ievent+1; end end - + % update hflags according to bolus for ievent = 1:nevent if(any(double(bolus{ievent}~=0))) @@ -236,9 +236,9 @@ function makeEvents( this ) end end end - + this.event = amievent.empty(); - + % update events for ievent = 1:nevent this.event(ievent) = amievent(trigger{ievent},bolus{ievent}(:),z{ievent}); @@ -279,11 +279,10 @@ function makeEvents( this ) this.sym.Jrz = sym(zeros(size(this.sym.Jz))); for iz = 1:length([this.event.z]) tmp = subs(this.sym.Jz(iz,:),var_z,var_rz); - this.sym.Jrz(iz,:) = subs(tmp,mz,sym(zeros(size(mz)))); + this.sym.Jrz(iz,:) = subs(tmp,mz,sym(zeros(size(mz)))); end end this.sym.Jrz = subs(this.sym.Jrz,rz,var_rz); end - diff --git a/deps/AMICI/matlab/@amimodel/makeSyms.m b/deps/AMICI/matlab/@amimodel/makeSyms.m index 13f022678..4f6f382e4 100644 --- a/deps/AMICI/matlab/@amimodel/makeSyms.m +++ b/deps/AMICI/matlab/@amimodel/makeSyms.m @@ -103,7 +103,7 @@ function makeSyms( this ) catch error('Could not transform model.sym.k into a symbolic variable, please check the definition!') end - + end if(isfield(this.sym,'root')) @@ -141,4 +141,3 @@ function makeSyms( this ) % error(['The symbolic variable ' char(symvars(find(svaridx,1))) ' is used in the differential equation right hand side but was not specified as parameter/state/constant!']); % end end - diff --git a/deps/AMICI/matlab/@amimodel/parseModel.m b/deps/AMICI/matlab/@amimodel/parseModel.m index beea83800..167819bb7 100644 --- a/deps/AMICI/matlab/@amimodel/parseModel.m +++ b/deps/AMICI/matlab/@amimodel/parseModel.m @@ -148,13 +148,13 @@ function parseModel(this) fprintf('sparse | ') M = double(logical(this.fun.J.sym~=sym(zeros(size(this.fun.J.sym))))); this.sparseidx = find(M); - + [ubw,lbw] = ami_bandwidth(M); - + this.ubw = ubw; this.lbw = lbw; this.nnz = length(find(M(:))); - + I = arrayfun(@(x) find(M(:,x))-1,1:nx,'UniformOutput',false); this.rowvals = []; this.colptrs = []; @@ -163,7 +163,7 @@ function parseModel(this) this.rowvals = [this.rowvals; I{ix}]; end this.colptrs(ix+1) = length(this.rowvals); - + if(this.adjoint) if(isfield(this.fun,'JB')) fprintf('sparseB | ') @@ -186,7 +186,7 @@ function parseModel(this) this.getFun([], 'M'); this.id = double(any(this.fun.M.sym)); else - + end else this.id = zeros(1, nx); diff --git a/deps/AMICI/matlab/@amioption/amioption.m b/deps/AMICI/matlab/@amioption/amioption.m index 6dcfdaf2b..2a92dc32e 100644 --- a/deps/AMICI/matlab/@amioption/amioption.m +++ b/deps/AMICI/matlab/@amioption/amioption.m @@ -57,10 +57,6 @@ sx0 = double.empty(); % newton solver: maximum newton steps newton_maxsteps = 40; - % newton solver: maximum linear steps - newton_maxlinsteps = 100; - % preequilibration of system via newton solver - newton_preeq = false; % mapping of event ouputs to events z2event = double.empty(); % parameter scaling @@ -241,22 +237,6 @@ this.newton_maxsteps = value; end - function this = set.newton_maxlinsteps(this,value) - assert(isnumeric(value),'The option newton_maxlinsteps must have a numeric value!') - assert(floor(value)==value,'The option newton_maxlinsteps must be an integer!') - this.newton_maxlinsteps = value; - end - - function this = set.newton_preeq(this,value) - if(isnumeric(value)) - assert(floor(value)==value,'The option newton_preeq must be a logical!') - assert(value<=1,'Only 0 and 1 are valid options for newton_preeq!') - assert(value>=0,'Only 0 and 1 are valid options for newton_preeq!') - else - assert(islogical(value),'The option newton_preeq must have a logical value!') - end - this.newton_preeq = value; - end end methods(Static) diff --git a/deps/AMICI/matlab/@amised/amised.m b/deps/AMICI/matlab/@amised/amised.m index 2d69c2bb2..a8f1be640 100644 --- a/deps/AMICI/matlab/@amised/amised.m +++ b/deps/AMICI/matlab/@amised/amised.m @@ -4,7 +4,7 @@ % classdef amised < handle % AMISED is a container for SED-ML objects - + properties ( GetAccess = 'public', SetAccess = 'private' ) % amimodel from the specified model model = struct('event',[],'sym',[]); @@ -20,16 +20,16 @@ varsym = sym([]); % symbolic expressions for data datasym = sym([]); - + end - + properties ( GetAccess = 'public', SetAccess = 'public' ) - + end - + methods function ASED = amised(sedname) - %amised reads in an SEDML document using the JAVA binding of + %amised reads in an SEDML document using the JAVA binding of % of libSEDML % % Parameters: @@ -38,7 +38,7 @@ % Return values: % ASED: amised object which contains all the information from % the SEDML document - + % get models for imodel = 1:length(ASED.sedml.listOfModels.model) % get the model sbml @@ -115,11 +115,10 @@ ASED.varsym(idata,ivar) = sym(variable.Attributes.id); end ASED.datasym(idata) = sym(variable.Attributes.id); - + end - + end end end - diff --git a/deps/AMICI/matlab/@optsym/optsym.m b/deps/AMICI/matlab/@optsym/optsym.m index b825a34ba..6209712fe 100644 --- a/deps/AMICI/matlab/@optsym/optsym.m +++ b/deps/AMICI/matlab/@optsym/optsym.m @@ -4,12 +4,12 @@ % classdef optsym0) stoichsymbols = [reactant_id{:},product_id{:}]; stoichmath = [tmp_rs,tmp_ps]; - + stoichidx = not(strcmp(stoichsymbols,'')); stoichsymbols = stoichsymbols(stoichidx); stoichmath = stoichmath(stoichidx); @@ -440,7 +440,7 @@ function importSBML(this,filename) error('Event priorities are currently not supported!'); end end - + try tmp = cellfun(@(x) sym(sanitizeString(x)),{model.event.trigger},'UniformOutput',false); this.trigger = [tmp{:}]; @@ -459,10 +459,10 @@ function importSBML(this,filename) for ievent = 1:length(this.trigger) tmp = cellfun(@(x) {x.variable},{model.event(ievent).eventAssignment},'UniformOutput',false); assignments = sym(cat(2,tmp{:})); - + tmp = cellfun(@(x) {x.math},{model.event(ievent).eventAssignment},'UniformOutput',false); assignments_math = cleanedsym(cat(2,tmp{:})); - + for iassign = 1:length(assignments) state_assign_idx = find(assignments(iassign)==this.state); param_assign_idx = find(assignments(iassign)==this.param); @@ -470,37 +470,37 @@ function importSBML(this,filename) bound_assign_idx = find(assignments(iassign)==boundary_sym); stoich_assign_idx = find(assignments(iassign)==stoichsymbols); vol_assign_idx = find(assignments(iassign)==compartments_sym); - + if(np>0 && ~isempty(param_assign_idx)) error('Assignments of parameters via events are currently not supported') this.param(param_assign_idx) = this.param(param_assign_idx)*heaviside(-this.trigger(ievent)) + assignments_math(iassign)*heaviside(this.trigger(ievent)); end - + if(nk>0 && ~isempty(cond_assign_idx)) error('Assignments of constants via events are currently not supported') conditions(cond_assign_idx) = conditions(cond_assign_idx)*heaviside(-this.trigger(ievent)) + assignments_math(iassign)*heaviside(this.trigger(ievent)); end - + if(length(boundaries)>0 && ~isempty(bound_assign_idx)) error('Assignments of boundary conditions via events are currently not supported') boundaries(bound_assign_idx) = conditions(bound_assign_idx)*heaviside(-this.trigger(ievent)) + assignments_math(iassign)*heaviside(this.trigger(ievent)); end - + if(length(stoichsymbols)>0 && ~isempty(stoich_assign_idx)) error('Assignments of stoichiometries via events are currently not supported') stoichmath(stoich_assign_idx) = stoichmath(stoich_assign_idx)*heaviside(-this.trigger(ievent)) + assignments_math(iassign)*heaviside(this.trigger(ievent)); end - + if(length(compartments_sym)>0 && ~isempty(vol_assign_idx)) error('Assignments of compartment volumes via events are currently not supported') end - + if(length(this.state)>0 && ~isempty(state_assign_idx)) - + this.bolus(state_assign_idx,ievent) = -this.state(state_assign_idx); addToBolus = sym(zeros(size(this.bolus(:,ievent)))); addToBolus(state_assign_idx) = assignments_math(iassign); - + this.bolus(:,ievent) = this.bolus(:,ievent) + addToBolus; end @@ -526,15 +526,15 @@ function importSBML(this,filename) tmpfun = cellfun(@(x) ['fun_' num2str(x)],num2cell(1:length(model.functionDefinition)),'UniformOutput',false); this.funmath = strrep(this.funmath,{model.functionDefinition.id},tmpfun); % replace helper functions - + checkIllegalFunctions(this.funmath); this.funmath = replaceLogicalFunctions(this.funmath); - + this.funmath = strrep(this.funmath,tmpfun,{model.functionDefinition.id}); this.funarg = cellfun(@(x,y) [y '(' strjoin(transpose(x(1:end-1)),',') ')'],lambdas,replaceReservedFunctionIDs({model.functionDefinition.id}),'UniformOutput',false); - + % make functions available in this file - + for ifun = 1:length(this.funmath) token = regexp(this.funarg(ifun),'\(([0-9\w\,]*)\)','tokens'); start = regexp(this.funarg(ifun),'\(([0-9\w\,]*)\)'); @@ -567,7 +567,7 @@ function importSBML(this,filename) if(ismember(initassignments_sym(iIA),this.param)) if(ismember(sym(model.time_symbol),symvar(initassignments_math(iIA)))) error('Time dependent initial assignments are currently not supported!') - end + end param_idx = find(initassignments_sym(iIA)==this.param); parameter_sym(param_idx) = []; parameter_val(param_idx) = []; @@ -579,7 +579,7 @@ function importSBML(this,filename) this.param = subs(this.param,initassignments_sym(iIA),initassignments_math(iIA)); rulemath = subs(rulemath,initassignments_sym(iIA),initassignments_math(iIA)); np = np-1; - end + end end applyRule(this,model,'param',rulevars,rulemath) @@ -760,7 +760,7 @@ function checkIllegalFunctions(str) if(isfield(y,'math')) expr = cleanedsym(y.math); else - expr = cleanedsym(); + expr = cleanedsym(); end end @@ -771,4 +771,3 @@ function checkIllegalFunctions(str) id = {x.species}; end end - diff --git a/deps/AMICI/matlab/SBMLimporter/@SBMLode/writeAMICI.m b/deps/AMICI/matlab/SBMLimporter/@SBMLode/writeAMICI.m index 422a80515..3550241cd 100644 --- a/deps/AMICI/matlab/SBMLimporter/@SBMLode/writeAMICI.m +++ b/deps/AMICI/matlab/SBMLimporter/@SBMLode/writeAMICI.m @@ -7,10 +7,10 @@ function writeAMICI(this,modelname) % % Return values: % void - + fprintf('writing file ...\n') fid = fopen([modelname '_syms.m'],'w'); - + fprintf(fid,['function model = ' modelname '_syms()\n']); fprintf(fid,'\n'); if(strcmp(this.time_symbol,'')) @@ -20,7 +20,7 @@ function writeAMICI(this,modelname) end fprintf(fid,'\n'); fprintf(fid,'avogadro = 6.02214179e23;'); - + % fprintf(fid,'model.debug = true;\n'); writeDefinition('STATES','x','state',this,fid) writeDefinition('PARAMETERS','p','parameter',this,fid) @@ -54,7 +54,7 @@ function writeAMICI(this,modelname) fprintf(fid,'\n'); fprintf(fid,'end\n'); fprintf(fid,'\n'); - + for ifun = 1:length(this.funmath) fprintf(fid,['function r = ' this.funarg{ifun} '\n']); fprintf(fid,'\n'); @@ -62,12 +62,12 @@ function writeAMICI(this,modelname) fprintf(fid,'\n'); fprintf(fid,'end\n'); fprintf(fid,'\n'); - end - + end + for fun = {'factorial','cei','psi'} fprintUnsupportedFunctionError(fun{1},fid) end - + fclose(fid); end @@ -99,4 +99,4 @@ function fprintUnsupportedFunctionError(functionName,fid) fprintf(fid,'\n'); fprintf(fid,'end\n'); fprintf(fid,'\n'); -end \ No newline at end of file +end diff --git a/deps/AMICI/matlab/SBMLimporter/computeBracketLevel.m b/deps/AMICI/matlab/SBMLimporter/computeBracketLevel.m index ec1c6621d..5bbffb961 100644 --- a/deps/AMICI/matlab/SBMLimporter/computeBracketLevel.m +++ b/deps/AMICI/matlab/SBMLimporter/computeBracketLevel.m @@ -11,7 +11,7 @@ % % Return values: % brl: bracket levels @type *int - + % compute bracket levels add one for each (, (before) remove 1 for each % ) (after) open = (cstr == '('); @@ -24,6 +24,5 @@ for ifun = 1:length(fun_startidx) brl(fun_startidx(ifun):(fun_endidx(ifun)-1)) = brl(fun_endidx(ifun)); end - -end +end diff --git a/deps/AMICI/matlab/amiwrap.m b/deps/AMICI/matlab/amiwrap.m index 838d95c10..6fbf8353a 100644 --- a/deps/AMICI/matlab/amiwrap.m +++ b/deps/AMICI/matlab/amiwrap.m @@ -199,4 +199,3 @@ function amiwrap( varargin ) end warning(warningreset); end - diff --git a/deps/AMICI/matlab/auxiliary/CalcMD5/CalcMD5.c b/deps/AMICI/matlab/auxiliary/CalcMD5/CalcMD5.c index d84dcfb81..a078a2349 100644 --- a/deps/AMICI/matlab/auxiliary/CalcMD5/CalcMD5.c +++ b/deps/AMICI/matlab/auxiliary/CalcMD5/CalcMD5.c @@ -90,7 +90,7 @@ ** documentation and/or software. ** ********************************************************************** */ - + /* % $JRev: R5.00z V:025 Sum:/kHGslMmCpAS Date:17-Dec-2009 12:46:26 $ % $File: CalcMD5\CalcMD5.c $ @@ -209,33 +209,33 @@ void MD5Update(MD5_CTX *context, UCHAR *input, UINT inputLen) /* Compute number of bytes mod 64: */ index = (UINT)((context->count[0] >> 3) & 0x3F); - + /* Update number of bits: */ if ((context->count[0] += ((UINT32)inputLen << 3)) < ((UINT32)inputLen << 3)) { context->count[1]++; } context->count[1] += ((UINT32)inputLen >> 29); - + partLen = 64 - index; - + /* Transform as many times as possible: */ if (inputLen >= partLen) { int i; memcpy((POINTER)&context->buffer[index], (POINTER)input, partLen); MD5Transform(context->state, context->buffer); - + inputLenM63 = inputLen - 63; for (i = partLen; i < inputLenM63; i += 64) { MD5Transform(context->state, &input[i]); } - + /* Buffer remaining input: index = 0 */ memcpy((POINTER)&context->buffer[0], (POINTER)&input[i], inputLen - i); } else { /* Buffer remaining input: i = 0 */ memcpy((POINTER)&context->buffer[index], (POINTER)input, inputLen); } - + return; } @@ -254,13 +254,13 @@ void MD5Final(UCHAR digest[16], MD5_CTX *context) index = (UINT)((context->count[0] >> 3) & 0x3f); padLen = (index < 56) ? (56 - index) : (120 - index); MD5Update(context, PADDING, padLen); - + /* Append length before padding: */ MD5Update(context, bits, 8); - + /* Store state in digest: */ MD5Encode(digest, context->state, 4); - + /* Zero sensitive information: */ memset((POINTER)context, 0, sizeof(MD5_CTX)); } @@ -312,7 +312,7 @@ void MD5Transform(UINT32 state[4], UCHAR block[64]) (((UINT32)block[58]) << 16) | (((UINT32)block[59]) << 24); x[15] = ( (UINT32)block[60]) | (((UINT32)block[61]) << 8) | (((UINT32)block[62]) << 16) | (((UINT32)block[63]) << 24); - + /* Round 1 */ FF(a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ FF(d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ @@ -398,7 +398,7 @@ void MD5Transform(UINT32 state[4], UCHAR block[64]) void MD5Encode(UCHAR *output, UINT32 *input, UINT len) { UINT j; - + for (j = 0; j < len; j++) { *output++ = (UCHAR)( *input & 0xff); *output++ = (UCHAR)((*input >> 8) & 0xff); @@ -417,17 +417,17 @@ void MD5Char(mxChar *array, mwSize inputLen, UCHAR digest[16]) MD5_CTX context; UINT Chunk; UCHAR *bufferP, *bufferEnd = buffer + BUFFER_LEN, *arrayP; - + /* Limit length to 32 bit address, because I cannot test this function */ /* with 64 bit arrays currently (under construction): */ if (inputLen >> 31 != 0) { /* Detect sign-bit if mwSize is int */ mexErrMsgTxt("*** CalcMD5[mex]: Input > 2^31 byte not handled yet."); } - + arrayP = (UCHAR *) array; /* UCHAR *, not mxChar *!*/ - + MD5Init(&context); - + /* Copy chunks of input data - only the first byte of each mxChar: */ Chunk = inputLen / BUFFER_LEN; while (Chunk--) { @@ -436,10 +436,10 @@ void MD5Char(mxChar *array, mwSize inputLen, UCHAR digest[16]) *bufferP++ = *arrayP; arrayP += 2; } - + MD5Update(&context, buffer, BUFFER_LEN); } - + /* Last chunk: */ Chunk = inputLen % BUFFER_LEN; if (Chunk != 0) { @@ -449,12 +449,12 @@ void MD5Char(mxChar *array, mwSize inputLen, UCHAR digest[16]) *bufferP++ = *arrayP; arrayP += 2; } - + MD5Update(&context, buffer, Chunk); } - + MD5Final(digest, &context); - + return; } @@ -462,13 +462,13 @@ void MD5Char(mxChar *array, mwSize inputLen, UCHAR digest[16]) void MD5Array(UCHAR *array, mwSize inputLen, UCHAR digest[16]) { MD5_CTX context; - + /* Limit length to 32 bit address, because I cannot test this function */ /* with 64 bit arrays currently (under construction): */ if (inputLen >> 31 != 0) { /* Detect sign-bit if mwSize is signed int */ mexErrMsgTxt("*** CalcMD5[mex]: Input > 2^31 byte not handled yet."); } - + MD5Init(&context); MD5Update(&context, array, (UINT) inputLen); MD5Final(digest, &context); @@ -481,13 +481,13 @@ void MD5File(char *filename, UCHAR digest[16]) MD5_CTX context; int len; UINT32 allLen = 0; - + /* Open the file in binary mode: */ if ((FID = fopen(filename, "rb")) == NULL) { mexPrintf("*** Error for file: [%s]\n", filename); mexErrMsgTxt("*** CalcMD5[mex]: Cannot open file."); } - + MD5Init(&context); while ((len = fread(buffer, 1, BUFFER_LEN, FID)) != 0) { /* Limit length to 32 bit address, because I cannot test this function */ @@ -497,7 +497,7 @@ void MD5File(char *filename, UCHAR digest[16]) fclose(FID); mexErrMsgTxt("*** CalcMD5[mex]: Cannot handle files > 2.1GB yet."); } - + MD5Update(&context, buffer, (UINT) len); } MD5Final(digest, &context); @@ -509,7 +509,7 @@ void MD5File(char *filename, UCHAR digest[16]) void ToHex(const UCHAR digest[16], char *output, int LowerCase) { char *outputEnd; - + if (LowerCase) { for (outputEnd = output + 32; output < outputEnd; output += 2) { sprintf(output, "%02x", *(digest++)); @@ -519,7 +519,7 @@ void ToHex(const UCHAR digest[16], char *output, int LowerCase) sprintf(output, "%02X", *(digest++)); } } - + return; } @@ -535,7 +535,7 @@ void ToBase64(const UCHAR In[16], char *Out) int i; char *p; const UCHAR *s; - + p = Out; s = In; for (i = 0; i < 5; i++) { @@ -545,11 +545,11 @@ void ToBase64(const UCHAR In[16], char *Out) *p++ = B64[s[2] & 0x3F]; s += 3; } - + *p++ = B64[(*s >> 2) & 0x3F]; *p++ = B64[((*s & 0x3) << 4)]; *p = '\0'; - + return; } @@ -560,12 +560,12 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) /* - Define default values of optional arguments. */ /* - Forward input data to different calculators according to the input type. */ /* - Convert digest to output format. */ - + char *FileName, InType, hexOut[33], b64Out[23]; UCHAR digest[16], *digestP, OutType = 'h'; int isFile = false, isUnicode = false; double *outP, *outEnd; - + /* Check number of inputs and outputs: */ if (nrhs == 0 || nrhs > 3) { mexErrMsgTxt("*** CalcMD5[mex]: 1 to 3 inputs required."); @@ -573,27 +573,27 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) if (nlhs > 1) { mexErrMsgTxt("*** CalcMD5[mex]: Too many output arguments."); } - + /* If 2nd input starts with 'f', treat string in 1st argument as file name: */ if (nrhs >= 2 && mxGetNumberOfElements(prhs[1]) > 0) { if (mxIsChar(prhs[1]) == 0) { mexErrMsgTxt("*** CalcMD5[mex]: 2nd input must be a string."); } - + InType = (char) tolower(*(POINTER) mxGetData(prhs[1])); isFile = (InType == 'f'); isUnicode = (InType == 'u'); } /* Default otherwise! */ - + /* Output type - default: hex: */ if (nrhs == 3 && !mxIsEmpty(prhs[2])) { if (mxIsChar(prhs[2]) == 0) { mexErrMsgTxt("*** CalcMD5[mex]: 3rd input must be a string."); } - + OutType = *(POINTER) mxGetData(prhs[2]); /* Just 1st character */ } - + /* Calculate check sum: */ if (isFile) { if ((FileName = mxArrayToString(prhs[0])) == NULL) { @@ -601,21 +601,21 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) } MD5File(FileName, digest); mxFree(FileName); - + } else if (mxIsNumeric(prhs[0]) || isUnicode) { MD5Array((POINTER) mxGetData(prhs[0]), mxGetNumberOfElements(prhs[0]) * mxGetElementSize(prhs[0]), digest); - + } else if (mxIsChar(prhs[0])) { MD5Char((mxChar *) mxGetData(prhs[0]), mxGetNumberOfElements(prhs[0]), digest); - + } else { mexErrMsgTxt("*** CalcMD5[mex]: Input type not accepted."); } - + /* Create output: */ switch (OutType) { case 'H': @@ -623,7 +623,7 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) ToHex(digest, hexOut, OutType == 'h'); plhs[0] = mxCreateString(hexOut); break; - + case 'D': case 'd': /* DOUBLE with integer values: */ plhs[0] = mxCreateDoubleMatrix(1, 16, mxREAL); @@ -633,7 +633,7 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) *outP = (double) *digestP++; } break; - + case 'B': case 'b': /* Base64: */ /* strtobase64(b64Out, 26, digest, 16); // included in LCC3.8 */ @@ -641,10 +641,10 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) ToBase64(digest, b64Out); /* Locally implemented */ plhs[0] = mxCreateString(b64Out); break; - + default: mexErrMsgTxt("*** CalcMD5[mex]: Unknown output type."); } - + return; } diff --git a/deps/AMICI/matlab/auxiliary/CalcMD5/TestCalcMD5.m b/deps/AMICI/matlab/auxiliary/CalcMD5/TestCalcMD5.m index 5cc14e4d2..ec09ecfc6 100644 --- a/deps/AMICI/matlab/auxiliary/CalcMD5/TestCalcMD5.m +++ b/deps/AMICI/matlab/auxiliary/CalcMD5/TestCalcMD5.m @@ -54,7 +54,7 @@ function TestCalcMD5(doSpeed) error(['*** ', FuncName, ': Failed for string:', ... char(10), '[', TestData{iTest, 1}, ']']); end - + % Check file input: FID = fopen(TestFile, 'wb+'); if FID < 0 @@ -63,7 +63,7 @@ function TestCalcMD5(doSpeed) end fwrite(FID, TestData{iTest, 1}, 'uchar'); fclose(FID); - + Str2 = CalcMD5(TestFile, 'file'); if strcmpi(Str2, TestData{iTest, 2}) == 0 fprintf('\n'); @@ -83,14 +83,14 @@ function TestCalcMD5(doSpeed) upHexOut = CalcMD5(data, 'char', 'HEX'); decOut = CalcMD5(data, 'char', 'Dec'); b64Out = CalcMD5(data, 'char', 'Base64'); - + if not(strcmpi(lowHexOut, upHexOut) && ... isequal(sscanf(lowHexOut, '%2x'), decOut(:)) && ... isequal(Base64decode(b64Out), decOut)) fprintf('\n'); error(['*** ', FuncName, ': Different results for output types.']); end - + % Check unicode, if the data length is a multiple of 2: if rem(length(data), 2) == 0 doubleData = double(data); @@ -110,12 +110,12 @@ function TestCalcMD5(doSpeed) disp('== Test speed:'); disp('(Short data: mainly the overhead of calling the function)'); Delay = 2; - + for Len = [10, 100, 1000, 10000, 1e5, 1e6, 1e7] [Number, Unit] = UnitPrint(Len); fprintf(' Data length: %s %s:\n', Number, Unit); data = uint8(fix(rand(1, Len) * 256)); - + % Measure java time: iniTime = cputime; finTime = iniTime + Delay; @@ -129,7 +129,7 @@ function TestCalcMD5(doSpeed) javaLoopPerSec = javaLoop / (cputime - iniTime); [Number, Unit] = UnitPrint(javaLoopPerSec * Len); fprintf(' java: %6s %s/sec\n', Number, Unit); - + % Measure Mex time: iniTime = cputime; finTime = iniTime + Delay; @@ -142,7 +142,7 @@ function TestCalcMD5(doSpeed) [Number, Unit] = UnitPrint(mexLoopPerSec * Len); fprintf(' mex: %6s %s/sec: %.1f times faster\n', ... Number, Unit, mexLoopPerSec / javaLoopPerSec); - + % Compare the results: if ~isequal(javaHash(:), mexHash(:)) error(['*** ', FuncName, ': Different results from java and Mex.']); @@ -151,7 +151,7 @@ function TestCalcMD5(doSpeed) end fprintf('\nCalcMD5 seems to work well.\n'); - + return; % ****************************************************************************** diff --git a/deps/AMICI/matlab/auxiliary/am_setdefault.m b/deps/AMICI/matlab/auxiliary/am_setdefault.m index 6065734eb..f96ac7461 100755 --- a/deps/AMICI/matlab/auxiliary/am_setdefault.m +++ b/deps/AMICI/matlab/auxiliary/am_setdefault.m @@ -7,7 +7,7 @@ % % Return values: % robj: updated obj @type struct - + fieldlist = fieldnames(obj); for i = 1:length(fieldlist) dobj.(fieldlist{i}) = obj.(fieldlist{i}); diff --git a/deps/AMICI/matlab/auxiliary/betterSym.m b/deps/AMICI/matlab/auxiliary/betterSym.m index e3eec9baf..cf93b7409 100644 --- a/deps/AMICI/matlab/auxiliary/betterSym.m +++ b/deps/AMICI/matlab/auxiliary/betterSym.m @@ -5,4 +5,4 @@ else csym = sym(str); end -end \ No newline at end of file +end diff --git a/deps/AMICI/matlab/auxiliary/compileAMICIDependencies.m b/deps/AMICI/matlab/auxiliary/compileAMICIDependencies.m index 1292336e1..cac063df3 100644 --- a/deps/AMICI/matlab/auxiliary/compileAMICIDependencies.m +++ b/deps/AMICI/matlab/auxiliary/compileAMICIDependencies.m @@ -1,6 +1,6 @@ function [objectsstr, includesstr] = compileAMICIDependencies(dependencyPath, objectFolder, o_suffix, COPT, DEBUG) %COMPILEAMICIDEPENDENCIES Compiles Sundials and SuiteSparse libraries required by AMICI - + COPT = ['CFLAGS=''$CFLAGS -std=c99'' ' COPT]; sundials_path = fullfile(dependencyPath,'sundials'); sundials_ver = '5.2.0'; @@ -43,14 +43,17 @@ end end - % sundials compatible int type for suitesparse - COPT = [COPT ' -DDLONG']; - % compile if(~strcmp(sourcesToCompile, '')) - eval(['mex ' DEBUG ' ' COPT ' -c -outdir "' ... + cmd = ['mex ' DEBUG ' ' COPT ' -c -outdir "' ... objectFolder '" ' ... - includesstr ' ' sourcesToCompile ]); + includesstr ' ' sourcesToCompile]; + try + eval(cmd); + catch ME + disp(cmd); + rethrow(ME); + end end % only write versions.txt if we are done compiling @@ -65,6 +68,7 @@ includesstr = ''; includesstr = strcat(includesstr,' -I"', fullfile(sundials_path, 'include'), '"'); includesstr = strcat(includesstr,' -I"', fullfile(sundials_path, 'src'), '"'); + includesstr = strcat(includesstr,' -I"', fullfile(sundials_path, 'src', 'sundials'), '"'); includesstr = strcat(includesstr,' -I"', fullfile(amici_root_path), '"'); includesstr = strcat(includesstr,' -I"', fullfile(amici_root_path, 'src'), '"'); includesstr = strcat(includesstr,' -I"', fullfile(amici_root_path, 'include'), '"'); @@ -170,40 +174,39 @@ function sources_ssparse = getSourcesSSparse() sources_ssparse = { - fullfile('KLU','Source','klu_analyze_given.c'); - fullfile('KLU','Source','klu_analyze.c'); - fullfile('KLU','Source','klu_defaults.c'); - fullfile('KLU','Source','klu_diagnostics.c'); - fullfile('KLU','Source','klu_dump.c'); - fullfile('KLU','Source','klu_extract.c'); - fullfile('KLU','Source','klu_factor.c'); - fullfile('KLU','Source','klu_free_numeric.c'); - fullfile('KLU','Source','klu_free_symbolic.c'); - fullfile('KLU','Source','klu_kernel.c'); - fullfile('KLU','Source','klu_memory.c'); - fullfile('KLU','Source','klu_refactor.c'); - fullfile('KLU','Source','klu_scale.c'); - fullfile('KLU','Source','klu_sort.c'); - fullfile('KLU','Source','klu_solve.c'); - fullfile('KLU','Source','klu_tsolve.c'); - fullfile('KLU','Source','klu.c'); - fullfile('AMD','Source','amd_1.c'); - fullfile('AMD','Source','amd_2.c'); - fullfile('AMD','Source','amd_aat.c'); - fullfile('AMD','Source','amd_control.c'); - fullfile('AMD','Source','amd_defaults.c'); - fullfile('AMD','Source','amd_dump.c'); - fullfile('AMD','Source','amd_global.c'); - fullfile('AMD','Source','amd_info.c'); - fullfile('AMD','Source','amd_order.c'); - fullfile('AMD','Source','amd_post_tree.c'); - fullfile('AMD','Source','amd_postorder.c'); - fullfile('AMD','Source','amd_preprocess.c'); - fullfile('AMD','Source','amd_valid.c'); - fullfile('COLAMD','Source','colamd.c'); - fullfile('BTF','Source','btf_maxtrans.c'); - fullfile('BTF','Source','btf_order.c'); - fullfile('BTF','Source','btf_strongcomp.c'); + fullfile('KLU','Source','klu_l_analyze_given.c'); + fullfile('KLU','Source','klu_l_analyze.c'); + fullfile('KLU','Source','klu_l_defaults.c'); + fullfile('KLU','Source','klu_l_diagnostics.c'); + fullfile('KLU','Source','klu_l_dump.c'); + fullfile('KLU','Source','klu_l_extract.c'); + fullfile('KLU','Source','klu_l_factor.c'); + fullfile('KLU','Source','klu_l_free_numeric.c'); + fullfile('KLU','Source','klu_l_free_symbolic.c'); + fullfile('KLU','Source','klu_l_kernel.c'); + fullfile('KLU','Source','klu_l_memory.c'); + fullfile('KLU','Source','klu_l_refactor.c'); + fullfile('KLU','Source','klu_l_scale.c'); + fullfile('KLU','Source','klu_l_sort.c'); + fullfile('KLU','Source','klu_l_solve.c'); + fullfile('KLU','Source','klu_l_tsolve.c'); + fullfile('KLU','Source','klu_l.c'); + fullfile('AMD','Source','amd_l1.c'); + fullfile('AMD','Source','amd_l2.c'); + fullfile('AMD','Source','amd_l_aat.c'); + fullfile('AMD','Source','amd_l_control.c'); + fullfile('AMD','Source','amd_l_defaults.c'); + fullfile('AMD','Source','amd_l_dump.c'); + fullfile('AMD','Source','amd_l_info.c'); + fullfile('AMD','Source','amd_l_order.c'); + fullfile('AMD','Source','amd_l_postorder.c'); + fullfile('AMD','Source','amd_l_post_tree.c'); + fullfile('AMD','Source','amd_l_preprocess.c'); + fullfile('AMD','Source','amd_l_valid.c'); + fullfile('COLAMD','Source','colamd_l.c'); + fullfile('BTF','Source','btf_l_maxtrans.c'); + fullfile('BTF','Source','btf_l_order.c'); + fullfile('BTF','Source','btf_l_strongcomp.c'); fullfile('SuiteSparse_config','SuiteSparse_config.c'); }; end @@ -211,40 +214,39 @@ function objects_ssparse = getObjectsSSparse(o_suffix) objects_ssparse = { - 'klu_analyze_given.o'; - 'klu_analyze.o'; - 'klu_defaults.o'; - 'klu_diagnostics.o'; - 'klu_dump.o'; - 'klu_extract.o'; - 'klu_factor.o'; - 'klu_free_numeric.o'; - 'klu_free_symbolic.o'; - 'klu_kernel.o'; - 'klu_memory.o'; - 'klu_refactor.o'; - 'klu_scale.o'; - 'klu_sort.o'; - 'klu_solve.o'; - 'klu_tsolve.o'; - 'klu.o'; - 'amd_1.o'; - 'amd_2.o'; - 'amd_aat.o'; - 'amd_control.o'; - 'amd_defaults.o'; - 'amd_dump.o'; - 'amd_global.o'; - 'amd_info.o'; - 'amd_order.o'; - 'amd_post_tree.o'; - 'amd_postorder.o'; - 'amd_preprocess.o'; - 'amd_valid.o'; - 'colamd.o'; - 'btf_maxtrans.o'; - 'btf_order.o'; - 'btf_strongcomp.o'; + 'klu_l_analyze_given.o'; + 'klu_l_analyze.o'; + 'klu_l_defaults.o'; + 'klu_l_diagnostics.o'; + 'klu_l_dump.o'; + 'klu_l_extract.o'; + 'klu_l_factor.o'; + 'klu_l_free_numeric.o'; + 'klu_l_free_symbolic.o'; + 'klu_l_kernel.o'; + 'klu_l_memory.o'; + 'klu_l_refactor.o'; + 'klu_l_scale.o'; + 'klu_l_sort.o'; + 'klu_l_solve.o'; + 'klu_l_tsolve.o'; + 'klu_l.o'; + 'amd_l1.o'; + 'amd_l2.o'; + 'amd_l_aat.o'; + 'amd_l_control.o'; + 'amd_l_defaults.o'; + 'amd_l_dump.o'; + 'amd_l_info.o'; + 'amd_l_order.o'; + 'amd_l_post_tree.o'; + 'amd_l_postorder.o'; + 'amd_l_preprocess.o'; + 'amd_l_valid.o'; + 'colamd_l.o'; + 'btf_l_maxtrans.o'; + 'btf_l_order.o'; + 'btf_l_strongcomp.o'; 'SuiteSparse_config.o'; }; diff --git a/deps/AMICI/matlab/auxiliary/getCommitHash.m b/deps/AMICI/matlab/auxiliary/getCommitHash.m index b33e820e4..c39f33f7a 100644 --- a/deps/AMICI/matlab/auxiliary/getCommitHash.m +++ b/deps/AMICI/matlab/auxiliary/getCommitHash.m @@ -9,7 +9,7 @@ % commit_hash: extracted hash value @type char % branch: branch of the repository @type char % url: employed remote origin @type char - + try fid = fopen(fullfile(wrap_path,'..','.git','FETCH_HEAD')); str = fgetl(fid); @@ -25,11 +25,11 @@ fid = fopen(fullfile(wrap_path,'.git','ORIG_HEAD')); commit_hash = ['dev_' fgetl(fid)]; fclose(fid); - + fid = fopen(fullfile(wrap_path,'.git','HEAD')); branch = strrep(fgetl(fid),'ref: refs/heads/',''); fclose(fid); - + url = 'local'; end catch @@ -38,4 +38,3 @@ url = 'unknown repository'; end end - diff --git a/deps/AMICI/matlab/auxiliary/struct2xml/struct2xml.m b/deps/AMICI/matlab/auxiliary/struct2xml/struct2xml.m index 228acb99f..7fa900ff0 100644 --- a/deps/AMICI/matlab/auxiliary/struct2xml/struct2xml.m +++ b/deps/AMICI/matlab/auxiliary/struct2xml/struct2xml.m @@ -1,5 +1,5 @@ function varargout = struct2xml( s, varargin ) -%Convert a MATLAB structure into a xml file +%Convert a MATLAB structure into a xml file % [ ] = struct2xml( s, file ) % xml = struct2xml( s ) % @@ -26,7 +26,7 @@ % On-screen output functionality added by P. Orth, 01-12-2010 % Multiple space to single space conversion adapted for speed by T. Lohuis, 11-04-2011 % Val2str subfunction bugfix by H. Gsenger, 19-9-2011 - + if (nargin ~= 2) if(nargout ~= 1 || nargin ~= 1) error(['Supported function calls:' sprintf('\n')... @@ -46,17 +46,17 @@ file = [file '.xml']; end end - + if (~isstruct(s)) error([inputname(1) ' is not a structure']); end - + if (length(fieldnames(s)) > 1) error(['Error processing the structure:' sprintf('\n') 'There should be a single field in the main structure.']); end xmlname = fieldnames(s); xmlname = xmlname{1}; - + %substitute special characters xmlname_sc = xmlname; xmlname_sc = strrep(xmlname_sc,'_dash_','-'); @@ -77,35 +77,35 @@ xmlwrite(file,docNode); else varargout{1} = xmlwrite(docNode); - end + end end % ----- Subfunction parseStruct ----- function [] = parseStruct(s,docNode,curNode,pName) - + fnames = fieldnames(s); for i = 1:length(fnames) curfield = fnames{i}; - + %substitute special characters curfield_sc = curfield; curfield_sc = strrep(curfield_sc,'_dash_','-'); curfield_sc = strrep(curfield_sc,'_colon_',':'); curfield_sc = strrep(curfield_sc,'_dot_','.'); - + if (strcmp(curfield,'Attributes')) %Attribute data if (isstruct(s.(curfield))) attr_names = fieldnames(s.Attributes); for a = 1:length(attr_names) cur_attr = attr_names{a}; - + %substitute special characters cur_attr_sc = cur_attr; cur_attr_sc = strrep(cur_attr_sc,'_dash_','-'); cur_attr_sc = strrep(cur_attr_sc,'_colon_',':'); cur_attr_sc = strrep(cur_attr_sc,'_dot_','.'); - + [cur_str,succes] = val2str(s.Attributes.(cur_attr)); if (succes) curNode.setAttribute(cur_attr_sc,cur_str); @@ -161,10 +161,10 @@ %----- Subfunction val2str ----- function [str,succes] = val2str(val) - + succes = true; str = []; - + if (isempty(val)) return; %bugfix from H. Gsenger elseif (ischar(val)) @@ -174,16 +174,16 @@ else succes = false; end - + if (ischar(val)) %add line breaks to all lines except the last (for multiline strings) lines = size(val,1); val = [val char(sprintf('\n')*[ones(lines-1,1);0])]; - - %transpose is required since indexing (i.e., val(nonspace) or val(:)) produces a 1-D vector. + + %transpose is required since indexing (i.e., val(nonspace) or val(:)) produces a 1-D vector. %This should be row based (line based) and not column based. valt = val'; - + remove_multiple_white_spaces = true; if (remove_multiple_white_spaces) %remove multiple white spaces using isspace, suggestion of T. Lohuis diff --git a/deps/AMICI/matlab/auxiliary/structToHDF5Attribute.m b/deps/AMICI/matlab/auxiliary/structToHDF5Attribute.m index a692ed69e..78b2f4096 100644 --- a/deps/AMICI/matlab/auxiliary/structToHDF5Attribute.m +++ b/deps/AMICI/matlab/auxiliary/structToHDF5Attribute.m @@ -39,4 +39,4 @@ end end end -end \ No newline at end of file +end diff --git a/deps/AMICI/matlab/auxiliary/template.m b/deps/AMICI/matlab/auxiliary/template.m index 2d1ff3732..24fd06ff8 100644 --- a/deps/AMICI/matlab/auxiliary/template.m +++ b/deps/AMICI/matlab/auxiliary/template.m @@ -1,13 +1,13 @@ classdef template < handle %TEMPLATE A class to replace strings in template files - + properties % strings in the template to be replaced templateStrings = {}; % strings for replacement templateReplacements = {}; end - + methods function replace(this, infile, outfile) % apply all provided template substitutions to infile and write @@ -22,17 +22,17 @@ function replace(this, infile, outfile) fclose(fin); fclose(fout); end - + function add(this, templateStr, replacementStr) % add a new template string and replacement nextIdx = numel(this.templateStrings); this.templateStrings{nextIdx + 1} = templateStr; this.templateReplacements{nextIdx + 1} = replacementStr; end - + function s = replaceStr(this, s) - % apply all provided template substitutions to s - + % apply all provided template substitutions to s + % do not use cellfun to guarantee order of replacements for n = 1:numel(this.templateStrings) s = strrep(s, this.templateStrings(n), this.templateReplacements(n)); @@ -40,6 +40,5 @@ function add(this, templateStr, replacementStr) end end end - -end +end diff --git a/deps/AMICI/matlab/auxiliary/xml2struct/xml2struct.m b/deps/AMICI/matlab/auxiliary/xml2struct/xml2struct.m index dcd85a207..a7b93058f 100644 --- a/deps/AMICI/matlab/auxiliary/xml2struct/xml2struct.m +++ b/deps/AMICI/matlab/auxiliary/xml2struct/xml2struct.m @@ -32,7 +32,7 @@ help xml2struct return end - + if isa(file, 'org.apache.xerces.dom.DeferredDocumentImpl') || isa(file, 'org.apache.xerces.dom.DeferredElementImpl') % input is a java xml object xDoc = file; @@ -44,7 +44,7 @@ if (isempty(strfind(file,'.xml'))) file = [file '.xml']; end - + if (exist(file,'file') == 0) error(['The file ' file ' could not be found']); end @@ -52,10 +52,10 @@ %read the xml file xDoc = xmlread(file); end - + %parse xDoc into a MATLAB structure s = parseChildNodes(xDoc); - + end % ----- Subfunction parseChildNodes ----- @@ -70,7 +70,7 @@ for count = 1:numChildNodes theChild = item(childNodes,count-1); [text,name,attr,childs,textflag] = getNodeData(theChild); - + if (~strcmp(name,'#text') && ~strcmp(name,'#comment') && ~strcmp(name,'#cdata_dash_section')) %XML allows the same elements to be defined multiple times, %put each in a different cell @@ -83,19 +83,19 @@ %add new element children.(name){index} = childs; if(~isempty(fieldnames(text))) - children.(name){index} = text; + children.(name){index} = text; end - if(~isempty(attr)) - children.(name){index}.('Attributes') = attr; + if(~isempty(attr)) + children.(name){index}.('Attributes') = attr; end else %add previously unknown (new) element to the structure children.(name) = childs; if(~isempty(text) && ~isempty(fieldnames(text))) - children.(name) = text; + children.(name) = text; end - if(~isempty(attr)) - children.(name).('Attributes') = attr; + if(~isempty(attr)) + children.(name).('Attributes') = attr; end end else @@ -105,25 +105,25 @@ elseif (strcmp(name, '#comment')) ptextflag = 'Comment'; end - - %this is the text in an element (i.e., the parentNode) + + %this is the text in an element (i.e., the parentNode) if (~isempty(regexprep(text.(textflag),'[\s]*',''))) if (~isfield(ptext,ptextflag) || isempty(ptext.(ptextflag))) ptext.(ptextflag) = text.(textflag); else %what to do when element data is as follows: %Text More text - + %put the text in different cells: % if (~iscell(ptext)) ptext = {ptext}; end % ptext{length(ptext)+1} = text; - + %just append the text ptext.(ptextflag) = [ptext.(ptextflag) text.(textflag)]; end end end - + end end end @@ -131,7 +131,7 @@ % ----- Subfunction getNodeData ----- function [text,name,attr,childs,textflag] = getNodeData(theNode) % Create structure of node info. - + %make sure name is allowed as structure name name = toCharArray(getNodeName(theNode))'; name = strrep(name, '-', '_dash_'); @@ -139,13 +139,13 @@ name = strrep(name, '.', '_dot_'); attr = parseAttributes(theNode); - if (isempty(fieldnames(attr))) - attr = []; + if (isempty(fieldnames(attr))) + attr = []; end - + %parse child nodes [childs,text,textflag] = parseChildNodes(theNode); - + if (isempty(fieldnames(childs)) && isempty(fieldnames(text))) %get the data of any childless nodes % faster than if any(strcmp(methods(theNode), 'getData')) @@ -153,7 +153,7 @@ % faster than text = char(getData(theNode)); text.(textflag) = toCharArray(getTextContent(theNode))'; end - + end % ----- Subfunction parseAttributes ----- @@ -172,7 +172,7 @@ %Suggestion of Adrian Wanner str = toCharArray(toString(item(theAttributes,count-1)))'; - k = strfind(str,'='); + k = strfind(str,'='); attr_name = str(1:(k(1)-1)); attr_name = strrep(attr_name, '-', '_dash_'); attr_name = strrep(attr_name, ':', '_colon_'); @@ -180,4 +180,4 @@ attributes.(attr_name) = str((k(1)+2):(end-1)); end end -end \ No newline at end of file +end diff --git a/deps/AMICI/matlab/examples/example_adjoint/example_adjoint.m b/deps/AMICI/matlab/examples/example_adjoint/example_adjoint.m index fc0584b2d..3e787c211 100644 --- a/deps/AMICI/matlab/examples/example_adjoint/example_adjoint.m +++ b/deps/AMICI/matlab/examples/example_adjoint/example_adjoint.m @@ -42,17 +42,17 @@ function example_adjoint() errorbar(t,D.Y,D.Sigma_Y) hold on % plot(t,sol.y) - + xlabel('time t') ylabel('observable') title(['log-likelihood: ' num2str(sol.llh) ]) - + y = (p(2)*t + p(3)).*(t<2) + ( (2*p(2)+p(3)-p(2)/p(1))*exp(-p(1)*(t-2))+p(2)/p(1) ).*(t>=2); - - + + tfine = linspace(0,4,100001); xfine = (p(2)*tfine + 1).*(tfine<2) + ( (2*p(2)+p(3)-p(2)/p(1))*exp(-p(1)*(tfine-2))+p(2)/p(1) ).*(tfine>=2); - + mu = zeros(1,length(tfine)); for it = 1:length(t) if(t(it)<=2) @@ -69,9 +69,9 @@ function example_adjoint() ylabel('adjoint') xlabel('time t') xlim([min(t)-0.5,max(t)+0.5]) - + subplot(3,1,3) - + plot(fliplr(tfine),-cumsum(fliplr(-mu.*xfine.*(tfine>2)))*p(1)*log(10)*(t(end)/numel(tfine))) hold on plot(fliplr(tfine),-cumsum(fliplr(mu))*p(2)*log(10)*(t(end)/numel(tfine))) @@ -79,13 +79,13 @@ function example_adjoint() xlim([min(t)-0.5,max(t)+0.5]) ylabel('integral') xlabel('time t') - + legend('p1','p2','p3') - + grad(1,1) = -trapz(tfine,-mu.*xfine.*(tfine>2))*p(1)*log(10); grad(2,1) = -trapz(tfine,mu)*p(2)*log(10); grad(3,1) = -mu(1)*p(3)*log(10); - + plot(zeros(3,1),grad,'ko') end @@ -123,7 +123,7 @@ function example_adjoint() xlabel('analytic absolute value of gradient element') ylabel('computed absolute value of gradient element') set(gcf,'Position',[100 300 1200 500]) - + drawnow end diff --git a/deps/AMICI/matlab/examples/example_adjoint/model_adjoint_syms.m b/deps/AMICI/matlab/examples/example_adjoint/model_adjoint_syms.m index 8473fcc6d..0eddd4d12 100644 --- a/deps/AMICI/matlab/examples/example_adjoint/model_adjoint_syms.m +++ b/deps/AMICI/matlab/examples/example_adjoint/model_adjoint_syms.m @@ -13,9 +13,9 @@ % PARAMETERS ( for these sensitivities will be computed ) % create parameter syms -syms p1 p2 p3 +syms p1 p2 p3 -% create parameter vector +% create parameter vector model.sym.p = [p1 p2 p3]; @@ -48,4 +48,4 @@ model.sym.y(1) = x1; -end \ No newline at end of file +end diff --git a/deps/AMICI/matlab/examples/example_adjoint_hessian/example_adjoint_hessian.m b/deps/AMICI/matlab/examples/example_adjoint_hessian/example_adjoint_hessian.m index 6133dc656..91aa105bd 100644 --- a/deps/AMICI/matlab/examples/example_adjoint_hessian/example_adjoint_hessian.m +++ b/deps/AMICI/matlab/examples/example_adjoint_hessian/example_adjoint_hessian.m @@ -91,4 +91,4 @@ success=0; end -end \ No newline at end of file +end diff --git a/deps/AMICI/matlab/examples/example_adjoint_hessian/model_adjoint_hessian_syms.m b/deps/AMICI/matlab/examples/example_adjoint_hessian/model_adjoint_hessian_syms.m index 05b06bd4d..3648341db 100644 --- a/deps/AMICI/matlab/examples/example_adjoint_hessian/model_adjoint_hessian_syms.m +++ b/deps/AMICI/matlab/examples/example_adjoint_hessian/model_adjoint_hessian_syms.m @@ -13,9 +13,9 @@ % PARAMETERS ( for these sensitivities will be computed ) % create parameter syms -syms p1 p2 p3 +syms p1 p2 p3 -% create parameter vector +% create parameter vector model.sym.p = [p1 p2 p3]; @@ -48,4 +48,4 @@ model.sym.y(1) = x1; -end \ No newline at end of file +end diff --git a/deps/AMICI/matlab/examples/example_calvetti/example_calvetti.m b/deps/AMICI/matlab/examples/example_calvetti/example_calvetti.m index 334f977be..e78667716 100755 --- a/deps/AMICI/matlab/examples/example_calvetti/example_calvetti.m +++ b/deps/AMICI/matlab/examples/example_calvetti/example_calvetti.m @@ -26,13 +26,13 @@ function example_calvetti() % ODE15S y0 = [k(1); k(3); k(5); 1; 1; 1;]; -M = [1 0 0 0 0 0 +M = [1 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]; - + function [xdot] = dae_system(t,x,p,k,it) if it<3 h0 = 0; @@ -95,7 +95,7 @@ function example_calvetti() legend('error x1','error x2','error x3','error x4','error x5','error x6','Location','NorthEastOutside') legend boxoff ylabel('x') - + set(gcf,'Position',[100 300 1200 500]) end end diff --git a/deps/AMICI/matlab/examples/example_calvetti/model_calvetti_syms.m b/deps/AMICI/matlab/examples/example_calvetti/model_calvetti_syms.m index 5fc3661eb..697e4c134 100755 --- a/deps/AMICI/matlab/examples/example_calvetti/model_calvetti_syms.m +++ b/deps/AMICI/matlab/examples/example_calvetti/model_calvetti_syms.m @@ -6,28 +6,28 @@ %% % STATES -% create state syms +% create state syms syms V1 V2 V3 f1 f2 f3 % create state vector model.sym.x = [V1, V2, V3, f1, f2, f3]; %% % PARAMETERS ( for these sensitivities will be computed ) -% create parameter syms -% create parameter vector +% create parameter syms +% create parameter vector model.sym.p = [ ]; -%% +%% % CONSTANTS ( for these no sensitivities will be computed ) % this part is optional and can be ommited % create parameter syms syms V1ss R1ss V2ss R2ss V3ss R3ss -% create parameter vector +% create parameter vector model.sym.k = [V1ss, R1ss, V2ss, R2ss, V3ss, R3ss]; %% % SYSTEM EQUATIONS % create symbolic variable for time -syms t f0 +syms t f0 model.sym.xdot = sym(zeros(size(model.sym.x))); p1=1; p2=1-R1ss; @@ -72,4 +72,3 @@ model.sym.y(5)=f1; model.sym.y(6)=f2; end - diff --git a/deps/AMICI/matlab/examples/example_dirac/example_dirac.m b/deps/AMICI/matlab/examples/example_dirac/example_dirac.m index 937a59002..ec1dc3832 100644 --- a/deps/AMICI/matlab/examples/example_dirac/example_dirac.m +++ b/deps/AMICI/matlab/examples/example_dirac/example_dirac.m @@ -56,7 +56,7 @@ function example_dirac() hold on plot(t,X_ode45(:,ix),'--','Color',c_x(ix,:)) end - + legend('x1','x1_{ode45}','x2','x2_{ode15s}','Location','NorthEastOutside') legend boxoff xlabel('time t') @@ -68,7 +68,7 @@ function example_dirac() ylim([1e-10,1e0]) legend('error x1','error x2','Location','NorthEastOutside') legend boxoff - + subplot(2,2,3) plot(t,sol.y,'.-','Color',c_x(1,:)) hold on @@ -78,7 +78,7 @@ function example_dirac() xlabel('time t') ylabel('y') box on - + subplot(2,2,4) plot(t,abs(sol.y-X_ode45(:,2)),'--') set(gca,'YScale','log') @@ -130,7 +130,7 @@ function example_dirac() xlabel('time t') ylabel('x') box on - + subplot(length(options.sens_ind),2,ip*2) plot(t,abs(sol.sx(:,:,ip)-sx_fd(:,:,ip)),'r--') legend('error x1','error x2','Location','NorthEastOutside') @@ -143,7 +143,7 @@ function example_dirac() box on end set(gcf,'Position',[100 300 1200 500]) - + figure for ip = 1:length(options.sens_ind) subplot(length(options.sens_ind),2,ip*2-1) @@ -159,7 +159,7 @@ function example_dirac() xlabel('time t') ylabel('y') box on - + subplot(length(options.sens_ind),2,ip*2) plot(t,abs(sol.sy(:,:,ip)-sy_fd(:,:,ip)),'r--') legend('error y1','Location','NorthEastOutside') @@ -172,8 +172,8 @@ function example_dirac() box on end set(gcf,'Position',[100 300 1200 500]) - + drawnow end -end \ No newline at end of file +end diff --git a/deps/AMICI/matlab/examples/example_dirac/model_dirac_syms.m b/deps/AMICI/matlab/examples/example_dirac/model_dirac_syms.m index 6c89ab26e..3c0c9dd80 100644 --- a/deps/AMICI/matlab/examples/example_dirac/model_dirac_syms.m +++ b/deps/AMICI/matlab/examples/example_dirac/model_dirac_syms.m @@ -15,12 +15,12 @@ % create parameter syms syms p1 p2 p3 p4 -% create parameter vector +% create parameter vector model.sym.p = [p1,p2,p3,p4]; % set the parametrisation of the problem options are 'log', 'log10' and % 'lin' (default). -model.param = 'log10'; +model.param = 'log10'; %% % SYSTEM EQUATIONS @@ -49,4 +49,4 @@ model.sym.y = sym(zeros(1,1)); model.sym.y(1) = x2; -end \ No newline at end of file +end diff --git a/deps/AMICI/matlab/examples/example_dirac_adjoint/example_dirac_adjoint.m b/deps/AMICI/matlab/examples/example_dirac_adjoint/example_dirac_adjoint.m index f5f2b23da..60c1d327f 100644 --- a/deps/AMICI/matlab/examples/example_dirac_adjoint/example_dirac_adjoint.m +++ b/deps/AMICI/matlab/examples/example_dirac_adjoint/example_dirac_adjoint.m @@ -79,7 +79,7 @@ function example_dirac_adjoint() xlabel('adjoint sensitivity absolute value of gradient element') ylabel('computed absolute value of gradient element') set(gcf,'Position',[100 300 1200 500]) - + drawnow end -end \ No newline at end of file +end diff --git a/deps/AMICI/matlab/examples/example_dirac_adjoint/example_model_5_paper.m b/deps/AMICI/matlab/examples/example_dirac_adjoint/example_model_5_paper.m index d3c0758f6..93aab4b31 100644 --- a/deps/AMICI/matlab/examples/example_dirac_adjoint/example_model_5_paper.m +++ b/deps/AMICI/matlab/examples/example_dirac_adjoint/example_model_5_paper.m @@ -63,7 +63,7 @@ syms xB1(t) xB2(t) eqn1B = diff(xB1) == p(1)*xB1 - p(3)*xB2; eqn3B = diff(xB2) == p(4)*xB2; -syms sigma my +syms sigma my x = sym('x',[2,1]); J = -0.5*((x(2) - my)/sigma)^2; dJdx = jacobian(J,x); @@ -241,9 +241,3 @@ syms xB1(t) xB2(t) xlim([-0.1,4.1]) set(gcf,'PaperPositionMode','auto','Position',[100 300 300 200]) print('-depsc','-r300',['sJ_asa']) - - - - - - diff --git a/deps/AMICI/matlab/examples/example_dirac_adjoint/model_dirac_adjoint_syms.m b/deps/AMICI/matlab/examples/example_dirac_adjoint/model_dirac_adjoint_syms.m index 488d97b90..ce2d21f20 100644 --- a/deps/AMICI/matlab/examples/example_dirac_adjoint/model_dirac_adjoint_syms.m +++ b/deps/AMICI/matlab/examples/example_dirac_adjoint/model_dirac_adjoint_syms.m @@ -16,7 +16,7 @@ % create parameter syms syms p1 p2 p3 p4 -% create parameter vector +% create parameter vector model.sym.p = [p1,p2,p3,p4]; % set the parametrisation of the problem options are 'log', 'log10' and @@ -51,4 +51,4 @@ model.sym.y(1) = x2; -end \ No newline at end of file +end diff --git a/deps/AMICI/matlab/examples/example_dirac_adjoint_hessVecProd/example_dirac_adjoint_hessVecProd.m b/deps/AMICI/matlab/examples/example_dirac_adjoint_hessVecProd/example_dirac_adjoint_hessVecProd.m index 25cbd23ff..4c4a76c9b 100644 --- a/deps/AMICI/matlab/examples/example_dirac_adjoint_hessVecProd/example_dirac_adjoint_hessVecProd.m +++ b/deps/AMICI/matlab/examples/example_dirac_adjoint_hessVecProd/example_dirac_adjoint_hessVecProd.m @@ -56,4 +56,3 @@ fprintf('Finite differences, HVP: \n'); disp(FD_HVP); - diff --git a/deps/AMICI/matlab/examples/example_dirac_adjoint_hessVecProd/model_dirac_adjoint_hessVecProd_syms.m b/deps/AMICI/matlab/examples/example_dirac_adjoint_hessVecProd/model_dirac_adjoint_hessVecProd_syms.m index b254bfcc5..8f144cb4c 100644 --- a/deps/AMICI/matlab/examples/example_dirac_adjoint_hessVecProd/model_dirac_adjoint_hessVecProd_syms.m +++ b/deps/AMICI/matlab/examples/example_dirac_adjoint_hessVecProd/model_dirac_adjoint_hessVecProd_syms.m @@ -16,7 +16,7 @@ % create parameter syms syms p1 p2 p3 p4 -% create parameter vector +% create parameter vector model.sym.p = [p1,p2,p3,p4]; % set the parametrisation of the problem options are 'log', 'log10' and @@ -51,4 +51,4 @@ model.sym.y(1) = x2; -end \ No newline at end of file +end diff --git a/deps/AMICI/matlab/examples/example_dirac_secondorder/example_dirac_secondorder.m b/deps/AMICI/matlab/examples/example_dirac_secondorder/example_dirac_secondorder.m index cba5ba56c..fe9392ead 100644 --- a/deps/AMICI/matlab/examples/example_dirac_secondorder/example_dirac_secondorder.m +++ b/deps/AMICI/matlab/examples/example_dirac_secondorder/example_dirac_secondorder.m @@ -87,7 +87,7 @@ function example_dirac_secondorder() end end set(gcf,'Position',[100 300 1200 500]) - + drawnow end diff --git a/deps/AMICI/matlab/examples/example_dirac_secondorder/model_dirac_secondorder_syms.m b/deps/AMICI/matlab/examples/example_dirac_secondorder/model_dirac_secondorder_syms.m index 657f2216a..c2db18d77 100644 --- a/deps/AMICI/matlab/examples/example_dirac_secondorder/model_dirac_secondorder_syms.m +++ b/deps/AMICI/matlab/examples/example_dirac_secondorder/model_dirac_secondorder_syms.m @@ -1,5 +1,5 @@ function [model] = model_dirac_secondorder_syms() - + %% % STATES @@ -15,7 +15,7 @@ % create parameter syms syms p1 p2 p3 p4 -% create parameter vector +% create parameter vector model.sym.p = [p1,p2,p3,p4]; % set the parametrisation of the problem options are 'log', 'log10' and @@ -49,4 +49,4 @@ model.sym.y = sym(zeros(1,1)); model.sym.y(1) = x2; -end \ No newline at end of file +end diff --git a/deps/AMICI/matlab/examples/example_dirac_secondorder_vectmult/example_dirac_secondorder_vectmult.m b/deps/AMICI/matlab/examples/example_dirac_secondorder_vectmult/example_dirac_secondorder_vectmult.m index 327e268fa..e7f4c9e3b 100644 --- a/deps/AMICI/matlab/examples/example_dirac_secondorder_vectmult/example_dirac_secondorder_vectmult.m +++ b/deps/AMICI/matlab/examples/example_dirac_secondorder_vectmult/example_dirac_secondorder_vectmult.m @@ -64,7 +64,7 @@ function example_dirac_secondorder_vectmult() xlabel('time t') ylabel('x') box on - + subplot(4,2,ip*2) plot(t,abs(sol.s2x(:,:,ip)-s2x_fd(:,:,ip)),'r--') legend('error x1','error x2','Location','NorthEastOutside') @@ -77,7 +77,7 @@ function example_dirac_secondorder_vectmult() box on end set(gcf,'Position',[100 300 1200 500]) - + figure for ip = 1:4 subplot(4,2,ip*2-1) @@ -93,7 +93,7 @@ function example_dirac_secondorder_vectmult() xlabel('time t') ylabel('y') box on - + subplot(4,2,ip*2) plot(t,abs(sol.s2y(:,:,ip)-s2y_fd(:,:,ip)),'r--') legend('error y1','Location','NorthEastOutside') @@ -106,8 +106,8 @@ function example_dirac_secondorder_vectmult() box on end set(gcf,'Position',[100 300 1200 500]) - + drawnow end -end \ No newline at end of file +end diff --git a/deps/AMICI/matlab/examples/example_dirac_secondorder_vectmult/model_dirac_secondorder_vectmult_syms.m b/deps/AMICI/matlab/examples/example_dirac_secondorder_vectmult/model_dirac_secondorder_vectmult_syms.m index cc366495f..5f217f4da 100644 --- a/deps/AMICI/matlab/examples/example_dirac_secondorder_vectmult/model_dirac_secondorder_vectmult_syms.m +++ b/deps/AMICI/matlab/examples/example_dirac_secondorder_vectmult/model_dirac_secondorder_vectmult_syms.m @@ -1,5 +1,5 @@ function [model] = model_dirac_secondorder_vectmult_syms() - + %% % STATES @@ -15,12 +15,12 @@ % create parameter syms syms p1 p2 p3 p4 -% create parameter vector +% create parameter vector model.sym.p = [p1,p2,p3,p4]; % set the parametrisation of the problem options are 'log', 'log10' and % 'lin' (default). -model.param = 'log10'; +model.param = 'log10'; %% % SYSTEM EQUATIONS @@ -49,4 +49,4 @@ model.sym.y = sym(zeros(1,1)); model.sym.y(1) = x2; -end \ No newline at end of file +end diff --git a/deps/AMICI/matlab/examples/example_events/example_events.m b/deps/AMICI/matlab/examples/example_events/example_events.m index 1f5e2a47f..8dfcdbb21 100644 --- a/deps/AMICI/matlab/examples/example_events/example_events.m +++ b/deps/AMICI/matlab/examples/example_events/example_events.m @@ -64,7 +64,7 @@ function example_events() legend('error x1','error x2','error x3','Location','NorthEastOutside') legend boxoff ylabel('x') - + subplot(2,2,3) plot(t,sol.y,'.-','Color',c_x(1,:)) hold on @@ -74,7 +74,7 @@ function example_events() xlabel('time t') ylabel('y') box on - + subplot(2,2,4) plot(t,abs(sol.y-p(4)*sum(X_ode15s,2)),'--') set(gca,'YScale','log') @@ -83,7 +83,7 @@ function example_events() xlabel('time t') ylabel('y') box on - + set(gcf,'Position',[100 300 1200 500]) end @@ -125,7 +125,7 @@ function example_events() xlabel('time t') ylabel('sx') box on - + subplot(4,2,ip*2) plot(t,abs(sol.sx(:,:,ip)-sx_fd(:,:,ip)),'--') legend('error sx1','error sx2','error sx3','Location','NorthEastOutside') @@ -137,7 +137,7 @@ function example_events() box on end set(gcf,'Position',[100 300 1200 500]) - + figure for ip = 1:4 subplot(4,2,ip*2-1) @@ -152,7 +152,7 @@ function example_events() xlabel('time t') ylabel('sy') box on - + subplot(4,2,ip*2) plot(t,abs(sol.sy(:,:,ip)-sy_fd(:,:,ip)),'--') legend('error sy1','Location','NorthEastOutside') @@ -164,7 +164,7 @@ function example_events() box on end set(gcf,'Position',[100 300 1200 500]) - + figure for ip = 1:4 subplot(4,2,2*ip-1) @@ -177,7 +177,7 @@ function example_events() xlabel('event #') ylabel('sz') box on - + subplot(4,2,2*ip) bar(1:D.ne,sol.sz(1:D.ne,:,ip)-sz_fd(1:D.ne,:,ip),0.8) hold on @@ -189,7 +189,7 @@ function example_events() box on end set(gcf,'Position',[100 300 1200 500]) - + drawnow end -end \ No newline at end of file +end diff --git a/deps/AMICI/matlab/examples/example_events/model_events_syms.m b/deps/AMICI/matlab/examples/example_events/model_events_syms.m index f98298c8d..7bf9ac6d3 100644 --- a/deps/AMICI/matlab/examples/example_events/model_events_syms.m +++ b/deps/AMICI/matlab/examples/example_events/model_events_syms.m @@ -22,12 +22,12 @@ % create parameter syms syms p1 p2 p3 p4 -% create parameter vector +% create parameter vector model.sym.p = [p1,p2,p3,p4]; % set the parametrisation of the problem options are 'log', 'log10' and % 'lin' (default). -model.param = 'log10'; +model.param = 'log10'; %% % CONSTANTS ( for these no sensitivities will be computed ) @@ -36,7 +36,7 @@ % create parameter syms syms k1 k2 k3 k4 -% create parameter vector +% create parameter vector model.sym.k = [k1 k2 k3 k4]; %% @@ -79,4 +79,4 @@ model.event(1) = amievent(am_ge(x2,x3),0,t); model.event(2) = amievent(am_ge(x1,x3),0,t); -end \ No newline at end of file +end diff --git a/deps/AMICI/matlab/examples/example_jakstat_adjoint/example_jakstat_adjoint.m b/deps/AMICI/matlab/examples/example_jakstat_adjoint/example_jakstat_adjoint.m index 55cd78dbe..45a900262 100644 --- a/deps/AMICI/matlab/examples/example_jakstat_adjoint/example_jakstat_adjoint.m +++ b/deps/AMICI/matlab/examples/example_jakstat_adjoint/example_jakstat_adjoint.m @@ -1,17 +1,17 @@ function example_jakstat_adjoint() - + % compile the model [exdir,~,~]=fileparts(which('example_jakstat_adjoint.m')); amiwrap('model_jakstat_adjoint','model_jakstat_adjoint_syms',exdir,1) - + num = xlsread(fullfile(exdir,'pnas_data_original.xls')); - + D.t = num(:,1); D.condition= [1.4,0.45]; D.Y = num(:,[2,4,6]); D.Sigma_Y = NaN(size(D.Y)); D = amidata(D); - + xi = [0.60 3 -0.95 @@ -29,10 +29,10 @@ function example_jakstat_adjoint() -0.5 0 -0.5]; - + options.sensi = 0; sol = simulate_model_jakstat_adjoint([],xi,[],D,options); - + if(usejava('jvm')) figure for iy = 1:3 @@ -54,7 +54,7 @@ function example_jakstat_adjoint() end set(gcf,'Position',[100 300 1200 500]) end - + % generate new xi_rand = xi + 0.1; options.sensi = 2; @@ -62,7 +62,7 @@ function example_jakstat_adjoint() sol = simulate_model_jakstat_adjoint([],xi_rand,[],D,options); options.sensi_meth = 'forward'; solf = simulate_model_jakstat_adjoint([],xi_rand,[],D,options); - + options.sensi = 1; eps = 1e-4; fd_grad = NaN(length(xi),1); @@ -73,7 +73,7 @@ function example_jakstat_adjoint() fd_grad(ip) = (psol.llh-sol.llh)/eps; fd_hess(:,ip) = (psol.sllh-sol.sllh)/eps; end - + if(usejava('jvm')) figure subplot(1,2,1) @@ -91,7 +91,7 @@ function example_jakstat_adjoint() axis square xlabel('absolute value forward sensitivity gradient entries') ylabel('absolute value gradient entries') - + subplot(1,2,2) plot(abs(solf.s2llh(:)),abs(fd_hess(:)),'rx') hold on @@ -107,11 +107,11 @@ function example_jakstat_adjoint() axis square xlabel('absolute value forward sensitivity hessian entries') ylabel('absolute value hessian entries') - + set(gcf,'Position',[100 300 1200 500]) end - - + + drawnow - + end diff --git a/deps/AMICI/matlab/examples/example_jakstat_adjoint/model_jakstat_adjoint_syms.m b/deps/AMICI/matlab/examples/example_jakstat_adjoint/model_jakstat_adjoint_syms.m index d1426d53b..a1e937e16 100644 --- a/deps/AMICI/matlab/examples/example_jakstat_adjoint/model_jakstat_adjoint_syms.m +++ b/deps/AMICI/matlab/examples/example_jakstat_adjoint/model_jakstat_adjoint_syms.m @@ -1,36 +1,36 @@ function [model] = model_jakstat_syms() - + %% % STATES - + syms STAT pSTAT pSTAT_pSTAT npSTAT_npSTAT nSTAT1 nSTAT2 nSTAT3 nSTAT4 nSTAT5 - + model.sym.x = [ STAT, pSTAT, pSTAT_pSTAT, npSTAT_npSTAT, nSTAT1, nSTAT2, nSTAT3, nSTAT4, nSTAT5 ... ]; %% % PARAMETERS - + syms p1 p2 p3 p4 init_STAT Omega_cyt Omega_nuc sp1 sp2 sp3 sp4 sp5 offset_tSTAT offset_pSTAT scale_tSTAT scale_pSTAT sigma_pSTAT sigma_tSTAT sigma_pEpoR - + model.sym.p = [p1,p2,p3,p4,init_STAT,sp1,sp2,sp3,sp4,sp5,offset_tSTAT,offset_pSTAT,scale_tSTAT,scale_pSTAT,sigma_pSTAT,sigma_tSTAT,sigma_pEpoR]; - + model.param = 'log10'; - + model.sym.k = [Omega_cyt,Omega_nuc]; - + %% % INPUT syms t % u(1) = spline_pos5(t, 0.0, sp1, 5.0, sp2, 10.0, sp3, 20.0, sp4, 60.0, sp5, 0, 0.0); u(1) = am_spline_pos(t, 5, 0.0, sp1, 5.0, sp2, 10.0, sp3, 20.0, sp4, 60.0, sp5, 0, 0.0); - + %% % SYSTEM EQUATIONS - + model.sym.xdot = sym(zeros(size(model.sym.x))); - + model.sym.xdot(1) = (Omega_nuc*p4*nSTAT5 - Omega_cyt*STAT*p1*u(1))/Omega_cyt; model.sym.xdot(2) = STAT*p1*u(1) - 2*p2*pSTAT^2; model.sym.xdot(3) = p2*pSTAT^2 - p3*pSTAT_pSTAT; @@ -40,30 +40,30 @@ model.sym.xdot(7) = p4*(nSTAT2 - nSTAT3); model.sym.xdot(8) = p4*(nSTAT3 - nSTAT4); model.sym.xdot(9) = p4*(nSTAT4 - nSTAT5); - + %% % INITIAL CONDITIONS - + model.sym.x0 = sym(zeros(size(model.sym.x))); - + model.sym.x0(1) = init_STAT; - + %% % OBSERVABLES - + model.sym.y = sym(zeros(3,1)); - + model.sym.y(1) = offset_pSTAT + scale_pSTAT/init_STAT*(pSTAT + 2*pSTAT_pSTAT); model.sym.y(2) = offset_tSTAT + scale_tSTAT/init_STAT*(STAT + pSTAT + 2*(pSTAT_pSTAT)); model.sym.y(3) = u(1); - + %% % SIGMA - + model.sym.sigma_y = sym(size(model.sym.y)); - + model.sym.sigma_y(1) = sigma_pSTAT; model.sym.sigma_y(2) = sigma_tSTAT; model.sym.sigma_y(3) = sigma_pEpoR; - -end \ No newline at end of file + +end diff --git a/deps/AMICI/matlab/examples/example_jakstat_adjoint_hvp/example_jakstat_adjoint_hvp.m b/deps/AMICI/matlab/examples/example_jakstat_adjoint_hvp/example_jakstat_adjoint_hvp.m index 2654e7bd9..2865fd7b2 100644 --- a/deps/AMICI/matlab/examples/example_jakstat_adjoint_hvp/example_jakstat_adjoint_hvp.m +++ b/deps/AMICI/matlab/examples/example_jakstat_adjoint_hvp/example_jakstat_adjoint_hvp.m @@ -1,16 +1,16 @@ function example_jakstat_adjoint_hvp() - + % compile the model [exdir,~,~]=fileparts(which('example_jakstat_adjoint_hvp.m')); amiwrap('model_jakstat_adjoint_hvp','model_jakstat_adjoint_hvp_syms',exdir,2) num = xlsread(fullfile(exdir,'pnas_data_original.xls')); - + D.t = num(:,1); D.condition= [1.4,0.45]; D.Y = num(:,[2,4,6]); D.Sigma_Y = NaN(size(D.Y)); D = amidata(D); - + xi = [0.60 3 -0.95 @@ -28,26 +28,26 @@ function example_jakstat_adjoint_hvp() -0.5 0 -0.5]; - - + + % generate new xi_rand = xi - 0.1; options.atol = 1e-12; options.rtol = 1e-12; - + % Get time for simulation tic; options.sensi = 0; sol0 = simulate_model_jakstat_adjoint_hvp([],xi_rand,[],D,options); t0 = toc; - + % Get time for usual evaluation tic; options.sensi = 1; options.sensi_meth = 'adjoint'; sol1 = simulate_model_jakstat_adjoint_hvp([],xi_rand,[],D,options); t1 = toc; - + % Get time for Finite Differences hvp = zeros(17,1); hvp_f = zeros(17,1); @@ -63,7 +63,7 @@ function example_jakstat_adjoint_hvp() hvp_f = hvp_f + (solp.sllh - sol2.sllh) / (delta); hvp_b = hvp_b + (sol2.sllh - solm.sllh) / (delta); t2 = toc; - + % Get time for Second order adjoints hvpasa = zeros(17,1); tic; @@ -81,7 +81,7 @@ function example_jakstat_adjoint_hvp() if(usejava('jvm')) figure(); - + subplot(1,2,1); bar([abs((sol.s2llh-hvp)./sol.s2llh),abs((sol.s2llh-hvp_f)./sol.s2llh),abs((sol.s2llh-hvp_b)./sol.s2llh),abs((sol.s2llh-solf.s2llh)./sol.s2llh)]) hold on @@ -95,13 +95,13 @@ function example_jakstat_adjoint_hvp() legend('FD_{central}','FD_{forward}','FD_{backward}','forward sensitivities','Orientation','horizontal') legend boxoff set(gcf,'Position',[100 300 1200 500]) - + subplot(1,2,2); hold on; bar([t0,t1,t2,t3]); xlabel('runtime [s]') set(gca,'XTick',1:4,'XTickLabel',{'ODE Integration', 'Gradient computation (ASA)', 'HVP from FD via 1st order ASA', 'HVP via 2nd order ASA'},'XTickLabelRotation',20); - + box on; hold off; end diff --git a/deps/AMICI/matlab/examples/example_jakstat_adjoint_hvp/model_jakstat_adjoint_hvp_syms.m b/deps/AMICI/matlab/examples/example_jakstat_adjoint_hvp/model_jakstat_adjoint_hvp_syms.m index b14eaacdb..ab145a047 100644 --- a/deps/AMICI/matlab/examples/example_jakstat_adjoint_hvp/model_jakstat_adjoint_hvp_syms.m +++ b/deps/AMICI/matlab/examples/example_jakstat_adjoint_hvp/model_jakstat_adjoint_hvp_syms.m @@ -1,36 +1,36 @@ function [model] = model_jakstat_adjoint_hvp_syms() - + %% % STATES - + syms STAT pSTAT pSTAT_pSTAT npSTAT_npSTAT nSTAT1 nSTAT2 nSTAT3 nSTAT4 nSTAT5 - + model.sym.x = [ STAT, pSTAT, pSTAT_pSTAT, npSTAT_npSTAT, nSTAT1, nSTAT2, nSTAT3, nSTAT4, nSTAT5 ... ]; - + %% % PARAMETERS - + syms p1 p2 p3 p4 init_STAT Omega_cyt Omega_nuc sp1 sp2 sp3 sp4 sp5 offset_tSTAT offset_pSTAT scale_tSTAT scale_pSTAT sigma_pSTAT sigma_tSTAT sigma_pEpoR - + model.sym.p = [p1,p2,p3,p4,init_STAT,sp1,sp2,sp3,sp4,sp5,offset_tSTAT,offset_pSTAT,scale_tSTAT,scale_pSTAT,sigma_pSTAT,sigma_tSTAT,sigma_pEpoR]; - + model.param = 'log10'; - + model.sym.k = [Omega_cyt,Omega_nuc]; - + %% % INPUT syms t u(1) = am_spline_pos(t, 5, 0, sp1, 5.0, sp2, 10.0, sp3, 20.0, sp4, 60.0, sp5, 0, 0); % u(1) = spline_pos5(t, 0, sp1, 5.0, sp2, 10.0, sp3, 20.0, sp4, 60.0, sp5, 0, 0); - + %% % SYSTEM EQUATIONS - + model.sym.xdot = sym(zeros(size(model.sym.x))); - + model.sym.xdot(1) = (Omega_nuc*p4*nSTAT5 - Omega_cyt*STAT*p1*u(1))/Omega_cyt; model.sym.xdot(2) = STAT*p1*u(1) - 2*p2*pSTAT^2; model.sym.xdot(3) = p2*pSTAT^2 - p3*pSTAT_pSTAT; @@ -40,30 +40,30 @@ model.sym.xdot(7) = p4*(nSTAT2 - nSTAT3); model.sym.xdot(8) = p4*(nSTAT3 - nSTAT4); model.sym.xdot(9) = p4*(nSTAT4 - nSTAT5); - + %% % INITIAL CONDITIONS - + model.sym.x0 = sym(zeros(size(model.sym.x))); model.sym.x0(1) = init_STAT; - + %% % OBSERVABLES - + model.sym.y = sym(zeros(3,1)); - + model.sym.y(1) = offset_pSTAT + scale_pSTAT/init_STAT*(pSTAT + 2*pSTAT_pSTAT); model.sym.y(2) = offset_tSTAT + scale_tSTAT/init_STAT*(STAT + pSTAT + 2*(pSTAT_pSTAT)); model.sym.y(3) = u(1); - + %% % SIGMA - + model.sym.sigma_y = sym(size(model.sym.y)); - + model.sym.sigma_y(1) = sigma_pSTAT; model.sym.sigma_y(2) = sigma_tSTAT; model.sym.sigma_y(3) = sigma_pEpoR; - -end \ No newline at end of file + +end diff --git a/deps/AMICI/matlab/examples/example_nested_events/example_nested_events.m b/deps/AMICI/matlab/examples/example_nested_events/example_nested_events.m index 1bd5ed9d5..52d46b97a 100644 --- a/deps/AMICI/matlab/examples/example_nested_events/example_nested_events.m +++ b/deps/AMICI/matlab/examples/example_nested_events/example_nested_events.m @@ -70,7 +70,7 @@ function example_events() legend('error x1','Location','NorthEastOutside') legend boxoff ylabel('x') - + set(gcf,'Position',[100 300 1200 300]) end @@ -111,7 +111,7 @@ function example_events() xlabel('time t') ylabel('sx') box on - + subplot(5,2,ip*2) plot(t,abs(sol.sx(:,:,ip)-sx_fd(:,:,ip)),'--') legend('error sx1','Location','NorthEastOutside') @@ -123,7 +123,7 @@ function example_events() box on end set(gcf,'Position',[100 300 1200 500]) - + drawnow end -end \ No newline at end of file +end diff --git a/deps/AMICI/matlab/examples/example_nested_events/model_nested_events_syms.m b/deps/AMICI/matlab/examples/example_nested_events/model_nested_events_syms.m index 42a1b48ae..1bfa941a3 100644 --- a/deps/AMICI/matlab/examples/example_nested_events/model_nested_events_syms.m +++ b/deps/AMICI/matlab/examples/example_nested_events/model_nested_events_syms.m @@ -1,4 +1,4 @@ -function [model] = model_nested_events_syms() +function [model] = model_nested_events_syms() %% CVODES OPTIONS % set the parametrisation of the problem options are 'log', 'log10' and 'lin' (default) @@ -61,4 +61,4 @@ model.sym.x0 = x0; model.sym.y = y; -end \ No newline at end of file +end diff --git a/deps/AMICI/matlab/examples/example_neuron/example_neuron.m b/deps/AMICI/matlab/examples/example_neuron/example_neuron.m index d05b0cfe6..603c1af0c 100644 --- a/deps/AMICI/matlab/examples/example_neuron/example_neuron.m +++ b/deps/AMICI/matlab/examples/example_neuron/example_neuron.m @@ -34,7 +34,7 @@ function example_neuron() t = linspace(0,D.Z(end)-0.1,100); D.Z = D.Z + 0.5*randn(size(D.Z)); D.Z(3) = NaN; -D.Sigma_Z = 0.5*ones(size(D.Z)); +D.Sigma_Z = 0.5*ones(size(D.Z)); D.Z = D.Z + D.Sigma_Z.*randn(size(D.Z)); D.t = t; @@ -64,7 +64,7 @@ function example_neuron() hold on end stem(sol.z,zeros(size(sol.z))) - + legend('x1','x2','events','Location','NorthEastOutside') legend boxoff xlabel('time t') @@ -135,7 +135,7 @@ function example_neuron() set(gca,'XScale','log') box on axis square - + subplot(2,3,4) plot(abs(sol.sz(:)),abs(sol.sz(:)-sz_fd(:)),'ro') hold on @@ -149,7 +149,7 @@ function example_neuron() set(gca,'XScale','log') box on axis square - + subplot(2,3,2) hold on plot(abs(sol.srz(:)),abs(srz_fd(:)),'bo') @@ -164,7 +164,7 @@ function example_neuron() set(gca,'XScale','log') box on axis square - + subplot(2,3,5) plot(abs(sol.srz(:)),abs(sol.srz(:)-srz_fd(:)),'ro') hold on @@ -178,7 +178,7 @@ function example_neuron() set(gca,'XScale','log') box on axis square - + subplot(2,3,3) hold on plot(abs(sol.sllh),abs(sllh_fd),'ko') @@ -193,7 +193,7 @@ function example_neuron() title('abs llh sensitivity') box on axis square - + subplot(2,3,6) plot(abs(sol.sllh),abs(sol.sllh-sllh_fd),'ro') hold on @@ -208,7 +208,7 @@ function example_neuron() box on axis square set(gcf,'Position',[100 300 1200 500]) - + figure subplot(2,3,1) hold on @@ -224,7 +224,7 @@ function example_neuron() set(gca,'XScale','log') box on axis square - + subplot(2,3,4) plot(abs(sol.s2z(:)),abs(sol.s2z(:)-s2z_fd(:)),'ro') hold on @@ -238,7 +238,7 @@ function example_neuron() set(gca,'XScale','log') box on axis square - + subplot(2,3,2) hold on plot(abs(sol.s2rz(:)),abs(s2rz_fd(:)),'bo') @@ -253,7 +253,7 @@ function example_neuron() set(gca,'XScale','log') box on axis square - + subplot(2,3,5) plot(abs(sol.s2rz(:)),abs(sol.s2rz(:)-s2rz_fd(:)),'ro') hold on @@ -267,7 +267,7 @@ function example_neuron() set(gca,'XScale','log') box on axis square - + subplot(2,3,3) hold on plot(abs(sol.s2llh),abs(s2llh_fd),'ko') @@ -282,7 +282,7 @@ function example_neuron() title('abs llh sensitivity') box on axis square - + subplot(2,3,6) plot(abs(sol.s2llh),abs(sol.s2llh-s2llh_fd),'ro') hold on @@ -297,8 +297,8 @@ function example_neuron() box on axis square set(gcf,'Position',[100 300 1200 500]) - + drawnow end -end \ No newline at end of file +end diff --git a/deps/AMICI/matlab/examples/example_neuron/model_neuron_syms.m b/deps/AMICI/matlab/examples/example_neuron/model_neuron_syms.m index e5786fb8e..fddb86946 100644 --- a/deps/AMICI/matlab/examples/example_neuron/model_neuron_syms.m +++ b/deps/AMICI/matlab/examples/example_neuron/model_neuron_syms.m @@ -1,33 +1,33 @@ function model = neuron_syms() - + model.param = 'log10'; - - syms a b c d - + + syms a b c d + p = [a b c d]; - + syms v0 I0 - + k = [v0,I0]; - + syms v u - + x = [v u]; - + syms I t - + I = I0; - + f(1) = 0.04*v^2 + 5*v + 140 - u + I ; f(2) = a*(b*v - u); y(1) = v; - - + + x0 = [v0,b*v0]; - + event = amievent(v-30,[-c-v,d],t); - + model.sym.p = p; model.sym.k = k; model.sym.x = x; @@ -35,6 +35,6 @@ model.sym.f = f; model.event = event; model.sym.x0 = x0; - - -end \ No newline at end of file + + +end diff --git a/deps/AMICI/matlab/examples/example_robertson/example_robertson.m b/deps/AMICI/matlab/examples/example_robertson/example_robertson.m index a5daeaa8b..b6b2bc08f 100644 --- a/deps/AMICI/matlab/examples/example_robertson/example_robertson.m +++ b/deps/AMICI/matlab/examples/example_robertson/example_robertson.m @@ -70,7 +70,7 @@ function example_robertson() legend('error x1','error x2','error x3','Location','NorthEastOutside') legend boxoff ylabel('x') - + set(gcf,'Position',[100 300 1200 500]) end @@ -111,7 +111,7 @@ function example_robertson() set(gca,'XScale','log') ylabel('sx') box on - + subplot(length(p),2,ip*2) plot(t,abs(sol.sy(:,:,ip)-sy_fd(:,:,ip)),'--') legend('error sy1','error sy2','error sy3','Location','NorthEastOutside') @@ -124,8 +124,8 @@ function example_robertson() box on end set(gcf,'Position',[100 300 1200 500]) - - + + drawnow end -end \ No newline at end of file +end diff --git a/deps/AMICI/matlab/examples/example_robertson/model_robertson_syms.m b/deps/AMICI/matlab/examples/example_robertson/model_robertson_syms.m index bf95c2121..9cca91e56 100644 --- a/deps/AMICI/matlab/examples/example_robertson/model_robertson_syms.m +++ b/deps/AMICI/matlab/examples/example_robertson/model_robertson_syms.m @@ -18,12 +18,12 @@ % create parameter syms syms p1 p2 p3 -% create parameter vector +% create parameter vector model.sym.p = [p1,p2,p3]; % set the parametrisation of the problem options are 'log', 'log10' and % 'lin' (default). -model.param = 'log10'; +model.param = 'log10'; %% @@ -33,7 +33,7 @@ % create parameter syms syms k1 -% create parameter vector +% create parameter vector model.sym.k = [k1]; %% @@ -65,4 +65,4 @@ model.sym.y = model.sym.x; model.sym.y(2) = 1e4*model.sym.x(2); -end \ No newline at end of file +end diff --git a/deps/AMICI/matlab/examples/example_steadystate/example_steadystate.m b/deps/AMICI/matlab/examples/example_steadystate/example_steadystate.m index 118b1f5cf..a6503527f 100644 --- a/deps/AMICI/matlab/examples/example_steadystate/example_steadystate.m +++ b/deps/AMICI/matlab/examples/example_steadystate/example_steadystate.m @@ -1,46 +1,46 @@ function example_steadystate %% % COMPILATION - + [exdir,~,~]=fileparts(which('example_steadystate.m')); % compile the model amiwrap('model_steadystate','model_steadystate_syms',exdir) - + %% % SIMULATION - + % time vector t = linspace(0,100,50); p = [1;0.5;0.4;2;0.1]; k = [0.1,0.4,0.7,1]; - + options = amioption(... 'sensi', 0, ... 'maxsteps', 1e4 ... ); - + % load mex into memory simulate_model_steadystate(t,log10(p),k,[],options); - + tic; sol = simulate_model_steadystate([t, inf],log10(p),k,[],options); display(['Time elapsed with cvodes: ' num2str(toc) ' seconds']); - + %% % ODE15S - + ode_system = @(t,x,p,k) [-2*p(1)*x(1)^2 - p(2)*x(1)*x(2) + 2*p(3)*x(2) + p(4)*x(3) + p(5); + p(1)*x(1)^2 - p(2)*x(1)*x(2) - p(3)*x(2) + p(4)*x(3); + p(2)*x(1)*x(2) - p(4)*x(3) - k(4)*x(3)]; options_ode15s = odeset('RelTol',options.rtol,'AbsTol',options.atol,'MaxStep',options.maxsteps); - + tic; [~, X_ode15s] = ode15s(@(t,x) ode_system(t,x,p,k),t,k(1:3),options_ode15s); disp(['Time elapsed with ode15s: ' num2str(toc) ' seconds']) - + %% % PLOTTING - + if(usejava('jvm')) figure('Name', 'Example SteadyState'); c_x = get(gca,'ColorOrder'); @@ -63,19 +63,19 @@ legend boxoff; set(gcf,'Position',[100 300 1200 500]); end - + %% % FORWARD SENSITIVITY ANALYSIS - + options.sensi = 1; options.sens_ind = [3,1,2,4]; sol = simulate_model_steadystate([t, inf],log10(p),k,[],options); - + %% % FINITE DIFFERENCES - + eps = 1e-3; - + xi = log10(p); sx_ffd = zeros(length(t)+1, 3, length(p)); sx_bfd = zeros(length(t)+1, 3, length(p)); @@ -91,7 +91,7 @@ sx_bfd(:,:,ip) = (sol.x - solm.x) / eps; sx_cfd(:,:,ip) = (solp.x - solm.x) / (2*eps); end - + %% % PLOTTING if(usejava('jvm')) @@ -122,10 +122,10 @@ box on; end set(gcf,'Position',[100 300 1200 500]); - + sxss = squeeze(sol.sx(length(t),:,:)); sxss_fd = squeeze(sx_cfd(length(t),:,options.sens_ind)); - + % Sensitivities for steady state figure('Name', 'Example SteadyState'); subplot(1,2,1); @@ -148,8 +148,8 @@ xlabel('Steady state sensitivities'); ylabel('finite differences'); box on; - - + + subplot(1,2,2); hold on; for ip = 1:4 @@ -172,10 +172,10 @@ set(gca,'YScale','log'); set(gcf,'Position',[100 300 1200 500]); end - + %% % XDOT FOR DIFFERENT TIME POINTS - + t = [10,25,100,250,1000]; options.sensi = 0; ssxdot = NaN(length(t), size(sol.x, 2)); @@ -187,13 +187,13 @@ % Compute steady state wihtout integration before sol = simulate_model_steadystate(inf,log10(p),k,[],options); - + % Test recapturing in the case of Newton solver failing options.newton_maxsteps = 4; options.maxsteps = 300; sol_newton_fail = simulate_model_steadystate(inf,log10(p),k,[],options); - + %% % PLOTTING if(usejava('jvm')) @@ -216,7 +216,7 @@ box on; set(gca,'YScale','log'); set(gca,'XScale','log'); - + subplot(1,3,3); hold on; bar(sol_newton_fail.diagnosis.posteq_numsteps([1, 3])); @@ -229,10 +229,8 @@ a = gca(); a.Children.BarWidth = 0.6; box on; - + set(gcf,'Position',[100 300 1200 500]); end - -end - +end diff --git a/deps/AMICI/matlab/examples/example_steadystate/model_steadystate_syms.m b/deps/AMICI/matlab/examples/example_steadystate/model_steadystate_syms.m index 713cd1ad6..75b441cff 100644 --- a/deps/AMICI/matlab/examples/example_steadystate/model_steadystate_syms.m +++ b/deps/AMICI/matlab/examples/example_steadystate/model_steadystate_syms.m @@ -18,12 +18,12 @@ % create parameter syms syms p1 p2 p3 p4 p5 -% create parameter vector +% create parameter vector model.sym.p = [p1,p2,p3,p4,p5]; % set the parametrisation of the problem options are 'log', 'log10' and % 'lin' (default). -model.param = 'log10'; +model.param = 'log10'; %% @@ -33,7 +33,7 @@ % create parameter syms syms k1 k2 k3 k4 -% create parameter vector +% create parameter vector model.sym.k = [k1 k2 k3 k4]; %% @@ -63,4 +63,4 @@ % OBSERVALES model.sym.y = model.sym.x; -end \ No newline at end of file +end diff --git a/deps/AMICI/matlab/installAMICI.m b/deps/AMICI/matlab/installAMICI.m index 9a6ccbc1c..d3e40dd10 100644 --- a/deps/AMICI/matlab/installAMICI.m +++ b/deps/AMICI/matlab/installAMICI.m @@ -3,4 +3,4 @@ addpath(fullfile(amipath,'auxiliary')) addpath(fullfile(amipath,'auxiliary','CalcMD5')) addpath(fullfile(amipath,'symbolic')) -addpath(fullfile(amipath,'SBMLimporter')) \ No newline at end of file +addpath(fullfile(amipath,'SBMLimporter')) diff --git a/deps/AMICI/matlab/mtoc/MatlabDocMaker.m b/deps/AMICI/matlab/mtoc/MatlabDocMaker.m index cdbcfd7dd..816063523 100644 --- a/deps/AMICI/matlab/mtoc/MatlabDocMaker.m +++ b/deps/AMICI/matlab/mtoc/MatlabDocMaker.m @@ -23,7 +23,7 @@ % @change{1,5,dw,2013-12-03} Fixed default value selection for properties, % now not having set a description or logo does not cause an error to be % thrown. -% +% % @change{1,5,dw,2013-02-21} Fixed the callback for suggested direct documentation creation % after MatlabDocMaker.setup (Thanks to Aurelien Queffurust) % @@ -111,7 +111,7 @@ % % @type char @default 'Doxyfile.template' DOXYFILE_TEMPLATE = 'Doxyfile.template'; - + % File name for the latex extras style file processed by the MatlabDocMaker. % % Assumed to reside in the MatlabDocMaker.getConfigDirectory. @@ -119,7 +119,7 @@ % % @type char @default 'latexextras.template' LATEXEXTRAS_TEMPLATE = 'latexextras.template'; - + % File name the mtoc++ configuration file. % % Assumed to reside in the MatlabDocMaker.getConfigDirectory. @@ -139,17 +139,17 @@ % % Return values: % name: The project name @type char - + %error('Please replace this by returning your project name as string.'); % Example: name = 'AMICI'; end end - + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% End of user defined part. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - + methods(Static, Sealed) function dir = getOutputDirectory % Returns the directory where the applications source files @@ -159,7 +159,7 @@ % dir: The output directory @type char dir = MatlabDocMaker.getPref('outdir'); end - + function dir = getSourceDirectory % Returns the directory where the applications source files % reside @@ -168,7 +168,7 @@ % dir: The project source directory @type char dir = MatlabDocMaker.getPref('srcdir'); end - + function dir = getConfigDirectory % Returns the directory where the applications documentation % configuration files reside @@ -180,7 +180,7 @@ % dir: The documentation configuration directory @type char dir = MatlabDocMaker.getPref('confdir'); end - + function desc = getProjectDescription % Returns the short project description. % @@ -190,7 +190,7 @@ % See also: setProjectDescription desc = MatlabDocMaker.getPref('proj_desc', ''); end - + function setProjectDescription(value) % Sets the project description. % @@ -203,7 +203,7 @@ function setProjectDescription(value) end MatlabDocMaker.setPref('proj_desc', value); end - + function version = getProjectVersion % Returns the current version of the project. % @@ -216,7 +216,7 @@ function setProjectDescription(value) % See also: setProjectVersion version = MatlabDocMaker.getPref('proj_ver', '0'); end - + function setProjectVersion(value) % Sets the project version. % @@ -229,7 +229,7 @@ function setProjectVersion(value) end MatlabDocMaker.setPref('proj_ver', value); end - + function fullPath = getProjectLogo % Returns the logo image file for the project. Either an absolute path or a plain % filename. For the latter case the image file is assumed to reside inside the @@ -253,7 +253,7 @@ function setProjectVersion(value) end end end - + function setProjectLogo(value) % Sets the project logo. Set to '' to unset. % @@ -289,9 +289,9 @@ function setProjectLogo(value) MatlabDocMaker.setPref('proj_logo', value); end end - + methods(Static) - + function open % Opens the generated documentation. % @@ -308,7 +308,7 @@ function setProjectLogo(value) end end end - + function create(varargin) % Creates the Doxygen documentation % @@ -318,14 +318,14 @@ function create(varargin) % successful compilation @type logical @default false % latex: Set to true if `\text{\LaTeX}` output should be generated, too. @type logical % @default false - + %% Preparations ip = inputParser; ip.addParameter('open',false,@islogical); ip.addParameter('latex',false,@islogical); ip.parse(varargin{:}); genlatex = ip.Results.latex; - + % Check for correct setup cdir = MatlabDocMaker.getConfigDirectory; srcdir = MatlabDocMaker.getSourceDirectory; @@ -335,7 +335,7 @@ function create(varargin) if exist(doxyfile_in,'file') ~= 2 error('No doxygen configuration file template found at "%s"',doxyfile_in); end - + lstr = ''; if genlatex lstr = '(+Latex)'; @@ -344,7 +344,7 @@ function create(varargin) 'Sources: %s\nOutput to: %s\nCreating config files...'],lstr,... MatlabDocMaker.getProjectName,MatlabDocMaker.getProjectVersion,... srcdir,outdir); - + % Operation-system dependent strings strs = struct; if isunix @@ -356,17 +356,17 @@ function create(varargin) else error('Current platform not supported.'); end - + % Save current working dir and change into the KerMor home % directory; only from there all classes and packages are % detected properly. curdir = pwd; cd(srcdir); - + % Append the configuration file directory to the current PATH pathadd = [pathsep cdir]; setenv('PATH',[getenv('PATH') pathadd]); - + mtoc_conf = fullfile(cdir,MatlabDocMaker.MTOCPP_CONFIGFILE); filter = sprintf('%smtocpp',strs.silencer); if exist(mtoc_conf,'file') @@ -381,7 +381,7 @@ function create(varargin) end %% Creation part cdir = MatlabDocMaker.getConfigDirectory; - % Create "configured" filter script for inclusion in doxygen + % Create "configured" filter script for inclusion in doxygen filter = fullfile(cdir,strs.filter); f = fopen(filter,'w'); fprintf(f,'%smtocpp %s %s',strs.silencer,strs.farg,mtoc_conf); @@ -390,7 +390,7 @@ function create(varargin) unix(['chmod +x ' filter]); end end - + %% Prepare placeholders in the Doxyfile template m = {'_OutputDir_' strrep(outdir,'\','\\'); ... '_SourceDir_' strrep(MatlabDocMaker.getSourceDirectory,'\','\\');... @@ -401,11 +401,11 @@ function create(varargin) '_ProjectVersion_' MatlabDocMaker.getProjectVersion; ... '_MTOCFILTER_' strrep(filter,'\','\\'); ... }; - + % Check for latex extra stuff texin = fullfile(cdir,MatlabDocMaker.LATEXEXTRAS_TEMPLATE); latexextras = ''; - if exist(texin,'file') == 2 + if exist(texin,'file') == 2 latexstr = strrep(fileread(texin),'_ConfDir_',strrep(cdir,'\','/')); latexextras = fullfile(cdir,'latexextras.sty'); fid = fopen(latexextras,'w+'); fprintf(fid,'%s',latexstr); fclose(fid); @@ -418,7 +418,7 @@ function create(varargin) L = 'YES'; end m(end+1,:) = {'_GenLatex_',L}; - + % Check how to set the HAVE_DOT flag [s, ~] = system('dot -V'); if s == 0 @@ -428,12 +428,12 @@ function create(varargin) fprintf('no "dot" found...'); end m(end+1,:) = {'_HaveDot_',HD}; - + % Read, replace & write doxygen config file doxyfile = fullfile(cdir,'Doxyfile'); doxyconfstr = regexprep(fileread(doxyfile_in),m(:,1),m(:,2)); fid = fopen(doxyfile,'w'); fprintf(fid,'%s',doxyconfstr); fclose(fid); - + % Fix for unix systems where the MatLab installation uses older % GLIBSTD libraries than doxygen/mtoc++ ldpath = ''; @@ -443,7 +443,7 @@ function create(varargin) % Call doxygen fprintf('running doxygen with mtoc++ filter...'); [~,warn] = system(sprintf('%sdoxygen "%s" 1>%s',ldpath, doxyfile, strs.null)); - + % Postprocess fprintf('running mtoc++ postprocessor...'); [~,postwarn] = system(sprintf('%smtocpp_post "%s" 1>%s',ldpath,... @@ -451,7 +451,7 @@ function create(varargin) if ~isempty(postwarn) warn = [warn sprintf('mtoc++ postprocessor messages:\n') postwarn]; end - + % Create latex document if desired if genlatex oldd = pwd; @@ -460,7 +460,7 @@ function create(varargin) if exist(latexdir,'dir') == 7 if exist(fullfile(latexdir,'refman.tex'),'file') == 2 fprintf('compiling LaTeX output...'); - cd(latexdir); + cd(latexdir); [s, latexmsg] = system('make'); if s ~= 0 warn = [warn sprintf('LaTeX compiler output:\n') latexmsg]; @@ -472,7 +472,7 @@ function create(varargin) end cd(oldd); end - + % Tidy up fprintf('cleaning up...'); if isfield(strs,'filter') @@ -482,14 +482,14 @@ function create(varargin) delete(latexextras); end delete(doxyfile); - - %% Post generation phase + + %% Post generation phase cd(curdir); % Restore PATH to previous value curpath = getenv('PATH'); setenv('PATH',curpath(1:end-length(pathadd))); fprintf('done!\n'); - + % Process warnings showchars = 800; warn = strtrim(warn); @@ -514,21 +514,21 @@ function create(varargin) else fprintf('MatlabDocMaker finished with no warnings!\n'); end - + % Open index.html if wanted if ip.Results.open MatlabDocMaker.open; end end - + function setup % Runs the setup script for MatlabDocMaker and collects all % necessary paths in order for the documentation creation to % work properly. - + %% Validity checks fprintf('<<<< Welcome to the MatlabDocMaker setup for your project "%s"! >>>>\n',MatlabDocMaker.getProjectName); - + %% Setup directories % Source directory srcdir = MatlabDocMaker.getPref('srcdir',''); @@ -547,7 +547,7 @@ function create(varargin) srcdir = d; end MatlabDocMaker.setPref('srcdir',srcdir); - + % Config directory confdir = MatlabDocMaker.getPref('confdir',''); word = 'keep'; @@ -565,7 +565,7 @@ function create(varargin) confdir = d; end MatlabDocMaker.setPref('confdir',confdir); - + % Output directory outdir = MatlabDocMaker.getPref('outdir',''); word = 'keep'; @@ -583,7 +583,7 @@ function create(varargin) outdir = d; end MatlabDocMaker.setPref('outdir',outdir); - + %% Additional Project properties if isequal(lower(input(['Do you want to specify further project details?\n'... 'You can set them later using provided set methods. (Y)es/(N)o?: '],'s')),'y') @@ -591,7 +591,7 @@ function create(varargin) MatlabDocMaker.setPref('proj_desc',input('Please specify a short project description: ','s')); MatlabDocMaker.setProjectLogo; end - + %% Check for necessary and recommended tools hasall = true; setenv('PATH',[getenv('PATH') pathsep confdir]); @@ -601,7 +601,7 @@ function create(varargin) fprintf(' found %s\n',vers(1:end-1)); else fprintf(2,' not found!\n'); - hasall = false; + hasall = false; end fprintf('[Required] Checking for mtoc++... '); ldpath = ''; @@ -653,20 +653,20 @@ function create(varargin) end end end - + methods(Static, Access=private) function value = getProjPrefTag % Gets the tag for the MatLab preferences struct. - % + % % @change{0,7,dw,2013-04-02} Now also removing "~" and "-" characters from ProjectName tags for preferences. str = regexprep(strrep(strtrim(MatlabDocMaker.getProjectName),' ','_'),'[^\d\w]',''); value = sprintf('MatlabDocMaker_on_%s',str); end - + function value = getPref(name, default) if nargin < 2 def = []; - else + else def = default; end value = getpref(MatlabDocMaker.getProjPrefTag,name,def); @@ -674,7 +674,7 @@ function create(varargin) error('MatlabDocMaker preferences not found/set correctly. (Re-)Run the MatlabDocMaker.setup method.'); end end - + function value = setPref(name, value) setpref(MatlabDocMaker.getProjPrefTag,name,value); end diff --git a/deps/AMICI/matlab/mtoc/config/Doxyfile.template b/deps/AMICI/matlab/mtoc/config/Doxyfile.template index ed2f623f1..f5eed70f7 100644 --- a/deps/AMICI/matlab/mtoc/config/Doxyfile.template +++ b/deps/AMICI/matlab/mtoc/config/Doxyfile.template @@ -1,6 +1,4 @@ -############################################################################ - -# Doxyfile 1.9.0 +# Doxyfile 1.9.7 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. @@ -14,6 +12,16 @@ # For lists, items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (\" \"). +# +# Note: +# +# Use doxygen to compare the used configuration file with the template +# configuration file: +# doxygen -x [configFile] +# Use doxygen to compare the used configuration file with the template +# configuration file without replacing the environment variables or CMake type +# replacement variables: +# doxygen -x_noenv [configFile] #--------------------------------------------------------------------------- # Project related configuration options @@ -26,6 +34,7 @@ # https://www.gnu.org/software/libiconv/ for the list of possible encodings. # The default value is: UTF-8. +############################################################################ ############################################################################ ############################################################################ ################# mtoc++ related information ############################### @@ -47,7 +56,7 @@ DOXYFILE_ENCODING = UTF-8 ############### MTOC++ RELATED CONFIGURATION ################################### ################################################################################ -PROJECT_NAME = "_ProjectName_" +PROJECT_NAME = _ProjectName_ # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version @@ -59,32 +68,44 @@ PROJECT_NUMBER = _ProjectVersion_ # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. -PROJECT_BRIEF = "_ProjectDescription_" +PROJECT_BRIEF = _ProjectDescription_ # With the PROJECT_LOGO tag one can specify a logo or an icon that is included # in the documentation. The maximum height of the logo should not exceed 55 # pixels and the maximum width should not exceed 200 pixels. Doxygen will copy # the logo to the output directory. -PROJECT_LOGO = "_ProjectLogo_" +PROJECT_LOGO = _ProjectLogo_ # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. -OUTPUT_DIRECTORY = "_OutputDir_" +OUTPUT_DIRECTORY = _OutputDir_ -# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- -# directories (in 2 levels) under the output directory of each output format and -# will distribute the generated files over these directories. Enabling this +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 4096 +# sub-directories (in 2 levels) under the output directory of each output format +# and will distribute the generated files over these directories. Enabling this # option can be useful when feeding doxygen a huge amount of source files, where # putting all generated files in the same directory would otherwise causes -# performance problems for the file system. +# performance problems for the file system. Adapt CREATE_SUBDIRS_LEVEL to +# control the number of sub-directories. # The default value is: NO. CREATE_SUBDIRS = NO +# Controls the number of sub-directories that will be created when +# CREATE_SUBDIRS tag is set to YES. Level 0 represents 16 directories, and every +# level increment doubles the number of directories, resulting in 4096 +# directories at level 8 which is the default and also the maximum value. The +# sub-directories are organized in 2 levels, the first level always has a fixed +# number of 16 directories. +# Minimum value: 0, maximum value: 8, default value: 8. +# This tag requires that the tag CREATE_SUBDIRS is set to YES. + +CREATE_SUBDIRS_LEVEL = 8 + # If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII # characters to appear in the names of generated files. If set to NO, non-ASCII # characters will be escaped, for example _xE3_x81_x84 will be used for Unicode @@ -96,26 +117,18 @@ ALLOW_UNICODE_NAMES = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. -# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, -# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), -# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, -# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), -# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, -# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, -# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, -# Ukrainian and Vietnamese. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Bulgarian, +# Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, English +# (United States), Esperanto, Farsi (Persian), Finnish, French, German, Greek, +# Hindi, Hungarian, Indonesian, Italian, Japanese, Japanese-en (Japanese with +# English messages), Korean, Korean-en (Korean with English messages), Latvian, +# Lithuanian, Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, +# Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, +# Swedish, Turkish, Ukrainian and Vietnamese. # The default value is: English. OUTPUT_LANGUAGE = English -# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all -# documentation generated by doxygen is written. Doxygen will use this -# information to generate all generated output in the proper direction. -# Possible values are: None, LTR, RTL and Context. -# The default value is: None. - -OUTPUT_TEXT_DIRECTION = None - # If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. @@ -273,28 +286,28 @@ TAB_SIZE = 4 # the documentation. An alias has the form: # name=value # For example adding -# "sideeffect=@par Side Effects:\n" +# "sideeffect=@par Side Effects:^^" # will allow you to put the command \sideeffect (or @sideeffect) in the # documentation, which will result in a user-defined paragraph with heading -# "Side Effects:". You can put \n's in the value part of an alias to insert -# newlines (in the resulting output). You can put ^^ in the value part of an -# alias to insert a newline as if a physical newline was in the original file. -# When you need a literal { or } or , in the value part of an alias you have to -# escape them by means of a backslash (\), this can lead to conflicts with the -# commands \{ and \} for these it is advised to use the version @{ and @} or use -# a double escape (\\{ and \\}) +# "Side Effects:". Note that you cannot put \n's in the value part of an alias +# to insert newlines (in the resulting output). You can put ^^ in the value part +# of an alias to insert a newline as if a physical newline was in the original +# file. When you need a literal { or } or , in the value part of an alias you +# have to escape them by means of a backslash (\), this can lead to conflicts +# with the commands \{ and \} for these it is advised to use the version @{ and +# @} or use a double escape (\\{ and \\}) ALIASES = "synupdate=\xrefitem synupdate \"Syntax Update\" \"Syntax needs to be updated\"" \ "docupdate=\xrefitem docupdate \"Documentation Update\" \"Documentation needs to be updated\"" \ "event=\xrefitem event \"Events\" \"List of all Events\"" \ "default=\par Default:\n" \ - "type=
Type: " \ + "type=
Type:" \ "changexref{2}=\xrefitem changelog\1_\2 \"Change in \1.\2\" \"Changes in _ProjectName_ Version \1.\2\"" \ - "change{4} = \changexref{\1,\2} (\ref \3, \4) " \ - "change{3} = \changexref{\1,\2} (\ref \3, undated) " \ + "change{4} = \changexref{\1,\2} (\ref \3, \4)" \ + "change{3} = \changexref{\1,\2} (\ref \3, undated)" \ "newxref{2}=\xrefitem newfeat\1_\2 \"New in \1.\2\" \"New features in _ProjectName_ Version \1.\2\"" \ - "new{4} = \newxref{\1,\2} (\ref \3, \4) " \ - "new{3} = \newxref{\1,\2} (\ref \3, undated) " \ + "new{4} = \newxref{\1,\2} (\ref \3, \4)" \ + "new{3} = \newxref{\1,\2} (\ref \3, undated)" \ "propclass{1}=\xrefitem propclass_\1 \"Property class \1\" \"Properties with level \1\"" # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources @@ -338,8 +351,8 @@ OPTIMIZE_OUTPUT_SLICE = NO # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and # language is one of the parsers supported by doxygen: IDL, Java, JavaScript, -# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, VHDL, -# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: +# Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice, +# VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: # FortranFree, unknown formatted Fortran: Fortran. In the later case the parser # tries to guess whether the code is fixed or free formatted code, this is the # default for Fortran type files). For instance to make doxygen treat .inc files @@ -349,7 +362,10 @@ OPTIMIZE_OUTPUT_SLICE = NO # Note: For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise -# the files are not read by doxygen. +# the files are not read by doxygen. When specifying no_extension you should add +# * to the FILE_PATTERNS. +# +# Note see also the list of default file extension mappings. ############### CRITICAL MTOC++ RELATED CONFIGURATION ########################## ################################################################################ @@ -486,13 +502,13 @@ TYPEDEF_HIDES_STRUCT = YES LOOKUP_CACHE_SIZE = 0 -# The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use +# The NUM_PROC_THREADS specifies the number of threads doxygen is allowed to use # during processing. When set to 0 doxygen will based this on the number of # cores available in the system. You can set it explicitly to a value larger # than 0 to get more control over the balance between CPU load and processing # speed. At this moment only the input processing can be done using multiple # threads. Since this is still an experimental feature the default is set to 1, -# which efficively disables parallel processing. Please report any issues you +# which effectively disables parallel processing. Please report any issues you # encounter. Generating dot graphs in parallel is controlled by the # DOT_NUM_THREADS setting. # Minimum value: 0, maximum value: 32, default value: 1. @@ -562,6 +578,13 @@ EXTRACT_LOCAL_METHODS = YES EXTRACT_ANON_NSPACES = NO +# If this flag is set to YES, the name of an unnamed parameter in a declaration +# will be determined by the corresponding definition. By default unnamed +# parameters remain unnamed in the output. +# The default value is: YES. + +RESOLVE_UNNAMED_PARAMS = YES + # If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all # undocumented members inside documented classes or files. If set to NO these # members will be included in the various overviews, but no documentation @@ -573,7 +596,8 @@ HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set # to NO, these classes will be included in the various overviews. This option -# has no effect if EXTRACT_ALL is enabled. +# will also hide undocumented C++ concepts if enabled. This option has no effect +# if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_CLASSES = NO @@ -599,12 +623,20 @@ HIDE_IN_BODY_DOCS = NO INTERNAL_DOCS = NO -# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file -# names in lower-case letters. If set to YES, upper-case letters are also -# allowed. This is useful if you have classes or files whose names only differ -# in case and if your file system supports case sensitive file names. Windows -# (including Cygwin) and Mac users are advised to set this option to NO. -# The default value is: system dependent. +# With the correct setting of option CASE_SENSE_NAMES doxygen will better be +# able to match the capabilities of the underlying filesystem. In case the +# filesystem is case sensitive (i.e. it supports files in the same directory +# whose names only differ in casing), the option must be set to YES to properly +# deal with such files in case they appear in the input. For filesystems that +# are not case sensitive the option should be set to NO to properly deal with +# output files written for symbols that only differ in casing, such as for two +# classes, one named CLASS and the other named Class, and to also support +# references to files without having to specify the exact matching casing. On +# Windows (including Cygwin) and MacOS, users should typically set this option +# to NO, whereas on Linux or other Unix flavors it should typically be set to +# YES. +# Possible values are: SYSTEM, NO and YES. +# The default value is: SYSTEM. CASE_SENSE_NAMES = NO @@ -622,6 +654,12 @@ HIDE_SCOPE_NAMES = YES HIDE_COMPOUND_REFERENCE= NO +# If the SHOW_HEADERFILE tag is set to YES then the documentation for a class +# will show which file needs to be included to use the class. +# The default value is: YES. + +SHOW_HEADERFILE = YES + # If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. @@ -779,7 +817,8 @@ FILE_VERSION_FILTER = # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. You can # optionally specify a file name after the option, if omitted DoxygenLayout.xml -# will be used as the name of the layout file. +# will be used as the name of the layout file. See also section "Changing the +# layout of pages" for information. # # Note that if you run doxygen from a directory containing a file called # DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE @@ -825,24 +864,43 @@ WARNINGS = YES WARN_IF_UNDOCUMENTED = YES # If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some parameters -# in a documented function, or documenting parameters that don't exist or using -# markup commands wrongly. +# potential errors in the documentation, such as documenting some parameters in +# a documented function twice, or documenting parameters that don't exist or +# using markup commands wrongly. # The default value is: YES. WARN_IF_DOC_ERROR = YES +# If WARN_IF_INCOMPLETE_DOC is set to YES, doxygen will warn about incomplete +# function parameter documentation. If set to NO, doxygen will accept that some +# parameters have no documentation without warning. +# The default value is: YES. + +WARN_IF_INCOMPLETE_DOC = YES + # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return -# value. If set to NO, doxygen will only warn about wrong or incomplete -# parameter documentation, but not about the absence of documentation. If -# EXTRACT_ALL is set to YES then this flag will automatically be disabled. +# value. If set to NO, doxygen will only warn about wrong parameter +# documentation, but not about the absence of documentation. If EXTRACT_ALL is +# set to YES then this flag will automatically be disabled. See also +# WARN_IF_INCOMPLETE_DOC # The default value is: NO. WARN_NO_PARAMDOC = YES +# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, doxygen will warn about +# undocumented enumeration values. If set to NO, doxygen will accept +# undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: NO. + +WARN_IF_UNDOC_ENUM_VAL = NO + # If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when -# a warning is encountered. +# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS +# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but +# at the end of the doxygen process doxygen will return with a non-zero status. +# Possible values are: NO, YES and FAIL_ON_WARNINGS. # The default value is: NO. WARN_AS_ERROR = NO @@ -853,13 +911,27 @@ WARN_AS_ERROR = NO # and the warning text. Optionally the format may contain $version, which will # be replaced by the version of the file (if it could be obtained via # FILE_VERSION_FILTER) +# See also: WARN_LINE_FORMAT # The default value is: $file:$line: $text. WARN_FORMAT = "$file:$line: $text" +# In the $text part of the WARN_FORMAT command it is possible that a reference +# to a more specific place is given. To make it easier to jump to this place +# (outside of doxygen) the user can define a custom "cut" / "paste" string. +# Example: +# WARN_LINE_FORMAT = "'vi $file +$line'" +# See also: WARN_FORMAT +# The default value is: at line $line of file $file. + +WARN_LINE_FORMAT = "at line $line of file $file" + # The WARN_LOGFILE tag can be used to specify a file to which warning and error # messages should be written. If left blank the output is written to standard -# error (stderr). +# error (stderr). In case the file specified cannot be opened for writing the +# warning and error messages are written to standard error. When as file - is +# specified the warning and error messages are written to standard output +# (stdout). WARN_LOGFILE = @@ -880,16 +952,26 @@ INPUT = "_SourceDir_/README.md" \ "_SourceDir_/include" \ "_SourceDir_/matlab" - # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv -# documentation (see: https://www.gnu.org/software/libiconv/) for the list of -# possible encodings. +# documentation (see: +# https://www.gnu.org/software/libiconv/) for the list of possible encodings. +# See also: INPUT_FILE_ENCODING # The default value is: UTF-8. INPUT_ENCODING = UTF-8 +# This tag can be used to specify the character encoding of the source files +# that doxygen parses The INPUT_FILE_ENCODING tag can be used to specify +# character encoding on a per file pattern basis. Doxygen will compare the file +# name with each pattern and apply the encoding instead of the default +# INPUT_ENCODING) if there is a match. The character encodings are a list of the +# form: pattern=encoding (like *.php=ISO-8859-1). See cfg_input_encoding +# "INPUT_ENCODING" for further information on supported encodings. + +INPUT_FILE_ENCODING = + # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and # *.h) to filter out the source-files in the directories. @@ -898,12 +980,14 @@ INPUT_ENCODING = UTF-8 # need to set EXTENSION_MAPPING for the extension otherwise the files are not # read by doxygen. # +# Note the list of default checked file patterns might differ from the list of +# default file extension mappings. +# # If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, # *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, -# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, -# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment), -# *.doc (to be provided as doxygen C comment), *.txt (to be provided as doxygen -# C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, +# *.hh, *.hxx, *.hpp, *.h++, *.l, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, +# *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C +# comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, # *.vhdl, *.ucf, *.qsf and *.ice. FILE_PATTERNS = *.m \ @@ -973,7 +1057,7 @@ EXCLUDE_PATTERNS = "_SourceDir_/models/*" \ # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, -# AClass::ANamespace, ANamespace::*Test +# ANamespace::AClass, ANamespace::*Test # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories use the pattern */test/* @@ -1021,6 +1105,11 @@ IMAGE_PATH = # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. # +# Note that doxygen will use the data processed and written to standard output +# for further processing, therefore nothing else, like debug statements or used +# commands (so in case of a Windows batch file always use @echo OFF), should be +# written to standard output. +# # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by doxygen. @@ -1060,7 +1149,16 @@ FILTER_SOURCE_PATTERNS = # (index.html). This can be useful if you have a project on for instance GitHub # and want to reuse the introduction page also for the doxygen output. -USE_MDFILE_AS_MAINPAGE = "_SourceDir_/README.md" +USE_MDFILE_AS_MAINPAGE = _SourceDir_/README.md + +# The Fortran standard specifies that for fixed formatted Fortran code all +# characters from position 72 are to be considered as comment. A common +# extension is to allow longer lines before the automatic comment starts. The +# setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can +# be processed before the automatic comment starts. +# Minimum value: 7, maximum value: 10000, default value: 72. + +FORTRAN_COMMENT_AFTER = 72 #--------------------------------------------------------------------------- # Configuration options related to source browsing @@ -1159,17 +1257,11 @@ VERBATIM_HEADERS = NO ALPHABETICAL_INDEX = NO -# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in -# which the alphabetical index list will be split. -# Minimum value: 1, maximum value: 20, default value: 5. -# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. - -COLS_IN_ALPHA_INDEX = 5 - -# In case all classes in a project start with a common prefix, all classes will -# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag -# can be used to specify a prefix (or a list of prefixes) that should be ignored -# while generating the index headers. +# The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes) +# that should be ignored while generating the index headers. The IGNORE_PREFIX +# tag works for classes, function and member names. The entity will be placed in +# the alphabetical list under the first letter of the entity name that remains +# after removing the prefix. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. IGNORE_PREFIX = @@ -1189,7 +1281,7 @@ GENERATE_HTML = YES # The default directory is: html. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_OUTPUT = "_OutputDir_" +HTML_OUTPUT = _OutputDir_ # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each # generated HTML page (for example: .htm, .php, .asp). @@ -1238,7 +1330,7 @@ HTML_FOOTER = # obsolete. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_STYLESHEET = "_ConfDir_/customdoxygen.css" +HTML_STYLESHEET = _ConfDir_/customdoxygen.css # The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined # cascading style sheets that are included after the standard style sheets @@ -1248,7 +1340,12 @@ HTML_STYLESHEET = "_ConfDir_/customdoxygen.css" # Doxygen will copy the style sheet files to the output directory. # Note: The order of the extra style sheet files is of importance (e.g. the last # style sheet in the list overrules the setting of the previous ones in the -# list). For an example see the documentation. +# list). +# Note: Since the styling of scrollbars can currently not be overruled in +# Webkit/Chromium, the styling will be left out of the default doxygen.css if +# one or more extra stylesheets have been specified. So if scrollbar +# customization is desired it has to be added explicitly. For an example see the +# documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_STYLESHEET = @@ -1263,9 +1360,22 @@ HTML_EXTRA_STYLESHEET = HTML_EXTRA_FILES = +# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output +# should be rendered with a dark or light theme. +# Possible values are: LIGHT always generate light mode output, DARK always +# generate dark mode output, AUTO_LIGHT automatically set the mode according to +# the user preference, use light mode if no preference is set (the default), +# AUTO_DARK automatically set the mode according to the user preference, use +# dark mode if no preference is set and TOGGLE allow to user to switch between +# light and dark mode via a button. +# The default value is: AUTO_LIGHT. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE = AUTO_LIGHT + # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the style sheet and background images according to -# this color. Hue is specified as an angle on a colorwheel, see +# this color. Hue is specified as an angle on a color-wheel, see # https://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. @@ -1275,7 +1385,7 @@ HTML_EXTRA_FILES = HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors -# in the HTML output. For a value of 0 the output will use grayscales only. A +# in the HTML output. For a value of 0 the output will use gray-scales only. A # value of 255 will produce the most vivid colors. # Minimum value: 0, maximum value: 255, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. @@ -1336,10 +1446,11 @@ HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development -# environment (see: https://developer.apple.com/xcode/), introduced with OSX -# 10.5 (Leopard). To create a documentation set, doxygen will generate a -# Makefile in the HTML output directory. Running make will produce the docset in -# that directory and running make install will install the docset in +# environment (see: +# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To +# create a documentation set, doxygen will generate a Makefile in the HTML +# output directory. Running make will produce the docset in that directory and +# running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at # startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy # genXcode/_index.html for more information. @@ -1356,6 +1467,13 @@ GENERATE_DOCSET = NO DOCSET_FEEDNAME = "_ProjectName_ documentation" +# This tag determines the URL of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDURL = + # This tag specifies a string that should uniquely identify the documentation # set bundle. This should be a reverse domain-name style string, e.g. # com.mycompany.MyDocSet. Doxygen will append .docset to the name. @@ -1381,8 +1499,12 @@ DOCSET_PUBLISHER_NAME = _ProjectName_ # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop -# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on -# Windows. +# on Windows. In the beginning of 2021 Microsoft took the original page, with +# a.o. the download links, offline the HTML help workshop was already many years +# in maintenance mode). You can download the HTML help workshop from the web +# archives at Installation executable (see: +# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo +# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe). # # The HTML Help Workshop contains a compiler that can convert all HTML output # generated by doxygen into a single compiled HTML file (.chm). Compiled HTML @@ -1457,7 +1579,8 @@ QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace -# (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). +# (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1465,8 +1588,8 @@ QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual -# Folders (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual- -# folders). +# Folders (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1474,16 +1597,16 @@ QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom -# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- -# filters). +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom -# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- -# filters). +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_ATTRS = @@ -1495,9 +1618,9 @@ QHP_CUST_FILTER_ATTRS = QHP_SECT_FILTER_ATTRS = -# The QHG_LOCATION tag can be used to specify the location of Qt's -# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the -# generated .qhp file. +# The QHG_LOCATION tag can be used to specify the location (absolute path +# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to +# run qhelpgenerator on the generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. QHG_LOCATION = @@ -1540,16 +1663,28 @@ DISABLE_INDEX = NO # to work a browser that supports JavaScript, DHTML, CSS and frames is required # (i.e. any modern browser). Windows users are probably better off using the # HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can -# further fine-tune the look of the index. As an example, the default style -# sheet generated by doxygen has an example that shows how to put an image at -# the root of the tree instead of the PROJECT_NAME. Since the tree basically has -# the same information as the tab index, you could consider setting -# DISABLE_INDEX to YES when enabling this option. +# further fine tune the look of the index (see "Fine-tuning the output"). As an +# example, the default style sheet generated by doxygen has an example that +# shows how to put an image at the root of the tree instead of the PROJECT_NAME. +# Since the tree basically has the same information as the tab index, you could +# consider setting DISABLE_INDEX to YES when enabling this option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = ALL +# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the +# FULL_SIDEBAR option determines if the side bar is limited to only the treeview +# area (value NO) or if it should extend to the full height of the window (value +# YES). Setting this to YES gives a layout similar to +# https://docs.readthedocs.io with more room for contents, but less room for the +# project logo, title, and description. If either GENERATE_TREEVIEW or +# DISABLE_INDEX is set to NO, this option has no effect. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FULL_SIDEBAR = NO + # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that # doxygen will group on one line in the generated HTML documentation. # @@ -1574,6 +1709,13 @@ TREEVIEW_WIDTH = 250 EXT_LINKS_IN_WINDOW = NO +# If the OBFUSCATE_EMAILS tag is set to YES, doxygen will obfuscate email +# addresses. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +OBFUSCATE_EMAILS = YES + # If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg # tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see # https://inkscape.org) to generate formulas as SVG images instead of PNGs for @@ -1594,17 +1736,6 @@ HTML_FORMULA_FORMAT = png FORMULA_FONTSIZE = 10 -# Use the FORMULA_TRANSPARENT tag to determine whether or not the images -# generated for formulas are transparent PNGs. Transparent PNGs are not -# supported properly for IE 6.0, but are supported on all modern browsers. -# -# Note that when changing this option you need to delete any form_*.png files in -# the HTML output directory before the changes have effect. -# The default value is: YES. -# This tag requires that the tag GENERATE_HTML is set to YES. - -FORMULA_TRANSPARENT = YES - # The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands # to create new LaTeX commands to be used in formulas as building blocks. See # the section "Including formulas" for details. @@ -1622,11 +1753,29 @@ FORMULA_MACROFILE = USE_MATHJAX = NO +# With MATHJAX_VERSION it is possible to specify the MathJax version to be used. +# Note that the different versions of MathJax have different requirements with +# regards to the different settings, so it is possible that also other MathJax +# settings have to be changed when switching between the different MathJax +# versions. +# Possible values are: MathJax_2 and MathJax_3. +# The default value is: MathJax_2. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_VERSION = MathJax_2 + # When MathJax is enabled you can set the default output format to be used for -# the MathJax output. See the MathJax site (see: -# http://docs.mathjax.org/en/latest/output.html) for more details. +# the MathJax output. For more details about the output format see MathJax +# version 2 (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3 +# (see: +# http://docs.mathjax.org/en/latest/web/components/output.html). # Possible values are: HTML-CSS (which is slower, but has the best -# compatibility), NativeMML (i.e. MathML) and SVG. +# compatibility. This is the name for Mathjax version 2, for MathJax version 3 +# this will be translated into chtml), NativeMML (i.e. MathML. Only supported +# for NathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This +# is the name for Mathjax version 3, for MathJax version 2 this will be +# translated into HTML-CSS) and SVG. # The default value is: HTML-CSS. # This tag requires that the tag USE_MATHJAX is set to YES. @@ -1639,22 +1788,29 @@ MATHJAX_FORMAT = HTML-CSS # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of -# MathJax from https://www.mathjax.org before deployment. -# The default value is: https://cdn.jsdelivr.net/npm/mathjax@2. +# MathJax from https://www.mathjax.org before deployment. The default value is: +# - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2 +# - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3 # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = http://www.mathjax.org/mathjax # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # extension names that should be enabled during MathJax rendering. For example +# for MathJax version 2 (see +# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions): # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# For example for MathJax version 3 (see +# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html): +# MATHJAX_EXTENSIONS = ams # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces # of code that will be used on startup of the MathJax code. See the MathJax site -# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. @@ -1701,7 +1857,8 @@ SERVER_BASED_SEARCH = NO # # Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library -# Xapian (see: https://xapian.org/). +# Xapian (see: +# https://xapian.org/). # # See the section "External Indexing and Searching" for details. # The default value is: NO. @@ -1714,8 +1871,9 @@ EXTERNAL_SEARCH = NO # # Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library -# Xapian (see: https://xapian.org/). See the section "External Indexing and -# Searching" for details. +# Xapian (see: +# https://xapian.org/). See the section "External Indexing and Searching" for +# details. # This tag requires that the tag SEARCHENGINE is set to YES. SEARCHENGINE_URL = @@ -1824,29 +1982,31 @@ PAPER_TYPE = a4 EXTRA_PACKAGES = "_LatexExtras_" -# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the -# generated LaTeX document. The header should contain everything until the first -# chapter. If it is left blank doxygen will generate a standard header. See -# section "Doxygen usage" for information on how to let doxygen write the -# default header to a separate file. +# The LATEX_HEADER tag can be used to specify a user-defined LaTeX header for +# the generated LaTeX document. The header should contain everything until the +# first chapter. If it is left blank doxygen will generate a standard header. It +# is highly recommended to start with a default header using +# doxygen -w latex new_header.tex new_footer.tex new_stylesheet.sty +# and then modify the file new_header.tex. See also section "Doxygen usage" for +# information on how to generate the default header that doxygen normally uses. # -# Note: Only use a user-defined header if you know what you are doing! The -# following commands have a special meaning inside the header: $title, -# $datetime, $date, $doxygenversion, $projectname, $projectnumber, -# $projectbrief, $projectlogo. Doxygen will replace $title with the empty -# string, for the replacement values of the other commands the user is referred -# to HTML_HEADER. +# Note: Only use a user-defined header if you know what you are doing! +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. The following +# commands have a special meaning inside the header (and footer): For a +# description of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_HEADER = -# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the -# generated LaTeX document. The footer should contain everything after the last -# chapter. If it is left blank doxygen will generate a standard footer. See +# The LATEX_FOOTER tag can be used to specify a user-defined LaTeX footer for +# the generated LaTeX document. The footer should contain everything after the +# last chapter. If it is left blank doxygen will generate a standard footer. See # LATEX_HEADER for more information on how to generate a default footer and what -# special commands can be used inside the footer. -# -# Note: Only use a user-defined footer if you know what you are doing! +# special commands can be used inside the footer. See also section "Doxygen +# usage" for information on how to generate the default footer that doxygen +# normally uses. Note: Only use a user-defined footer if you know what you are +# doing! # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_FOOTER = @@ -1891,8 +2051,7 @@ USE_PDFLATEX = YES # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode # command to the generated LaTeX files. This will instruct LaTeX to keep running -# if errors occur, instead of asking the user for help. This option is also used -# when generating formulas in HTML. +# if errors occur, instead of asking the user for help. # The default value is: NO. # This tag requires that the tag GENERATE_LATEX is set to YES. @@ -1905,16 +2064,6 @@ LATEX_BATCHMODE = YES LATEX_HIDE_INDICES = YES -# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source -# code with syntax highlighting in the LaTeX output. -# -# Note that which sources are shown also depends on other settings such as -# SOURCE_BROWSER. -# The default value is: NO. -# This tag requires that the tag GENERATE_LATEX is set to YES. - -LATEX_SOURCE_CODE = NO - # The LATEX_BIB_STYLE tag can be used to specify the style to use for the # bibliography, e.g. plainnat, or ieeetr. See # https://en.wikipedia.org/wiki/BibTeX and \cite for more info. @@ -1995,16 +2144,6 @@ RTF_STYLESHEET_FILE = RTF_EXTENSIONS_FILE = -# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code -# with syntax highlighting in the RTF output. -# -# Note that which sources are shown also depends on other settings such as -# SOURCE_BROWSER. -# The default value is: NO. -# This tag requires that the tag GENERATE_RTF is set to YES. - -RTF_SOURCE_CODE = NO - #--------------------------------------------------------------------------- # Configuration options related to the man page output #--------------------------------------------------------------------------- @@ -2101,15 +2240,6 @@ GENERATE_DOCBOOK = NO DOCBOOK_OUTPUT = docbook -# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the -# program listings (including syntax highlighting and cross-referencing -# information) to the DOCBOOK output. Note that enabling this will significantly -# increase the size of the DOCBOOK output. -# The default value is: NO. -# This tag requires that the tag GENERATE_DOCBOOK is set to YES. - -DOCBOOK_PROGRAMLISTING = NO - #--------------------------------------------------------------------------- # Configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- @@ -2196,7 +2326,8 @@ SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by the -# preprocessor. +# preprocessor. Note that the INCLUDE_PATH is not recursive, so the setting of +# RECURSIVE has no effect here. # This tag requires that the tag SEARCH_INCLUDES is set to YES. INCLUDE_PATH = @@ -2288,15 +2419,6 @@ EXTERNAL_PAGES = YES # Configuration options related to the dot tool #--------------------------------------------------------------------------- -# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram -# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to -# NO turns the diagrams off. Note that this option also works with HAVE_DOT -# disabled, but it is recommended to install and use dot, since it yields more -# powerful graphs. -# The default value is: YES. - -CLASS_DIAGRAMS = YES - # You can include diagrams made with dia in doxygen documentation. Doxygen will # then run dia to produce the diagram and insert it in the documentation. The # DIA_PATH tag allows you to specify the directory where the dia binary resides. @@ -2329,35 +2451,50 @@ HAVE_DOT = _HaveDot_ DOT_NUM_THREADS = 0 -# When you want a differently looking font in the dot files that doxygen -# generates you can specify the font name using DOT_FONTNAME. You need to make -# sure dot is able to find the font, which can be done by putting it in a -# standard location or by setting the DOTFONTPATH environment variable or by -# setting DOT_FONTPATH to the directory containing the font. -# The default value is: Helvetica. +# DOT_COMMON_ATTR is common attributes for nodes, edges and labels of +# subgraphs. When you want a differently looking font in the dot files that +# doxygen generates you can specify fontname, fontcolor and fontsize attributes. +# For details please see Node, +# Edge and Graph Attributes specification You need to make sure dot is able +# to find the font, which can be done by putting it in a standard location or by +# setting the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the +# directory containing the font. Default graphviz fontsize is 14. +# The default value is: fontname=Helvetica,fontsize=10. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_COMMON_ATTR = "fontname=Arial,fontsize=10" + +# DOT_EDGE_ATTR is concatenated with DOT_COMMON_ATTR. For elegant style you can +# add 'arrowhead=open, arrowtail=open, arrowsize=0.5'. Complete documentation about +# arrows shapes. +# The default value is: labelfontname=Helvetica,labelfontsize=10. # This tag requires that the tag HAVE_DOT is set to YES. -DOT_FONTNAME = Arial +DOT_EDGE_ATTR = "labelfontname=Arial,labelfontsize=10" -# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of -# dot graphs. -# Minimum value: 4, maximum value: 24, default value: 10. +# DOT_NODE_ATTR is concatenated with DOT_COMMON_ATTR. For view without boxes +# around nodes set 'shape=plain' or 'shape=plaintext' Shapes specification +# The default value is: shape=box,height=0.2,width=0.4. # This tag requires that the tag HAVE_DOT is set to YES. -DOT_FONTSIZE = 10 +DOT_NODE_ATTR = "shape=box,height=0.2,width=0.4" -# By default doxygen will tell dot to use the default font as specified with -# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set -# the path where dot can find it using this tag. +# You can set the path where dot can find font specified with fontname in +# DOT_COMMON_ATTR and others dot attributes. # This tag requires that the tag HAVE_DOT is set to YES. DOT_FONTPATH = -# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for -# each documented class showing the direct and indirect inheritance relations. -# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO. +# If the CLASS_GRAPH tag is set to YES (or GRAPH) then doxygen will generate a +# graph for each documented class showing the direct and indirect inheritance +# relations. In case HAVE_DOT is set as well dot will be used to draw the graph, +# otherwise the built-in generator will be used. If the CLASS_GRAPH tag is set +# to TEXT the direct and indirect inheritance relations will be shown as texts / +# links. +# Possible values are: NO, YES, TEXT and GRAPH. # The default value is: YES. -# This tag requires that the tag HAVE_DOT is set to YES. CLASS_GRAPH = YES @@ -2371,7 +2508,8 @@ CLASS_GRAPH = YES COLLABORATION_GRAPH = NO # If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for -# groups, showing the direct groups dependencies. +# groups, showing the direct groups dependencies. See also the chapter Grouping +# in the manual. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2394,10 +2532,32 @@ UML_LOOK = NO # but if the number exceeds 15, the total amount of fields shown is limited to # 10. # Minimum value: 0, maximum value: 100, default value: 10. -# This tag requires that the tag HAVE_DOT is set to YES. +# This tag requires that the tag UML_LOOK is set to YES. UML_LIMIT_NUM_FIELDS = 10 +# If the DOT_UML_DETAILS tag is set to NO, doxygen will show attributes and +# methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS +# tag is set to YES, doxygen will add type and arguments for attributes and +# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, doxygen +# will not generate fields with class member information in the UML graphs. The +# class diagrams will look similar to the default class diagrams but using UML +# notation for the relationships. +# Possible values are: NO, YES and NONE. +# The default value is: NO. +# This tag requires that the tag UML_LOOK is set to YES. + +DOT_UML_DETAILS = NO + +# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters +# to display on a single line. If the actual line length exceeds this threshold +# significantly it will wrapped across multiple lines. Some heuristics are apply +# to avoid ugly line breaks. +# Minimum value: 0, maximum value: 1000, default value: 17. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_WRAP_THRESHOLD = 17 + # If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and # collaboration graphs will show the relations between templates and their # instances. @@ -2464,6 +2624,13 @@ GRAPHICAL_HIERARCHY = YES DIRECTORY_GRAPH = YES +# The DIR_GRAPH_MAX_DEPTH tag can be used to limit the maximum number of levels +# of child directories generated in directory dependency graphs by dot. +# Minimum value: 1, maximum value: 25, default value: 1. +# This tag requires that the tag DIRECTORY_GRAPH is set to YES. + +DIR_GRAPH_MAX_DEPTH = 1 + # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. For an explanation of the image formats see the section # output formats in the documentation of the dot tool (Graphviz (see: @@ -2517,10 +2684,10 @@ MSCFILE_DIRS = DIAFILE_DIRS = # When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the -# path where java can find the plantuml.jar file. If left blank, it is assumed -# PlantUML is not used or called during a preprocessing step. Doxygen will -# generate a warning when it encounters a \startuml command in this case and -# will not generate output for the diagram. +# path where java can find the plantuml.jar file or to the filename of jar file +# to be used. If left blank, it is assumed PlantUML is not used or called during +# a preprocessing step. Doxygen will generate a warning when it encounters a +# \startuml command in this case and will not generate output for the diagram. PLANTUML_JAR_PATH = @@ -2558,18 +2725,6 @@ DOT_GRAPH_MAX_NODES = 100 MAX_DOT_GRAPH_DEPTH = 3 -# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent -# background. This is disabled by default, because dot on Windows does not seem -# to support this out of the box. -# -# Warning: Depending on the platform used, enabling this option may lead to -# badly anti-aliased labels on the edges of a graph (i.e. they become hard to -# read). -# The default value is: NO. -# This tag requires that the tag HAVE_DOT is set to YES. - -DOT_TRANSPARENT = YES - # Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) support @@ -2582,14 +2737,18 @@ DOT_MULTI_TARGETS = YES # If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page # explaining the meaning of the various boxes and arrows in the dot generated # graphs. +# Note: This tag requires that UML_LOOK isn't set, i.e. the doxygen internal +# graphical representation for inheritance and collaboration diagrams is used. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. GENERATE_LEGEND = YES -# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot +# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate # files that are used to generate the various graphs. +# +# Note: This setting is not only used for dot files but also for msc temporary +# files. # The default value is: YES. -# This tag requires that the tag HAVE_DOT is set to YES. DOT_CLEANUP = YES diff --git a/deps/AMICI/matlab/mtoc/config/customdoxygen.css b/deps/AMICI/matlab/mtoc/config/customdoxygen.css index 685cdb5d0..8a2de47fb 100644 --- a/deps/AMICI/matlab/mtoc/config/customdoxygen.css +++ b/deps/AMICI/matlab/mtoc/config/customdoxygen.css @@ -138,11 +138,11 @@ a.elRef { } a.code, a.code:visited, a.line, a.line:visited { - color: #4665A2; + color: #4665A2; } a.codeRef, a.codeRef:visited, a.lineRef, a.lineRef:visited { - color: #4665A2; + color: #4665A2; } /* @end */ @@ -294,7 +294,7 @@ p.formulaDsp { } img.formulaDsp { - + } img.formulaInl { @@ -352,20 +352,20 @@ span.charliteral { color: #008080 } -span.vhdldigit { - color: #ff00ff +span.vhdldigit { + color: #ff00ff } -span.vhdlchar { - color: #000000 +span.vhdlchar { + color: #000000 } -span.vhdlkeyword { - color: #700070 +span.vhdlkeyword { + color: #700070 } -span.vhdllogic { - color: #ff0000 +span.vhdllogic { + color: #ff0000 } blockquote { @@ -560,9 +560,9 @@ table.memberdecls { } .memdoc, dl.reflist dd { - border-bottom: 1px solid #A8B8D9; - border-left: 1px solid #A8B8D9; - border-right: 1px solid #A8B8D9; + border-bottom: 1px solid #A8B8D9; + border-left: 1px solid #A8B8D9; + border-right: 1px solid #A8B8D9; padding: 6px 10px 2px 10px; border-top-width: 0; background-image:url('nav_g.png'); @@ -613,18 +613,18 @@ dl.reflist dd { .params, .retval, .exception, .tparams { margin-left: 0px; padding-left: 0px; -} +} .params .paramname, .retval .paramname { font-weight: bold; vertical-align: top; } - + .params .paramtype { font-style: italic; vertical-align: top; -} - +} + .params .paramdir { font-family: "courier new",courier,monospace; vertical-align: top; @@ -876,8 +876,8 @@ table.fieldtable { .fieldtable td.fielddoc p:first-child { margin-top: 0px; -} - +} + .fieldtable td.fielddoc p:last-child { margin-bottom: 2px; } @@ -950,7 +950,7 @@ table.fieldtable { color: #283A5D; font-family: 'Lucida Grande',Geneva,Helvetica,Arial,sans-serif; text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9); - text-decoration: none; + text-decoration: none; } .navpath li.navelem a:hover @@ -979,7 +979,7 @@ div.summary padding-right: 5px; width: 50%; text-align: right; -} +} div.summary a { @@ -1091,12 +1091,12 @@ dl.section dd { vertical-align: bottom; border-collapse: separate; } - + #projectlogo img -{ +{ border: 0px none; } - + #projectalign { vertical-align: middle; @@ -1107,7 +1107,7 @@ dl.section dd { font-size: 200%; font-weight: bold; } - + #projectbrief { font-size: 100%; @@ -1206,7 +1206,7 @@ div.toc ul { list-style: none outside none; border: medium none; padding: 0px; -} +} div.toc li.level1 { margin-left: 0px; @@ -1522,5 +1522,3 @@ div#nav-path ul { border-left: 1px solid #d1d5da; border-right: 1px solid #d1d5da; } - - diff --git a/deps/AMICI/matlab/mtoc/config/latexextras.template b/deps/AMICI/matlab/mtoc/config/latexextras.template index 352d89e27..5abd2c045 100644 --- a/deps/AMICI/matlab/mtoc/config/latexextras.template +++ b/deps/AMICI/matlab/mtoc/config/latexextras.template @@ -1,7 +1,7 @@ % Additional LaTeX inclusions for mtoc++/doxygen tools % % Use the _ConfDir_ tag to insert the folder where this file resides. -% Thus you can include more custom latex files/styles/packages which reside in this folder +% Thus you can include more custom latex files/styles/packages which reside in this folder % Default packages \usepackage{amsmath} @@ -12,4 +12,4 @@ %\input{_ConfDirFwdSlash_/myexternalstyle.sty} \setcounter{tocdepth}{2} -%\uchyph=0 \ No newline at end of file +%\uchyph=0 diff --git a/deps/AMICI/matlab/mtoc/config/mtocpp.conf b/deps/AMICI/matlab/mtoc/config/mtocpp.conf index 2a792649a..9be3840ae 100644 --- a/deps/AMICI/matlab/mtoc/config/mtocpp.conf +++ b/deps/AMICI/matlab/mtoc/config/mtocpp.conf @@ -71,9 +71,9 @@ COPY_TYPIFIED_FIELD_DOCU := false; # By default their documentation strings are ignored. GENERATE_SUBFUNTION_DOCUMENTATION := true; -# Leave this ## there, it marks the end of variable definitions +# Leave this ## there, it marks the end of variable definitions # and switches the parser to mtoc++ rules! -## +## # ########################### mtoc++ rules ############################ @@ -102,6 +102,6 @@ GENERATE_SUBFUNTION_DOCUMENTATION := true; # # }; # } - + # add(doc) = """ docu for all functions !!! """; # add(extra) = """ extra comments: @ref s_rand !!!! """; diff --git a/deps/AMICI/matlab/symbolic/am_and.m b/deps/AMICI/matlab/symbolic/am_and.m index 6362b6e8a..5d8f5a1e0 100644 --- a/deps/AMICI/matlab/symbolic/am_and.m +++ b/deps/AMICI/matlab/symbolic/am_and.m @@ -8,4 +8,4 @@ % Return values: % fun: logical value, negative for false, positive for true fun = am_min(a,b); -end \ No newline at end of file +end diff --git a/deps/AMICI/matlab/symbolic/am_eq.m b/deps/AMICI/matlab/symbolic/am_eq.m index 6ede0e560..a366dbbe4 100644 --- a/deps/AMICI/matlab/symbolic/am_eq.m +++ b/deps/AMICI/matlab/symbolic/am_eq.m @@ -2,9 +2,9 @@ % am_eq is currently a placeholder that simply produces an error message % % Parameters: -% varargin: elements for chain of equalities +% varargin: elements for chain of equalities % % Return values: % fun: logical value, negative for false, positive for true error('Logical operator ''eq'' is currently not supported!'); -end \ No newline at end of file +end diff --git a/deps/AMICI/matlab/symbolic/am_ge.m b/deps/AMICI/matlab/symbolic/am_ge.m index 8225db706..28ba88ff8 100644 --- a/deps/AMICI/matlab/symbolic/am_ge.m +++ b/deps/AMICI/matlab/symbolic/am_ge.m @@ -14,4 +14,4 @@ if(nargin>2) fun = am_and(a-b,am_ge(varargin{2:end})); end -end \ No newline at end of file +end diff --git a/deps/AMICI/matlab/symbolic/am_gt.m b/deps/AMICI/matlab/symbolic/am_gt.m index 3c076b3ab..cd2b42033 100644 --- a/deps/AMICI/matlab/symbolic/am_gt.m +++ b/deps/AMICI/matlab/symbolic/am_gt.m @@ -14,4 +14,4 @@ if(nargin>2) fun = am_and(a-b,am_gt(varargin{2:end})); end -end \ No newline at end of file +end diff --git a/deps/AMICI/matlab/symbolic/am_if.m b/deps/AMICI/matlab/symbolic/am_if.m index 6673023a6..729df3415 100644 --- a/deps/AMICI/matlab/symbolic/am_if.m +++ b/deps/AMICI/matlab/symbolic/am_if.m @@ -21,4 +21,4 @@ fun = falsepart; end end -end \ No newline at end of file +end diff --git a/deps/AMICI/matlab/symbolic/am_le.m b/deps/AMICI/matlab/symbolic/am_le.m index 3016b7d6b..49fda8572 100644 --- a/deps/AMICI/matlab/symbolic/am_le.m +++ b/deps/AMICI/matlab/symbolic/am_le.m @@ -14,4 +14,4 @@ if(nargin>2) fun = am_and(b-a,am_le(varargin{2:end})); end -end \ No newline at end of file +end diff --git a/deps/AMICI/matlab/symbolic/am_lt.m b/deps/AMICI/matlab/symbolic/am_lt.m index 67ec79f60..53b3006bd 100644 --- a/deps/AMICI/matlab/symbolic/am_lt.m +++ b/deps/AMICI/matlab/symbolic/am_lt.m @@ -14,4 +14,4 @@ if(nargin>2) fun = am_and(b-a,am_lt(varargin{2:end})); end -end \ No newline at end of file +end diff --git a/deps/AMICI/matlab/symbolic/am_min.m b/deps/AMICI/matlab/symbolic/am_min.m index 068d2dc2d..3059eccaf 100644 --- a/deps/AMICI/matlab/symbolic/am_min.m +++ b/deps/AMICI/matlab/symbolic/am_min.m @@ -8,4 +8,4 @@ % Return values: % fun: minimum of a and b fun = -am_max(-a,-b); -end \ No newline at end of file +end diff --git a/deps/AMICI/matlab/symbolic/am_or.m b/deps/AMICI/matlab/symbolic/am_or.m index e94807006..0c5d48423 100644 --- a/deps/AMICI/matlab/symbolic/am_or.m +++ b/deps/AMICI/matlab/symbolic/am_or.m @@ -8,4 +8,4 @@ % Return values: % fun: logical value, negative for false, positive for true fun = am_max(a,b); -end \ No newline at end of file +end diff --git a/deps/AMICI/matlab/symbolic/am_piecewise.m b/deps/AMICI/matlab/symbolic/am_piecewise.m index 7a9be67bf..07b2f6205 100644 --- a/deps/AMICI/matlab/symbolic/am_piecewise.m +++ b/deps/AMICI/matlab/symbolic/am_piecewise.m @@ -10,4 +10,3 @@ % fun: return value, piece if condition is true, default if not fun = am_if(condition,piece,default); end - diff --git a/deps/AMICI/matlab/symbolic/am_spline_pos.m b/deps/AMICI/matlab/symbolic/am_spline_pos.m index 32775d48e..de3f9e2c4 100644 --- a/deps/AMICI/matlab/symbolic/am_spline_pos.m +++ b/deps/AMICI/matlab/symbolic/am_spline_pos.m @@ -28,6 +28,6 @@ end str = strcat('(',strcat(strcat(str, char(varargin{n})), ')')); str = strrep(str, ' ', ''); - + splinefun = sym(strcat('spline_pos', str)); end diff --git a/deps/AMICI/matlab/symbolic/am_stepfun.m b/deps/AMICI/matlab/symbolic/am_stepfun.m index 53749f1d4..3cc305257 100644 --- a/deps/AMICI/matlab/symbolic/am_stepfun.m +++ b/deps/AMICI/matlab/symbolic/am_stepfun.m @@ -1,5 +1,5 @@ function fun = am_stepfun(t,tstart,vstart,tend,vend) -% am_stepfun is the amici implementation of the step function +% am_stepfun is the amici implementation of the step function % % Parameters: % t: input variable @type sym @@ -11,4 +11,4 @@ % Return values: % fun: 0 before tstart, vstart between tstart and tend and vend after tend fun = heaviside(t-tstart)*vstart - heaviside(t-tend)*(vstart-vend); -end \ No newline at end of file +end diff --git a/deps/AMICI/matlab/symbolic/am_xor.m b/deps/AMICI/matlab/symbolic/am_xor.m index 9f8b26a23..93514c6c5 100644 --- a/deps/AMICI/matlab/symbolic/am_xor.m +++ b/deps/AMICI/matlab/symbolic/am_xor.m @@ -9,4 +9,4 @@ % fun: logical value, negative for false, positive for true fun = am_and(am_or(a,b),-am_and(a,b)); -end \ No newline at end of file +end diff --git a/deps/AMICI/matlab/tests/testModels.m b/deps/AMICI/matlab/tests/testModels.m index 047f86daf..479dcda19 100644 --- a/deps/AMICI/matlab/tests/testModels.m +++ b/deps/AMICI/matlab/tests/testModels.m @@ -71,9 +71,6 @@ function testModels() options = rmfield(options,'kappa'); t = options.ts; options = rmfield(options,'ts'); - if isempty(options.newton_preeq) - options.newton_preeq = false; - end if(isfield(options, 'sx0')) options.sx0 = transpose(options.sx0); end diff --git a/deps/AMICI/models/model_calvetti/CMakeLists.txt b/deps/AMICI/models/model_calvetti/CMakeLists.txt index 4832c4953..2d1347b55 100644 --- a/deps/AMICI/models/model_calvetti/CMakeLists.txt +++ b/deps/AMICI/models/model_calvetti/CMakeLists.txt @@ -1,102 +1,109 @@ -cmake_minimum_required(VERSION 3.3) - -if(POLICY CMP0060) - cmake_policy(SET CMP0060 NEW) -endif(POLICY CMP0060) -if(POLICY CMP0065) - cmake_policy(SET CMP0065 NEW) -endif(POLICY CMP0065) -if(POLICY CMP0074) - # Use package_ROOT environment variables - cmake_policy(SET CMP0074 NEW) -endif(POLICY CMP0074) +# Build AMICI model +cmake_minimum_required(VERSION 3.15) project(model_calvetti) -set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_POSITION_INDEPENDENT_CODE ON) include(CheckCXXCompilerFlag) -set(MY_CXX_FLAGS -Wall -Wno-unused-function -Wno-unused-variable -Wno-unused-but-set-variable) -foreach(FLAG ${MY_CXX_FLAGS}) - unset(CUR_FLAG_SUPPORTED CACHE) - CHECK_CXX_COMPILER_FLAG(${FLAG} CUR_FLAG_SUPPORTED) - if(${CUR_FLAG_SUPPORTED}) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FLAG}") - endif() -endforeach(FLAG) - -find_package(Amici HINTS ${CMAKE_CURRENT_LIST_DIR}/../../build) +set(MY_CXX_FLAGS -Wall -Wno-unused-function -Wno-unused-variable) +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + list(APPEND MY_CXX_FLAGS -Wno-unused-but-set-variable) +endif() +foreach(flag ${MY_CXX_FLAGS}) + unset(CUR_FLAG_SUPPORTED CACHE) + check_cxx_compiler_flag(-Werror ${flag} CUR_FLAG_SUPPORTED) + if(${CUR_FLAG_SUPPORTED}) + string(APPEND CMAKE_CXX_FLAGS " ${flag}") + endif() +endforeach() + +if(DEFINED ENV{AMICI_CXXFLAGS}) + message(STATUS "Appending flags from AMICI_CXXFLAGS: $ENV{AMICI_CXXFLAGS}") + add_compile_options("$ENV{AMICI_CXXFLAGS}") +endif() +if(DEFINED ENV{AMICI_LDFLAGS}) + message(STATUS "Appending flags from AMICI_LDFLAGS: $ENV{AMICI_LDFLAGS}") + link_libraries("$ENV{AMICI_LDFLAGS}") +endif() + +find_package(Amici REQUIRED HINTS + ${CMAKE_CURRENT_LIST_DIR}/../../build) message(STATUS "Found AMICI ${Amici_DIR}") set(MODEL_DIR ${CMAKE_CURRENT_LIST_DIR}) -set(SRC_LIST_LIB ${MODEL_DIR}/model_calvetti_JSparse.cpp -${MODEL_DIR}/model_calvetti_Jy.cpp -${MODEL_DIR}/model_calvetti_M.cpp -${MODEL_DIR}/model_calvetti_dJydsigma.cpp -${MODEL_DIR}/model_calvetti_dJydy.cpp -${MODEL_DIR}/model_calvetti_dwdx.cpp -${MODEL_DIR}/model_calvetti_dydx.cpp -${MODEL_DIR}/model_calvetti_root.cpp -${MODEL_DIR}/model_calvetti_sigmay.cpp -${MODEL_DIR}/model_calvetti_w.cpp -${MODEL_DIR}/model_calvetti_x0.cpp -${MODEL_DIR}/model_calvetti_xdot.cpp -${MODEL_DIR}/model_calvetti_y.cpp - -${MODEL_DIR}/wrapfunctions.cpp -) +set(SRC_LIST_LIB ${MODEL_DIR}/JSparse.cpp +${MODEL_DIR}/Jy.cpp +${MODEL_DIR}/M.cpp +${MODEL_DIR}/dJydsigma.cpp +${MODEL_DIR}/dJydy.cpp +${MODEL_DIR}/dwdx.cpp +${MODEL_DIR}/dydx.cpp +${MODEL_DIR}/root.cpp +${MODEL_DIR}/sigmay.cpp +${MODEL_DIR}/w.cpp +${MODEL_DIR}/x0.cpp +${MODEL_DIR}/xdot.cpp +${MODEL_DIR}/y.cpp + ${MODEL_DIR}/wrapfunctions.cpp) add_library(${PROJECT_NAME} ${SRC_LIST_LIB}) add_library(model ALIAS ${PROJECT_NAME}) -# This option can be helpful when using the Intel compiler and compilation of -# wrapfunctions.cpp fails due to insufficient memory. -option(ENABLE_WRAPFUNCTIONS_OPTIMIZATIONS "Enable compiler optimizations for wrapfunctions.cpp?" ON) -if(NOT ENABLE_WRAPFUNCTIONS_OPTIMIZATIONS) - set_source_files_properties(wrapfunctions.cpp PROPERTIES COMPILE_FLAGS -O0) -endif() +# Some special functions require boost +# +# TODO: set some flag during code generation whether the given model requires +# boost. for now, try to find it, add include directories and link against it. +# let the compiler/linker error if it is required but not found +find_package(Boost) target_include_directories(${PROJECT_NAME} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") -target_link_libraries(${PROJECT_NAME} - PUBLIC Upstream::amici -) +target_link_libraries( + ${PROJECT_NAME} + PUBLIC Upstream::amici + PRIVATE $<$:Boost::boost>) -set(SRC_LIST_EXE main.cpp) - -add_executable(simulate_${PROJECT_NAME} ${SRC_LIST_EXE}) +if(NOT "${AMICI_PYTHON_BUILD_EXT_ONLY}") + set(SRC_LIST_EXE main.cpp) + add_executable(simulate_${PROJECT_NAME} ${SRC_LIST_EXE}) + target_link_libraries(simulate_${PROJECT_NAME} ${PROJECT_NAME}) +endif() -target_link_libraries(simulate_${PROJECT_NAME} ${PROJECT_NAME}) +# 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}) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 --coverage") + string(APPEND CMAKE_CXX_FLAGS_DEBUG " --coverage") + string(APPEND CMAKE_EXE_LINKER_FLAGS_DEBUG " --coverage") endif() -## SWIG +# SWIG option(ENABLE_SWIG "Build swig/python library?" ON) if(ENABLE_SWIG) - if(NOT(${CMAKE_VERSION} VERSION_LESS 3.8)) - add_subdirectory(swig) - else() - message(WARNING "Unable to build SWIG interface, upgrade CMake to >=3.8.") - endif() + add_subdirectory(swig) endif() - # include(GNUInstallDirs) -install(TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME}Targets - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} - PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} -) -export(EXPORT ${PROJECT_NAME}Targets FILE ${PROJECT_NAME}Config.cmake - NAMESPACE Upstream:: - ) +install( + TARGETS ${PROJECT_NAME} + EXPORT ${PROJECT_NAME}Targets + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + INCLUDES + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +export( + EXPORT ${PROJECT_NAME}Targets + FILE ${PROJECT_NAME}Config.cmake + NAMESPACE Upstream::) # - diff --git a/deps/AMICI/models/model_calvetti/model_calvetti_JSparse.cpp b/deps/AMICI/models/model_calvetti/JSparse.cpp similarity index 100% rename from deps/AMICI/models/model_calvetti/model_calvetti_JSparse.cpp rename to deps/AMICI/models/model_calvetti/JSparse.cpp diff --git a/deps/AMICI/models/model_calvetti/model_calvetti_Jy.cpp b/deps/AMICI/models/model_calvetti/Jy.cpp similarity index 100% rename from deps/AMICI/models/model_calvetti/model_calvetti_Jy.cpp rename to deps/AMICI/models/model_calvetti/Jy.cpp diff --git a/deps/AMICI/models/model_calvetti/model_calvetti_M.cpp b/deps/AMICI/models/model_calvetti/M.cpp similarity index 100% rename from deps/AMICI/models/model_calvetti/model_calvetti_M.cpp rename to deps/AMICI/models/model_calvetti/M.cpp diff --git a/deps/AMICI/models/model_calvetti/model_calvetti_dJydsigma.cpp b/deps/AMICI/models/model_calvetti/dJydsigma.cpp similarity index 100% rename from deps/AMICI/models/model_calvetti/model_calvetti_dJydsigma.cpp rename to deps/AMICI/models/model_calvetti/dJydsigma.cpp diff --git a/deps/AMICI/models/model_calvetti/model_calvetti_dJydy.cpp b/deps/AMICI/models/model_calvetti/dJydy.cpp similarity index 100% rename from deps/AMICI/models/model_calvetti/model_calvetti_dJydy.cpp rename to deps/AMICI/models/model_calvetti/dJydy.cpp diff --git a/deps/AMICI/models/model_calvetti/model_calvetti_dwdx.cpp b/deps/AMICI/models/model_calvetti/dwdx.cpp similarity index 97% rename from deps/AMICI/models/model_calvetti/model_calvetti_dwdx.cpp rename to deps/AMICI/models/model_calvetti/dwdx.cpp index f52276f40..9066d0e95 100644 --- a/deps/AMICI/models/model_calvetti/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) { +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) { 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/model_calvetti_dydx.cpp b/deps/AMICI/models/model_calvetti/dydx.cpp similarity index 100% rename from deps/AMICI/models/model_calvetti/model_calvetti_dydx.cpp rename to deps/AMICI/models/model_calvetti/dydx.cpp diff --git a/deps/AMICI/models/model_calvetti/model_calvetti.h b/deps/AMICI/models/model_calvetti/model_calvetti.h index 0a326de31..828be8272 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) 2bd39716102b9842354964196889d40d2eb2731c */ +/* Generated by amiwrap (R2017b) 223c5075608273b17f304556d0e8ccb41233bd21 */ #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); +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 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); -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); +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 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, + 0, 1, 38, 53, @@ -52,6 +53,9 @@ class Model_model_calvetti : public amici::Model_DAE { 0, 0, {}, + 0, + 0, + 0, 26, 5, 3 @@ -67,7 +71,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 "2bd39716102b9842354964196889d40d2eb2731c"; }; + std::string getAmiciCommit() const override { return "223c5075608273b17f304556d0e8ccb41233bd21"; }; 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); @@ -110,7 +114,7 @@ class Model_model_calvetti : public amici::Model_DAE { void fdeltaqB(double *deltaqB, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const int ip, const int ie, const realtype *xdot, const realtype *xdot_old, const realtype *xB) override { } - void fdeltasx(double *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) override { + void fdeltasx(double *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) override { } void fdeltax(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) override { @@ -125,17 +129,17 @@ class Model_model_calvetti : public amici::Model_DAE { void fdrzdx(double *drzdx, const int ie, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h) override { } - void fdsigmaydp(double *dsigmaydp, const realtype t, const realtype *p, const realtype *k, const int ip) override { + void fdsigmaydp(double *dsigmaydp, const realtype t, const realtype *p, const realtype *k, const realtype *y, const int ip) override { } 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) 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 fdwdx(realtype *dwdx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl) override { - dwdx_model_calvetti(dwdx, t, x, p, k, h, w, tcl); + 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 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 { @@ -161,8 +165,8 @@ class Model_model_calvetti : public amici::Model_DAE { void frz(double *rz, const int ie, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h) override { } - void fsigmay(double *sigmay, const realtype t, const realtype *p, const realtype *k) override { - sigmay_model_calvetti(sigmay, t, p, k); + void fsigmay(double *sigmay, const realtype t, const realtype *p, const realtype *k, const realtype *y) override { + sigmay_model_calvetti(sigmay, t, p, k, y); } void fsigmaz(double *sigmaz, const realtype t, const realtype *p, const realtype *k) override { @@ -171,7 +175,7 @@ class Model_model_calvetti : public amici::Model_DAE { void fsrz(double *srz, 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 fstau(double *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) override { + void fstau(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) override { } void fsx0(realtype *sx0, const realtype t,const realtype *x0, const realtype *p, const realtype *k, const int ip) override { @@ -180,8 +184,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) override { - w_model_calvetti(w, t, x, p, k, h, tcl); + 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 fx0(realtype *x0, const realtype t, const realtype *p, const realtype *k) override { diff --git a/deps/AMICI/models/model_calvetti/model_calvetti_root.cpp b/deps/AMICI/models/model_calvetti/root.cpp similarity index 100% rename from deps/AMICI/models/model_calvetti/model_calvetti_root.cpp rename to deps/AMICI/models/model_calvetti/root.cpp diff --git a/deps/AMICI/models/model_calvetti/model_calvetti_sigmay.cpp b/deps/AMICI/models/model_calvetti/sigmay.cpp similarity index 89% rename from deps/AMICI/models/model_calvetti/model_calvetti_sigmay.cpp rename to deps/AMICI/models/model_calvetti/sigmay.cpp index a970a9b28..690d1a01a 100644 --- a/deps/AMICI/models/model_calvetti/model_calvetti_sigmay.cpp +++ b/deps/AMICI/models/model_calvetti/sigmay.cpp @@ -10,7 +10,7 @@ namespace amici { namespace model_model_calvetti{ -void sigmay_model_calvetti(double *sigmay, const realtype t, const realtype *p, const realtype *k) { +void sigmay_model_calvetti(double *sigmay, const realtype t, const realtype *p, const realtype *k, const realtype *y) { sigmay[0] = 1.0; sigmay[1] = 1.0; sigmay[2] = 1.0; diff --git a/deps/AMICI/models/model_calvetti/swig/CMakeLists.txt b/deps/AMICI/models/model_calvetti/swig/CMakeLists.txt index af0f64a76..eb9fe681c 100644 --- a/deps/AMICI/models/model_calvetti/swig/CMakeLists.txt +++ b/deps/AMICI/models/model_calvetti/swig/CMakeLists.txt @@ -1,28 +1,19 @@ -cmake_minimum_required(VERSION 3.8) # swig_add_library - -if(POLICY CMP0078) - cmake_policy(SET CMP0078 OLD) -endif(POLICY CMP0078) -if(POLICY CMP0074) - # Use package_ROOT environment variables - cmake_policy(SET CMP0074 NEW) -endif(POLICY CMP0074) -if(POLICY CMP0086) - cmake_policy(SET CMP0086 NEW) -endif(POLICY CMP0086) +cmake_minimum_required(VERSION 3.15) +if(DEFINED ENV{SWIG}) + set(SWIG_EXECUTABLE $ENV{SWIG}) +endif() find_package(SWIG REQUIRED) include(${SWIG_USE_FILE}) -if(${CMAKE_VERSION} VERSION_LESS "3.12.0") - find_package(PythonLibs REQUIRED) - include_directories(${PYTHON_INCLUDE_DIRS}) - set(Python3_LIBRARIES ${PYTHON_LIBRARIES}) -else() - find_package (Python3 COMPONENTS Development) - include_directories(${Python3_INCLUDE_DIRS}) +if(DEFINED ENV{PYTHON_EXECUTABLE}) + set(Python3_EXECUTABLE $ENV{PYTHON_EXECUTABLE}) endif() +# We don't need "Interpreter" here, but without that, FindPython3 will +# ignore the Python version selected via $Python3_EXECUTABLE +find_package(Python3 COMPONENTS Interpreter Development) +include_directories(${Python3_INCLUDE_DIRS}) set(SWIG_LIBRARY_NAME _${PROJECT_NAME}) set(CMAKE_SWIG_FLAGS "") @@ -30,17 +21,38 @@ set_source_files_properties(${PROJECT_NAME}.i PROPERTIES CPLUSPLUS ON) # swig does not use INTERFACE_INCLUDE_DIRS of linked libraries, so add manually get_target_property(AMICI_INCLUDE_DIRS Upstream::amici INTERFACE_INCLUDE_DIRECTORIES) -include_directories(${AMICI_INCLUDE_DIRS} ..) +include_directories(${AMICI_INCLUDE_DIRS} .. ${AMICI_INCLUDE_DIRS}/../swig) -swig_add_library(${PROJECT_NAME} +swig_add_library(${SWIG_LIBRARY_NAME} TYPE MODULE LANGUAGE python SOURCES ${PROJECT_NAME}.i) -swig_link_libraries(${PROJECT_NAME} + +set_target_properties(${SWIG_LIBRARY_NAME} + PROPERTIES + SWIG_USE_TARGET_INCLUDE_DIRECTORIES TRUE + PREFIX "" +) + +# Python extension suffix +execute_process( + COMMAND ${Python3_EXECUTABLE} -c + "import sysconfig; print(sysconfig.get_config_var('EXT_SUFFIX'))" + OUTPUT_VARIABLE PY_EXT_SUFFIX OUTPUT_STRIP_TRAILING_WHITESPACE) +if(NOT "${PY_EXT_SUFFIX}" STREQUAL "") + message(STATUS "Python extension suffix is ${PY_EXT_SUFFIX}") + set_target_properties(${SWIG_LIBRARY_NAME} PROPERTIES SUFFIX "${PY_EXT_SUFFIX}" ) +endif() + + +swig_link_libraries(${SWIG_LIBRARY_NAME} ${Python3_LIBRARIES} model) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.py + $ DESTINATION .) + # configure module setup script set(SETUP_PY_IN ${Amici_DIR}/model_setup.template.py) set(SETUP_PY_OUT ${CMAKE_CURRENT_BINARY_DIR}/setup.py) diff --git a/deps/AMICI/models/model_calvetti/swig/model_calvetti.i b/deps/AMICI/models/model_calvetti/swig/model_calvetti.i index 29a288c12..a90474587 100644 --- a/deps/AMICI/models/model_calvetti/swig/model_calvetti.i +++ b/deps/AMICI/models/model_calvetti/swig/model_calvetti.i @@ -10,5 +10,13 @@ using namespace amici; %} +// Make model module accessible from the model +%feature("pythonappend") amici::generic_model::getModel %{ + if '.' in __name__: + import sys + val.module = sys.modules['.'.join(__name__.split('.')[:-1])] +%} + + // Process symbols in header %include "wrapfunctions.h" diff --git a/deps/AMICI/models/model_calvetti/model_calvetti_w.cpp b/deps/AMICI/models/model_calvetti/w.cpp similarity index 96% rename from deps/AMICI/models/model_calvetti/model_calvetti_w.cpp rename to deps/AMICI/models/model_calvetti/w.cpp index 771f36799..fb1aaef8e 100644 --- a/deps/AMICI/models/model_calvetti/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) { +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) { 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_calvetti/model_calvetti_x0.cpp b/deps/AMICI/models/model_calvetti/x0.cpp similarity index 100% rename from deps/AMICI/models/model_calvetti/model_calvetti_x0.cpp rename to deps/AMICI/models/model_calvetti/x0.cpp diff --git a/deps/AMICI/models/model_calvetti/model_calvetti_xdot.cpp b/deps/AMICI/models/model_calvetti/xdot.cpp similarity index 100% rename from deps/AMICI/models/model_calvetti/model_calvetti_xdot.cpp rename to deps/AMICI/models/model_calvetti/xdot.cpp diff --git a/deps/AMICI/models/model_calvetti/model_calvetti_y.cpp b/deps/AMICI/models/model_calvetti/y.cpp similarity index 100% rename from deps/AMICI/models/model_calvetti/model_calvetti_y.cpp rename to deps/AMICI/models/model_calvetti/y.cpp diff --git a/deps/AMICI/models/model_dirac/CMakeLists.txt b/deps/AMICI/models/model_dirac/CMakeLists.txt index 58c689a92..64f02dca9 100644 --- a/deps/AMICI/models/model_dirac/CMakeLists.txt +++ b/deps/AMICI/models/model_dirac/CMakeLists.txt @@ -1,102 +1,109 @@ -cmake_minimum_required(VERSION 3.3) - -if(POLICY CMP0060) - cmake_policy(SET CMP0060 NEW) -endif(POLICY CMP0060) -if(POLICY CMP0065) - cmake_policy(SET CMP0065 NEW) -endif(POLICY CMP0065) -if(POLICY CMP0074) - # Use package_ROOT environment variables - cmake_policy(SET CMP0074 NEW) -endif(POLICY CMP0074) +# Build AMICI model +cmake_minimum_required(VERSION 3.15) project(model_dirac) -set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_POSITION_INDEPENDENT_CODE ON) include(CheckCXXCompilerFlag) -set(MY_CXX_FLAGS -Wall -Wno-unused-function -Wno-unused-variable -Wno-unused-but-set-variable) -foreach(FLAG ${MY_CXX_FLAGS}) - unset(CUR_FLAG_SUPPORTED CACHE) - CHECK_CXX_COMPILER_FLAG(${FLAG} CUR_FLAG_SUPPORTED) - if(${CUR_FLAG_SUPPORTED}) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FLAG}") - endif() -endforeach(FLAG) - -find_package(Amici HINTS ${CMAKE_CURRENT_LIST_DIR}/../../build) +set(MY_CXX_FLAGS -Wall -Wno-unused-function -Wno-unused-variable) +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + list(APPEND MY_CXX_FLAGS -Wno-unused-but-set-variable) +endif() +foreach(flag ${MY_CXX_FLAGS}) + unset(CUR_FLAG_SUPPORTED CACHE) + check_cxx_compiler_flag(-Werror ${flag} CUR_FLAG_SUPPORTED) + if(${CUR_FLAG_SUPPORTED}) + string(APPEND CMAKE_CXX_FLAGS " ${flag}") + endif() +endforeach() + +if(DEFINED ENV{AMICI_CXXFLAGS}) + message(STATUS "Appending flags from AMICI_CXXFLAGS: $ENV{AMICI_CXXFLAGS}") + add_compile_options("$ENV{AMICI_CXXFLAGS}") +endif() +if(DEFINED ENV{AMICI_LDFLAGS}) + message(STATUS "Appending flags from AMICI_LDFLAGS: $ENV{AMICI_LDFLAGS}") + link_libraries("$ENV{AMICI_LDFLAGS}") +endif() + +find_package(Amici REQUIRED HINTS + ${CMAKE_CURRENT_LIST_DIR}/../../build) message(STATUS "Found AMICI ${Amici_DIR}") set(MODEL_DIR ${CMAKE_CURRENT_LIST_DIR}) -set(SRC_LIST_LIB ${MODEL_DIR}/model_dirac_JSparse.cpp -${MODEL_DIR}/model_dirac_Jy.cpp -${MODEL_DIR}/model_dirac_dJydsigma.cpp -${MODEL_DIR}/model_dirac_dJydy.cpp -${MODEL_DIR}/model_dirac_deltasx.cpp -${MODEL_DIR}/model_dirac_deltax.cpp -${MODEL_DIR}/model_dirac_dxdotdp.cpp -${MODEL_DIR}/model_dirac_dydx.cpp -${MODEL_DIR}/model_dirac_root.cpp -${MODEL_DIR}/model_dirac_sigmay.cpp -${MODEL_DIR}/model_dirac_stau.cpp -${MODEL_DIR}/model_dirac_xdot.cpp -${MODEL_DIR}/model_dirac_y.cpp - -${MODEL_DIR}/wrapfunctions.cpp -) +set(SRC_LIST_LIB ${MODEL_DIR}/JSparse.cpp +${MODEL_DIR}/Jy.cpp +${MODEL_DIR}/dJydsigma.cpp +${MODEL_DIR}/dJydy.cpp +${MODEL_DIR}/deltasx.cpp +${MODEL_DIR}/deltax.cpp +${MODEL_DIR}/dxdotdp.cpp +${MODEL_DIR}/dydx.cpp +${MODEL_DIR}/root.cpp +${MODEL_DIR}/sigmay.cpp +${MODEL_DIR}/stau.cpp +${MODEL_DIR}/xdot.cpp +${MODEL_DIR}/y.cpp + ${MODEL_DIR}/wrapfunctions.cpp) add_library(${PROJECT_NAME} ${SRC_LIST_LIB}) add_library(model ALIAS ${PROJECT_NAME}) -# This option can be helpful when using the Intel compiler and compilation of -# wrapfunctions.cpp fails due to insufficient memory. -option(ENABLE_WRAPFUNCTIONS_OPTIMIZATIONS "Enable compiler optimizations for wrapfunctions.cpp?" ON) -if(NOT ENABLE_WRAPFUNCTIONS_OPTIMIZATIONS) - set_source_files_properties(wrapfunctions.cpp PROPERTIES COMPILE_FLAGS -O0) -endif() +# Some special functions require boost +# +# TODO: set some flag during code generation whether the given model requires +# boost. for now, try to find it, add include directories and link against it. +# let the compiler/linker error if it is required but not found +find_package(Boost) target_include_directories(${PROJECT_NAME} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") -target_link_libraries(${PROJECT_NAME} - PUBLIC Upstream::amici -) +target_link_libraries( + ${PROJECT_NAME} + PUBLIC Upstream::amici + PRIVATE $<$:Boost::boost>) -set(SRC_LIST_EXE main.cpp) - -add_executable(simulate_${PROJECT_NAME} ${SRC_LIST_EXE}) +if(NOT "${AMICI_PYTHON_BUILD_EXT_ONLY}") + set(SRC_LIST_EXE main.cpp) + add_executable(simulate_${PROJECT_NAME} ${SRC_LIST_EXE}) + target_link_libraries(simulate_${PROJECT_NAME} ${PROJECT_NAME}) +endif() -target_link_libraries(simulate_${PROJECT_NAME} ${PROJECT_NAME}) +# 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}) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 --coverage") + string(APPEND CMAKE_CXX_FLAGS_DEBUG " --coverage") + string(APPEND CMAKE_EXE_LINKER_FLAGS_DEBUG " --coverage") endif() -## SWIG +# SWIG option(ENABLE_SWIG "Build swig/python library?" ON) if(ENABLE_SWIG) - if(NOT(${CMAKE_VERSION} VERSION_LESS 3.8)) - add_subdirectory(swig) - else() - message(WARNING "Unable to build SWIG interface, upgrade CMake to >=3.8.") - endif() + add_subdirectory(swig) endif() - # include(GNUInstallDirs) -install(TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME}Targets - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} - PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} -) -export(EXPORT ${PROJECT_NAME}Targets FILE ${PROJECT_NAME}Config.cmake - NAMESPACE Upstream:: - ) +install( + TARGETS ${PROJECT_NAME} + EXPORT ${PROJECT_NAME}Targets + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + INCLUDES + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +export( + EXPORT ${PROJECT_NAME}Targets + FILE ${PROJECT_NAME}Config.cmake + NAMESPACE Upstream::) # - diff --git a/deps/AMICI/models/model_dirac/model_dirac_JSparse.cpp b/deps/AMICI/models/model_dirac/JSparse.cpp similarity index 100% rename from deps/AMICI/models/model_dirac/model_dirac_JSparse.cpp rename to deps/AMICI/models/model_dirac/JSparse.cpp diff --git a/deps/AMICI/models/model_dirac/model_dirac_Jy.cpp b/deps/AMICI/models/model_dirac/Jy.cpp similarity index 100% rename from deps/AMICI/models/model_dirac/model_dirac_Jy.cpp rename to deps/AMICI/models/model_dirac/Jy.cpp diff --git a/deps/AMICI/models/model_dirac/model_dirac_dJydsigma.cpp b/deps/AMICI/models/model_dirac/dJydsigma.cpp similarity index 100% rename from deps/AMICI/models/model_dirac/model_dirac_dJydsigma.cpp rename to deps/AMICI/models/model_dirac/dJydsigma.cpp diff --git a/deps/AMICI/models/model_dirac/model_dirac_dJydy.cpp b/deps/AMICI/models/model_dirac/dJydy.cpp similarity index 100% rename from deps/AMICI/models/model_dirac/model_dirac_dJydy.cpp rename to deps/AMICI/models/model_dirac/dJydy.cpp diff --git a/deps/AMICI/models/model_dirac/model_dirac_deltasx.cpp b/deps/AMICI/models/model_dirac/deltasx.cpp similarity index 98% rename from deps/AMICI/models/model_dirac/model_dirac_deltasx.cpp rename to deps/AMICI/models/model_dirac/deltasx.cpp index 12b79f937..ebb0be64c 100644 --- a/deps/AMICI/models/model_dirac/model_dirac_deltasx.cpp +++ b/deps/AMICI/models/model_dirac/deltasx.cpp @@ -10,7 +10,7 @@ namespace amici { namespace model_model_dirac{ -void deltasx_model_dirac(double *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) { +void deltasx_model_dirac(double *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) { switch (ip) { case 0: { switch(ie) { diff --git a/deps/AMICI/models/model_dirac/model_dirac_deltax.cpp b/deps/AMICI/models/model_dirac/deltax.cpp similarity index 100% rename from deps/AMICI/models/model_dirac/model_dirac_deltax.cpp rename to deps/AMICI/models/model_dirac/deltax.cpp diff --git a/deps/AMICI/models/model_dirac/model_dirac_dxdotdp.cpp b/deps/AMICI/models/model_dirac/dxdotdp.cpp similarity index 100% rename from deps/AMICI/models/model_dirac/model_dirac_dxdotdp.cpp rename to deps/AMICI/models/model_dirac/dxdotdp.cpp diff --git a/deps/AMICI/models/model_dirac/model_dirac_dydx.cpp b/deps/AMICI/models/model_dirac/dydx.cpp similarity index 100% rename from deps/AMICI/models/model_dirac/model_dirac_dydx.cpp rename to deps/AMICI/models/model_dirac/dydx.cpp diff --git a/deps/AMICI/models/model_dirac/model_dirac.h b/deps/AMICI/models/model_dirac/model_dirac.h index 2eb5cdb4b..7a762479b 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) 2bd39716102b9842354964196889d40d2eb2731c */ +/* Generated by amiwrap (R2017b) 223c5075608273b17f304556d0e8ccb41233bd21 */ #include #include #include "amici/defines.h" @@ -18,13 +18,13 @@ extern void JSparse_model_dirac(SUNMatrixContent_Sparse JSparse, const realtype extern void Jy_model_dirac(double *nllh, const int iy, const realtype *p, const realtype *k, const double *y, const double *sigmay, const double *my); extern void dJydsigma_model_dirac(double *dJydsigma, const int iy, const realtype *p, const realtype *k, const double *y, const double *sigmay, const double *my); extern void dJydy_model_dirac(double *dJydy, const int iy, const realtype *p, const realtype *k, const double *y, const double *sigmay, const double *my); -extern void deltasx_model_dirac(double *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); +extern void deltasx_model_dirac(double *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); extern void deltax_model_dirac(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 dxdotdp_model_dirac(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_dirac(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_dirac(realtype *root, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h); -extern void sigmay_model_dirac(double *sigmay, const realtype t, const realtype *p, const realtype *k); -extern void stau_model_dirac(double *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); +extern void root_model_dirac(realtype *root, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl); +extern void sigmay_model_dirac(double *sigmay, const realtype t, const realtype *p, const realtype *k, const realtype *y); +extern void stau_model_dirac(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 xdot_model_dirac(realtype *xdot, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w); extern void y_model_dirac(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_dirac : public amici::Model_ODE { 0, 0, 2, + 0, 1, 0, 0, @@ -52,6 +53,9 @@ class Model_model_dirac : public amici::Model_ODE { 0, 0, {}, + 0, + 0, + 0, 3, 0, 1 @@ -67,7 +71,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 "2bd39716102b9842354964196889d40d2eb2731c"; }; + std::string getAmiciCommit() const override { return "223c5075608273b17f304556d0e8ccb41233bd21"; }; 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); @@ -106,8 +110,8 @@ class Model_model_dirac : public amici::Model_ODE { void fdeltaqB(double *deltaqB, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const int ip, const int ie, const realtype *xdot, const realtype *xdot_old, const realtype *xB) override { } - void fdeltasx(double *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) override { - deltasx_model_dirac(deltasx, t, x, p, k, h, w, ip, ie, xdot, xdot_old, sx, stau); + void fdeltasx(double *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) override { + deltasx_model_dirac(deltasx, t, x, p, k, h, w, ip, ie, xdot, xdot_old, sx, stau, tcl); } void fdeltax(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) override { @@ -123,16 +127,16 @@ class Model_model_dirac : public amici::Model_ODE { void fdrzdx(double *drzdx, const int ie, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h) override { } - void fdsigmaydp(double *dsigmaydp, const realtype t, const realtype *p, const realtype *k, const int ip) override { + void fdsigmaydp(double *dsigmaydp, const realtype t, const realtype *p, const realtype *k, const realtype *y, const int ip) override { } 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) 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 fdwdx(realtype *dwdx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl) 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 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 { @@ -152,15 +156,15 @@ class Model_model_dirac : public amici::Model_ODE { void fdzdx(double *dzdx, const int ie, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h) override { } - void froot(realtype *root, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h) override { - root_model_dirac(root, t, x, p, k, h); + void froot(realtype *root, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl) override { + root_model_dirac(root, t, x, p, k, h, tcl); } void frz(double *rz, const int ie, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h) override { } - void fsigmay(double *sigmay, const realtype t, const realtype *p, const realtype *k) override { - sigmay_model_dirac(sigmay, t, p, k); + void fsigmay(double *sigmay, const realtype t, const realtype *p, const realtype *k, const realtype *y) override { + sigmay_model_dirac(sigmay, t, p, k, y); } void fsigmaz(double *sigmaz, const realtype t, const realtype *p, const realtype *k) override { @@ -169,8 +173,8 @@ class Model_model_dirac : public amici::Model_ODE { void fsrz(double *srz, 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 fstau(double *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) override { - stau_model_dirac(stau, t, x, p, k, h, sx, ip, ie); + void fstau(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) override { + stau_model_dirac(stau, t, x, p, k, h, tcl, sx, ip, ie); } void fsx0(realtype *sx0, const realtype t,const realtype *x0, const realtype *p, const realtype *k, const int ip) override { @@ -179,7 +183,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) 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 fx0(realtype *x0, const realtype t, const realtype *p, const realtype *k) override { diff --git a/deps/AMICI/models/model_dirac/model_dirac_root.cpp b/deps/AMICI/models/model_dirac/root.cpp similarity index 94% rename from deps/AMICI/models/model_dirac/model_dirac_root.cpp rename to deps/AMICI/models/model_dirac/root.cpp index a54473970..ce1c5f436 100644 --- a/deps/AMICI/models/model_dirac/model_dirac_root.cpp +++ b/deps/AMICI/models/model_dirac/root.cpp @@ -10,7 +10,7 @@ namespace amici { namespace model_model_dirac{ -void root_model_dirac(realtype *root, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h) { +void root_model_dirac(realtype *root, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl) { root[0] = -t+p[1]; root[1] = t-p[1]; } diff --git a/deps/AMICI/models/model_dirac/model_dirac_sigmay.cpp b/deps/AMICI/models/model_dirac/sigmay.cpp similarity index 87% rename from deps/AMICI/models/model_dirac/model_dirac_sigmay.cpp rename to deps/AMICI/models/model_dirac/sigmay.cpp index 97538bff2..9b5782f79 100644 --- a/deps/AMICI/models/model_dirac/model_dirac_sigmay.cpp +++ b/deps/AMICI/models/model_dirac/sigmay.cpp @@ -10,7 +10,7 @@ namespace amici { namespace model_model_dirac{ -void sigmay_model_dirac(double *sigmay, const realtype t, const realtype *p, const realtype *k) { +void sigmay_model_dirac(double *sigmay, const realtype t, const realtype *p, const realtype *k, const realtype *y) { sigmay[0] = 1.0; } diff --git a/deps/AMICI/models/model_dirac/model_dirac_stau.cpp b/deps/AMICI/models/model_dirac/stau.cpp similarity index 88% rename from deps/AMICI/models/model_dirac/model_dirac_stau.cpp rename to deps/AMICI/models/model_dirac/stau.cpp index 24fb6db81..67c725fce 100644 --- a/deps/AMICI/models/model_dirac/model_dirac_stau.cpp +++ b/deps/AMICI/models/model_dirac/stau.cpp @@ -10,7 +10,7 @@ namespace amici { namespace model_model_dirac{ -void stau_model_dirac(double *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) { +void stau_model_dirac(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) { switch (ip) { case 1: { switch(ie) { diff --git a/deps/AMICI/models/model_dirac/swig/CMakeLists.txt b/deps/AMICI/models/model_dirac/swig/CMakeLists.txt index af0f64a76..eb9fe681c 100644 --- a/deps/AMICI/models/model_dirac/swig/CMakeLists.txt +++ b/deps/AMICI/models/model_dirac/swig/CMakeLists.txt @@ -1,28 +1,19 @@ -cmake_minimum_required(VERSION 3.8) # swig_add_library - -if(POLICY CMP0078) - cmake_policy(SET CMP0078 OLD) -endif(POLICY CMP0078) -if(POLICY CMP0074) - # Use package_ROOT environment variables - cmake_policy(SET CMP0074 NEW) -endif(POLICY CMP0074) -if(POLICY CMP0086) - cmake_policy(SET CMP0086 NEW) -endif(POLICY CMP0086) +cmake_minimum_required(VERSION 3.15) +if(DEFINED ENV{SWIG}) + set(SWIG_EXECUTABLE $ENV{SWIG}) +endif() find_package(SWIG REQUIRED) include(${SWIG_USE_FILE}) -if(${CMAKE_VERSION} VERSION_LESS "3.12.0") - find_package(PythonLibs REQUIRED) - include_directories(${PYTHON_INCLUDE_DIRS}) - set(Python3_LIBRARIES ${PYTHON_LIBRARIES}) -else() - find_package (Python3 COMPONENTS Development) - include_directories(${Python3_INCLUDE_DIRS}) +if(DEFINED ENV{PYTHON_EXECUTABLE}) + set(Python3_EXECUTABLE $ENV{PYTHON_EXECUTABLE}) endif() +# We don't need "Interpreter" here, but without that, FindPython3 will +# ignore the Python version selected via $Python3_EXECUTABLE +find_package(Python3 COMPONENTS Interpreter Development) +include_directories(${Python3_INCLUDE_DIRS}) set(SWIG_LIBRARY_NAME _${PROJECT_NAME}) set(CMAKE_SWIG_FLAGS "") @@ -30,17 +21,38 @@ set_source_files_properties(${PROJECT_NAME}.i PROPERTIES CPLUSPLUS ON) # swig does not use INTERFACE_INCLUDE_DIRS of linked libraries, so add manually get_target_property(AMICI_INCLUDE_DIRS Upstream::amici INTERFACE_INCLUDE_DIRECTORIES) -include_directories(${AMICI_INCLUDE_DIRS} ..) +include_directories(${AMICI_INCLUDE_DIRS} .. ${AMICI_INCLUDE_DIRS}/../swig) -swig_add_library(${PROJECT_NAME} +swig_add_library(${SWIG_LIBRARY_NAME} TYPE MODULE LANGUAGE python SOURCES ${PROJECT_NAME}.i) -swig_link_libraries(${PROJECT_NAME} + +set_target_properties(${SWIG_LIBRARY_NAME} + PROPERTIES + SWIG_USE_TARGET_INCLUDE_DIRECTORIES TRUE + PREFIX "" +) + +# Python extension suffix +execute_process( + COMMAND ${Python3_EXECUTABLE} -c + "import sysconfig; print(sysconfig.get_config_var('EXT_SUFFIX'))" + OUTPUT_VARIABLE PY_EXT_SUFFIX OUTPUT_STRIP_TRAILING_WHITESPACE) +if(NOT "${PY_EXT_SUFFIX}" STREQUAL "") + message(STATUS "Python extension suffix is ${PY_EXT_SUFFIX}") + set_target_properties(${SWIG_LIBRARY_NAME} PROPERTIES SUFFIX "${PY_EXT_SUFFIX}" ) +endif() + + +swig_link_libraries(${SWIG_LIBRARY_NAME} ${Python3_LIBRARIES} model) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.py + $ DESTINATION .) + # configure module setup script set(SETUP_PY_IN ${Amici_DIR}/model_setup.template.py) set(SETUP_PY_OUT ${CMAKE_CURRENT_BINARY_DIR}/setup.py) diff --git a/deps/AMICI/models/model_dirac/swig/model_dirac.i b/deps/AMICI/models/model_dirac/swig/model_dirac.i index 908c43b49..928a88349 100644 --- a/deps/AMICI/models/model_dirac/swig/model_dirac.i +++ b/deps/AMICI/models/model_dirac/swig/model_dirac.i @@ -10,5 +10,13 @@ using namespace amici; %} +// Make model module accessible from the model +%feature("pythonappend") amici::generic_model::getModel %{ + if '.' in __name__: + import sys + val.module = sys.modules['.'.join(__name__.split('.')[:-1])] +%} + + // Process symbols in header %include "wrapfunctions.h" diff --git a/deps/AMICI/models/model_dirac/model_dirac_xdot.cpp b/deps/AMICI/models/model_dirac/xdot.cpp similarity index 100% rename from deps/AMICI/models/model_dirac/model_dirac_xdot.cpp rename to deps/AMICI/models/model_dirac/xdot.cpp diff --git a/deps/AMICI/models/model_dirac/model_dirac_y.cpp b/deps/AMICI/models/model_dirac/y.cpp similarity index 100% rename from deps/AMICI/models/model_dirac/model_dirac_y.cpp rename to deps/AMICI/models/model_dirac/y.cpp diff --git a/deps/AMICI/models/model_events/CMakeLists.txt b/deps/AMICI/models/model_events/CMakeLists.txt index 9ba942706..277573426 100644 --- a/deps/AMICI/models/model_events/CMakeLists.txt +++ b/deps/AMICI/models/model_events/CMakeLists.txt @@ -1,116 +1,123 @@ -cmake_minimum_required(VERSION 3.3) - -if(POLICY CMP0060) - cmake_policy(SET CMP0060 NEW) -endif(POLICY CMP0060) -if(POLICY CMP0065) - cmake_policy(SET CMP0065 NEW) -endif(POLICY CMP0065) -if(POLICY CMP0074) - # Use package_ROOT environment variables - cmake_policy(SET CMP0074 NEW) -endif(POLICY CMP0074) +# Build AMICI model +cmake_minimum_required(VERSION 3.15) project(model_events) -set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_POSITION_INDEPENDENT_CODE ON) include(CheckCXXCompilerFlag) -set(MY_CXX_FLAGS -Wall -Wno-unused-function -Wno-unused-variable -Wno-unused-but-set-variable) -foreach(FLAG ${MY_CXX_FLAGS}) - unset(CUR_FLAG_SUPPORTED CACHE) - CHECK_CXX_COMPILER_FLAG(${FLAG} CUR_FLAG_SUPPORTED) - if(${CUR_FLAG_SUPPORTED}) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FLAG}") - endif() -endforeach(FLAG) - -find_package(Amici HINTS ${CMAKE_CURRENT_LIST_DIR}/../../build) +set(MY_CXX_FLAGS -Wall -Wno-unused-function -Wno-unused-variable) +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + list(APPEND MY_CXX_FLAGS -Wno-unused-but-set-variable) +endif() +foreach(flag ${MY_CXX_FLAGS}) + unset(CUR_FLAG_SUPPORTED CACHE) + check_cxx_compiler_flag(-Werror ${flag} CUR_FLAG_SUPPORTED) + if(${CUR_FLAG_SUPPORTED}) + string(APPEND CMAKE_CXX_FLAGS " ${flag}") + endif() +endforeach() + +if(DEFINED ENV{AMICI_CXXFLAGS}) + message(STATUS "Appending flags from AMICI_CXXFLAGS: $ENV{AMICI_CXXFLAGS}") + add_compile_options("$ENV{AMICI_CXXFLAGS}") +endif() +if(DEFINED ENV{AMICI_LDFLAGS}) + message(STATUS "Appending flags from AMICI_LDFLAGS: $ENV{AMICI_LDFLAGS}") + link_libraries("$ENV{AMICI_LDFLAGS}") +endif() + +find_package(Amici REQUIRED HINTS + ${CMAKE_CURRENT_LIST_DIR}/../../build) message(STATUS "Found AMICI ${Amici_DIR}") set(MODEL_DIR ${CMAKE_CURRENT_LIST_DIR}) -set(SRC_LIST_LIB ${MODEL_DIR}/model_events_JSparse.cpp -${MODEL_DIR}/model_events_Jrz.cpp -${MODEL_DIR}/model_events_Jy.cpp -${MODEL_DIR}/model_events_Jz.cpp -${MODEL_DIR}/model_events_dJrzdsigma.cpp -${MODEL_DIR}/model_events_dJrzdz.cpp -${MODEL_DIR}/model_events_dJydsigma.cpp -${MODEL_DIR}/model_events_dJydy.cpp -${MODEL_DIR}/model_events_dJzdsigma.cpp -${MODEL_DIR}/model_events_dJzdz.cpp -${MODEL_DIR}/model_events_deltasx.cpp -${MODEL_DIR}/model_events_drzdx.cpp -${MODEL_DIR}/model_events_dxdotdp.cpp -${MODEL_DIR}/model_events_dydp.cpp -${MODEL_DIR}/model_events_dydx.cpp -${MODEL_DIR}/model_events_dzdx.cpp -${MODEL_DIR}/model_events_root.cpp -${MODEL_DIR}/model_events_rz.cpp -${MODEL_DIR}/model_events_sigmay.cpp -${MODEL_DIR}/model_events_sigmaz.cpp -${MODEL_DIR}/model_events_srz.cpp -${MODEL_DIR}/model_events_stau.cpp -${MODEL_DIR}/model_events_sz.cpp -${MODEL_DIR}/model_events_x0.cpp -${MODEL_DIR}/model_events_xdot.cpp -${MODEL_DIR}/model_events_y.cpp -${MODEL_DIR}/model_events_z.cpp - -${MODEL_DIR}/wrapfunctions.cpp -) +set(SRC_LIST_LIB ${MODEL_DIR}/JSparse.cpp +${MODEL_DIR}/Jrz.cpp +${MODEL_DIR}/Jy.cpp +${MODEL_DIR}/Jz.cpp +${MODEL_DIR}/dJrzdsigma.cpp +${MODEL_DIR}/dJrzdz.cpp +${MODEL_DIR}/dJydsigma.cpp +${MODEL_DIR}/dJydy.cpp +${MODEL_DIR}/dJzdsigma.cpp +${MODEL_DIR}/dJzdz.cpp +${MODEL_DIR}/deltasx.cpp +${MODEL_DIR}/drzdx.cpp +${MODEL_DIR}/dxdotdp.cpp +${MODEL_DIR}/dydp.cpp +${MODEL_DIR}/dydx.cpp +${MODEL_DIR}/dzdx.cpp +${MODEL_DIR}/root.cpp +${MODEL_DIR}/rz.cpp +${MODEL_DIR}/sigmay.cpp +${MODEL_DIR}/sigmaz.cpp +${MODEL_DIR}/srz.cpp +${MODEL_DIR}/stau.cpp +${MODEL_DIR}/sz.cpp +${MODEL_DIR}/x0.cpp +${MODEL_DIR}/xdot.cpp +${MODEL_DIR}/y.cpp +${MODEL_DIR}/z.cpp + ${MODEL_DIR}/wrapfunctions.cpp) add_library(${PROJECT_NAME} ${SRC_LIST_LIB}) add_library(model ALIAS ${PROJECT_NAME}) -# This option can be helpful when using the Intel compiler and compilation of -# wrapfunctions.cpp fails due to insufficient memory. -option(ENABLE_WRAPFUNCTIONS_OPTIMIZATIONS "Enable compiler optimizations for wrapfunctions.cpp?" ON) -if(NOT ENABLE_WRAPFUNCTIONS_OPTIMIZATIONS) - set_source_files_properties(wrapfunctions.cpp PROPERTIES COMPILE_FLAGS -O0) -endif() +# Some special functions require boost +# +# TODO: set some flag during code generation whether the given model requires +# boost. for now, try to find it, add include directories and link against it. +# let the compiler/linker error if it is required but not found +find_package(Boost) target_include_directories(${PROJECT_NAME} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") -target_link_libraries(${PROJECT_NAME} - PUBLIC Upstream::amici -) +target_link_libraries( + ${PROJECT_NAME} + PUBLIC Upstream::amici + PRIVATE $<$:Boost::boost>) -set(SRC_LIST_EXE main.cpp) - -add_executable(simulate_${PROJECT_NAME} ${SRC_LIST_EXE}) +if(NOT "${AMICI_PYTHON_BUILD_EXT_ONLY}") + set(SRC_LIST_EXE main.cpp) + add_executable(simulate_${PROJECT_NAME} ${SRC_LIST_EXE}) + target_link_libraries(simulate_${PROJECT_NAME} ${PROJECT_NAME}) +endif() -target_link_libraries(simulate_${PROJECT_NAME} ${PROJECT_NAME}) +# 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}) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 --coverage") + string(APPEND CMAKE_CXX_FLAGS_DEBUG " --coverage") + string(APPEND CMAKE_EXE_LINKER_FLAGS_DEBUG " --coverage") endif() -## SWIG +# SWIG option(ENABLE_SWIG "Build swig/python library?" ON) if(ENABLE_SWIG) - if(NOT(${CMAKE_VERSION} VERSION_LESS 3.8)) - add_subdirectory(swig) - else() - message(WARNING "Unable to build SWIG interface, upgrade CMake to >=3.8.") - endif() + add_subdirectory(swig) endif() - # include(GNUInstallDirs) -install(TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME}Targets - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} - PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} -) -export(EXPORT ${PROJECT_NAME}Targets FILE ${PROJECT_NAME}Config.cmake - NAMESPACE Upstream:: - ) +install( + TARGETS ${PROJECT_NAME} + EXPORT ${PROJECT_NAME}Targets + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + INCLUDES + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +export( + EXPORT ${PROJECT_NAME}Targets + FILE ${PROJECT_NAME}Config.cmake + NAMESPACE Upstream::) # - diff --git a/deps/AMICI/models/model_events/model_events_JSparse.cpp b/deps/AMICI/models/model_events/JSparse.cpp similarity index 100% rename from deps/AMICI/models/model_events/model_events_JSparse.cpp rename to deps/AMICI/models/model_events/JSparse.cpp diff --git a/deps/AMICI/models/model_events/model_events_Jrz.cpp b/deps/AMICI/models/model_events/Jrz.cpp similarity index 100% rename from deps/AMICI/models/model_events/model_events_Jrz.cpp rename to deps/AMICI/models/model_events/Jrz.cpp diff --git a/deps/AMICI/models/model_events/model_events_Jy.cpp b/deps/AMICI/models/model_events/Jy.cpp similarity index 100% rename from deps/AMICI/models/model_events/model_events_Jy.cpp rename to deps/AMICI/models/model_events/Jy.cpp diff --git a/deps/AMICI/models/model_events/model_events_Jz.cpp b/deps/AMICI/models/model_events/Jz.cpp similarity index 100% rename from deps/AMICI/models/model_events/model_events_Jz.cpp rename to deps/AMICI/models/model_events/Jz.cpp diff --git a/deps/AMICI/models/model_events/model_events_dJrzdsigma.cpp b/deps/AMICI/models/model_events/dJrzdsigma.cpp similarity index 100% rename from deps/AMICI/models/model_events/model_events_dJrzdsigma.cpp rename to deps/AMICI/models/model_events/dJrzdsigma.cpp diff --git a/deps/AMICI/models/model_events/model_events_dJrzdz.cpp b/deps/AMICI/models/model_events/dJrzdz.cpp similarity index 100% rename from deps/AMICI/models/model_events/model_events_dJrzdz.cpp rename to deps/AMICI/models/model_events/dJrzdz.cpp diff --git a/deps/AMICI/models/model_events/model_events_dJydsigma.cpp b/deps/AMICI/models/model_events/dJydsigma.cpp similarity index 100% rename from deps/AMICI/models/model_events/model_events_dJydsigma.cpp rename to deps/AMICI/models/model_events/dJydsigma.cpp diff --git a/deps/AMICI/models/model_events/model_events_dJydy.cpp b/deps/AMICI/models/model_events/dJydy.cpp similarity index 100% rename from deps/AMICI/models/model_events/model_events_dJydy.cpp rename to deps/AMICI/models/model_events/dJydy.cpp diff --git a/deps/AMICI/models/model_events/model_events_dJzdsigma.cpp b/deps/AMICI/models/model_events/dJzdsigma.cpp similarity index 100% rename from deps/AMICI/models/model_events/model_events_dJzdsigma.cpp rename to deps/AMICI/models/model_events/dJzdsigma.cpp diff --git a/deps/AMICI/models/model_events/model_events_dJzdz.cpp b/deps/AMICI/models/model_events/dJzdz.cpp similarity index 100% rename from deps/AMICI/models/model_events/model_events_dJzdz.cpp rename to deps/AMICI/models/model_events/dJzdz.cpp diff --git a/deps/AMICI/models/model_events/model_events_deltasx.cpp b/deps/AMICI/models/model_events/deltasx.cpp similarity index 98% rename from deps/AMICI/models/model_events/model_events_deltasx.cpp rename to deps/AMICI/models/model_events/deltasx.cpp index 62516b1ab..9dacb2ee5 100644 --- a/deps/AMICI/models/model_events/model_events_deltasx.cpp +++ b/deps/AMICI/models/model_events/deltasx.cpp @@ -10,7 +10,7 @@ namespace amici { namespace model_model_events{ -void deltasx_model_events(double *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) { +void deltasx_model_events(double *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) { switch (ip) { case 0: { switch(ie) { diff --git a/deps/AMICI/models/model_events/model_events_drzdx.cpp b/deps/AMICI/models/model_events/drzdx.cpp similarity index 100% rename from deps/AMICI/models/model_events/model_events_drzdx.cpp rename to deps/AMICI/models/model_events/drzdx.cpp diff --git a/deps/AMICI/models/model_events/model_events_dxdotdp.cpp b/deps/AMICI/models/model_events/dxdotdp.cpp similarity index 100% rename from deps/AMICI/models/model_events/model_events_dxdotdp.cpp rename to deps/AMICI/models/model_events/dxdotdp.cpp diff --git a/deps/AMICI/models/model_events/model_events_dydp.cpp b/deps/AMICI/models/model_events/dydp.cpp similarity index 100% rename from deps/AMICI/models/model_events/model_events_dydp.cpp rename to deps/AMICI/models/model_events/dydp.cpp diff --git a/deps/AMICI/models/model_events/model_events_dydx.cpp b/deps/AMICI/models/model_events/dydx.cpp similarity index 100% rename from deps/AMICI/models/model_events/model_events_dydx.cpp rename to deps/AMICI/models/model_events/dydx.cpp diff --git a/deps/AMICI/models/model_events/model_events_dzdx.cpp b/deps/AMICI/models/model_events/dzdx.cpp similarity index 100% rename from deps/AMICI/models/model_events/model_events_dzdx.cpp rename to deps/AMICI/models/model_events/dzdx.cpp diff --git a/deps/AMICI/models/model_events/model_events.h b/deps/AMICI/models/model_events/model_events.h index 2cf13ace8..df4bb68ae 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) 2bd39716102b9842354964196889d40d2eb2731c */ +/* Generated by amiwrap (R2017b) 223c5075608273b17f304556d0e8ccb41233bd21 */ #include #include #include "amici/defines.h" @@ -24,18 +24,18 @@ extern void dJydsigma_model_events(double *dJydsigma, const int iy, const realty extern void dJydy_model_events(double *dJydy, const int iy, const realtype *p, const realtype *k, const double *y, const double *sigmay, const double *my); extern void dJzdsigma_model_events(double *dJzdsigma, const int iz, const realtype *p, const realtype *k, const double *z, const double *sigmaz, const double *mz); extern void dJzdz_model_events(double *dJzdz, const int iz, const realtype *p, const realtype *k, const double *z, const double *sigmaz, const double *mz); -extern void deltasx_model_events(double *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); +extern void deltasx_model_events(double *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); extern void drzdx_model_events(double *drzdx, const int ie, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h); extern void dxdotdp_model_events(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_events(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_events(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_events(double *dzdx, const int ie, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h); -extern void root_model_events(realtype *root, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h); +extern void root_model_events(realtype *root, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl); extern void rz_model_events(double *rz, const int ie, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h); -extern void sigmay_model_events(double *sigmay, const realtype t, const realtype *p, const realtype *k); +extern void sigmay_model_events(double *sigmay, const realtype t, const realtype *p, const realtype *k, const realtype *y); extern void sigmaz_model_events(double *sigmaz, const realtype t, const realtype *p, const realtype *k); extern void srz_model_events(double *srz, 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 stau_model_events(double *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); +extern void stau_model_events(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 sz_model_events(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 x0_model_events(realtype *x0, const realtype t, const realtype *p, const realtype *k); extern void xdot_model_events(realtype *xdot, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w); @@ -59,6 +59,7 @@ class Model_model_events : public amici::Model_ODE { 2, 2, 6, + 0, 1, 0, 0, @@ -66,6 +67,9 @@ class Model_model_events : public amici::Model_ODE { 0, 0, {}, + 0, + 0, + 0, 4, 0, 1 @@ -81,7 +85,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 "2bd39716102b9842354964196889d40d2eb2731c"; }; + std::string getAmiciCommit() const override { return "223c5075608273b17f304556d0e8ccb41233bd21"; }; 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); @@ -126,8 +130,8 @@ class Model_model_events : public amici::Model_ODE { void fdeltaqB(double *deltaqB, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const int ip, const int ie, const realtype *xdot, const realtype *xdot_old, const realtype *xB) override { } - void fdeltasx(double *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) override { - deltasx_model_events(deltasx, t, x, p, k, h, w, ip, ie, xdot, xdot_old, sx, stau); + void fdeltasx(double *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) override { + deltasx_model_events(deltasx, t, x, p, k, h, w, ip, ie, xdot, xdot_old, sx, stau, tcl); } void fdeltax(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) override { @@ -143,16 +147,16 @@ class Model_model_events : public amici::Model_ODE { drzdx_model_events(drzdx, ie, t, x, p, k, h); } - void fdsigmaydp(double *dsigmaydp, const realtype t, const realtype *p, const realtype *k, const int ip) override { + void fdsigmaydp(double *dsigmaydp, const realtype t, const realtype *p, const realtype *k, const realtype *y, const int ip) override { } 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) 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 fdwdx(realtype *dwdx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl) 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 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 { @@ -174,16 +178,16 @@ class Model_model_events : public amici::Model_ODE { dzdx_model_events(dzdx, ie, t, x, p, k, h); } - void froot(realtype *root, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h) override { - root_model_events(root, t, x, p, k, h); + void froot(realtype *root, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl) override { + root_model_events(root, t, x, p, k, h, tcl); } void frz(double *rz, const int ie, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h) override { rz_model_events(rz, ie, t, x, p, k, h); } - void fsigmay(double *sigmay, const realtype t, const realtype *p, const realtype *k) override { - sigmay_model_events(sigmay, t, p, k); + void fsigmay(double *sigmay, const realtype t, const realtype *p, const realtype *k, const realtype *y) override { + sigmay_model_events(sigmay, t, p, k, y); } void fsigmaz(double *sigmaz, const realtype t, const realtype *p, const realtype *k) override { @@ -194,8 +198,8 @@ class Model_model_events : public amici::Model_ODE { srz_model_events(srz, ie, t, x, p, k, h, sx, ip); } - void fstau(double *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) override { - stau_model_events(stau, t, x, p, k, h, sx, ip, ie); + void fstau(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) override { + stau_model_events(stau, t, x, p, k, h, tcl, sx, ip, ie); } void fsx0(realtype *sx0, const realtype t,const realtype *x0, const realtype *p, const realtype *k, const int ip) override { @@ -205,7 +209,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) 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 fx0(realtype *x0, const realtype t, const realtype *p, const realtype *k) override { diff --git a/deps/AMICI/models/model_events/model_events_root.cpp b/deps/AMICI/models/model_events/root.cpp similarity index 94% rename from deps/AMICI/models/model_events/model_events_root.cpp rename to deps/AMICI/models/model_events/root.cpp index 4da74dc79..9207a8a25 100644 --- a/deps/AMICI/models/model_events/model_events_root.cpp +++ b/deps/AMICI/models/model_events/root.cpp @@ -10,7 +10,7 @@ namespace amici { namespace model_model_events{ -void root_model_events(realtype *root, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h) { +void root_model_events(realtype *root, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl) { root[0] = x[1]-x[2]; root[1] = x[0]-x[2]; root[2] = -t+4.0; diff --git a/deps/AMICI/models/model_events/model_events_rz.cpp b/deps/AMICI/models/model_events/rz.cpp similarity index 100% rename from deps/AMICI/models/model_events/model_events_rz.cpp rename to deps/AMICI/models/model_events/rz.cpp diff --git a/deps/AMICI/models/model_events/model_events_sigmay.cpp b/deps/AMICI/models/model_events/sigmay.cpp similarity index 86% rename from deps/AMICI/models/model_events/model_events_sigmay.cpp rename to deps/AMICI/models/model_events/sigmay.cpp index 42fac3dfb..87bd4ffb3 100644 --- a/deps/AMICI/models/model_events/model_events_sigmay.cpp +++ b/deps/AMICI/models/model_events/sigmay.cpp @@ -10,7 +10,7 @@ namespace amici { namespace model_model_events{ -void sigmay_model_events(double *sigmay, const realtype t, const realtype *p, const realtype *k) { +void sigmay_model_events(double *sigmay, const realtype t, const realtype *p, const realtype *k, const realtype *y) { sigmay[0] = 1.0; } diff --git a/deps/AMICI/models/model_events/model_events_sigmaz.cpp b/deps/AMICI/models/model_events/sigmaz.cpp similarity index 100% rename from deps/AMICI/models/model_events/model_events_sigmaz.cpp rename to deps/AMICI/models/model_events/sigmaz.cpp diff --git a/deps/AMICI/models/model_events/model_events_srz.cpp b/deps/AMICI/models/model_events/srz.cpp similarity index 100% rename from deps/AMICI/models/model_events/model_events_srz.cpp rename to deps/AMICI/models/model_events/srz.cpp diff --git a/deps/AMICI/models/model_events/model_events_stau.cpp b/deps/AMICI/models/model_events/stau.cpp similarity index 96% rename from deps/AMICI/models/model_events/model_events_stau.cpp rename to deps/AMICI/models/model_events/stau.cpp index 9649efab4..6a192189e 100644 --- a/deps/AMICI/models/model_events/model_events_stau.cpp +++ b/deps/AMICI/models/model_events/stau.cpp @@ -10,7 +10,7 @@ namespace amici { namespace model_model_events{ -void stau_model_events(double *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) { +void stau_model_events(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) { switch (ip) { case 0: { switch(ie) { diff --git a/deps/AMICI/models/model_events/swig/CMakeLists.txt b/deps/AMICI/models/model_events/swig/CMakeLists.txt index af0f64a76..eb9fe681c 100644 --- a/deps/AMICI/models/model_events/swig/CMakeLists.txt +++ b/deps/AMICI/models/model_events/swig/CMakeLists.txt @@ -1,28 +1,19 @@ -cmake_minimum_required(VERSION 3.8) # swig_add_library - -if(POLICY CMP0078) - cmake_policy(SET CMP0078 OLD) -endif(POLICY CMP0078) -if(POLICY CMP0074) - # Use package_ROOT environment variables - cmake_policy(SET CMP0074 NEW) -endif(POLICY CMP0074) -if(POLICY CMP0086) - cmake_policy(SET CMP0086 NEW) -endif(POLICY CMP0086) +cmake_minimum_required(VERSION 3.15) +if(DEFINED ENV{SWIG}) + set(SWIG_EXECUTABLE $ENV{SWIG}) +endif() find_package(SWIG REQUIRED) include(${SWIG_USE_FILE}) -if(${CMAKE_VERSION} VERSION_LESS "3.12.0") - find_package(PythonLibs REQUIRED) - include_directories(${PYTHON_INCLUDE_DIRS}) - set(Python3_LIBRARIES ${PYTHON_LIBRARIES}) -else() - find_package (Python3 COMPONENTS Development) - include_directories(${Python3_INCLUDE_DIRS}) +if(DEFINED ENV{PYTHON_EXECUTABLE}) + set(Python3_EXECUTABLE $ENV{PYTHON_EXECUTABLE}) endif() +# We don't need "Interpreter" here, but without that, FindPython3 will +# ignore the Python version selected via $Python3_EXECUTABLE +find_package(Python3 COMPONENTS Interpreter Development) +include_directories(${Python3_INCLUDE_DIRS}) set(SWIG_LIBRARY_NAME _${PROJECT_NAME}) set(CMAKE_SWIG_FLAGS "") @@ -30,17 +21,38 @@ set_source_files_properties(${PROJECT_NAME}.i PROPERTIES CPLUSPLUS ON) # swig does not use INTERFACE_INCLUDE_DIRS of linked libraries, so add manually get_target_property(AMICI_INCLUDE_DIRS Upstream::amici INTERFACE_INCLUDE_DIRECTORIES) -include_directories(${AMICI_INCLUDE_DIRS} ..) +include_directories(${AMICI_INCLUDE_DIRS} .. ${AMICI_INCLUDE_DIRS}/../swig) -swig_add_library(${PROJECT_NAME} +swig_add_library(${SWIG_LIBRARY_NAME} TYPE MODULE LANGUAGE python SOURCES ${PROJECT_NAME}.i) -swig_link_libraries(${PROJECT_NAME} + +set_target_properties(${SWIG_LIBRARY_NAME} + PROPERTIES + SWIG_USE_TARGET_INCLUDE_DIRECTORIES TRUE + PREFIX "" +) + +# Python extension suffix +execute_process( + COMMAND ${Python3_EXECUTABLE} -c + "import sysconfig; print(sysconfig.get_config_var('EXT_SUFFIX'))" + OUTPUT_VARIABLE PY_EXT_SUFFIX OUTPUT_STRIP_TRAILING_WHITESPACE) +if(NOT "${PY_EXT_SUFFIX}" STREQUAL "") + message(STATUS "Python extension suffix is ${PY_EXT_SUFFIX}") + set_target_properties(${SWIG_LIBRARY_NAME} PROPERTIES SUFFIX "${PY_EXT_SUFFIX}" ) +endif() + + +swig_link_libraries(${SWIG_LIBRARY_NAME} ${Python3_LIBRARIES} model) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.py + $ DESTINATION .) + # configure module setup script set(SETUP_PY_IN ${Amici_DIR}/model_setup.template.py) set(SETUP_PY_OUT ${CMAKE_CURRENT_BINARY_DIR}/setup.py) diff --git a/deps/AMICI/models/model_events/swig/model_events.i b/deps/AMICI/models/model_events/swig/model_events.i index e62e99d55..e4a556aa1 100644 --- a/deps/AMICI/models/model_events/swig/model_events.i +++ b/deps/AMICI/models/model_events/swig/model_events.i @@ -10,5 +10,13 @@ using namespace amici; %} +// Make model module accessible from the model +%feature("pythonappend") amici::generic_model::getModel %{ + if '.' in __name__: + import sys + val.module = sys.modules['.'.join(__name__.split('.')[:-1])] +%} + + // Process symbols in header %include "wrapfunctions.h" diff --git a/deps/AMICI/models/model_events/model_events_sz.cpp b/deps/AMICI/models/model_events/sz.cpp similarity index 100% rename from deps/AMICI/models/model_events/model_events_sz.cpp rename to deps/AMICI/models/model_events/sz.cpp diff --git a/deps/AMICI/models/model_events/model_events_x0.cpp b/deps/AMICI/models/model_events/x0.cpp similarity index 100% rename from deps/AMICI/models/model_events/model_events_x0.cpp rename to deps/AMICI/models/model_events/x0.cpp diff --git a/deps/AMICI/models/model_events/model_events_xdot.cpp b/deps/AMICI/models/model_events/xdot.cpp similarity index 100% rename from deps/AMICI/models/model_events/model_events_xdot.cpp rename to deps/AMICI/models/model_events/xdot.cpp diff --git a/deps/AMICI/models/model_events/model_events_y.cpp b/deps/AMICI/models/model_events/y.cpp similarity index 100% rename from deps/AMICI/models/model_events/model_events_y.cpp rename to deps/AMICI/models/model_events/y.cpp diff --git a/deps/AMICI/models/model_events/model_events_z.cpp b/deps/AMICI/models/model_events/z.cpp similarity index 100% rename from deps/AMICI/models/model_events/model_events_z.cpp rename to deps/AMICI/models/model_events/z.cpp diff --git a/deps/AMICI/models/model_jakstat_adjoint/CMakeLists.txt b/deps/AMICI/models/model_jakstat_adjoint/CMakeLists.txt index fe9f38b9c..1670a68ee 100644 --- a/deps/AMICI/models/model_jakstat_adjoint/CMakeLists.txt +++ b/deps/AMICI/models/model_jakstat_adjoint/CMakeLists.txt @@ -1,105 +1,112 @@ -cmake_minimum_required(VERSION 3.3) - -if(POLICY CMP0060) - cmake_policy(SET CMP0060 NEW) -endif(POLICY CMP0060) -if(POLICY CMP0065) - cmake_policy(SET CMP0065 NEW) -endif(POLICY CMP0065) -if(POLICY CMP0074) - # Use package_ROOT environment variables - cmake_policy(SET CMP0074 NEW) -endif(POLICY CMP0074) +# Build AMICI model +cmake_minimum_required(VERSION 3.15) project(model_jakstat_adjoint) -set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_POSITION_INDEPENDENT_CODE ON) include(CheckCXXCompilerFlag) -set(MY_CXX_FLAGS -Wall -Wno-unused-function -Wno-unused-variable -Wno-unused-but-set-variable) -foreach(FLAG ${MY_CXX_FLAGS}) - unset(CUR_FLAG_SUPPORTED CACHE) - CHECK_CXX_COMPILER_FLAG(${FLAG} CUR_FLAG_SUPPORTED) - if(${CUR_FLAG_SUPPORTED}) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FLAG}") - endif() -endforeach(FLAG) - -find_package(Amici HINTS ${CMAKE_CURRENT_LIST_DIR}/../../build) +set(MY_CXX_FLAGS -Wall -Wno-unused-function -Wno-unused-variable) +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + list(APPEND MY_CXX_FLAGS -Wno-unused-but-set-variable) +endif() +foreach(flag ${MY_CXX_FLAGS}) + unset(CUR_FLAG_SUPPORTED CACHE) + check_cxx_compiler_flag(-Werror ${flag} CUR_FLAG_SUPPORTED) + if(${CUR_FLAG_SUPPORTED}) + string(APPEND CMAKE_CXX_FLAGS " ${flag}") + endif() +endforeach() + +if(DEFINED ENV{AMICI_CXXFLAGS}) + message(STATUS "Appending flags from AMICI_CXXFLAGS: $ENV{AMICI_CXXFLAGS}") + add_compile_options("$ENV{AMICI_CXXFLAGS}") +endif() +if(DEFINED ENV{AMICI_LDFLAGS}) + message(STATUS "Appending flags from AMICI_LDFLAGS: $ENV{AMICI_LDFLAGS}") + link_libraries("$ENV{AMICI_LDFLAGS}") +endif() + +find_package(Amici REQUIRED HINTS + ${CMAKE_CURRENT_LIST_DIR}/../../build) message(STATUS "Found AMICI ${Amici_DIR}") set(MODEL_DIR ${CMAKE_CURRENT_LIST_DIR}) -set(SRC_LIST_LIB ${MODEL_DIR}/model_jakstat_adjoint_JSparse.cpp -${MODEL_DIR}/model_jakstat_adjoint_Jy.cpp -${MODEL_DIR}/model_jakstat_adjoint_dJydsigma.cpp -${MODEL_DIR}/model_jakstat_adjoint_dJydy.cpp -${MODEL_DIR}/model_jakstat_adjoint_dsigmaydp.cpp -${MODEL_DIR}/model_jakstat_adjoint_dwdp.cpp -${MODEL_DIR}/model_jakstat_adjoint_dwdx.cpp -${MODEL_DIR}/model_jakstat_adjoint_dxdotdp.cpp -${MODEL_DIR}/model_jakstat_adjoint_dydp.cpp -${MODEL_DIR}/model_jakstat_adjoint_dydx.cpp -${MODEL_DIR}/model_jakstat_adjoint_sigmay.cpp -${MODEL_DIR}/model_jakstat_adjoint_sx0.cpp -${MODEL_DIR}/model_jakstat_adjoint_w.cpp -${MODEL_DIR}/model_jakstat_adjoint_x0.cpp -${MODEL_DIR}/model_jakstat_adjoint_xdot.cpp -${MODEL_DIR}/model_jakstat_adjoint_y.cpp - -${MODEL_DIR}/wrapfunctions.cpp -) +set(SRC_LIST_LIB ${MODEL_DIR}/JSparse.cpp +${MODEL_DIR}/Jy.cpp +${MODEL_DIR}/dJydsigma.cpp +${MODEL_DIR}/dJydy.cpp +${MODEL_DIR}/dsigmaydp.cpp +${MODEL_DIR}/dwdp.cpp +${MODEL_DIR}/dwdx.cpp +${MODEL_DIR}/dxdotdp.cpp +${MODEL_DIR}/dydp.cpp +${MODEL_DIR}/dydx.cpp +${MODEL_DIR}/sigmay.cpp +${MODEL_DIR}/sx0.cpp +${MODEL_DIR}/w.cpp +${MODEL_DIR}/x0.cpp +${MODEL_DIR}/xdot.cpp +${MODEL_DIR}/y.cpp + ${MODEL_DIR}/wrapfunctions.cpp) add_library(${PROJECT_NAME} ${SRC_LIST_LIB}) add_library(model ALIAS ${PROJECT_NAME}) -# This option can be helpful when using the Intel compiler and compilation of -# wrapfunctions.cpp fails due to insufficient memory. -option(ENABLE_WRAPFUNCTIONS_OPTIMIZATIONS "Enable compiler optimizations for wrapfunctions.cpp?" ON) -if(NOT ENABLE_WRAPFUNCTIONS_OPTIMIZATIONS) - set_source_files_properties(wrapfunctions.cpp PROPERTIES COMPILE_FLAGS -O0) -endif() +# Some special functions require boost +# +# TODO: set some flag during code generation whether the given model requires +# boost. for now, try to find it, add include directories and link against it. +# let the compiler/linker error if it is required but not found +find_package(Boost) target_include_directories(${PROJECT_NAME} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") -target_link_libraries(${PROJECT_NAME} - PUBLIC Upstream::amici -) +target_link_libraries( + ${PROJECT_NAME} + PUBLIC Upstream::amici + PRIVATE $<$:Boost::boost>) -set(SRC_LIST_EXE main.cpp) - -add_executable(simulate_${PROJECT_NAME} ${SRC_LIST_EXE}) +if(NOT "${AMICI_PYTHON_BUILD_EXT_ONLY}") + set(SRC_LIST_EXE main.cpp) + add_executable(simulate_${PROJECT_NAME} ${SRC_LIST_EXE}) + target_link_libraries(simulate_${PROJECT_NAME} ${PROJECT_NAME}) +endif() -target_link_libraries(simulate_${PROJECT_NAME} ${PROJECT_NAME}) +# 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}) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 --coverage") + string(APPEND CMAKE_CXX_FLAGS_DEBUG " --coverage") + string(APPEND CMAKE_EXE_LINKER_FLAGS_DEBUG " --coverage") endif() -## SWIG +# SWIG option(ENABLE_SWIG "Build swig/python library?" ON) if(ENABLE_SWIG) - if(NOT(${CMAKE_VERSION} VERSION_LESS 3.8)) - add_subdirectory(swig) - else() - message(WARNING "Unable to build SWIG interface, upgrade CMake to >=3.8.") - endif() + add_subdirectory(swig) endif() - # include(GNUInstallDirs) -install(TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME}Targets - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} - PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} -) -export(EXPORT ${PROJECT_NAME}Targets FILE ${PROJECT_NAME}Config.cmake - NAMESPACE Upstream:: - ) +install( + TARGETS ${PROJECT_NAME} + EXPORT ${PROJECT_NAME}Targets + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + INCLUDES + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +export( + EXPORT ${PROJECT_NAME}Targets + FILE ${PROJECT_NAME}Config.cmake + NAMESPACE Upstream::) # - diff --git a/deps/AMICI/models/model_jakstat_adjoint/model_jakstat_adjoint_JSparse.cpp b/deps/AMICI/models/model_jakstat_adjoint/JSparse.cpp similarity index 100% rename from deps/AMICI/models/model_jakstat_adjoint/model_jakstat_adjoint_JSparse.cpp rename to deps/AMICI/models/model_jakstat_adjoint/JSparse.cpp diff --git a/deps/AMICI/models/model_jakstat_adjoint/model_jakstat_adjoint_Jy.cpp b/deps/AMICI/models/model_jakstat_adjoint/Jy.cpp similarity index 100% rename from deps/AMICI/models/model_jakstat_adjoint/model_jakstat_adjoint_Jy.cpp rename to deps/AMICI/models/model_jakstat_adjoint/Jy.cpp diff --git a/deps/AMICI/models/model_jakstat_adjoint/model_jakstat_adjoint_dJydsigma.cpp b/deps/AMICI/models/model_jakstat_adjoint/dJydsigma.cpp similarity index 100% rename from deps/AMICI/models/model_jakstat_adjoint/model_jakstat_adjoint_dJydsigma.cpp rename to deps/AMICI/models/model_jakstat_adjoint/dJydsigma.cpp diff --git a/deps/AMICI/models/model_jakstat_adjoint/model_jakstat_adjoint_dJydy.cpp b/deps/AMICI/models/model_jakstat_adjoint/dJydy.cpp similarity index 100% rename from deps/AMICI/models/model_jakstat_adjoint/model_jakstat_adjoint_dJydy.cpp rename to deps/AMICI/models/model_jakstat_adjoint/dJydy.cpp diff --git a/deps/AMICI/models/model_jakstat_adjoint/model_jakstat_adjoint_dsigmaydp.cpp b/deps/AMICI/models/model_jakstat_adjoint/dsigmaydp.cpp similarity index 86% rename from deps/AMICI/models/model_jakstat_adjoint/model_jakstat_adjoint_dsigmaydp.cpp rename to deps/AMICI/models/model_jakstat_adjoint/dsigmaydp.cpp index 38045122b..7313fb731 100644 --- a/deps/AMICI/models/model_jakstat_adjoint/model_jakstat_adjoint_dsigmaydp.cpp +++ b/deps/AMICI/models/model_jakstat_adjoint/dsigmaydp.cpp @@ -10,7 +10,7 @@ namespace amici { namespace model_model_jakstat_adjoint{ -void dsigmaydp_model_jakstat_adjoint(double *dsigmaydp, const realtype t, const realtype *p, const realtype *k, const int ip) { +void dsigmaydp_model_jakstat_adjoint(double *dsigmaydp, const realtype t, const realtype *p, const realtype *k, const realtype *y, const int ip) { switch (ip) { case 14: { dsigmaydp[0] = 1.0; diff --git a/deps/AMICI/models/model_jakstat_adjoint/model_jakstat_adjoint_dwdp.cpp b/deps/AMICI/models/model_jakstat_adjoint/dwdp.cpp similarity index 94% rename from deps/AMICI/models/model_jakstat_adjoint/model_jakstat_adjoint_dwdp.cpp rename to deps/AMICI/models/model_jakstat_adjoint/dwdp.cpp index 431552d3c..3213a319d 100644 --- a/deps/AMICI/models/model_jakstat_adjoint/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) { +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) { 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/model_jakstat_adjoint_dwdx.cpp b/deps/AMICI/models/model_jakstat_adjoint/dwdx.cpp similarity index 85% rename from deps/AMICI/models/model_jakstat_adjoint/model_jakstat_adjoint_dwdx.cpp rename to deps/AMICI/models/model_jakstat_adjoint/dwdx.cpp index c9c14945c..70a26b8a2 100644 --- a/deps/AMICI/models/model_jakstat_adjoint/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) { +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) { dwdx[0] = x[1]*2.0; } diff --git a/deps/AMICI/models/model_jakstat_adjoint/model_jakstat_adjoint_dxdotdp.cpp b/deps/AMICI/models/model_jakstat_adjoint/dxdotdp.cpp similarity index 100% rename from deps/AMICI/models/model_jakstat_adjoint/model_jakstat_adjoint_dxdotdp.cpp rename to deps/AMICI/models/model_jakstat_adjoint/dxdotdp.cpp diff --git a/deps/AMICI/models/model_jakstat_adjoint/model_jakstat_adjoint_dydp.cpp b/deps/AMICI/models/model_jakstat_adjoint/dydp.cpp similarity index 100% rename from deps/AMICI/models/model_jakstat_adjoint/model_jakstat_adjoint_dydp.cpp rename to deps/AMICI/models/model_jakstat_adjoint/dydp.cpp diff --git a/deps/AMICI/models/model_jakstat_adjoint/model_jakstat_adjoint_dydx.cpp b/deps/AMICI/models/model_jakstat_adjoint/dydx.cpp similarity index 100% rename from deps/AMICI/models/model_jakstat_adjoint/model_jakstat_adjoint_dydx.cpp rename to deps/AMICI/models/model_jakstat_adjoint/dydx.cpp 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 ce4eb29b7..fdac2a9f9 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) 2bd39716102b9842354964196889d40d2eb2731c */ +/* Generated by amiwrap (R2017b) 223c5075608273b17f304556d0e8ccb41233bd21 */ #include #include #include "amici/defines.h" @@ -18,15 +18,15 @@ extern void JSparse_model_jakstat_adjoint(SUNMatrixContent_Sparse JSparse, const extern void Jy_model_jakstat_adjoint(double *nllh, const int iy, const realtype *p, const realtype *k, const double *y, const double *sigmay, const double *my); 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 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); -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); +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 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); +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); +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 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); @@ -48,6 +48,7 @@ class Model_model_jakstat_adjoint : public amici::Model_ODE { 0, 0, 0, + 0, 1, 2, 1, @@ -55,6 +56,9 @@ class Model_model_jakstat_adjoint : public amici::Model_ODE { 0, 0, {}, + 0, + 0, + 0, 18, 8, 1 @@ -70,7 +74,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 "2bd39716102b9842354964196889d40d2eb2731c"; }; + std::string getAmiciCommit() const override { return "223c5075608273b17f304556d0e8ccb41233bd21"; }; 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); @@ -109,7 +113,7 @@ class Model_model_jakstat_adjoint : public amici::Model_ODE { void fdeltaqB(double *deltaqB, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const int ip, const int ie, const realtype *xdot, const realtype *xdot_old, const realtype *xB) override { } - void fdeltasx(double *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) override { + void fdeltasx(double *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) override { } void fdeltax(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) override { @@ -124,19 +128,19 @@ class Model_model_jakstat_adjoint : public amici::Model_ODE { void fdrzdx(double *drzdx, const int ie, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h) override { } - void fdsigmaydp(double *dsigmaydp, const realtype t, const realtype *p, const realtype *k, const int ip) override { - dsigmaydp_model_jakstat_adjoint(dsigmaydp, t, p, k, ip); + void fdsigmaydp(double *dsigmaydp, const realtype t, const realtype *p, const realtype *k, const realtype *y, const int ip) override { + dsigmaydp_model_jakstat_adjoint(dsigmaydp, t, p, k, y, ip); } 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) override { - dwdp_model_jakstat_adjoint(dwdp, t, x, p, k, h, w, tcl, stcl); + 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 fdwdx(realtype *dwdx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl) override { - dwdx_model_jakstat_adjoint(dwdx, t, x, p, k, h, w, tcl); + 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 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 { @@ -157,14 +161,14 @@ class Model_model_jakstat_adjoint : public amici::Model_ODE { void fdzdx(double *dzdx, const int ie, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h) override { } - void froot(realtype *root, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h) override { + void froot(realtype *root, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl) override { } void frz(double *rz, const int ie, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h) override { } - void fsigmay(double *sigmay, const realtype t, const realtype *p, const realtype *k) override { - sigmay_model_jakstat_adjoint(sigmay, t, p, k); + void fsigmay(double *sigmay, const realtype t, const realtype *p, const realtype *k, const realtype *y) override { + sigmay_model_jakstat_adjoint(sigmay, t, p, k, y); } void fsigmaz(double *sigmaz, const realtype t, const realtype *p, const realtype *k) override { @@ -173,7 +177,7 @@ class Model_model_jakstat_adjoint : public amici::Model_ODE { void fsrz(double *srz, 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 fstau(double *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) override { + void fstau(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) override { } void fsx0(realtype *sx0, const realtype t,const realtype *x0, const realtype *p, const realtype *k, const int ip) override { @@ -183,8 +187,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) override { - w_model_jakstat_adjoint(w, t, x, p, k, h, tcl); + 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 fx0(realtype *x0, const realtype t, const realtype *p, const realtype *k) override { diff --git a/deps/AMICI/models/model_jakstat_adjoint/model_jakstat_adjoint_sigmay.cpp b/deps/AMICI/models/model_jakstat_adjoint/sigmay.cpp similarity index 86% rename from deps/AMICI/models/model_jakstat_adjoint/model_jakstat_adjoint_sigmay.cpp rename to deps/AMICI/models/model_jakstat_adjoint/sigmay.cpp index 18172dff2..99dd28dff 100644 --- a/deps/AMICI/models/model_jakstat_adjoint/model_jakstat_adjoint_sigmay.cpp +++ b/deps/AMICI/models/model_jakstat_adjoint/sigmay.cpp @@ -10,7 +10,7 @@ namespace amici { namespace model_model_jakstat_adjoint{ -void sigmay_model_jakstat_adjoint(double *sigmay, const realtype t, const realtype *p, const realtype *k) { +void sigmay_model_jakstat_adjoint(double *sigmay, const realtype t, const realtype *p, const realtype *k, const realtype *y) { sigmay[0] = p[14]; sigmay[1] = p[15]; sigmay[2] = p[16]; diff --git a/deps/AMICI/models/model_jakstat_adjoint/swig/CMakeLists.txt b/deps/AMICI/models/model_jakstat_adjoint/swig/CMakeLists.txt index af0f64a76..eb9fe681c 100644 --- a/deps/AMICI/models/model_jakstat_adjoint/swig/CMakeLists.txt +++ b/deps/AMICI/models/model_jakstat_adjoint/swig/CMakeLists.txt @@ -1,28 +1,19 @@ -cmake_minimum_required(VERSION 3.8) # swig_add_library - -if(POLICY CMP0078) - cmake_policy(SET CMP0078 OLD) -endif(POLICY CMP0078) -if(POLICY CMP0074) - # Use package_ROOT environment variables - cmake_policy(SET CMP0074 NEW) -endif(POLICY CMP0074) -if(POLICY CMP0086) - cmake_policy(SET CMP0086 NEW) -endif(POLICY CMP0086) +cmake_minimum_required(VERSION 3.15) +if(DEFINED ENV{SWIG}) + set(SWIG_EXECUTABLE $ENV{SWIG}) +endif() find_package(SWIG REQUIRED) include(${SWIG_USE_FILE}) -if(${CMAKE_VERSION} VERSION_LESS "3.12.0") - find_package(PythonLibs REQUIRED) - include_directories(${PYTHON_INCLUDE_DIRS}) - set(Python3_LIBRARIES ${PYTHON_LIBRARIES}) -else() - find_package (Python3 COMPONENTS Development) - include_directories(${Python3_INCLUDE_DIRS}) +if(DEFINED ENV{PYTHON_EXECUTABLE}) + set(Python3_EXECUTABLE $ENV{PYTHON_EXECUTABLE}) endif() +# We don't need "Interpreter" here, but without that, FindPython3 will +# ignore the Python version selected via $Python3_EXECUTABLE +find_package(Python3 COMPONENTS Interpreter Development) +include_directories(${Python3_INCLUDE_DIRS}) set(SWIG_LIBRARY_NAME _${PROJECT_NAME}) set(CMAKE_SWIG_FLAGS "") @@ -30,17 +21,38 @@ set_source_files_properties(${PROJECT_NAME}.i PROPERTIES CPLUSPLUS ON) # swig does not use INTERFACE_INCLUDE_DIRS of linked libraries, so add manually get_target_property(AMICI_INCLUDE_DIRS Upstream::amici INTERFACE_INCLUDE_DIRECTORIES) -include_directories(${AMICI_INCLUDE_DIRS} ..) +include_directories(${AMICI_INCLUDE_DIRS} .. ${AMICI_INCLUDE_DIRS}/../swig) -swig_add_library(${PROJECT_NAME} +swig_add_library(${SWIG_LIBRARY_NAME} TYPE MODULE LANGUAGE python SOURCES ${PROJECT_NAME}.i) -swig_link_libraries(${PROJECT_NAME} + +set_target_properties(${SWIG_LIBRARY_NAME} + PROPERTIES + SWIG_USE_TARGET_INCLUDE_DIRECTORIES TRUE + PREFIX "" +) + +# Python extension suffix +execute_process( + COMMAND ${Python3_EXECUTABLE} -c + "import sysconfig; print(sysconfig.get_config_var('EXT_SUFFIX'))" + OUTPUT_VARIABLE PY_EXT_SUFFIX OUTPUT_STRIP_TRAILING_WHITESPACE) +if(NOT "${PY_EXT_SUFFIX}" STREQUAL "") + message(STATUS "Python extension suffix is ${PY_EXT_SUFFIX}") + set_target_properties(${SWIG_LIBRARY_NAME} PROPERTIES SUFFIX "${PY_EXT_SUFFIX}" ) +endif() + + +swig_link_libraries(${SWIG_LIBRARY_NAME} ${Python3_LIBRARIES} model) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.py + $ DESTINATION .) + # configure module setup script set(SETUP_PY_IN ${Amici_DIR}/model_setup.template.py) set(SETUP_PY_OUT ${CMAKE_CURRENT_BINARY_DIR}/setup.py) diff --git a/deps/AMICI/models/model_jakstat_adjoint/swig/model_jakstat_adjoint.i b/deps/AMICI/models/model_jakstat_adjoint/swig/model_jakstat_adjoint.i index 3f0f1374f..a986a6a5d 100644 --- a/deps/AMICI/models/model_jakstat_adjoint/swig/model_jakstat_adjoint.i +++ b/deps/AMICI/models/model_jakstat_adjoint/swig/model_jakstat_adjoint.i @@ -10,5 +10,13 @@ using namespace amici; %} +// Make model module accessible from the model +%feature("pythonappend") amici::generic_model::getModel %{ + if '.' in __name__: + import sys + val.module = sys.modules['.'.join(__name__.split('.')[:-1])] +%} + + // Process symbols in header %include "wrapfunctions.h" diff --git a/deps/AMICI/models/model_jakstat_adjoint/model_jakstat_adjoint_sx0.cpp b/deps/AMICI/models/model_jakstat_adjoint/sx0.cpp similarity index 100% rename from deps/AMICI/models/model_jakstat_adjoint/model_jakstat_adjoint_sx0.cpp rename to deps/AMICI/models/model_jakstat_adjoint/sx0.cpp diff --git a/deps/AMICI/models/model_jakstat_adjoint/model_jakstat_adjoint_w.cpp b/deps/AMICI/models/model_jakstat_adjoint/w.cpp similarity index 91% rename from deps/AMICI/models/model_jakstat_adjoint/model_jakstat_adjoint_w.cpp rename to deps/AMICI/models/model_jakstat_adjoint/w.cpp index 942ba399f..06238238c 100644 --- a/deps/AMICI/models/model_jakstat_adjoint/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) { +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) { 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/model_jakstat_adjoint_x0.cpp b/deps/AMICI/models/model_jakstat_adjoint/x0.cpp similarity index 100% rename from deps/AMICI/models/model_jakstat_adjoint/model_jakstat_adjoint_x0.cpp rename to deps/AMICI/models/model_jakstat_adjoint/x0.cpp diff --git a/deps/AMICI/models/model_jakstat_adjoint/model_jakstat_adjoint_xdot.cpp b/deps/AMICI/models/model_jakstat_adjoint/xdot.cpp similarity index 100% rename from deps/AMICI/models/model_jakstat_adjoint/model_jakstat_adjoint_xdot.cpp rename to deps/AMICI/models/model_jakstat_adjoint/xdot.cpp diff --git a/deps/AMICI/models/model_jakstat_adjoint/model_jakstat_adjoint_y.cpp b/deps/AMICI/models/model_jakstat_adjoint/y.cpp similarity index 100% rename from deps/AMICI/models/model_jakstat_adjoint/model_jakstat_adjoint_y.cpp rename to deps/AMICI/models/model_jakstat_adjoint/y.cpp diff --git a/deps/AMICI/models/model_jakstat_adjoint_o2/CMakeLists.txt b/deps/AMICI/models/model_jakstat_adjoint_o2/CMakeLists.txt index a37600c51..b4b9cc03c 100644 --- a/deps/AMICI/models/model_jakstat_adjoint_o2/CMakeLists.txt +++ b/deps/AMICI/models/model_jakstat_adjoint_o2/CMakeLists.txt @@ -1,105 +1,112 @@ -cmake_minimum_required(VERSION 3.3) - -if(POLICY CMP0060) - cmake_policy(SET CMP0060 NEW) -endif(POLICY CMP0060) -if(POLICY CMP0065) - cmake_policy(SET CMP0065 NEW) -endif(POLICY CMP0065) -if(POLICY CMP0074) - # Use package_ROOT environment variables - cmake_policy(SET CMP0074 NEW) -endif(POLICY CMP0074) +# Build AMICI model +cmake_minimum_required(VERSION 3.15) project(model_jakstat_adjoint_o2) -set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_POSITION_INDEPENDENT_CODE ON) include(CheckCXXCompilerFlag) -set(MY_CXX_FLAGS -Wall -Wno-unused-function -Wno-unused-variable -Wno-unused-but-set-variable) -foreach(FLAG ${MY_CXX_FLAGS}) - unset(CUR_FLAG_SUPPORTED CACHE) - CHECK_CXX_COMPILER_FLAG(${FLAG} CUR_FLAG_SUPPORTED) - if(${CUR_FLAG_SUPPORTED}) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FLAG}") - endif() -endforeach(FLAG) - -find_package(Amici HINTS ${CMAKE_CURRENT_LIST_DIR}/../../build) +set(MY_CXX_FLAGS -Wall -Wno-unused-function -Wno-unused-variable) +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + list(APPEND MY_CXX_FLAGS -Wno-unused-but-set-variable) +endif() +foreach(flag ${MY_CXX_FLAGS}) + unset(CUR_FLAG_SUPPORTED CACHE) + check_cxx_compiler_flag(-Werror ${flag} CUR_FLAG_SUPPORTED) + if(${CUR_FLAG_SUPPORTED}) + string(APPEND CMAKE_CXX_FLAGS " ${flag}") + endif() +endforeach() + +if(DEFINED ENV{AMICI_CXXFLAGS}) + message(STATUS "Appending flags from AMICI_CXXFLAGS: $ENV{AMICI_CXXFLAGS}") + add_compile_options("$ENV{AMICI_CXXFLAGS}") +endif() +if(DEFINED ENV{AMICI_LDFLAGS}) + message(STATUS "Appending flags from AMICI_LDFLAGS: $ENV{AMICI_LDFLAGS}") + link_libraries("$ENV{AMICI_LDFLAGS}") +endif() + +find_package(Amici REQUIRED HINTS + ${CMAKE_CURRENT_LIST_DIR}/../../build) message(STATUS "Found AMICI ${Amici_DIR}") set(MODEL_DIR ${CMAKE_CURRENT_LIST_DIR}) -set(SRC_LIST_LIB ${MODEL_DIR}/model_jakstat_adjoint_o2_JSparse.cpp -${MODEL_DIR}/model_jakstat_adjoint_o2_Jy.cpp -${MODEL_DIR}/model_jakstat_adjoint_o2_dJydsigma.cpp -${MODEL_DIR}/model_jakstat_adjoint_o2_dJydy.cpp -${MODEL_DIR}/model_jakstat_adjoint_o2_dsigmaydp.cpp -${MODEL_DIR}/model_jakstat_adjoint_o2_dwdp.cpp -${MODEL_DIR}/model_jakstat_adjoint_o2_dwdx.cpp -${MODEL_DIR}/model_jakstat_adjoint_o2_dxdotdp.cpp -${MODEL_DIR}/model_jakstat_adjoint_o2_dydp.cpp -${MODEL_DIR}/model_jakstat_adjoint_o2_dydx.cpp -${MODEL_DIR}/model_jakstat_adjoint_o2_sigmay.cpp -${MODEL_DIR}/model_jakstat_adjoint_o2_sx0.cpp -${MODEL_DIR}/model_jakstat_adjoint_o2_w.cpp -${MODEL_DIR}/model_jakstat_adjoint_o2_x0.cpp -${MODEL_DIR}/model_jakstat_adjoint_o2_xdot.cpp -${MODEL_DIR}/model_jakstat_adjoint_o2_y.cpp - -${MODEL_DIR}/wrapfunctions.cpp -) +set(SRC_LIST_LIB ${MODEL_DIR}/JSparse.cpp +${MODEL_DIR}/Jy.cpp +${MODEL_DIR}/dJydsigma.cpp +${MODEL_DIR}/dJydy.cpp +${MODEL_DIR}/dsigmaydp.cpp +${MODEL_DIR}/dwdp.cpp +${MODEL_DIR}/dwdx.cpp +${MODEL_DIR}/dxdotdp.cpp +${MODEL_DIR}/dydp.cpp +${MODEL_DIR}/dydx.cpp +${MODEL_DIR}/sigmay.cpp +${MODEL_DIR}/sx0.cpp +${MODEL_DIR}/w.cpp +${MODEL_DIR}/x0.cpp +${MODEL_DIR}/xdot.cpp +${MODEL_DIR}/y.cpp + ${MODEL_DIR}/wrapfunctions.cpp) add_library(${PROJECT_NAME} ${SRC_LIST_LIB}) add_library(model ALIAS ${PROJECT_NAME}) -# This option can be helpful when using the Intel compiler and compilation of -# wrapfunctions.cpp fails due to insufficient memory. -option(ENABLE_WRAPFUNCTIONS_OPTIMIZATIONS "Enable compiler optimizations for wrapfunctions.cpp?" ON) -if(NOT ENABLE_WRAPFUNCTIONS_OPTIMIZATIONS) - set_source_files_properties(wrapfunctions.cpp PROPERTIES COMPILE_FLAGS -O0) -endif() +# Some special functions require boost +# +# TODO: set some flag during code generation whether the given model requires +# boost. for now, try to find it, add include directories and link against it. +# let the compiler/linker error if it is required but not found +find_package(Boost) target_include_directories(${PROJECT_NAME} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") -target_link_libraries(${PROJECT_NAME} - PUBLIC Upstream::amici -) +target_link_libraries( + ${PROJECT_NAME} + PUBLIC Upstream::amici + PRIVATE $<$:Boost::boost>) -set(SRC_LIST_EXE main.cpp) - -add_executable(simulate_${PROJECT_NAME} ${SRC_LIST_EXE}) +if(NOT "${AMICI_PYTHON_BUILD_EXT_ONLY}") + set(SRC_LIST_EXE main.cpp) + add_executable(simulate_${PROJECT_NAME} ${SRC_LIST_EXE}) + target_link_libraries(simulate_${PROJECT_NAME} ${PROJECT_NAME}) +endif() -target_link_libraries(simulate_${PROJECT_NAME} ${PROJECT_NAME}) +# 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}) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 --coverage") + string(APPEND CMAKE_CXX_FLAGS_DEBUG " --coverage") + string(APPEND CMAKE_EXE_LINKER_FLAGS_DEBUG " --coverage") endif() -## SWIG +# SWIG option(ENABLE_SWIG "Build swig/python library?" ON) if(ENABLE_SWIG) - if(NOT(${CMAKE_VERSION} VERSION_LESS 3.8)) - add_subdirectory(swig) - else() - message(WARNING "Unable to build SWIG interface, upgrade CMake to >=3.8.") - endif() + add_subdirectory(swig) endif() - # include(GNUInstallDirs) -install(TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME}Targets - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} - PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} -) -export(EXPORT ${PROJECT_NAME}Targets FILE ${PROJECT_NAME}Config.cmake - NAMESPACE Upstream:: - ) +install( + TARGETS ${PROJECT_NAME} + EXPORT ${PROJECT_NAME}Targets + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + INCLUDES + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +export( + EXPORT ${PROJECT_NAME}Targets + FILE ${PROJECT_NAME}Config.cmake + NAMESPACE Upstream::) # - diff --git a/deps/AMICI/models/model_jakstat_adjoint_o2/model_jakstat_adjoint_o2_JSparse.cpp b/deps/AMICI/models/model_jakstat_adjoint_o2/JSparse.cpp similarity index 100% rename from deps/AMICI/models/model_jakstat_adjoint_o2/model_jakstat_adjoint_o2_JSparse.cpp rename to deps/AMICI/models/model_jakstat_adjoint_o2/JSparse.cpp diff --git a/deps/AMICI/models/model_jakstat_adjoint_o2/model_jakstat_adjoint_o2_Jy.cpp b/deps/AMICI/models/model_jakstat_adjoint_o2/Jy.cpp similarity index 100% rename from deps/AMICI/models/model_jakstat_adjoint_o2/model_jakstat_adjoint_o2_Jy.cpp rename to deps/AMICI/models/model_jakstat_adjoint_o2/Jy.cpp diff --git a/deps/AMICI/models/model_jakstat_adjoint_o2/model_jakstat_adjoint_o2_dJydsigma.cpp b/deps/AMICI/models/model_jakstat_adjoint_o2/dJydsigma.cpp similarity index 100% rename from deps/AMICI/models/model_jakstat_adjoint_o2/model_jakstat_adjoint_o2_dJydsigma.cpp rename to deps/AMICI/models/model_jakstat_adjoint_o2/dJydsigma.cpp diff --git a/deps/AMICI/models/model_jakstat_adjoint_o2/model_jakstat_adjoint_o2_dJydy.cpp b/deps/AMICI/models/model_jakstat_adjoint_o2/dJydy.cpp similarity index 100% rename from deps/AMICI/models/model_jakstat_adjoint_o2/model_jakstat_adjoint_o2_dJydy.cpp rename to deps/AMICI/models/model_jakstat_adjoint_o2/dJydy.cpp diff --git a/deps/AMICI/models/model_jakstat_adjoint_o2/model_jakstat_adjoint_o2_dsigmaydp.cpp b/deps/AMICI/models/model_jakstat_adjoint_o2/dsigmaydp.cpp similarity index 85% rename from deps/AMICI/models/model_jakstat_adjoint_o2/model_jakstat_adjoint_o2_dsigmaydp.cpp rename to deps/AMICI/models/model_jakstat_adjoint_o2/dsigmaydp.cpp index 7ebea670b..ac9cb8730 100644 --- a/deps/AMICI/models/model_jakstat_adjoint_o2/model_jakstat_adjoint_o2_dsigmaydp.cpp +++ b/deps/AMICI/models/model_jakstat_adjoint_o2/dsigmaydp.cpp @@ -10,7 +10,7 @@ namespace amici { namespace model_model_jakstat_adjoint_o2{ -void dsigmaydp_model_jakstat_adjoint_o2(double *dsigmaydp, const realtype t, const realtype *p, const realtype *k, const int ip) { +void dsigmaydp_model_jakstat_adjoint_o2(double *dsigmaydp, const realtype t, const realtype *p, const realtype *k, const realtype *y, const int ip) { switch (ip) { case 14: { dsigmaydp[0] = 1.0; diff --git a/deps/AMICI/models/model_jakstat_adjoint_o2/model_jakstat_adjoint_o2_dwdp.cpp b/deps/AMICI/models/model_jakstat_adjoint_o2/dwdp.cpp similarity index 98% rename from deps/AMICI/models/model_jakstat_adjoint_o2/model_jakstat_adjoint_o2_dwdp.cpp rename to deps/AMICI/models/model_jakstat_adjoint_o2/dwdp.cpp index 3035ef9c7..b3e591fcb 100644 --- a/deps/AMICI/models/model_jakstat_adjoint_o2/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) { +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) { 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/model_jakstat_adjoint_o2_dwdx.cpp b/deps/AMICI/models/model_jakstat_adjoint_o2/dwdx.cpp similarity index 85% rename from deps/AMICI/models/model_jakstat_adjoint_o2/model_jakstat_adjoint_o2_dwdx.cpp rename to deps/AMICI/models/model_jakstat_adjoint_o2/dwdx.cpp index 0c441f5c2..3226a7535 100644 --- a/deps/AMICI/models/model_jakstat_adjoint_o2/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) { +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) { dwdx[0] = x[1]*2.0; dwdx[1] = 2.0; } diff --git a/deps/AMICI/models/model_jakstat_adjoint_o2/model_jakstat_adjoint_o2_dxdotdp.cpp b/deps/AMICI/models/model_jakstat_adjoint_o2/dxdotdp.cpp similarity index 100% rename from deps/AMICI/models/model_jakstat_adjoint_o2/model_jakstat_adjoint_o2_dxdotdp.cpp rename to deps/AMICI/models/model_jakstat_adjoint_o2/dxdotdp.cpp diff --git a/deps/AMICI/models/model_jakstat_adjoint_o2/model_jakstat_adjoint_o2_dydp.cpp b/deps/AMICI/models/model_jakstat_adjoint_o2/dydp.cpp similarity index 100% rename from deps/AMICI/models/model_jakstat_adjoint_o2/model_jakstat_adjoint_o2_dydp.cpp rename to deps/AMICI/models/model_jakstat_adjoint_o2/dydp.cpp diff --git a/deps/AMICI/models/model_jakstat_adjoint_o2/model_jakstat_adjoint_o2_dydx.cpp b/deps/AMICI/models/model_jakstat_adjoint_o2/dydx.cpp similarity index 100% rename from deps/AMICI/models/model_jakstat_adjoint_o2/model_jakstat_adjoint_o2_dydx.cpp rename to deps/AMICI/models/model_jakstat_adjoint_o2/dydx.cpp 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 273de3a97..22ca27606 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) 2bd39716102b9842354964196889d40d2eb2731c */ +/* Generated by amiwrap (R2017b) 223c5075608273b17f304556d0e8ccb41233bd21 */ #include #include #include "amici/defines.h" @@ -18,15 +18,15 @@ extern void JSparse_model_jakstat_adjoint_o2(SUNMatrixContent_Sparse JSparse, co extern void Jy_model_jakstat_adjoint_o2(double *nllh, const int iy, const realtype *p, const realtype *k, const double *y, const double *sigmay, const double *my); 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 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); -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); +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 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); +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); +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 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); @@ -48,6 +48,7 @@ class Model_model_jakstat_adjoint_o2 : public amici::Model_ODE { 0, 0, 0, + 0, 18, 10, 2, @@ -55,6 +56,9 @@ class Model_model_jakstat_adjoint_o2 : public amici::Model_ODE { 0, 0, {}, + 0, + 0, + 0, 384, 8, 154 @@ -70,7 +74,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 "2bd39716102b9842354964196889d40d2eb2731c"; }; + std::string getAmiciCommit() const override { return "223c5075608273b17f304556d0e8ccb41233bd21"; }; 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); @@ -109,7 +113,7 @@ class Model_model_jakstat_adjoint_o2 : public amici::Model_ODE { void fdeltaqB(double *deltaqB, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const int ip, const int ie, const realtype *xdot, const realtype *xdot_old, const realtype *xB) override { } - void fdeltasx(double *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) override { + void fdeltasx(double *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) override { } void fdeltax(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) override { @@ -124,19 +128,19 @@ class Model_model_jakstat_adjoint_o2 : public amici::Model_ODE { void fdrzdx(double *drzdx, const int ie, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h) override { } - void fdsigmaydp(double *dsigmaydp, const realtype t, const realtype *p, const realtype *k, const int ip) override { - dsigmaydp_model_jakstat_adjoint_o2(dsigmaydp, t, p, k, ip); + void fdsigmaydp(double *dsigmaydp, const realtype t, const realtype *p, const realtype *k, const realtype *y, const int ip) override { + dsigmaydp_model_jakstat_adjoint_o2(dsigmaydp, t, p, k, y, ip); } 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) override { - dwdp_model_jakstat_adjoint_o2(dwdp, t, x, p, k, h, w, tcl, stcl); + 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 fdwdx(realtype *dwdx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl) override { - dwdx_model_jakstat_adjoint_o2(dwdx, t, x, p, k, h, w, tcl); + 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 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 { @@ -157,14 +161,14 @@ class Model_model_jakstat_adjoint_o2 : public amici::Model_ODE { void fdzdx(double *dzdx, const int ie, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h) override { } - void froot(realtype *root, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h) override { + void froot(realtype *root, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl) override { } void frz(double *rz, const int ie, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h) override { } - void fsigmay(double *sigmay, const realtype t, const realtype *p, const realtype *k) override { - sigmay_model_jakstat_adjoint_o2(sigmay, t, p, k); + void fsigmay(double *sigmay, const realtype t, const realtype *p, const realtype *k, const realtype *y) override { + sigmay_model_jakstat_adjoint_o2(sigmay, t, p, k, y); } void fsigmaz(double *sigmaz, const realtype t, const realtype *p, const realtype *k) override { @@ -173,7 +177,7 @@ class Model_model_jakstat_adjoint_o2 : public amici::Model_ODE { void fsrz(double *srz, 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 fstau(double *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) override { + void fstau(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) override { } void fsx0(realtype *sx0, const realtype t,const realtype *x0, const realtype *p, const realtype *k, const int ip) override { @@ -183,8 +187,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) override { - w_model_jakstat_adjoint_o2(w, t, x, p, k, h, tcl); + 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 fx0(realtype *x0, const realtype t, const realtype *p, const realtype *k) override { diff --git a/deps/AMICI/models/model_jakstat_adjoint_o2/model_jakstat_adjoint_o2_sigmay.cpp b/deps/AMICI/models/model_jakstat_adjoint_o2/sigmay.cpp similarity index 88% rename from deps/AMICI/models/model_jakstat_adjoint_o2/model_jakstat_adjoint_o2_sigmay.cpp rename to deps/AMICI/models/model_jakstat_adjoint_o2/sigmay.cpp index 26120455a..6b162c6f0 100644 --- a/deps/AMICI/models/model_jakstat_adjoint_o2/model_jakstat_adjoint_o2_sigmay.cpp +++ b/deps/AMICI/models/model_jakstat_adjoint_o2/sigmay.cpp @@ -10,7 +10,7 @@ namespace amici { namespace model_model_jakstat_adjoint_o2{ -void sigmay_model_jakstat_adjoint_o2(double *sigmay, const realtype t, const realtype *p, const realtype *k) { +void sigmay_model_jakstat_adjoint_o2(double *sigmay, const realtype t, const realtype *p, const realtype *k, const realtype *y) { sigmay[0] = p[14]; sigmay[1] = p[15]; sigmay[2] = p[16]; diff --git a/deps/AMICI/models/model_jakstat_adjoint_o2/swig/CMakeLists.txt b/deps/AMICI/models/model_jakstat_adjoint_o2/swig/CMakeLists.txt index af0f64a76..eb9fe681c 100644 --- a/deps/AMICI/models/model_jakstat_adjoint_o2/swig/CMakeLists.txt +++ b/deps/AMICI/models/model_jakstat_adjoint_o2/swig/CMakeLists.txt @@ -1,28 +1,19 @@ -cmake_minimum_required(VERSION 3.8) # swig_add_library - -if(POLICY CMP0078) - cmake_policy(SET CMP0078 OLD) -endif(POLICY CMP0078) -if(POLICY CMP0074) - # Use package_ROOT environment variables - cmake_policy(SET CMP0074 NEW) -endif(POLICY CMP0074) -if(POLICY CMP0086) - cmake_policy(SET CMP0086 NEW) -endif(POLICY CMP0086) +cmake_minimum_required(VERSION 3.15) +if(DEFINED ENV{SWIG}) + set(SWIG_EXECUTABLE $ENV{SWIG}) +endif() find_package(SWIG REQUIRED) include(${SWIG_USE_FILE}) -if(${CMAKE_VERSION} VERSION_LESS "3.12.0") - find_package(PythonLibs REQUIRED) - include_directories(${PYTHON_INCLUDE_DIRS}) - set(Python3_LIBRARIES ${PYTHON_LIBRARIES}) -else() - find_package (Python3 COMPONENTS Development) - include_directories(${Python3_INCLUDE_DIRS}) +if(DEFINED ENV{PYTHON_EXECUTABLE}) + set(Python3_EXECUTABLE $ENV{PYTHON_EXECUTABLE}) endif() +# We don't need "Interpreter" here, but without that, FindPython3 will +# ignore the Python version selected via $Python3_EXECUTABLE +find_package(Python3 COMPONENTS Interpreter Development) +include_directories(${Python3_INCLUDE_DIRS}) set(SWIG_LIBRARY_NAME _${PROJECT_NAME}) set(CMAKE_SWIG_FLAGS "") @@ -30,17 +21,38 @@ set_source_files_properties(${PROJECT_NAME}.i PROPERTIES CPLUSPLUS ON) # swig does not use INTERFACE_INCLUDE_DIRS of linked libraries, so add manually get_target_property(AMICI_INCLUDE_DIRS Upstream::amici INTERFACE_INCLUDE_DIRECTORIES) -include_directories(${AMICI_INCLUDE_DIRS} ..) +include_directories(${AMICI_INCLUDE_DIRS} .. ${AMICI_INCLUDE_DIRS}/../swig) -swig_add_library(${PROJECT_NAME} +swig_add_library(${SWIG_LIBRARY_NAME} TYPE MODULE LANGUAGE python SOURCES ${PROJECT_NAME}.i) -swig_link_libraries(${PROJECT_NAME} + +set_target_properties(${SWIG_LIBRARY_NAME} + PROPERTIES + SWIG_USE_TARGET_INCLUDE_DIRECTORIES TRUE + PREFIX "" +) + +# Python extension suffix +execute_process( + COMMAND ${Python3_EXECUTABLE} -c + "import sysconfig; print(sysconfig.get_config_var('EXT_SUFFIX'))" + OUTPUT_VARIABLE PY_EXT_SUFFIX OUTPUT_STRIP_TRAILING_WHITESPACE) +if(NOT "${PY_EXT_SUFFIX}" STREQUAL "") + message(STATUS "Python extension suffix is ${PY_EXT_SUFFIX}") + set_target_properties(${SWIG_LIBRARY_NAME} PROPERTIES SUFFIX "${PY_EXT_SUFFIX}" ) +endif() + + +swig_link_libraries(${SWIG_LIBRARY_NAME} ${Python3_LIBRARIES} model) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.py + $ DESTINATION .) + # configure module setup script set(SETUP_PY_IN ${Amici_DIR}/model_setup.template.py) set(SETUP_PY_OUT ${CMAKE_CURRENT_BINARY_DIR}/setup.py) diff --git a/deps/AMICI/models/model_jakstat_adjoint_o2/swig/model_jakstat_adjoint_o2.i b/deps/AMICI/models/model_jakstat_adjoint_o2/swig/model_jakstat_adjoint_o2.i index 97b5161c8..50591d090 100644 --- a/deps/AMICI/models/model_jakstat_adjoint_o2/swig/model_jakstat_adjoint_o2.i +++ b/deps/AMICI/models/model_jakstat_adjoint_o2/swig/model_jakstat_adjoint_o2.i @@ -10,5 +10,13 @@ using namespace amici; %} +// Make model module accessible from the model +%feature("pythonappend") amici::generic_model::getModel %{ + if '.' in __name__: + import sys + val.module = sys.modules['.'.join(__name__.split('.')[:-1])] +%} + + // Process symbols in header %include "wrapfunctions.h" diff --git a/deps/AMICI/models/model_jakstat_adjoint_o2/model_jakstat_adjoint_o2_sx0.cpp b/deps/AMICI/models/model_jakstat_adjoint_o2/sx0.cpp similarity index 100% rename from deps/AMICI/models/model_jakstat_adjoint_o2/model_jakstat_adjoint_o2_sx0.cpp rename to deps/AMICI/models/model_jakstat_adjoint_o2/sx0.cpp diff --git a/deps/AMICI/models/model_jakstat_adjoint_o2/model_jakstat_adjoint_o2_w.cpp b/deps/AMICI/models/model_jakstat_adjoint_o2/w.cpp similarity index 95% rename from deps/AMICI/models/model_jakstat_adjoint_o2/model_jakstat_adjoint_o2_w.cpp rename to deps/AMICI/models/model_jakstat_adjoint_o2/w.cpp index 2b1e113f0..766860cfa 100644 --- a/deps/AMICI/models/model_jakstat_adjoint_o2/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) { +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) { 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_jakstat_adjoint_o2/model_jakstat_adjoint_o2_x0.cpp b/deps/AMICI/models/model_jakstat_adjoint_o2/x0.cpp similarity index 100% rename from deps/AMICI/models/model_jakstat_adjoint_o2/model_jakstat_adjoint_o2_x0.cpp rename to deps/AMICI/models/model_jakstat_adjoint_o2/x0.cpp diff --git a/deps/AMICI/models/model_jakstat_adjoint_o2/model_jakstat_adjoint_o2_xdot.cpp b/deps/AMICI/models/model_jakstat_adjoint_o2/xdot.cpp similarity index 100% rename from deps/AMICI/models/model_jakstat_adjoint_o2/model_jakstat_adjoint_o2_xdot.cpp rename to deps/AMICI/models/model_jakstat_adjoint_o2/xdot.cpp diff --git a/deps/AMICI/models/model_jakstat_adjoint_o2/model_jakstat_adjoint_o2_y.cpp b/deps/AMICI/models/model_jakstat_adjoint_o2/y.cpp similarity index 100% rename from deps/AMICI/models/model_jakstat_adjoint_o2/model_jakstat_adjoint_o2_y.cpp rename to deps/AMICI/models/model_jakstat_adjoint_o2/y.cpp diff --git a/deps/AMICI/models/model_nested_events/CMakeLists.txt b/deps/AMICI/models/model_nested_events/CMakeLists.txt index af4214b97..1a67d0a2c 100644 --- a/deps/AMICI/models/model_nested_events/CMakeLists.txt +++ b/deps/AMICI/models/model_nested_events/CMakeLists.txt @@ -1,105 +1,112 @@ -cmake_minimum_required(VERSION 3.3) - -if(POLICY CMP0060) - cmake_policy(SET CMP0060 NEW) -endif(POLICY CMP0060) -if(POLICY CMP0065) - cmake_policy(SET CMP0065 NEW) -endif(POLICY CMP0065) -if(POLICY CMP0074) - # Use package_ROOT environment variables - cmake_policy(SET CMP0074 NEW) -endif(POLICY CMP0074) +# Build AMICI model +cmake_minimum_required(VERSION 3.15) project(model_nested_events) -set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_POSITION_INDEPENDENT_CODE ON) include(CheckCXXCompilerFlag) -set(MY_CXX_FLAGS -Wall -Wno-unused-function -Wno-unused-variable -Wno-unused-but-set-variable) -foreach(FLAG ${MY_CXX_FLAGS}) - unset(CUR_FLAG_SUPPORTED CACHE) - CHECK_CXX_COMPILER_FLAG(${FLAG} CUR_FLAG_SUPPORTED) - if(${CUR_FLAG_SUPPORTED}) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FLAG}") - endif() -endforeach(FLAG) - -find_package(Amici HINTS ${CMAKE_CURRENT_LIST_DIR}/../../build) +set(MY_CXX_FLAGS -Wall -Wno-unused-function -Wno-unused-variable) +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + list(APPEND MY_CXX_FLAGS -Wno-unused-but-set-variable) +endif() +foreach(flag ${MY_CXX_FLAGS}) + unset(CUR_FLAG_SUPPORTED CACHE) + check_cxx_compiler_flag(-Werror ${flag} CUR_FLAG_SUPPORTED) + if(${CUR_FLAG_SUPPORTED}) + string(APPEND CMAKE_CXX_FLAGS " ${flag}") + endif() +endforeach() + +if(DEFINED ENV{AMICI_CXXFLAGS}) + message(STATUS "Appending flags from AMICI_CXXFLAGS: $ENV{AMICI_CXXFLAGS}") + add_compile_options("$ENV{AMICI_CXXFLAGS}") +endif() +if(DEFINED ENV{AMICI_LDFLAGS}) + message(STATUS "Appending flags from AMICI_LDFLAGS: $ENV{AMICI_LDFLAGS}") + link_libraries("$ENV{AMICI_LDFLAGS}") +endif() + +find_package(Amici REQUIRED HINTS + ${CMAKE_CURRENT_LIST_DIR}/../../build) message(STATUS "Found AMICI ${Amici_DIR}") set(MODEL_DIR ${CMAKE_CURRENT_LIST_DIR}) -set(SRC_LIST_LIB ${MODEL_DIR}/model_nested_events_JSparse.cpp -${MODEL_DIR}/model_nested_events_Jy.cpp -${MODEL_DIR}/model_nested_events_dJydsigma.cpp -${MODEL_DIR}/model_nested_events_dJydy.cpp -${MODEL_DIR}/model_nested_events_deltaqB.cpp -${MODEL_DIR}/model_nested_events_deltasx.cpp -${MODEL_DIR}/model_nested_events_deltax.cpp -${MODEL_DIR}/model_nested_events_dxdotdp.cpp -${MODEL_DIR}/model_nested_events_dydx.cpp -${MODEL_DIR}/model_nested_events_root.cpp -${MODEL_DIR}/model_nested_events_sigmay.cpp -${MODEL_DIR}/model_nested_events_stau.cpp -${MODEL_DIR}/model_nested_events_sx0.cpp -${MODEL_DIR}/model_nested_events_x0.cpp -${MODEL_DIR}/model_nested_events_xdot.cpp -${MODEL_DIR}/model_nested_events_y.cpp - -${MODEL_DIR}/wrapfunctions.cpp -) +set(SRC_LIST_LIB ${MODEL_DIR}/JSparse.cpp +${MODEL_DIR}/Jy.cpp +${MODEL_DIR}/dJydsigma.cpp +${MODEL_DIR}/dJydy.cpp +${MODEL_DIR}/deltaqB.cpp +${MODEL_DIR}/deltasx.cpp +${MODEL_DIR}/deltax.cpp +${MODEL_DIR}/dxdotdp.cpp +${MODEL_DIR}/dydx.cpp +${MODEL_DIR}/root.cpp +${MODEL_DIR}/sigmay.cpp +${MODEL_DIR}/stau.cpp +${MODEL_DIR}/sx0.cpp +${MODEL_DIR}/x0.cpp +${MODEL_DIR}/xdot.cpp +${MODEL_DIR}/y.cpp + ${MODEL_DIR}/wrapfunctions.cpp) add_library(${PROJECT_NAME} ${SRC_LIST_LIB}) add_library(model ALIAS ${PROJECT_NAME}) -# This option can be helpful when using the Intel compiler and compilation of -# wrapfunctions.cpp fails due to insufficient memory. -option(ENABLE_WRAPFUNCTIONS_OPTIMIZATIONS "Enable compiler optimizations for wrapfunctions.cpp?" ON) -if(NOT ENABLE_WRAPFUNCTIONS_OPTIMIZATIONS) - set_source_files_properties(wrapfunctions.cpp PROPERTIES COMPILE_FLAGS -O0) -endif() +# Some special functions require boost +# +# TODO: set some flag during code generation whether the given model requires +# boost. for now, try to find it, add include directories and link against it. +# let the compiler/linker error if it is required but not found +find_package(Boost) target_include_directories(${PROJECT_NAME} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") -target_link_libraries(${PROJECT_NAME} - PUBLIC Upstream::amici -) +target_link_libraries( + ${PROJECT_NAME} + PUBLIC Upstream::amici + PRIVATE $<$:Boost::boost>) -set(SRC_LIST_EXE main.cpp) - -add_executable(simulate_${PROJECT_NAME} ${SRC_LIST_EXE}) +if(NOT "${AMICI_PYTHON_BUILD_EXT_ONLY}") + set(SRC_LIST_EXE main.cpp) + add_executable(simulate_${PROJECT_NAME} ${SRC_LIST_EXE}) + target_link_libraries(simulate_${PROJECT_NAME} ${PROJECT_NAME}) +endif() -target_link_libraries(simulate_${PROJECT_NAME} ${PROJECT_NAME}) +# 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}) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 --coverage") + string(APPEND CMAKE_CXX_FLAGS_DEBUG " --coverage") + string(APPEND CMAKE_EXE_LINKER_FLAGS_DEBUG " --coverage") endif() -## SWIG +# SWIG option(ENABLE_SWIG "Build swig/python library?" ON) if(ENABLE_SWIG) - if(NOT(${CMAKE_VERSION} VERSION_LESS 3.8)) - add_subdirectory(swig) - else() - message(WARNING "Unable to build SWIG interface, upgrade CMake to >=3.8.") - endif() + add_subdirectory(swig) endif() - # include(GNUInstallDirs) -install(TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME}Targets - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} - PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} -) -export(EXPORT ${PROJECT_NAME}Targets FILE ${PROJECT_NAME}Config.cmake - NAMESPACE Upstream:: - ) +install( + TARGETS ${PROJECT_NAME} + EXPORT ${PROJECT_NAME}Targets + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + INCLUDES + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +export( + EXPORT ${PROJECT_NAME}Targets + FILE ${PROJECT_NAME}Config.cmake + NAMESPACE Upstream::) # - diff --git a/deps/AMICI/models/model_nested_events/model_nested_events_JSparse.cpp b/deps/AMICI/models/model_nested_events/JSparse.cpp similarity index 100% rename from deps/AMICI/models/model_nested_events/model_nested_events_JSparse.cpp rename to deps/AMICI/models/model_nested_events/JSparse.cpp diff --git a/deps/AMICI/models/model_nested_events/model_nested_events_Jy.cpp b/deps/AMICI/models/model_nested_events/Jy.cpp similarity index 100% rename from deps/AMICI/models/model_nested_events/model_nested_events_Jy.cpp rename to deps/AMICI/models/model_nested_events/Jy.cpp diff --git a/deps/AMICI/models/model_nested_events/model_nested_events_dJydsigma.cpp b/deps/AMICI/models/model_nested_events/dJydsigma.cpp similarity index 100% rename from deps/AMICI/models/model_nested_events/model_nested_events_dJydsigma.cpp rename to deps/AMICI/models/model_nested_events/dJydsigma.cpp diff --git a/deps/AMICI/models/model_nested_events/model_nested_events_dJydy.cpp b/deps/AMICI/models/model_nested_events/dJydy.cpp similarity index 100% rename from deps/AMICI/models/model_nested_events/model_nested_events_dJydy.cpp rename to deps/AMICI/models/model_nested_events/dJydy.cpp diff --git a/deps/AMICI/models/model_nested_events/model_nested_events_deltaqB.cpp b/deps/AMICI/models/model_nested_events/deltaqB.cpp similarity index 100% rename from deps/AMICI/models/model_nested_events/model_nested_events_deltaqB.cpp rename to deps/AMICI/models/model_nested_events/deltaqB.cpp diff --git a/deps/AMICI/models/model_nested_events/model_nested_events_deltasx.cpp b/deps/AMICI/models/model_nested_events/deltasx.cpp similarity index 98% rename from deps/AMICI/models/model_nested_events/model_nested_events_deltasx.cpp rename to deps/AMICI/models/model_nested_events/deltasx.cpp index d464477b4..15b55bb52 100644 --- a/deps/AMICI/models/model_nested_events/model_nested_events_deltasx.cpp +++ b/deps/AMICI/models/model_nested_events/deltasx.cpp @@ -10,7 +10,7 @@ namespace amici { namespace model_model_nested_events{ -void deltasx_model_nested_events(double *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) { +void deltasx_model_nested_events(double *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) { switch (ip) { case 0: { switch(ie) { diff --git a/deps/AMICI/models/model_nested_events/model_nested_events_deltax.cpp b/deps/AMICI/models/model_nested_events/deltax.cpp similarity index 100% rename from deps/AMICI/models/model_nested_events/model_nested_events_deltax.cpp rename to deps/AMICI/models/model_nested_events/deltax.cpp diff --git a/deps/AMICI/models/model_nested_events/model_nested_events_dxdotdp.cpp b/deps/AMICI/models/model_nested_events/dxdotdp.cpp similarity index 100% rename from deps/AMICI/models/model_nested_events/model_nested_events_dxdotdp.cpp rename to deps/AMICI/models/model_nested_events/dxdotdp.cpp diff --git a/deps/AMICI/models/model_nested_events/model_nested_events_dydx.cpp b/deps/AMICI/models/model_nested_events/dydx.cpp similarity index 100% rename from deps/AMICI/models/model_nested_events/model_nested_events_dydx.cpp rename to deps/AMICI/models/model_nested_events/dydx.cpp 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 ceab6cb1a..9ff8f519f 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) 2bd39716102b9842354964196889d40d2eb2731c */ +/* Generated by amiwrap (R2017b) 223c5075608273b17f304556d0e8ccb41233bd21 */ #include #include #include "amici/defines.h" @@ -19,13 +19,13 @@ extern void Jy_model_nested_events(double *nllh, const int iy, const realtype *p extern void dJydsigma_model_nested_events(double *dJydsigma, const int iy, const realtype *p, const realtype *k, const double *y, const double *sigmay, const double *my); extern void dJydy_model_nested_events(double *dJydy, const int iy, const realtype *p, const realtype *k, const double *y, const double *sigmay, const double *my); extern void deltaqB_model_nested_events(double *deltaqB, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const int ip, const int ie, const realtype *xdot, const realtype *xdot_old, const realtype *xB); -extern void deltasx_model_nested_events(double *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); +extern void deltasx_model_nested_events(double *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); extern void deltax_model_nested_events(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 dxdotdp_model_nested_events(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_nested_events(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_nested_events(realtype *root, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h); -extern void sigmay_model_nested_events(double *sigmay, const realtype t, const realtype *p, const realtype *k); -extern void stau_model_nested_events(double *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); +extern void root_model_nested_events(realtype *root, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl); +extern void sigmay_model_nested_events(double *sigmay, const realtype t, const realtype *p, const realtype *k, const realtype *y); +extern void stau_model_nested_events(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_nested_events(realtype *sx0, const realtype t,const realtype *x0, const realtype *p, const realtype *k, const int ip); extern void x0_model_nested_events(realtype *x0, const realtype t, const realtype *p, const realtype *k); extern void xdot_model_nested_events(realtype *xdot, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w); @@ -48,6 +48,7 @@ class Model_model_nested_events : public amici::Model_ODE { 0, 0, 4, + 0, 1, 0, 0, @@ -55,6 +56,9 @@ class Model_model_nested_events : public amici::Model_ODE { 0, 0, {}, + 0, + 0, + 0, 1, 0, 0 @@ -70,7 +74,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 "2bd39716102b9842354964196889d40d2eb2731c"; }; + std::string getAmiciCommit() const override { return "223c5075608273b17f304556d0e8ccb41233bd21"; }; 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); @@ -110,8 +114,8 @@ class Model_model_nested_events : public amici::Model_ODE { deltaqB_model_nested_events(deltaqB, t, x, p, k, h, ip, ie, xdot, xdot_old, xB); } - void fdeltasx(double *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) override { - deltasx_model_nested_events(deltasx, t, x, p, k, h, w, ip, ie, xdot, xdot_old, sx, stau); + void fdeltasx(double *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) override { + deltasx_model_nested_events(deltasx, t, x, p, k, h, w, ip, ie, xdot, xdot_old, sx, stau, tcl); } void fdeltax(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) override { @@ -127,16 +131,16 @@ class Model_model_nested_events : public amici::Model_ODE { void fdrzdx(double *drzdx, const int ie, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h) override { } - void fdsigmaydp(double *dsigmaydp, const realtype t, const realtype *p, const realtype *k, const int ip) override { + void fdsigmaydp(double *dsigmaydp, const realtype t, const realtype *p, const realtype *k, const realtype *y, const int ip) override { } 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) 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 fdwdx(realtype *dwdx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl) 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 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 { @@ -156,15 +160,15 @@ class Model_model_nested_events : public amici::Model_ODE { void fdzdx(double *dzdx, const int ie, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h) override { } - void froot(realtype *root, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h) override { - root_model_nested_events(root, t, x, p, k, h); + void froot(realtype *root, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl) override { + root_model_nested_events(root, t, x, p, k, h, tcl); } void frz(double *rz, const int ie, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h) override { } - void fsigmay(double *sigmay, const realtype t, const realtype *p, const realtype *k) override { - sigmay_model_nested_events(sigmay, t, p, k); + void fsigmay(double *sigmay, const realtype t, const realtype *p, const realtype *k, const realtype *y) override { + sigmay_model_nested_events(sigmay, t, p, k, y); } void fsigmaz(double *sigmaz, const realtype t, const realtype *p, const realtype *k) override { @@ -173,8 +177,8 @@ class Model_model_nested_events : public amici::Model_ODE { void fsrz(double *srz, 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 fstau(double *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) override { - stau_model_nested_events(stau, t, x, p, k, h, sx, ip, ie); + void fstau(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) override { + stau_model_nested_events(stau, t, x, p, k, h, tcl, sx, ip, ie); } void fsx0(realtype *sx0, const realtype t,const realtype *x0, const realtype *p, const realtype *k, const int ip) override { @@ -184,7 +188,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) 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 fx0(realtype *x0, const realtype t, const realtype *p, const realtype *k) override { diff --git a/deps/AMICI/models/model_nested_events/model_nested_events_root.cpp b/deps/AMICI/models/model_nested_events/root.cpp similarity index 93% rename from deps/AMICI/models/model_nested_events/model_nested_events_root.cpp rename to deps/AMICI/models/model_nested_events/root.cpp index c18683f8e..0cf33d2d5 100644 --- a/deps/AMICI/models/model_nested_events/model_nested_events_root.cpp +++ b/deps/AMICI/models/model_nested_events/root.cpp @@ -10,7 +10,7 @@ namespace amici { namespace model_model_nested_events{ -void root_model_nested_events(realtype *root, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h) { +void root_model_nested_events(realtype *root, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl) { root[0] = -x[0]+1.0; root[1] = x[0]-1.0; root[2] = t-p[2]; diff --git a/deps/AMICI/models/model_nested_events/model_nested_events_sigmay.cpp b/deps/AMICI/models/model_nested_events/sigmay.cpp similarity index 85% rename from deps/AMICI/models/model_nested_events/model_nested_events_sigmay.cpp rename to deps/AMICI/models/model_nested_events/sigmay.cpp index 4852d83e1..0f928ca69 100644 --- a/deps/AMICI/models/model_nested_events/model_nested_events_sigmay.cpp +++ b/deps/AMICI/models/model_nested_events/sigmay.cpp @@ -10,7 +10,7 @@ namespace amici { namespace model_model_nested_events{ -void sigmay_model_nested_events(double *sigmay, const realtype t, const realtype *p, const realtype *k) { +void sigmay_model_nested_events(double *sigmay, const realtype t, const realtype *p, const realtype *k, const realtype *y) { sigmay[0] = 1.0; } diff --git a/deps/AMICI/models/model_nested_events/model_nested_events_stau.cpp b/deps/AMICI/models/model_nested_events/stau.cpp similarity index 96% rename from deps/AMICI/models/model_nested_events/model_nested_events_stau.cpp rename to deps/AMICI/models/model_nested_events/stau.cpp index c97800dea..bfaad2f01 100644 --- a/deps/AMICI/models/model_nested_events/model_nested_events_stau.cpp +++ b/deps/AMICI/models/model_nested_events/stau.cpp @@ -10,7 +10,7 @@ namespace amici { namespace model_model_nested_events{ -void stau_model_nested_events(double *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) { +void stau_model_nested_events(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) { switch (ip) { case 0: { switch(ie) { diff --git a/deps/AMICI/models/model_nested_events/swig/CMakeLists.txt b/deps/AMICI/models/model_nested_events/swig/CMakeLists.txt index af0f64a76..eb9fe681c 100644 --- a/deps/AMICI/models/model_nested_events/swig/CMakeLists.txt +++ b/deps/AMICI/models/model_nested_events/swig/CMakeLists.txt @@ -1,28 +1,19 @@ -cmake_minimum_required(VERSION 3.8) # swig_add_library - -if(POLICY CMP0078) - cmake_policy(SET CMP0078 OLD) -endif(POLICY CMP0078) -if(POLICY CMP0074) - # Use package_ROOT environment variables - cmake_policy(SET CMP0074 NEW) -endif(POLICY CMP0074) -if(POLICY CMP0086) - cmake_policy(SET CMP0086 NEW) -endif(POLICY CMP0086) +cmake_minimum_required(VERSION 3.15) +if(DEFINED ENV{SWIG}) + set(SWIG_EXECUTABLE $ENV{SWIG}) +endif() find_package(SWIG REQUIRED) include(${SWIG_USE_FILE}) -if(${CMAKE_VERSION} VERSION_LESS "3.12.0") - find_package(PythonLibs REQUIRED) - include_directories(${PYTHON_INCLUDE_DIRS}) - set(Python3_LIBRARIES ${PYTHON_LIBRARIES}) -else() - find_package (Python3 COMPONENTS Development) - include_directories(${Python3_INCLUDE_DIRS}) +if(DEFINED ENV{PYTHON_EXECUTABLE}) + set(Python3_EXECUTABLE $ENV{PYTHON_EXECUTABLE}) endif() +# We don't need "Interpreter" here, but without that, FindPython3 will +# ignore the Python version selected via $Python3_EXECUTABLE +find_package(Python3 COMPONENTS Interpreter Development) +include_directories(${Python3_INCLUDE_DIRS}) set(SWIG_LIBRARY_NAME _${PROJECT_NAME}) set(CMAKE_SWIG_FLAGS "") @@ -30,17 +21,38 @@ set_source_files_properties(${PROJECT_NAME}.i PROPERTIES CPLUSPLUS ON) # swig does not use INTERFACE_INCLUDE_DIRS of linked libraries, so add manually get_target_property(AMICI_INCLUDE_DIRS Upstream::amici INTERFACE_INCLUDE_DIRECTORIES) -include_directories(${AMICI_INCLUDE_DIRS} ..) +include_directories(${AMICI_INCLUDE_DIRS} .. ${AMICI_INCLUDE_DIRS}/../swig) -swig_add_library(${PROJECT_NAME} +swig_add_library(${SWIG_LIBRARY_NAME} TYPE MODULE LANGUAGE python SOURCES ${PROJECT_NAME}.i) -swig_link_libraries(${PROJECT_NAME} + +set_target_properties(${SWIG_LIBRARY_NAME} + PROPERTIES + SWIG_USE_TARGET_INCLUDE_DIRECTORIES TRUE + PREFIX "" +) + +# Python extension suffix +execute_process( + COMMAND ${Python3_EXECUTABLE} -c + "import sysconfig; print(sysconfig.get_config_var('EXT_SUFFIX'))" + OUTPUT_VARIABLE PY_EXT_SUFFIX OUTPUT_STRIP_TRAILING_WHITESPACE) +if(NOT "${PY_EXT_SUFFIX}" STREQUAL "") + message(STATUS "Python extension suffix is ${PY_EXT_SUFFIX}") + set_target_properties(${SWIG_LIBRARY_NAME} PROPERTIES SUFFIX "${PY_EXT_SUFFIX}" ) +endif() + + +swig_link_libraries(${SWIG_LIBRARY_NAME} ${Python3_LIBRARIES} model) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.py + $ DESTINATION .) + # configure module setup script set(SETUP_PY_IN ${Amici_DIR}/model_setup.template.py) set(SETUP_PY_OUT ${CMAKE_CURRENT_BINARY_DIR}/setup.py) diff --git a/deps/AMICI/models/model_nested_events/swig/model_nested_events.i b/deps/AMICI/models/model_nested_events/swig/model_nested_events.i index 3c12b36d5..bec3ccac5 100644 --- a/deps/AMICI/models/model_nested_events/swig/model_nested_events.i +++ b/deps/AMICI/models/model_nested_events/swig/model_nested_events.i @@ -10,5 +10,13 @@ using namespace amici; %} +// Make model module accessible from the model +%feature("pythonappend") amici::generic_model::getModel %{ + if '.' in __name__: + import sys + val.module = sys.modules['.'.join(__name__.split('.')[:-1])] +%} + + // Process symbols in header %include "wrapfunctions.h" diff --git a/deps/AMICI/models/model_nested_events/model_nested_events_sx0.cpp b/deps/AMICI/models/model_nested_events/sx0.cpp similarity index 100% rename from deps/AMICI/models/model_nested_events/model_nested_events_sx0.cpp rename to deps/AMICI/models/model_nested_events/sx0.cpp diff --git a/deps/AMICI/models/model_nested_events/model_nested_events_x0.cpp b/deps/AMICI/models/model_nested_events/x0.cpp similarity index 100% rename from deps/AMICI/models/model_nested_events/model_nested_events_x0.cpp rename to deps/AMICI/models/model_nested_events/x0.cpp diff --git a/deps/AMICI/models/model_nested_events/model_nested_events_xdot.cpp b/deps/AMICI/models/model_nested_events/xdot.cpp similarity index 100% rename from deps/AMICI/models/model_nested_events/model_nested_events_xdot.cpp rename to deps/AMICI/models/model_nested_events/xdot.cpp diff --git a/deps/AMICI/models/model_nested_events/model_nested_events_y.cpp b/deps/AMICI/models/model_nested_events/y.cpp similarity index 100% rename from deps/AMICI/models/model_nested_events/model_nested_events_y.cpp rename to deps/AMICI/models/model_nested_events/y.cpp diff --git a/deps/AMICI/models/model_neuron/CMakeLists.txt b/deps/AMICI/models/model_neuron/CMakeLists.txt index 8af91c20a..323427169 100644 --- a/deps/AMICI/models/model_neuron/CMakeLists.txt +++ b/deps/AMICI/models/model_neuron/CMakeLists.txt @@ -1,119 +1,126 @@ -cmake_minimum_required(VERSION 3.3) - -if(POLICY CMP0060) - cmake_policy(SET CMP0060 NEW) -endif(POLICY CMP0060) -if(POLICY CMP0065) - cmake_policy(SET CMP0065 NEW) -endif(POLICY CMP0065) -if(POLICY CMP0074) - # Use package_ROOT environment variables - cmake_policy(SET CMP0074 NEW) -endif(POLICY CMP0074) +# Build AMICI model +cmake_minimum_required(VERSION 3.15) project(model_neuron) -set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_POSITION_INDEPENDENT_CODE ON) include(CheckCXXCompilerFlag) -set(MY_CXX_FLAGS -Wall -Wno-unused-function -Wno-unused-variable -Wno-unused-but-set-variable) -foreach(FLAG ${MY_CXX_FLAGS}) - unset(CUR_FLAG_SUPPORTED CACHE) - CHECK_CXX_COMPILER_FLAG(${FLAG} CUR_FLAG_SUPPORTED) - if(${CUR_FLAG_SUPPORTED}) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FLAG}") - endif() -endforeach(FLAG) - -find_package(Amici HINTS ${CMAKE_CURRENT_LIST_DIR}/../../build) +set(MY_CXX_FLAGS -Wall -Wno-unused-function -Wno-unused-variable) +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + list(APPEND MY_CXX_FLAGS -Wno-unused-but-set-variable) +endif() +foreach(flag ${MY_CXX_FLAGS}) + unset(CUR_FLAG_SUPPORTED CACHE) + check_cxx_compiler_flag(-Werror ${flag} CUR_FLAG_SUPPORTED) + if(${CUR_FLAG_SUPPORTED}) + string(APPEND CMAKE_CXX_FLAGS " ${flag}") + endif() +endforeach() + +if(DEFINED ENV{AMICI_CXXFLAGS}) + message(STATUS "Appending flags from AMICI_CXXFLAGS: $ENV{AMICI_CXXFLAGS}") + add_compile_options("$ENV{AMICI_CXXFLAGS}") +endif() +if(DEFINED ENV{AMICI_LDFLAGS}) + message(STATUS "Appending flags from AMICI_LDFLAGS: $ENV{AMICI_LDFLAGS}") + link_libraries("$ENV{AMICI_LDFLAGS}") +endif() + +find_package(Amici REQUIRED HINTS + ${CMAKE_CURRENT_LIST_DIR}/../../build) message(STATUS "Found AMICI ${Amici_DIR}") set(MODEL_DIR ${CMAKE_CURRENT_LIST_DIR}) -set(SRC_LIST_LIB ${MODEL_DIR}/model_neuron_JSparse.cpp -${MODEL_DIR}/model_neuron_Jrz.cpp -${MODEL_DIR}/model_neuron_Jy.cpp -${MODEL_DIR}/model_neuron_Jz.cpp -${MODEL_DIR}/model_neuron_dJrzdsigma.cpp -${MODEL_DIR}/model_neuron_dJrzdz.cpp -${MODEL_DIR}/model_neuron_dJydsigma.cpp -${MODEL_DIR}/model_neuron_dJydy.cpp -${MODEL_DIR}/model_neuron_dJzdsigma.cpp -${MODEL_DIR}/model_neuron_dJzdz.cpp -${MODEL_DIR}/model_neuron_deltaqB.cpp -${MODEL_DIR}/model_neuron_deltasx.cpp -${MODEL_DIR}/model_neuron_deltax.cpp -${MODEL_DIR}/model_neuron_deltaxB.cpp -${MODEL_DIR}/model_neuron_drzdx.cpp -${MODEL_DIR}/model_neuron_dxdotdp.cpp -${MODEL_DIR}/model_neuron_dydx.cpp -${MODEL_DIR}/model_neuron_dzdx.cpp -${MODEL_DIR}/model_neuron_root.cpp -${MODEL_DIR}/model_neuron_rz.cpp -${MODEL_DIR}/model_neuron_sigmay.cpp -${MODEL_DIR}/model_neuron_sigmaz.cpp -${MODEL_DIR}/model_neuron_srz.cpp -${MODEL_DIR}/model_neuron_stau.cpp -${MODEL_DIR}/model_neuron_sx0.cpp -${MODEL_DIR}/model_neuron_sz.cpp -${MODEL_DIR}/model_neuron_x0.cpp -${MODEL_DIR}/model_neuron_xdot.cpp -${MODEL_DIR}/model_neuron_y.cpp -${MODEL_DIR}/model_neuron_z.cpp - -${MODEL_DIR}/wrapfunctions.cpp -) +set(SRC_LIST_LIB ${MODEL_DIR}/JSparse.cpp +${MODEL_DIR}/Jrz.cpp +${MODEL_DIR}/Jy.cpp +${MODEL_DIR}/Jz.cpp +${MODEL_DIR}/dJrzdsigma.cpp +${MODEL_DIR}/dJrzdz.cpp +${MODEL_DIR}/dJydsigma.cpp +${MODEL_DIR}/dJydy.cpp +${MODEL_DIR}/dJzdsigma.cpp +${MODEL_DIR}/dJzdz.cpp +${MODEL_DIR}/deltaqB.cpp +${MODEL_DIR}/deltasx.cpp +${MODEL_DIR}/deltax.cpp +${MODEL_DIR}/deltaxB.cpp +${MODEL_DIR}/drzdx.cpp +${MODEL_DIR}/dxdotdp.cpp +${MODEL_DIR}/dydx.cpp +${MODEL_DIR}/dzdx.cpp +${MODEL_DIR}/root.cpp +${MODEL_DIR}/rz.cpp +${MODEL_DIR}/sigmay.cpp +${MODEL_DIR}/sigmaz.cpp +${MODEL_DIR}/srz.cpp +${MODEL_DIR}/stau.cpp +${MODEL_DIR}/sx0.cpp +${MODEL_DIR}/sz.cpp +${MODEL_DIR}/x0.cpp +${MODEL_DIR}/xdot.cpp +${MODEL_DIR}/y.cpp +${MODEL_DIR}/z.cpp + ${MODEL_DIR}/wrapfunctions.cpp) add_library(${PROJECT_NAME} ${SRC_LIST_LIB}) add_library(model ALIAS ${PROJECT_NAME}) -# This option can be helpful when using the Intel compiler and compilation of -# wrapfunctions.cpp fails due to insufficient memory. -option(ENABLE_WRAPFUNCTIONS_OPTIMIZATIONS "Enable compiler optimizations for wrapfunctions.cpp?" ON) -if(NOT ENABLE_WRAPFUNCTIONS_OPTIMIZATIONS) - set_source_files_properties(wrapfunctions.cpp PROPERTIES COMPILE_FLAGS -O0) -endif() +# Some special functions require boost +# +# TODO: set some flag during code generation whether the given model requires +# boost. for now, try to find it, add include directories and link against it. +# let the compiler/linker error if it is required but not found +find_package(Boost) target_include_directories(${PROJECT_NAME} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") -target_link_libraries(${PROJECT_NAME} - PUBLIC Upstream::amici -) +target_link_libraries( + ${PROJECT_NAME} + PUBLIC Upstream::amici + PRIVATE $<$:Boost::boost>) -set(SRC_LIST_EXE main.cpp) - -add_executable(simulate_${PROJECT_NAME} ${SRC_LIST_EXE}) +if(NOT "${AMICI_PYTHON_BUILD_EXT_ONLY}") + set(SRC_LIST_EXE main.cpp) + add_executable(simulate_${PROJECT_NAME} ${SRC_LIST_EXE}) + target_link_libraries(simulate_${PROJECT_NAME} ${PROJECT_NAME}) +endif() -target_link_libraries(simulate_${PROJECT_NAME} ${PROJECT_NAME}) +# 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}) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 --coverage") + string(APPEND CMAKE_CXX_FLAGS_DEBUG " --coverage") + string(APPEND CMAKE_EXE_LINKER_FLAGS_DEBUG " --coverage") endif() -## SWIG +# SWIG option(ENABLE_SWIG "Build swig/python library?" ON) if(ENABLE_SWIG) - if(NOT(${CMAKE_VERSION} VERSION_LESS 3.8)) - add_subdirectory(swig) - else() - message(WARNING "Unable to build SWIG interface, upgrade CMake to >=3.8.") - endif() + add_subdirectory(swig) endif() - # include(GNUInstallDirs) -install(TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME}Targets - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} - PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} -) -export(EXPORT ${PROJECT_NAME}Targets FILE ${PROJECT_NAME}Config.cmake - NAMESPACE Upstream:: - ) +install( + TARGETS ${PROJECT_NAME} + EXPORT ${PROJECT_NAME}Targets + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + INCLUDES + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +export( + EXPORT ${PROJECT_NAME}Targets + FILE ${PROJECT_NAME}Config.cmake + NAMESPACE Upstream::) # - diff --git a/deps/AMICI/models/model_neuron/model_neuron_JSparse.cpp b/deps/AMICI/models/model_neuron/JSparse.cpp similarity index 100% rename from deps/AMICI/models/model_neuron/model_neuron_JSparse.cpp rename to deps/AMICI/models/model_neuron/JSparse.cpp diff --git a/deps/AMICI/models/model_neuron/model_neuron_Jrz.cpp b/deps/AMICI/models/model_neuron/Jrz.cpp similarity index 100% rename from deps/AMICI/models/model_neuron/model_neuron_Jrz.cpp rename to deps/AMICI/models/model_neuron/Jrz.cpp diff --git a/deps/AMICI/models/model_neuron/model_neuron_Jy.cpp b/deps/AMICI/models/model_neuron/Jy.cpp similarity index 100% rename from deps/AMICI/models/model_neuron/model_neuron_Jy.cpp rename to deps/AMICI/models/model_neuron/Jy.cpp diff --git a/deps/AMICI/models/model_neuron/model_neuron_Jz.cpp b/deps/AMICI/models/model_neuron/Jz.cpp similarity index 100% rename from deps/AMICI/models/model_neuron/model_neuron_Jz.cpp rename to deps/AMICI/models/model_neuron/Jz.cpp diff --git a/deps/AMICI/models/model_neuron/model_neuron_dJrzdsigma.cpp b/deps/AMICI/models/model_neuron/dJrzdsigma.cpp similarity index 100% rename from deps/AMICI/models/model_neuron/model_neuron_dJrzdsigma.cpp rename to deps/AMICI/models/model_neuron/dJrzdsigma.cpp diff --git a/deps/AMICI/models/model_neuron/model_neuron_dJrzdz.cpp b/deps/AMICI/models/model_neuron/dJrzdz.cpp similarity index 100% rename from deps/AMICI/models/model_neuron/model_neuron_dJrzdz.cpp rename to deps/AMICI/models/model_neuron/dJrzdz.cpp diff --git a/deps/AMICI/models/model_neuron/model_neuron_dJydsigma.cpp b/deps/AMICI/models/model_neuron/dJydsigma.cpp similarity index 100% rename from deps/AMICI/models/model_neuron/model_neuron_dJydsigma.cpp rename to deps/AMICI/models/model_neuron/dJydsigma.cpp diff --git a/deps/AMICI/models/model_neuron/model_neuron_dJydy.cpp b/deps/AMICI/models/model_neuron/dJydy.cpp similarity index 100% rename from deps/AMICI/models/model_neuron/model_neuron_dJydy.cpp rename to deps/AMICI/models/model_neuron/dJydy.cpp diff --git a/deps/AMICI/models/model_neuron/model_neuron_dJzdsigma.cpp b/deps/AMICI/models/model_neuron/dJzdsigma.cpp similarity index 100% rename from deps/AMICI/models/model_neuron/model_neuron_dJzdsigma.cpp rename to deps/AMICI/models/model_neuron/dJzdsigma.cpp diff --git a/deps/AMICI/models/model_neuron/model_neuron_dJzdz.cpp b/deps/AMICI/models/model_neuron/dJzdz.cpp similarity index 100% rename from deps/AMICI/models/model_neuron/model_neuron_dJzdz.cpp rename to deps/AMICI/models/model_neuron/dJzdz.cpp diff --git a/deps/AMICI/models/model_neuron/model_neuron_deltaqB.cpp b/deps/AMICI/models/model_neuron/deltaqB.cpp similarity index 100% rename from deps/AMICI/models/model_neuron/model_neuron_deltaqB.cpp rename to deps/AMICI/models/model_neuron/deltaqB.cpp diff --git a/deps/AMICI/models/model_neuron/model_neuron_deltasx.cpp b/deps/AMICI/models/model_neuron/deltasx.cpp similarity index 97% rename from deps/AMICI/models/model_neuron/model_neuron_deltasx.cpp rename to deps/AMICI/models/model_neuron/deltasx.cpp index 35dc79a78..29bd50384 100644 --- a/deps/AMICI/models/model_neuron/model_neuron_deltasx.cpp +++ b/deps/AMICI/models/model_neuron/deltasx.cpp @@ -10,7 +10,7 @@ namespace amici { namespace model_model_neuron{ -void deltasx_model_neuron(double *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) { +void deltasx_model_neuron(double *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) { switch (ip) { case 0: { switch(ie) { diff --git a/deps/AMICI/models/model_neuron/model_neuron_deltax.cpp b/deps/AMICI/models/model_neuron/deltax.cpp similarity index 100% rename from deps/AMICI/models/model_neuron/model_neuron_deltax.cpp rename to deps/AMICI/models/model_neuron/deltax.cpp diff --git a/deps/AMICI/models/model_neuron/model_neuron_deltaxB.cpp b/deps/AMICI/models/model_neuron/deltaxB.cpp similarity index 100% rename from deps/AMICI/models/model_neuron/model_neuron_deltaxB.cpp rename to deps/AMICI/models/model_neuron/deltaxB.cpp diff --git a/deps/AMICI/models/model_neuron/model_neuron_drzdx.cpp b/deps/AMICI/models/model_neuron/drzdx.cpp similarity index 100% rename from deps/AMICI/models/model_neuron/model_neuron_drzdx.cpp rename to deps/AMICI/models/model_neuron/drzdx.cpp diff --git a/deps/AMICI/models/model_neuron/model_neuron_dxdotdp.cpp b/deps/AMICI/models/model_neuron/dxdotdp.cpp similarity index 100% rename from deps/AMICI/models/model_neuron/model_neuron_dxdotdp.cpp rename to deps/AMICI/models/model_neuron/dxdotdp.cpp diff --git a/deps/AMICI/models/model_neuron/model_neuron_dydx.cpp b/deps/AMICI/models/model_neuron/dydx.cpp similarity index 100% rename from deps/AMICI/models/model_neuron/model_neuron_dydx.cpp rename to deps/AMICI/models/model_neuron/dydx.cpp diff --git a/deps/AMICI/models/model_neuron/model_neuron_dzdx.cpp b/deps/AMICI/models/model_neuron/dzdx.cpp similarity index 100% rename from deps/AMICI/models/model_neuron/model_neuron_dzdx.cpp rename to deps/AMICI/models/model_neuron/dzdx.cpp diff --git a/deps/AMICI/models/model_neuron/model_neuron.h b/deps/AMICI/models/model_neuron/model_neuron.h index 3aaeb06a4..e8f6f5c21 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) 2bd39716102b9842354964196889d40d2eb2731c */ +/* Generated by amiwrap (R2017b) 223c5075608273b17f304556d0e8ccb41233bd21 */ #include #include #include "amici/defines.h" @@ -25,19 +25,19 @@ extern void dJydy_model_neuron(double *dJydy, const int iy, const realtype *p, c extern void dJzdsigma_model_neuron(double *dJzdsigma, const int iz, const realtype *p, const realtype *k, const double *z, const double *sigmaz, const double *mz); extern void dJzdz_model_neuron(double *dJzdz, const int iz, const realtype *p, const realtype *k, const double *z, const double *sigmaz, const double *mz); extern void deltaqB_model_neuron(double *deltaqB, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const int ip, const int ie, const realtype *xdot, const realtype *xdot_old, const realtype *xB); -extern void deltasx_model_neuron(double *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); +extern void deltasx_model_neuron(double *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); extern void deltax_model_neuron(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(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(double *drzdx, const int ie, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h); extern void dxdotdp_model_neuron(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(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(double *dzdx, const int ie, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h); -extern void root_model_neuron(realtype *root, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h); +extern void root_model_neuron(realtype *root, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl); extern void rz_model_neuron(double *rz, const int ie, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h); -extern void sigmay_model_neuron(double *sigmay, const realtype t, const realtype *p, const realtype *k); +extern void sigmay_model_neuron(double *sigmay, const realtype t, const realtype *p, const realtype *k, const realtype *y); extern void sigmaz_model_neuron(double *sigmaz, const realtype t, const realtype *p, const realtype *k); extern void srz_model_neuron(double *srz, 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 stau_model_neuron(double *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); +extern void stau_model_neuron(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(realtype *sx0, const realtype t,const realtype *x0, const realtype *p, const realtype *k, const int ip); extern void sz_model_neuron(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 x0_model_neuron(realtype *x0, const realtype t, const realtype *p, const realtype *k); @@ -62,6 +62,7 @@ class Model_model_neuron : public amici::Model_ODE { 1, 1, 1, + 0, 1, 0, 0, @@ -69,6 +70,9 @@ class Model_model_neuron : public amici::Model_ODE { 0, 0, {}, + 0, + 0, + 0, 4, 1, 1 @@ -84,7 +88,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 "2bd39716102b9842354964196889d40d2eb2731c"; }; + std::string getAmiciCommit() const override { return "223c5075608273b17f304556d0e8ccb41233bd21"; }; 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); @@ -130,8 +134,8 @@ class Model_model_neuron : public amici::Model_ODE { deltaqB_model_neuron(deltaqB, t, x, p, k, h, ip, ie, xdot, xdot_old, xB); } - void fdeltasx(double *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) override { - deltasx_model_neuron(deltasx, t, x, p, k, h, w, ip, ie, xdot, xdot_old, sx, stau); + void fdeltasx(double *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) override { + deltasx_model_neuron(deltasx, t, x, p, k, h, w, ip, ie, xdot, xdot_old, sx, stau, tcl); } void fdeltax(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) override { @@ -149,16 +153,16 @@ class Model_model_neuron : public amici::Model_ODE { drzdx_model_neuron(drzdx, ie, t, x, p, k, h); } - void fdsigmaydp(double *dsigmaydp, const realtype t, const realtype *p, const realtype *k, const int ip) override { + void fdsigmaydp(double *dsigmaydp, const realtype t, const realtype *p, const realtype *k, const realtype *y, const int ip) override { } 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) 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 fdwdx(realtype *dwdx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl) 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 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 { @@ -179,16 +183,16 @@ class Model_model_neuron : public amici::Model_ODE { dzdx_model_neuron(dzdx, ie, t, x, p, k, h); } - void froot(realtype *root, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h) override { - root_model_neuron(root, t, x, p, k, h); + void froot(realtype *root, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl) override { + root_model_neuron(root, t, x, p, k, h, tcl); } void frz(double *rz, const int ie, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h) override { rz_model_neuron(rz, ie, t, x, p, k, h); } - void fsigmay(double *sigmay, const realtype t, const realtype *p, const realtype *k) override { - sigmay_model_neuron(sigmay, t, p, k); + void fsigmay(double *sigmay, const realtype t, const realtype *p, const realtype *k, const realtype *y) override { + sigmay_model_neuron(sigmay, t, p, k, y); } void fsigmaz(double *sigmaz, const realtype t, const realtype *p, const realtype *k) override { @@ -199,8 +203,8 @@ class Model_model_neuron : public amici::Model_ODE { srz_model_neuron(srz, ie, t, x, p, k, h, sx, ip); } - void fstau(double *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) override { - stau_model_neuron(stau, t, x, p, k, h, sx, ip, ie); + void fstau(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) override { + stau_model_neuron(stau, t, x, p, k, h, tcl, sx, ip, ie); } void fsx0(realtype *sx0, const realtype t,const realtype *x0, const realtype *p, const realtype *k, const int ip) override { @@ -211,7 +215,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) 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 fx0(realtype *x0, const realtype t, const realtype *p, const realtype *k) override { diff --git a/deps/AMICI/models/model_neuron/model_neuron_root.cpp b/deps/AMICI/models/model_neuron/root.cpp similarity index 93% rename from deps/AMICI/models/model_neuron/model_neuron_root.cpp rename to deps/AMICI/models/model_neuron/root.cpp index 516bc9130..6a68f6771 100644 --- a/deps/AMICI/models/model_neuron/model_neuron_root.cpp +++ b/deps/AMICI/models/model_neuron/root.cpp @@ -10,7 +10,7 @@ namespace amici { namespace model_model_neuron{ -void root_model_neuron(realtype *root, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h) { +void root_model_neuron(realtype *root, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl) { root[0] = x[0]-3.0E1; } diff --git a/deps/AMICI/models/model_neuron/model_neuron_rz.cpp b/deps/AMICI/models/model_neuron/rz.cpp similarity index 100% rename from deps/AMICI/models/model_neuron/model_neuron_rz.cpp rename to deps/AMICI/models/model_neuron/rz.cpp diff --git a/deps/AMICI/models/model_neuron/model_neuron_sigmay.cpp b/deps/AMICI/models/model_neuron/sigmay.cpp similarity index 86% rename from deps/AMICI/models/model_neuron/model_neuron_sigmay.cpp rename to deps/AMICI/models/model_neuron/sigmay.cpp index 092c62615..8bc0d292f 100644 --- a/deps/AMICI/models/model_neuron/model_neuron_sigmay.cpp +++ b/deps/AMICI/models/model_neuron/sigmay.cpp @@ -10,7 +10,7 @@ namespace amici { namespace model_model_neuron{ -void sigmay_model_neuron(double *sigmay, const realtype t, const realtype *p, const realtype *k) { +void sigmay_model_neuron(double *sigmay, const realtype t, const realtype *p, const realtype *k, const realtype *y) { sigmay[0] = 1.0; } diff --git a/deps/AMICI/models/model_neuron/model_neuron_sigmaz.cpp b/deps/AMICI/models/model_neuron/sigmaz.cpp similarity index 100% rename from deps/AMICI/models/model_neuron/model_neuron_sigmaz.cpp rename to deps/AMICI/models/model_neuron/sigmaz.cpp diff --git a/deps/AMICI/models/model_neuron/model_neuron_srz.cpp b/deps/AMICI/models/model_neuron/srz.cpp similarity index 100% rename from deps/AMICI/models/model_neuron/model_neuron_srz.cpp rename to deps/AMICI/models/model_neuron/srz.cpp diff --git a/deps/AMICI/models/model_neuron/model_neuron_stau.cpp b/deps/AMICI/models/model_neuron/stau.cpp similarity index 93% rename from deps/AMICI/models/model_neuron/model_neuron_stau.cpp rename to deps/AMICI/models/model_neuron/stau.cpp index 66b351857..d77cfb5bd 100644 --- a/deps/AMICI/models/model_neuron/model_neuron_stau.cpp +++ b/deps/AMICI/models/model_neuron/stau.cpp @@ -10,7 +10,7 @@ namespace amici { namespace model_model_neuron{ -void stau_model_neuron(double *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) { +void stau_model_neuron(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) { switch (ip) { case 0: { switch(ie) { diff --git a/deps/AMICI/models/model_neuron/swig/CMakeLists.txt b/deps/AMICI/models/model_neuron/swig/CMakeLists.txt index af0f64a76..eb9fe681c 100644 --- a/deps/AMICI/models/model_neuron/swig/CMakeLists.txt +++ b/deps/AMICI/models/model_neuron/swig/CMakeLists.txt @@ -1,28 +1,19 @@ -cmake_minimum_required(VERSION 3.8) # swig_add_library - -if(POLICY CMP0078) - cmake_policy(SET CMP0078 OLD) -endif(POLICY CMP0078) -if(POLICY CMP0074) - # Use package_ROOT environment variables - cmake_policy(SET CMP0074 NEW) -endif(POLICY CMP0074) -if(POLICY CMP0086) - cmake_policy(SET CMP0086 NEW) -endif(POLICY CMP0086) +cmake_minimum_required(VERSION 3.15) +if(DEFINED ENV{SWIG}) + set(SWIG_EXECUTABLE $ENV{SWIG}) +endif() find_package(SWIG REQUIRED) include(${SWIG_USE_FILE}) -if(${CMAKE_VERSION} VERSION_LESS "3.12.0") - find_package(PythonLibs REQUIRED) - include_directories(${PYTHON_INCLUDE_DIRS}) - set(Python3_LIBRARIES ${PYTHON_LIBRARIES}) -else() - find_package (Python3 COMPONENTS Development) - include_directories(${Python3_INCLUDE_DIRS}) +if(DEFINED ENV{PYTHON_EXECUTABLE}) + set(Python3_EXECUTABLE $ENV{PYTHON_EXECUTABLE}) endif() +# We don't need "Interpreter" here, but without that, FindPython3 will +# ignore the Python version selected via $Python3_EXECUTABLE +find_package(Python3 COMPONENTS Interpreter Development) +include_directories(${Python3_INCLUDE_DIRS}) set(SWIG_LIBRARY_NAME _${PROJECT_NAME}) set(CMAKE_SWIG_FLAGS "") @@ -30,17 +21,38 @@ set_source_files_properties(${PROJECT_NAME}.i PROPERTIES CPLUSPLUS ON) # swig does not use INTERFACE_INCLUDE_DIRS of linked libraries, so add manually get_target_property(AMICI_INCLUDE_DIRS Upstream::amici INTERFACE_INCLUDE_DIRECTORIES) -include_directories(${AMICI_INCLUDE_DIRS} ..) +include_directories(${AMICI_INCLUDE_DIRS} .. ${AMICI_INCLUDE_DIRS}/../swig) -swig_add_library(${PROJECT_NAME} +swig_add_library(${SWIG_LIBRARY_NAME} TYPE MODULE LANGUAGE python SOURCES ${PROJECT_NAME}.i) -swig_link_libraries(${PROJECT_NAME} + +set_target_properties(${SWIG_LIBRARY_NAME} + PROPERTIES + SWIG_USE_TARGET_INCLUDE_DIRECTORIES TRUE + PREFIX "" +) + +# Python extension suffix +execute_process( + COMMAND ${Python3_EXECUTABLE} -c + "import sysconfig; print(sysconfig.get_config_var('EXT_SUFFIX'))" + OUTPUT_VARIABLE PY_EXT_SUFFIX OUTPUT_STRIP_TRAILING_WHITESPACE) +if(NOT "${PY_EXT_SUFFIX}" STREQUAL "") + message(STATUS "Python extension suffix is ${PY_EXT_SUFFIX}") + set_target_properties(${SWIG_LIBRARY_NAME} PROPERTIES SUFFIX "${PY_EXT_SUFFIX}" ) +endif() + + +swig_link_libraries(${SWIG_LIBRARY_NAME} ${Python3_LIBRARIES} model) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.py + $ DESTINATION .) + # configure module setup script set(SETUP_PY_IN ${Amici_DIR}/model_setup.template.py) set(SETUP_PY_OUT ${CMAKE_CURRENT_BINARY_DIR}/setup.py) diff --git a/deps/AMICI/models/model_neuron/swig/model_neuron.i b/deps/AMICI/models/model_neuron/swig/model_neuron.i index 4511c79a4..856ba0497 100644 --- a/deps/AMICI/models/model_neuron/swig/model_neuron.i +++ b/deps/AMICI/models/model_neuron/swig/model_neuron.i @@ -10,5 +10,13 @@ using namespace amici; %} +// Make model module accessible from the model +%feature("pythonappend") amici::generic_model::getModel %{ + if '.' in __name__: + import sys + val.module = sys.modules['.'.join(__name__.split('.')[:-1])] +%} + + // Process symbols in header %include "wrapfunctions.h" diff --git a/deps/AMICI/models/model_neuron/model_neuron_sx0.cpp b/deps/AMICI/models/model_neuron/sx0.cpp similarity index 100% rename from deps/AMICI/models/model_neuron/model_neuron_sx0.cpp rename to deps/AMICI/models/model_neuron/sx0.cpp diff --git a/deps/AMICI/models/model_neuron/model_neuron_sz.cpp b/deps/AMICI/models/model_neuron/sz.cpp similarity index 100% rename from deps/AMICI/models/model_neuron/model_neuron_sz.cpp rename to deps/AMICI/models/model_neuron/sz.cpp diff --git a/deps/AMICI/models/model_neuron/model_neuron_x0.cpp b/deps/AMICI/models/model_neuron/x0.cpp similarity index 100% rename from deps/AMICI/models/model_neuron/model_neuron_x0.cpp rename to deps/AMICI/models/model_neuron/x0.cpp diff --git a/deps/AMICI/models/model_neuron/model_neuron_xdot.cpp b/deps/AMICI/models/model_neuron/xdot.cpp similarity index 100% rename from deps/AMICI/models/model_neuron/model_neuron_xdot.cpp rename to deps/AMICI/models/model_neuron/xdot.cpp diff --git a/deps/AMICI/models/model_neuron/model_neuron_y.cpp b/deps/AMICI/models/model_neuron/y.cpp similarity index 100% rename from deps/AMICI/models/model_neuron/model_neuron_y.cpp rename to deps/AMICI/models/model_neuron/y.cpp diff --git a/deps/AMICI/models/model_neuron/model_neuron_z.cpp b/deps/AMICI/models/model_neuron/z.cpp similarity index 100% rename from deps/AMICI/models/model_neuron/model_neuron_z.cpp rename to deps/AMICI/models/model_neuron/z.cpp diff --git a/deps/AMICI/models/model_neuron_o2/CMakeLists.txt b/deps/AMICI/models/model_neuron_o2/CMakeLists.txt index 07eea135a..42f5da90a 100644 --- a/deps/AMICI/models/model_neuron_o2/CMakeLists.txt +++ b/deps/AMICI/models/model_neuron_o2/CMakeLists.txt @@ -1,121 +1,128 @@ -cmake_minimum_required(VERSION 3.3) - -if(POLICY CMP0060) - cmake_policy(SET CMP0060 NEW) -endif(POLICY CMP0060) -if(POLICY CMP0065) - cmake_policy(SET CMP0065 NEW) -endif(POLICY CMP0065) -if(POLICY CMP0074) - # Use package_ROOT environment variables - cmake_policy(SET CMP0074 NEW) -endif(POLICY CMP0074) +# Build AMICI model +cmake_minimum_required(VERSION 3.15) project(model_neuron_o2) -set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_POSITION_INDEPENDENT_CODE ON) include(CheckCXXCompilerFlag) -set(MY_CXX_FLAGS -Wall -Wno-unused-function -Wno-unused-variable -Wno-unused-but-set-variable) -foreach(FLAG ${MY_CXX_FLAGS}) - unset(CUR_FLAG_SUPPORTED CACHE) - CHECK_CXX_COMPILER_FLAG(${FLAG} CUR_FLAG_SUPPORTED) - if(${CUR_FLAG_SUPPORTED}) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FLAG}") - endif() -endforeach(FLAG) - -find_package(Amici HINTS ${CMAKE_CURRENT_LIST_DIR}/../../build) +set(MY_CXX_FLAGS -Wall -Wno-unused-function -Wno-unused-variable) +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + list(APPEND MY_CXX_FLAGS -Wno-unused-but-set-variable) +endif() +foreach(flag ${MY_CXX_FLAGS}) + unset(CUR_FLAG_SUPPORTED CACHE) + check_cxx_compiler_flag(-Werror ${flag} CUR_FLAG_SUPPORTED) + if(${CUR_FLAG_SUPPORTED}) + string(APPEND CMAKE_CXX_FLAGS " ${flag}") + endif() +endforeach() + +if(DEFINED ENV{AMICI_CXXFLAGS}) + message(STATUS "Appending flags from AMICI_CXXFLAGS: $ENV{AMICI_CXXFLAGS}") + add_compile_options("$ENV{AMICI_CXXFLAGS}") +endif() +if(DEFINED ENV{AMICI_LDFLAGS}) + message(STATUS "Appending flags from AMICI_LDFLAGS: $ENV{AMICI_LDFLAGS}") + link_libraries("$ENV{AMICI_LDFLAGS}") +endif() + +find_package(Amici REQUIRED HINTS + ${CMAKE_CURRENT_LIST_DIR}/../../build) message(STATUS "Found AMICI ${Amici_DIR}") set(MODEL_DIR ${CMAKE_CURRENT_LIST_DIR}) -set(SRC_LIST_LIB ${MODEL_DIR}/model_neuron_o2_JSparse.cpp -${MODEL_DIR}/model_neuron_o2_Jrz.cpp -${MODEL_DIR}/model_neuron_o2_Jy.cpp -${MODEL_DIR}/model_neuron_o2_Jz.cpp -${MODEL_DIR}/model_neuron_o2_dJrzdsigma.cpp -${MODEL_DIR}/model_neuron_o2_dJrzdz.cpp -${MODEL_DIR}/model_neuron_o2_dJydsigma.cpp -${MODEL_DIR}/model_neuron_o2_dJydy.cpp -${MODEL_DIR}/model_neuron_o2_dJzdsigma.cpp -${MODEL_DIR}/model_neuron_o2_dJzdz.cpp -${MODEL_DIR}/model_neuron_o2_deltaqB.cpp -${MODEL_DIR}/model_neuron_o2_deltasx.cpp -${MODEL_DIR}/model_neuron_o2_deltax.cpp -${MODEL_DIR}/model_neuron_o2_deltaxB.cpp -${MODEL_DIR}/model_neuron_o2_drzdx.cpp -${MODEL_DIR}/model_neuron_o2_dwdx.cpp -${MODEL_DIR}/model_neuron_o2_dxdotdp.cpp -${MODEL_DIR}/model_neuron_o2_dydx.cpp -${MODEL_DIR}/model_neuron_o2_dzdx.cpp -${MODEL_DIR}/model_neuron_o2_root.cpp -${MODEL_DIR}/model_neuron_o2_rz.cpp -${MODEL_DIR}/model_neuron_o2_sigmay.cpp -${MODEL_DIR}/model_neuron_o2_sigmaz.cpp -${MODEL_DIR}/model_neuron_o2_srz.cpp -${MODEL_DIR}/model_neuron_o2_stau.cpp -${MODEL_DIR}/model_neuron_o2_sx0.cpp -${MODEL_DIR}/model_neuron_o2_sz.cpp -${MODEL_DIR}/model_neuron_o2_w.cpp -${MODEL_DIR}/model_neuron_o2_x0.cpp -${MODEL_DIR}/model_neuron_o2_xdot.cpp -${MODEL_DIR}/model_neuron_o2_y.cpp -${MODEL_DIR}/model_neuron_o2_z.cpp - -${MODEL_DIR}/wrapfunctions.cpp -) +set(SRC_LIST_LIB ${MODEL_DIR}/JSparse.cpp +${MODEL_DIR}/Jrz.cpp +${MODEL_DIR}/Jy.cpp +${MODEL_DIR}/Jz.cpp +${MODEL_DIR}/dJrzdsigma.cpp +${MODEL_DIR}/dJrzdz.cpp +${MODEL_DIR}/dJydsigma.cpp +${MODEL_DIR}/dJydy.cpp +${MODEL_DIR}/dJzdsigma.cpp +${MODEL_DIR}/dJzdz.cpp +${MODEL_DIR}/deltaqB.cpp +${MODEL_DIR}/deltasx.cpp +${MODEL_DIR}/deltax.cpp +${MODEL_DIR}/deltaxB.cpp +${MODEL_DIR}/drzdx.cpp +${MODEL_DIR}/dwdx.cpp +${MODEL_DIR}/dxdotdp.cpp +${MODEL_DIR}/dydx.cpp +${MODEL_DIR}/dzdx.cpp +${MODEL_DIR}/root.cpp +${MODEL_DIR}/rz.cpp +${MODEL_DIR}/sigmay.cpp +${MODEL_DIR}/sigmaz.cpp +${MODEL_DIR}/srz.cpp +${MODEL_DIR}/stau.cpp +${MODEL_DIR}/sx0.cpp +${MODEL_DIR}/sz.cpp +${MODEL_DIR}/w.cpp +${MODEL_DIR}/x0.cpp +${MODEL_DIR}/xdot.cpp +${MODEL_DIR}/y.cpp +${MODEL_DIR}/z.cpp + ${MODEL_DIR}/wrapfunctions.cpp) add_library(${PROJECT_NAME} ${SRC_LIST_LIB}) add_library(model ALIAS ${PROJECT_NAME}) -# This option can be helpful when using the Intel compiler and compilation of -# wrapfunctions.cpp fails due to insufficient memory. -option(ENABLE_WRAPFUNCTIONS_OPTIMIZATIONS "Enable compiler optimizations for wrapfunctions.cpp?" ON) -if(NOT ENABLE_WRAPFUNCTIONS_OPTIMIZATIONS) - set_source_files_properties(wrapfunctions.cpp PROPERTIES COMPILE_FLAGS -O0) -endif() +# Some special functions require boost +# +# TODO: set some flag during code generation whether the given model requires +# boost. for now, try to find it, add include directories and link against it. +# let the compiler/linker error if it is required but not found +find_package(Boost) target_include_directories(${PROJECT_NAME} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") -target_link_libraries(${PROJECT_NAME} - PUBLIC Upstream::amici -) +target_link_libraries( + ${PROJECT_NAME} + PUBLIC Upstream::amici + PRIVATE $<$:Boost::boost>) -set(SRC_LIST_EXE main.cpp) - -add_executable(simulate_${PROJECT_NAME} ${SRC_LIST_EXE}) +if(NOT "${AMICI_PYTHON_BUILD_EXT_ONLY}") + set(SRC_LIST_EXE main.cpp) + add_executable(simulate_${PROJECT_NAME} ${SRC_LIST_EXE}) + target_link_libraries(simulate_${PROJECT_NAME} ${PROJECT_NAME}) +endif() -target_link_libraries(simulate_${PROJECT_NAME} ${PROJECT_NAME}) +# 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}) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 --coverage") + string(APPEND CMAKE_CXX_FLAGS_DEBUG " --coverage") + string(APPEND CMAKE_EXE_LINKER_FLAGS_DEBUG " --coverage") endif() -## SWIG +# SWIG option(ENABLE_SWIG "Build swig/python library?" ON) if(ENABLE_SWIG) - if(NOT(${CMAKE_VERSION} VERSION_LESS 3.8)) - add_subdirectory(swig) - else() - message(WARNING "Unable to build SWIG interface, upgrade CMake to >=3.8.") - endif() + add_subdirectory(swig) endif() - # include(GNUInstallDirs) -install(TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME}Targets - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} - PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} -) -export(EXPORT ${PROJECT_NAME}Targets FILE ${PROJECT_NAME}Config.cmake - NAMESPACE Upstream:: - ) +install( + TARGETS ${PROJECT_NAME} + EXPORT ${PROJECT_NAME}Targets + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + INCLUDES + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +export( + EXPORT ${PROJECT_NAME}Targets + FILE ${PROJECT_NAME}Config.cmake + NAMESPACE Upstream::) # - diff --git a/deps/AMICI/models/model_neuron_o2/model_neuron_o2_JSparse.cpp b/deps/AMICI/models/model_neuron_o2/JSparse.cpp similarity index 100% rename from deps/AMICI/models/model_neuron_o2/model_neuron_o2_JSparse.cpp rename to deps/AMICI/models/model_neuron_o2/JSparse.cpp diff --git a/deps/AMICI/models/model_neuron_o2/model_neuron_o2_Jrz.cpp b/deps/AMICI/models/model_neuron_o2/Jrz.cpp similarity index 100% rename from deps/AMICI/models/model_neuron_o2/model_neuron_o2_Jrz.cpp rename to deps/AMICI/models/model_neuron_o2/Jrz.cpp diff --git a/deps/AMICI/models/model_neuron_o2/model_neuron_o2_Jy.cpp b/deps/AMICI/models/model_neuron_o2/Jy.cpp similarity index 100% rename from deps/AMICI/models/model_neuron_o2/model_neuron_o2_Jy.cpp rename to deps/AMICI/models/model_neuron_o2/Jy.cpp diff --git a/deps/AMICI/models/model_neuron_o2/model_neuron_o2_Jz.cpp b/deps/AMICI/models/model_neuron_o2/Jz.cpp similarity index 100% rename from deps/AMICI/models/model_neuron_o2/model_neuron_o2_Jz.cpp rename to deps/AMICI/models/model_neuron_o2/Jz.cpp diff --git a/deps/AMICI/models/model_neuron_o2/model_neuron_o2_dJrzdsigma.cpp b/deps/AMICI/models/model_neuron_o2/dJrzdsigma.cpp similarity index 100% rename from deps/AMICI/models/model_neuron_o2/model_neuron_o2_dJrzdsigma.cpp rename to deps/AMICI/models/model_neuron_o2/dJrzdsigma.cpp diff --git a/deps/AMICI/models/model_neuron_o2/model_neuron_o2_dJrzdz.cpp b/deps/AMICI/models/model_neuron_o2/dJrzdz.cpp similarity index 100% rename from deps/AMICI/models/model_neuron_o2/model_neuron_o2_dJrzdz.cpp rename to deps/AMICI/models/model_neuron_o2/dJrzdz.cpp diff --git a/deps/AMICI/models/model_neuron_o2/model_neuron_o2_dJydsigma.cpp b/deps/AMICI/models/model_neuron_o2/dJydsigma.cpp similarity index 100% rename from deps/AMICI/models/model_neuron_o2/model_neuron_o2_dJydsigma.cpp rename to deps/AMICI/models/model_neuron_o2/dJydsigma.cpp diff --git a/deps/AMICI/models/model_neuron_o2/model_neuron_o2_dJydy.cpp b/deps/AMICI/models/model_neuron_o2/dJydy.cpp similarity index 100% rename from deps/AMICI/models/model_neuron_o2/model_neuron_o2_dJydy.cpp rename to deps/AMICI/models/model_neuron_o2/dJydy.cpp diff --git a/deps/AMICI/models/model_neuron_o2/model_neuron_o2_dJzdsigma.cpp b/deps/AMICI/models/model_neuron_o2/dJzdsigma.cpp similarity index 100% rename from deps/AMICI/models/model_neuron_o2/model_neuron_o2_dJzdsigma.cpp rename to deps/AMICI/models/model_neuron_o2/dJzdsigma.cpp diff --git a/deps/AMICI/models/model_neuron_o2/model_neuron_o2_dJzdz.cpp b/deps/AMICI/models/model_neuron_o2/dJzdz.cpp similarity index 100% rename from deps/AMICI/models/model_neuron_o2/model_neuron_o2_dJzdz.cpp rename to deps/AMICI/models/model_neuron_o2/dJzdz.cpp diff --git a/deps/AMICI/models/model_neuron_o2/model_neuron_o2_deltaqB.cpp b/deps/AMICI/models/model_neuron_o2/deltaqB.cpp similarity index 100% rename from deps/AMICI/models/model_neuron_o2/model_neuron_o2_deltaqB.cpp rename to deps/AMICI/models/model_neuron_o2/deltaqB.cpp diff --git a/deps/AMICI/models/model_neuron_o2/model_neuron_o2_deltasx.cpp b/deps/AMICI/models/model_neuron_o2/deltasx.cpp similarity index 99% rename from deps/AMICI/models/model_neuron_o2/model_neuron_o2_deltasx.cpp rename to deps/AMICI/models/model_neuron_o2/deltasx.cpp index 621cb92ed..10c0d6a05 100644 --- a/deps/AMICI/models/model_neuron_o2/model_neuron_o2_deltasx.cpp +++ b/deps/AMICI/models/model_neuron_o2/deltasx.cpp @@ -10,7 +10,7 @@ namespace amici { namespace model_model_neuron_o2{ -void deltasx_model_neuron_o2(double *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) { +void deltasx_model_neuron_o2(double *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) { switch (ip) { case 0: { switch(ie) { diff --git a/deps/AMICI/models/model_neuron_o2/model_neuron_o2_deltax.cpp b/deps/AMICI/models/model_neuron_o2/deltax.cpp similarity index 100% rename from deps/AMICI/models/model_neuron_o2/model_neuron_o2_deltax.cpp rename to deps/AMICI/models/model_neuron_o2/deltax.cpp diff --git a/deps/AMICI/models/model_neuron_o2/model_neuron_o2_deltaxB.cpp b/deps/AMICI/models/model_neuron_o2/deltaxB.cpp similarity index 100% rename from deps/AMICI/models/model_neuron_o2/model_neuron_o2_deltaxB.cpp rename to deps/AMICI/models/model_neuron_o2/deltaxB.cpp diff --git a/deps/AMICI/models/model_neuron_o2/model_neuron_o2_drzdx.cpp b/deps/AMICI/models/model_neuron_o2/drzdx.cpp similarity index 100% rename from deps/AMICI/models/model_neuron_o2/model_neuron_o2_drzdx.cpp rename to deps/AMICI/models/model_neuron_o2/drzdx.cpp diff --git a/deps/AMICI/models/model_neuron_o2/model_neuron_o2_dwdx.cpp b/deps/AMICI/models/model_neuron_o2/dwdx.cpp similarity index 86% rename from deps/AMICI/models/model_neuron_o2/model_neuron_o2_dwdx.cpp rename to deps/AMICI/models/model_neuron_o2/dwdx.cpp index 6ec331519..a746d7549 100644 --- a/deps/AMICI/models/model_neuron_o2/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) { +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) { dwdx[0] = 2.0/2.5E1; dwdx[1] = dwdx[0]; } diff --git a/deps/AMICI/models/model_neuron_o2/model_neuron_o2_dxdotdp.cpp b/deps/AMICI/models/model_neuron_o2/dxdotdp.cpp similarity index 100% rename from deps/AMICI/models/model_neuron_o2/model_neuron_o2_dxdotdp.cpp rename to deps/AMICI/models/model_neuron_o2/dxdotdp.cpp diff --git a/deps/AMICI/models/model_neuron_o2/model_neuron_o2_dydx.cpp b/deps/AMICI/models/model_neuron_o2/dydx.cpp similarity index 100% rename from deps/AMICI/models/model_neuron_o2/model_neuron_o2_dydx.cpp rename to deps/AMICI/models/model_neuron_o2/dydx.cpp diff --git a/deps/AMICI/models/model_neuron_o2/model_neuron_o2_dzdx.cpp b/deps/AMICI/models/model_neuron_o2/dzdx.cpp similarity index 100% rename from deps/AMICI/models/model_neuron_o2/model_neuron_o2_dzdx.cpp rename to deps/AMICI/models/model_neuron_o2/dzdx.cpp 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 79904d771..23df2b9b3 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) 2bd39716102b9842354964196889d40d2eb2731c */ +/* Generated by amiwrap (R2017b) 223c5075608273b17f304556d0e8ccb41233bd21 */ #include #include #include "amici/defines.h" @@ -25,23 +25,23 @@ extern void dJydy_model_neuron_o2(double *dJydy, const int iy, const realtype *p extern void dJzdsigma_model_neuron_o2(double *dJzdsigma, const int iz, const realtype *p, const realtype *k, const double *z, const double *sigmaz, const double *mz); extern void dJzdz_model_neuron_o2(double *dJzdz, const int iz, const realtype *p, const realtype *k, const double *z, const double *sigmaz, const double *mz); extern void deltaqB_model_neuron_o2(double *deltaqB, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const int ip, const int ie, const realtype *xdot, const realtype *xdot_old, const realtype *xB); -extern void deltasx_model_neuron_o2(double *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); +extern void deltasx_model_neuron_o2(double *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); 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); +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 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); -extern void root_model_neuron_o2(realtype *root, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h); +extern void root_model_neuron_o2(realtype *root, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl); extern void rz_model_neuron_o2(double *rz, const int ie, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h); -extern void sigmay_model_neuron_o2(double *sigmay, const realtype t, const realtype *p, const realtype *k); +extern void sigmay_model_neuron_o2(double *sigmay, const realtype t, const realtype *p, const realtype *k, const realtype *y); extern void sigmaz_model_neuron_o2(double *sigmaz, const realtype t, const realtype *p, const realtype *k); extern void srz_model_neuron_o2(double *srz, 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 stau_model_neuron_o2(double *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); +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); +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 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, + 0, 5, 2, 2, @@ -71,6 +72,9 @@ class Model_model_neuron_o2 : public amici::Model_ODE { 0, 0, {}, + 0, + 0, + 0, 27, 1, 8 @@ -86,7 +90,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 "2bd39716102b9842354964196889d40d2eb2731c"; }; + std::string getAmiciCommit() const override { return "223c5075608273b17f304556d0e8ccb41233bd21"; }; 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); @@ -132,8 +136,8 @@ class Model_model_neuron_o2 : public amici::Model_ODE { deltaqB_model_neuron_o2(deltaqB, t, x, p, k, h, ip, ie, xdot, xdot_old, xB); } - void fdeltasx(double *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) override { - deltasx_model_neuron_o2(deltasx, t, x, p, k, h, w, ip, ie, xdot, xdot_old, sx, stau); + void fdeltasx(double *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) override { + deltasx_model_neuron_o2(deltasx, t, x, p, k, h, w, ip, ie, xdot, xdot_old, sx, stau, tcl); } void fdeltax(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) override { @@ -151,17 +155,17 @@ class Model_model_neuron_o2 : public amici::Model_ODE { drzdx_model_neuron_o2(drzdx, ie, t, x, p, k, h); } - void fdsigmaydp(double *dsigmaydp, const realtype t, const realtype *p, const realtype *k, const int ip) override { + void fdsigmaydp(double *dsigmaydp, const realtype t, const realtype *p, const realtype *k, const realtype *y, const int ip) override { } 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) 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 fdwdx(realtype *dwdx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl) override { - dwdx_model_neuron_o2(dwdx, t, x, p, k, h, w, tcl); + 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 fdxdotdp(realtype *dxdotdp, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const int ip, const realtype *w, const realtype *dwdp) override { @@ -182,16 +186,16 @@ class Model_model_neuron_o2 : public amici::Model_ODE { dzdx_model_neuron_o2(dzdx, ie, t, x, p, k, h); } - void froot(realtype *root, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h) override { - root_model_neuron_o2(root, t, x, p, k, h); + void froot(realtype *root, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl) override { + root_model_neuron_o2(root, t, x, p, k, h, tcl); } void frz(double *rz, const int ie, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h) override { rz_model_neuron_o2(rz, ie, t, x, p, k, h); } - void fsigmay(double *sigmay, const realtype t, const realtype *p, const realtype *k) override { - sigmay_model_neuron_o2(sigmay, t, p, k); + void fsigmay(double *sigmay, const realtype t, const realtype *p, const realtype *k, const realtype *y) override { + sigmay_model_neuron_o2(sigmay, t, p, k, y); } void fsigmaz(double *sigmaz, const realtype t, const realtype *p, const realtype *k) override { @@ -202,8 +206,8 @@ class Model_model_neuron_o2 : public amici::Model_ODE { srz_model_neuron_o2(srz, ie, t, x, p, k, h, sx, ip); } - void fstau(double *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) override { - stau_model_neuron_o2(stau, t, x, p, k, h, sx, ip, ie); + void fstau(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) override { + stau_model_neuron_o2(stau, t, x, p, k, h, tcl, sx, ip, ie); } void fsx0(realtype *sx0, const realtype t,const realtype *x0, const realtype *p, const realtype *k, const int ip) override { @@ -214,8 +218,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) override { - w_model_neuron_o2(w, t, x, p, k, h, tcl); + 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 fx0(realtype *x0, const realtype t, const realtype *p, const realtype *k) override { diff --git a/deps/AMICI/models/model_neuron_o2/model_neuron_o2_root.cpp b/deps/AMICI/models/model_neuron_o2/root.cpp similarity index 93% rename from deps/AMICI/models/model_neuron_o2/model_neuron_o2_root.cpp rename to deps/AMICI/models/model_neuron_o2/root.cpp index 852a2806b..7fba331de 100644 --- a/deps/AMICI/models/model_neuron_o2/model_neuron_o2_root.cpp +++ b/deps/AMICI/models/model_neuron_o2/root.cpp @@ -10,7 +10,7 @@ namespace amici { namespace model_model_neuron_o2{ -void root_model_neuron_o2(realtype *root, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h) { +void root_model_neuron_o2(realtype *root, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl) { root[0] = x[0]-3.0E1; } diff --git a/deps/AMICI/models/model_neuron_o2/model_neuron_o2_rz.cpp b/deps/AMICI/models/model_neuron_o2/rz.cpp similarity index 100% rename from deps/AMICI/models/model_neuron_o2/model_neuron_o2_rz.cpp rename to deps/AMICI/models/model_neuron_o2/rz.cpp diff --git a/deps/AMICI/models/model_neuron_o2/model_neuron_o2_sigmay.cpp b/deps/AMICI/models/model_neuron_o2/sigmay.cpp similarity index 86% rename from deps/AMICI/models/model_neuron_o2/model_neuron_o2_sigmay.cpp rename to deps/AMICI/models/model_neuron_o2/sigmay.cpp index 5938f5e08..3e5743cb7 100644 --- a/deps/AMICI/models/model_neuron_o2/model_neuron_o2_sigmay.cpp +++ b/deps/AMICI/models/model_neuron_o2/sigmay.cpp @@ -10,7 +10,7 @@ namespace amici { namespace model_model_neuron_o2{ -void sigmay_model_neuron_o2(double *sigmay, const realtype t, const realtype *p, const realtype *k) { +void sigmay_model_neuron_o2(double *sigmay, const realtype t, const realtype *p, const realtype *k, const realtype *y) { sigmay[0] = 1.0; } diff --git a/deps/AMICI/models/model_neuron_o2/model_neuron_o2_sigmaz.cpp b/deps/AMICI/models/model_neuron_o2/sigmaz.cpp similarity index 100% rename from deps/AMICI/models/model_neuron_o2/model_neuron_o2_sigmaz.cpp rename to deps/AMICI/models/model_neuron_o2/sigmaz.cpp diff --git a/deps/AMICI/models/model_neuron_o2/model_neuron_o2_srz.cpp b/deps/AMICI/models/model_neuron_o2/srz.cpp similarity index 100% rename from deps/AMICI/models/model_neuron_o2/model_neuron_o2_srz.cpp rename to deps/AMICI/models/model_neuron_o2/srz.cpp diff --git a/deps/AMICI/models/model_neuron_o2/model_neuron_o2_stau.cpp b/deps/AMICI/models/model_neuron_o2/stau.cpp similarity index 93% rename from deps/AMICI/models/model_neuron_o2/model_neuron_o2_stau.cpp rename to deps/AMICI/models/model_neuron_o2/stau.cpp index 70a8cf3a5..c639781b9 100644 --- a/deps/AMICI/models/model_neuron_o2/model_neuron_o2_stau.cpp +++ b/deps/AMICI/models/model_neuron_o2/stau.cpp @@ -10,7 +10,7 @@ namespace amici { namespace model_model_neuron_o2{ -void stau_model_neuron_o2(double *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) { +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) { switch (ip) { case 0: { switch(ie) { diff --git a/deps/AMICI/models/model_neuron_o2/swig/CMakeLists.txt b/deps/AMICI/models/model_neuron_o2/swig/CMakeLists.txt index af0f64a76..eb9fe681c 100644 --- a/deps/AMICI/models/model_neuron_o2/swig/CMakeLists.txt +++ b/deps/AMICI/models/model_neuron_o2/swig/CMakeLists.txt @@ -1,28 +1,19 @@ -cmake_minimum_required(VERSION 3.8) # swig_add_library - -if(POLICY CMP0078) - cmake_policy(SET CMP0078 OLD) -endif(POLICY CMP0078) -if(POLICY CMP0074) - # Use package_ROOT environment variables - cmake_policy(SET CMP0074 NEW) -endif(POLICY CMP0074) -if(POLICY CMP0086) - cmake_policy(SET CMP0086 NEW) -endif(POLICY CMP0086) +cmake_minimum_required(VERSION 3.15) +if(DEFINED ENV{SWIG}) + set(SWIG_EXECUTABLE $ENV{SWIG}) +endif() find_package(SWIG REQUIRED) include(${SWIG_USE_FILE}) -if(${CMAKE_VERSION} VERSION_LESS "3.12.0") - find_package(PythonLibs REQUIRED) - include_directories(${PYTHON_INCLUDE_DIRS}) - set(Python3_LIBRARIES ${PYTHON_LIBRARIES}) -else() - find_package (Python3 COMPONENTS Development) - include_directories(${Python3_INCLUDE_DIRS}) +if(DEFINED ENV{PYTHON_EXECUTABLE}) + set(Python3_EXECUTABLE $ENV{PYTHON_EXECUTABLE}) endif() +# We don't need "Interpreter" here, but without that, FindPython3 will +# ignore the Python version selected via $Python3_EXECUTABLE +find_package(Python3 COMPONENTS Interpreter Development) +include_directories(${Python3_INCLUDE_DIRS}) set(SWIG_LIBRARY_NAME _${PROJECT_NAME}) set(CMAKE_SWIG_FLAGS "") @@ -30,17 +21,38 @@ set_source_files_properties(${PROJECT_NAME}.i PROPERTIES CPLUSPLUS ON) # swig does not use INTERFACE_INCLUDE_DIRS of linked libraries, so add manually get_target_property(AMICI_INCLUDE_DIRS Upstream::amici INTERFACE_INCLUDE_DIRECTORIES) -include_directories(${AMICI_INCLUDE_DIRS} ..) +include_directories(${AMICI_INCLUDE_DIRS} .. ${AMICI_INCLUDE_DIRS}/../swig) -swig_add_library(${PROJECT_NAME} +swig_add_library(${SWIG_LIBRARY_NAME} TYPE MODULE LANGUAGE python SOURCES ${PROJECT_NAME}.i) -swig_link_libraries(${PROJECT_NAME} + +set_target_properties(${SWIG_LIBRARY_NAME} + PROPERTIES + SWIG_USE_TARGET_INCLUDE_DIRECTORIES TRUE + PREFIX "" +) + +# Python extension suffix +execute_process( + COMMAND ${Python3_EXECUTABLE} -c + "import sysconfig; print(sysconfig.get_config_var('EXT_SUFFIX'))" + OUTPUT_VARIABLE PY_EXT_SUFFIX OUTPUT_STRIP_TRAILING_WHITESPACE) +if(NOT "${PY_EXT_SUFFIX}" STREQUAL "") + message(STATUS "Python extension suffix is ${PY_EXT_SUFFIX}") + set_target_properties(${SWIG_LIBRARY_NAME} PROPERTIES SUFFIX "${PY_EXT_SUFFIX}" ) +endif() + + +swig_link_libraries(${SWIG_LIBRARY_NAME} ${Python3_LIBRARIES} model) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.py + $ DESTINATION .) + # configure module setup script set(SETUP_PY_IN ${Amici_DIR}/model_setup.template.py) set(SETUP_PY_OUT ${CMAKE_CURRENT_BINARY_DIR}/setup.py) diff --git a/deps/AMICI/models/model_neuron_o2/swig/model_neuron_o2.i b/deps/AMICI/models/model_neuron_o2/swig/model_neuron_o2.i index 978b2ffb1..6c9c51ddb 100644 --- a/deps/AMICI/models/model_neuron_o2/swig/model_neuron_o2.i +++ b/deps/AMICI/models/model_neuron_o2/swig/model_neuron_o2.i @@ -10,5 +10,13 @@ using namespace amici; %} +// Make model module accessible from the model +%feature("pythonappend") amici::generic_model::getModel %{ + if '.' in __name__: + import sys + val.module = sys.modules['.'.join(__name__.split('.')[:-1])] +%} + + // Process symbols in header %include "wrapfunctions.h" diff --git a/deps/AMICI/models/model_neuron_o2/model_neuron_o2_sx0.cpp b/deps/AMICI/models/model_neuron_o2/sx0.cpp similarity index 100% rename from deps/AMICI/models/model_neuron_o2/model_neuron_o2_sx0.cpp rename to deps/AMICI/models/model_neuron_o2/sx0.cpp diff --git a/deps/AMICI/models/model_neuron_o2/model_neuron_o2_sz.cpp b/deps/AMICI/models/model_neuron_o2/sz.cpp similarity index 100% rename from deps/AMICI/models/model_neuron_o2/model_neuron_o2_sz.cpp rename to deps/AMICI/models/model_neuron_o2/sz.cpp diff --git a/deps/AMICI/models/model_neuron_o2/model_neuron_o2_w.cpp b/deps/AMICI/models/model_neuron_o2/w.cpp similarity index 90% rename from deps/AMICI/models/model_neuron_o2/model_neuron_o2_w.cpp rename to deps/AMICI/models/model_neuron_o2/w.cpp index 4f49da479..cbd2f0a25 100644 --- a/deps/AMICI/models/model_neuron_o2/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) { +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) { w[0] = x[0]*(2.0/2.5E1); w[1] = w[0]+5.0; } diff --git a/deps/AMICI/models/model_neuron_o2/model_neuron_o2_x0.cpp b/deps/AMICI/models/model_neuron_o2/x0.cpp similarity index 100% rename from deps/AMICI/models/model_neuron_o2/model_neuron_o2_x0.cpp rename to deps/AMICI/models/model_neuron_o2/x0.cpp diff --git a/deps/AMICI/models/model_neuron_o2/model_neuron_o2_xdot.cpp b/deps/AMICI/models/model_neuron_o2/xdot.cpp similarity index 100% rename from deps/AMICI/models/model_neuron_o2/model_neuron_o2_xdot.cpp rename to deps/AMICI/models/model_neuron_o2/xdot.cpp diff --git a/deps/AMICI/models/model_neuron_o2/model_neuron_o2_y.cpp b/deps/AMICI/models/model_neuron_o2/y.cpp similarity index 100% rename from deps/AMICI/models/model_neuron_o2/model_neuron_o2_y.cpp rename to deps/AMICI/models/model_neuron_o2/y.cpp diff --git a/deps/AMICI/models/model_neuron_o2/model_neuron_o2_z.cpp b/deps/AMICI/models/model_neuron_o2/z.cpp similarity index 100% rename from deps/AMICI/models/model_neuron_o2/model_neuron_o2_z.cpp rename to deps/AMICI/models/model_neuron_o2/z.cpp diff --git a/deps/AMICI/models/model_robertson/CMakeLists.txt b/deps/AMICI/models/model_robertson/CMakeLists.txt index 0c6137577..9b27e3daa 100644 --- a/deps/AMICI/models/model_robertson/CMakeLists.txt +++ b/deps/AMICI/models/model_robertson/CMakeLists.txt @@ -1,103 +1,110 @@ -cmake_minimum_required(VERSION 3.3) - -if(POLICY CMP0060) - cmake_policy(SET CMP0060 NEW) -endif(POLICY CMP0060) -if(POLICY CMP0065) - cmake_policy(SET CMP0065 NEW) -endif(POLICY CMP0065) -if(POLICY CMP0074) - # Use package_ROOT environment variables - cmake_policy(SET CMP0074 NEW) -endif(POLICY CMP0074) +# Build AMICI model +cmake_minimum_required(VERSION 3.15) project(model_robertson) -set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_POSITION_INDEPENDENT_CODE ON) include(CheckCXXCompilerFlag) -set(MY_CXX_FLAGS -Wall -Wno-unused-function -Wno-unused-variable -Wno-unused-but-set-variable) -foreach(FLAG ${MY_CXX_FLAGS}) - unset(CUR_FLAG_SUPPORTED CACHE) - CHECK_CXX_COMPILER_FLAG(${FLAG} CUR_FLAG_SUPPORTED) - if(${CUR_FLAG_SUPPORTED}) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FLAG}") - endif() -endforeach(FLAG) - -find_package(Amici HINTS ${CMAKE_CURRENT_LIST_DIR}/../../build) +set(MY_CXX_FLAGS -Wall -Wno-unused-function -Wno-unused-variable) +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + list(APPEND MY_CXX_FLAGS -Wno-unused-but-set-variable) +endif() +foreach(flag ${MY_CXX_FLAGS}) + unset(CUR_FLAG_SUPPORTED CACHE) + check_cxx_compiler_flag(-Werror ${flag} CUR_FLAG_SUPPORTED) + if(${CUR_FLAG_SUPPORTED}) + string(APPEND CMAKE_CXX_FLAGS " ${flag}") + endif() +endforeach() + +if(DEFINED ENV{AMICI_CXXFLAGS}) + message(STATUS "Appending flags from AMICI_CXXFLAGS: $ENV{AMICI_CXXFLAGS}") + add_compile_options("$ENV{AMICI_CXXFLAGS}") +endif() +if(DEFINED ENV{AMICI_LDFLAGS}) + message(STATUS "Appending flags from AMICI_LDFLAGS: $ENV{AMICI_LDFLAGS}") + link_libraries("$ENV{AMICI_LDFLAGS}") +endif() + +find_package(Amici REQUIRED HINTS + ${CMAKE_CURRENT_LIST_DIR}/../../build) message(STATUS "Found AMICI ${Amici_DIR}") set(MODEL_DIR ${CMAKE_CURRENT_LIST_DIR}) -set(SRC_LIST_LIB ${MODEL_DIR}/model_robertson_JSparse.cpp -${MODEL_DIR}/model_robertson_Jy.cpp -${MODEL_DIR}/model_robertson_M.cpp -${MODEL_DIR}/model_robertson_dJydsigma.cpp -${MODEL_DIR}/model_robertson_dJydy.cpp -${MODEL_DIR}/model_robertson_dwdp.cpp -${MODEL_DIR}/model_robertson_dwdx.cpp -${MODEL_DIR}/model_robertson_dxdotdp.cpp -${MODEL_DIR}/model_robertson_dydx.cpp -${MODEL_DIR}/model_robertson_sigmay.cpp -${MODEL_DIR}/model_robertson_w.cpp -${MODEL_DIR}/model_robertson_x0.cpp -${MODEL_DIR}/model_robertson_xdot.cpp -${MODEL_DIR}/model_robertson_y.cpp - -${MODEL_DIR}/wrapfunctions.cpp -) +set(SRC_LIST_LIB ${MODEL_DIR}/JSparse.cpp +${MODEL_DIR}/Jy.cpp +${MODEL_DIR}/M.cpp +${MODEL_DIR}/dJydsigma.cpp +${MODEL_DIR}/dJydy.cpp +${MODEL_DIR}/dwdp.cpp +${MODEL_DIR}/dwdx.cpp +${MODEL_DIR}/dxdotdp.cpp +${MODEL_DIR}/dydx.cpp +${MODEL_DIR}/sigmay.cpp +${MODEL_DIR}/w.cpp +${MODEL_DIR}/x0.cpp +${MODEL_DIR}/xdot.cpp +${MODEL_DIR}/y.cpp + ${MODEL_DIR}/wrapfunctions.cpp) add_library(${PROJECT_NAME} ${SRC_LIST_LIB}) add_library(model ALIAS ${PROJECT_NAME}) -# This option can be helpful when using the Intel compiler and compilation of -# wrapfunctions.cpp fails due to insufficient memory. -option(ENABLE_WRAPFUNCTIONS_OPTIMIZATIONS "Enable compiler optimizations for wrapfunctions.cpp?" ON) -if(NOT ENABLE_WRAPFUNCTIONS_OPTIMIZATIONS) - set_source_files_properties(wrapfunctions.cpp PROPERTIES COMPILE_FLAGS -O0) -endif() +# Some special functions require boost +# +# TODO: set some flag during code generation whether the given model requires +# boost. for now, try to find it, add include directories and link against it. +# let the compiler/linker error if it is required but not found +find_package(Boost) target_include_directories(${PROJECT_NAME} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") -target_link_libraries(${PROJECT_NAME} - PUBLIC Upstream::amici -) +target_link_libraries( + ${PROJECT_NAME} + PUBLIC Upstream::amici + PRIVATE $<$:Boost::boost>) -set(SRC_LIST_EXE main.cpp) - -add_executable(simulate_${PROJECT_NAME} ${SRC_LIST_EXE}) +if(NOT "${AMICI_PYTHON_BUILD_EXT_ONLY}") + set(SRC_LIST_EXE main.cpp) + add_executable(simulate_${PROJECT_NAME} ${SRC_LIST_EXE}) + target_link_libraries(simulate_${PROJECT_NAME} ${PROJECT_NAME}) +endif() -target_link_libraries(simulate_${PROJECT_NAME} ${PROJECT_NAME}) +# 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}) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 --coverage") + string(APPEND CMAKE_CXX_FLAGS_DEBUG " --coverage") + string(APPEND CMAKE_EXE_LINKER_FLAGS_DEBUG " --coverage") endif() -## SWIG +# SWIG option(ENABLE_SWIG "Build swig/python library?" ON) if(ENABLE_SWIG) - if(NOT(${CMAKE_VERSION} VERSION_LESS 3.8)) - add_subdirectory(swig) - else() - message(WARNING "Unable to build SWIG interface, upgrade CMake to >=3.8.") - endif() + add_subdirectory(swig) endif() - # include(GNUInstallDirs) -install(TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME}Targets - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} - PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} -) -export(EXPORT ${PROJECT_NAME}Targets FILE ${PROJECT_NAME}Config.cmake - NAMESPACE Upstream:: - ) +install( + TARGETS ${PROJECT_NAME} + EXPORT ${PROJECT_NAME}Targets + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + INCLUDES + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +export( + EXPORT ${PROJECT_NAME}Targets + FILE ${PROJECT_NAME}Config.cmake + NAMESPACE Upstream::) # - diff --git a/deps/AMICI/models/model_robertson/model_robertson_JSparse.cpp b/deps/AMICI/models/model_robertson/JSparse.cpp similarity index 100% rename from deps/AMICI/models/model_robertson/model_robertson_JSparse.cpp rename to deps/AMICI/models/model_robertson/JSparse.cpp diff --git a/deps/AMICI/models/model_robertson/model_robertson_Jy.cpp b/deps/AMICI/models/model_robertson/Jy.cpp similarity index 100% rename from deps/AMICI/models/model_robertson/model_robertson_Jy.cpp rename to deps/AMICI/models/model_robertson/Jy.cpp diff --git a/deps/AMICI/models/model_robertson/model_robertson_M.cpp b/deps/AMICI/models/model_robertson/M.cpp similarity index 100% rename from deps/AMICI/models/model_robertson/model_robertson_M.cpp rename to deps/AMICI/models/model_robertson/M.cpp diff --git a/deps/AMICI/models/model_robertson/model_robertson_dJydsigma.cpp b/deps/AMICI/models/model_robertson/dJydsigma.cpp similarity index 100% rename from deps/AMICI/models/model_robertson/model_robertson_dJydsigma.cpp rename to deps/AMICI/models/model_robertson/dJydsigma.cpp diff --git a/deps/AMICI/models/model_robertson/model_robertson_dJydy.cpp b/deps/AMICI/models/model_robertson/dJydy.cpp similarity index 100% rename from deps/AMICI/models/model_robertson/model_robertson_dJydy.cpp rename to deps/AMICI/models/model_robertson/dJydy.cpp diff --git a/deps/AMICI/models/model_robertson/model_robertson_dwdp.cpp b/deps/AMICI/models/model_robertson/dwdp.cpp similarity index 90% rename from deps/AMICI/models/model_robertson/model_robertson_dwdp.cpp rename to deps/AMICI/models/model_robertson/dwdp.cpp index e7db2c139..831c448ca 100644 --- a/deps/AMICI/models/model_robertson/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) { +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) { dwdp[0] = x[1]*x[2]; } diff --git a/deps/AMICI/models/model_robertson/model_robertson_dwdx.cpp b/deps/AMICI/models/model_robertson/dwdx.cpp similarity index 86% rename from deps/AMICI/models/model_robertson/model_robertson_dwdx.cpp rename to deps/AMICI/models/model_robertson/dwdx.cpp index 8e25dc518..5c300a54e 100644 --- a/deps/AMICI/models/model_robertson/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) { +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) { dwdx[0] = p[1]*x[2]; dwdx[1] = p[1]*x[1]; } diff --git a/deps/AMICI/models/model_robertson/model_robertson_dxdotdp.cpp b/deps/AMICI/models/model_robertson/dxdotdp.cpp similarity index 100% rename from deps/AMICI/models/model_robertson/model_robertson_dxdotdp.cpp rename to deps/AMICI/models/model_robertson/dxdotdp.cpp diff --git a/deps/AMICI/models/model_robertson/model_robertson_dydx.cpp b/deps/AMICI/models/model_robertson/dydx.cpp similarity index 100% rename from deps/AMICI/models/model_robertson/model_robertson_dydx.cpp rename to deps/AMICI/models/model_robertson/dydx.cpp diff --git a/deps/AMICI/models/model_robertson/model_robertson.h b/deps/AMICI/models/model_robertson/model_robertson.h index 9e3c40f29..7f4377d78 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) 2bd39716102b9842354964196889d40d2eb2731c */ +/* Generated by amiwrap (R2017b) 223c5075608273b17f304556d0e8ccb41233bd21 */ #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); -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); +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 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); -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); +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 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); @@ -46,6 +46,7 @@ class Model_model_robertson : public amici::Model_DAE { 0, 0, 0, + 0, 1, 1, 2, @@ -53,6 +54,9 @@ class Model_model_robertson : public amici::Model_DAE { 0, 0, {}, + 0, + 0, + 0, 9, 2, 2 @@ -68,7 +72,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 "2bd39716102b9842354964196889d40d2eb2731c"; }; + std::string getAmiciCommit() const override { return "223c5075608273b17f304556d0e8ccb41233bd21"; }; 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); @@ -111,7 +115,7 @@ class Model_model_robertson : public amici::Model_DAE { void fdeltaqB(double *deltaqB, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const int ip, const int ie, const realtype *xdot, const realtype *xdot_old, const realtype *xB) override { } - void fdeltasx(double *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) override { + void fdeltasx(double *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) override { } void fdeltax(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) override { @@ -126,18 +130,18 @@ class Model_model_robertson : public amici::Model_DAE { void fdrzdx(double *drzdx, const int ie, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h) override { } - void fdsigmaydp(double *dsigmaydp, const realtype t, const realtype *p, const realtype *k, const int ip) override { + void fdsigmaydp(double *dsigmaydp, const realtype t, const realtype *p, const realtype *k, const realtype *y, const int ip) override { } 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) override { - dwdp_model_robertson(dwdp, t, x, p, k, h, w, tcl, stcl); + 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 fdwdx(realtype *dwdx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl) override { - dwdx_model_robertson(dwdx, t, x, p, k, h, w, tcl); + 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 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 { @@ -163,8 +167,8 @@ class Model_model_robertson : public amici::Model_DAE { void frz(double *rz, const int ie, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h) override { } - void fsigmay(double *sigmay, const realtype t, const realtype *p, const realtype *k) override { - sigmay_model_robertson(sigmay, t, p, k); + void fsigmay(double *sigmay, const realtype t, const realtype *p, const realtype *k, const realtype *y) override { + sigmay_model_robertson(sigmay, t, p, k, y); } void fsigmaz(double *sigmaz, const realtype t, const realtype *p, const realtype *k) override { @@ -173,7 +177,7 @@ class Model_model_robertson : public amici::Model_DAE { void fsrz(double *srz, 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 fstau(double *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) override { + void fstau(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) override { } void fsx0(realtype *sx0, const realtype t,const realtype *x0, const realtype *p, const realtype *k, const int ip) override { @@ -182,8 +186,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) override { - w_model_robertson(w, t, x, p, k, h, tcl); + 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 fx0(realtype *x0, const realtype t, const realtype *p, const realtype *k) override { diff --git a/deps/AMICI/models/model_robertson/model_robertson_sigmay.cpp b/deps/AMICI/models/model_robertson/sigmay.cpp similarity index 87% rename from deps/AMICI/models/model_robertson/model_robertson_sigmay.cpp rename to deps/AMICI/models/model_robertson/sigmay.cpp index 6e57e3c50..9e1e5e019 100644 --- a/deps/AMICI/models/model_robertson/model_robertson_sigmay.cpp +++ b/deps/AMICI/models/model_robertson/sigmay.cpp @@ -10,7 +10,7 @@ namespace amici { namespace model_model_robertson{ -void sigmay_model_robertson(double *sigmay, const realtype t, const realtype *p, const realtype *k) { +void sigmay_model_robertson(double *sigmay, const realtype t, const realtype *p, const realtype *k, const realtype *y) { sigmay[0] = 1.0; sigmay[1] = 1.0; sigmay[2] = 1.0; diff --git a/deps/AMICI/models/model_robertson/swig/CMakeLists.txt b/deps/AMICI/models/model_robertson/swig/CMakeLists.txt index af0f64a76..eb9fe681c 100644 --- a/deps/AMICI/models/model_robertson/swig/CMakeLists.txt +++ b/deps/AMICI/models/model_robertson/swig/CMakeLists.txt @@ -1,28 +1,19 @@ -cmake_minimum_required(VERSION 3.8) # swig_add_library - -if(POLICY CMP0078) - cmake_policy(SET CMP0078 OLD) -endif(POLICY CMP0078) -if(POLICY CMP0074) - # Use package_ROOT environment variables - cmake_policy(SET CMP0074 NEW) -endif(POLICY CMP0074) -if(POLICY CMP0086) - cmake_policy(SET CMP0086 NEW) -endif(POLICY CMP0086) +cmake_minimum_required(VERSION 3.15) +if(DEFINED ENV{SWIG}) + set(SWIG_EXECUTABLE $ENV{SWIG}) +endif() find_package(SWIG REQUIRED) include(${SWIG_USE_FILE}) -if(${CMAKE_VERSION} VERSION_LESS "3.12.0") - find_package(PythonLibs REQUIRED) - include_directories(${PYTHON_INCLUDE_DIRS}) - set(Python3_LIBRARIES ${PYTHON_LIBRARIES}) -else() - find_package (Python3 COMPONENTS Development) - include_directories(${Python3_INCLUDE_DIRS}) +if(DEFINED ENV{PYTHON_EXECUTABLE}) + set(Python3_EXECUTABLE $ENV{PYTHON_EXECUTABLE}) endif() +# We don't need "Interpreter" here, but without that, FindPython3 will +# ignore the Python version selected via $Python3_EXECUTABLE +find_package(Python3 COMPONENTS Interpreter Development) +include_directories(${Python3_INCLUDE_DIRS}) set(SWIG_LIBRARY_NAME _${PROJECT_NAME}) set(CMAKE_SWIG_FLAGS "") @@ -30,17 +21,38 @@ set_source_files_properties(${PROJECT_NAME}.i PROPERTIES CPLUSPLUS ON) # swig does not use INTERFACE_INCLUDE_DIRS of linked libraries, so add manually get_target_property(AMICI_INCLUDE_DIRS Upstream::amici INTERFACE_INCLUDE_DIRECTORIES) -include_directories(${AMICI_INCLUDE_DIRS} ..) +include_directories(${AMICI_INCLUDE_DIRS} .. ${AMICI_INCLUDE_DIRS}/../swig) -swig_add_library(${PROJECT_NAME} +swig_add_library(${SWIG_LIBRARY_NAME} TYPE MODULE LANGUAGE python SOURCES ${PROJECT_NAME}.i) -swig_link_libraries(${PROJECT_NAME} + +set_target_properties(${SWIG_LIBRARY_NAME} + PROPERTIES + SWIG_USE_TARGET_INCLUDE_DIRECTORIES TRUE + PREFIX "" +) + +# Python extension suffix +execute_process( + COMMAND ${Python3_EXECUTABLE} -c + "import sysconfig; print(sysconfig.get_config_var('EXT_SUFFIX'))" + OUTPUT_VARIABLE PY_EXT_SUFFIX OUTPUT_STRIP_TRAILING_WHITESPACE) +if(NOT "${PY_EXT_SUFFIX}" STREQUAL "") + message(STATUS "Python extension suffix is ${PY_EXT_SUFFIX}") + set_target_properties(${SWIG_LIBRARY_NAME} PROPERTIES SUFFIX "${PY_EXT_SUFFIX}" ) +endif() + + +swig_link_libraries(${SWIG_LIBRARY_NAME} ${Python3_LIBRARIES} model) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.py + $ DESTINATION .) + # configure module setup script set(SETUP_PY_IN ${Amici_DIR}/model_setup.template.py) set(SETUP_PY_OUT ${CMAKE_CURRENT_BINARY_DIR}/setup.py) diff --git a/deps/AMICI/models/model_robertson/swig/model_robertson.i b/deps/AMICI/models/model_robertson/swig/model_robertson.i index 1027282a2..a6b870578 100644 --- a/deps/AMICI/models/model_robertson/swig/model_robertson.i +++ b/deps/AMICI/models/model_robertson/swig/model_robertson.i @@ -10,5 +10,13 @@ using namespace amici; %} +// Make model module accessible from the model +%feature("pythonappend") amici::generic_model::getModel %{ + if '.' in __name__: + import sys + val.module = sys.modules['.'.join(__name__.split('.')[:-1])] +%} + + // Process symbols in header %include "wrapfunctions.h" diff --git a/deps/AMICI/models/model_robertson/model_robertson_w.cpp b/deps/AMICI/models/model_robertson/w.cpp similarity index 90% rename from deps/AMICI/models/model_robertson/model_robertson_w.cpp rename to deps/AMICI/models/model_robertson/w.cpp index f5e90c8e4..6905b49c0 100644 --- a/deps/AMICI/models/model_robertson/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) { +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) { w[0] = p[1]*x[1]*x[2]; } diff --git a/deps/AMICI/models/model_robertson/model_robertson_x0.cpp b/deps/AMICI/models/model_robertson/x0.cpp similarity index 100% rename from deps/AMICI/models/model_robertson/model_robertson_x0.cpp rename to deps/AMICI/models/model_robertson/x0.cpp diff --git a/deps/AMICI/models/model_robertson/model_robertson_xdot.cpp b/deps/AMICI/models/model_robertson/xdot.cpp similarity index 100% rename from deps/AMICI/models/model_robertson/model_robertson_xdot.cpp rename to deps/AMICI/models/model_robertson/xdot.cpp diff --git a/deps/AMICI/models/model_robertson/model_robertson_y.cpp b/deps/AMICI/models/model_robertson/y.cpp similarity index 100% rename from deps/AMICI/models/model_robertson/model_robertson_y.cpp rename to deps/AMICI/models/model_robertson/y.cpp diff --git a/deps/AMICI/models/model_steadystate/CMakeLists.txt b/deps/AMICI/models/model_steadystate/CMakeLists.txt index 20d2f4b3d..9b699da5b 100644 --- a/deps/AMICI/models/model_steadystate/CMakeLists.txt +++ b/deps/AMICI/models/model_steadystate/CMakeLists.txt @@ -1,102 +1,109 @@ -cmake_minimum_required(VERSION 3.3) - -if(POLICY CMP0060) - cmake_policy(SET CMP0060 NEW) -endif(POLICY CMP0060) -if(POLICY CMP0065) - cmake_policy(SET CMP0065 NEW) -endif(POLICY CMP0065) -if(POLICY CMP0074) - # Use package_ROOT environment variables - cmake_policy(SET CMP0074 NEW) -endif(POLICY CMP0074) +# Build AMICI model +cmake_minimum_required(VERSION 3.15) project(model_steadystate) -set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_POSITION_INDEPENDENT_CODE ON) include(CheckCXXCompilerFlag) -set(MY_CXX_FLAGS -Wall -Wno-unused-function -Wno-unused-variable -Wno-unused-but-set-variable) -foreach(FLAG ${MY_CXX_FLAGS}) - unset(CUR_FLAG_SUPPORTED CACHE) - CHECK_CXX_COMPILER_FLAG(${FLAG} CUR_FLAG_SUPPORTED) - if(${CUR_FLAG_SUPPORTED}) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FLAG}") - endif() -endforeach(FLAG) - -find_package(Amici HINTS ${CMAKE_CURRENT_LIST_DIR}/../../build) +set(MY_CXX_FLAGS -Wall -Wno-unused-function -Wno-unused-variable) +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + list(APPEND MY_CXX_FLAGS -Wno-unused-but-set-variable) +endif() +foreach(flag ${MY_CXX_FLAGS}) + unset(CUR_FLAG_SUPPORTED CACHE) + check_cxx_compiler_flag(-Werror ${flag} CUR_FLAG_SUPPORTED) + if(${CUR_FLAG_SUPPORTED}) + string(APPEND CMAKE_CXX_FLAGS " ${flag}") + endif() +endforeach() + +if(DEFINED ENV{AMICI_CXXFLAGS}) + message(STATUS "Appending flags from AMICI_CXXFLAGS: $ENV{AMICI_CXXFLAGS}") + add_compile_options("$ENV{AMICI_CXXFLAGS}") +endif() +if(DEFINED ENV{AMICI_LDFLAGS}) + message(STATUS "Appending flags from AMICI_LDFLAGS: $ENV{AMICI_LDFLAGS}") + link_libraries("$ENV{AMICI_LDFLAGS}") +endif() + +find_package(Amici REQUIRED HINTS + ${CMAKE_CURRENT_LIST_DIR}/../../build) message(STATUS "Found AMICI ${Amici_DIR}") set(MODEL_DIR ${CMAKE_CURRENT_LIST_DIR}) -set(SRC_LIST_LIB ${MODEL_DIR}/model_steadystate_JSparse.cpp -${MODEL_DIR}/model_steadystate_Jy.cpp -${MODEL_DIR}/model_steadystate_dJydsigma.cpp -${MODEL_DIR}/model_steadystate_dJydy.cpp -${MODEL_DIR}/model_steadystate_dwdp.cpp -${MODEL_DIR}/model_steadystate_dwdx.cpp -${MODEL_DIR}/model_steadystate_dxdotdp.cpp -${MODEL_DIR}/model_steadystate_dydx.cpp -${MODEL_DIR}/model_steadystate_sigmay.cpp -${MODEL_DIR}/model_steadystate_w.cpp -${MODEL_DIR}/model_steadystate_x0.cpp -${MODEL_DIR}/model_steadystate_xdot.cpp -${MODEL_DIR}/model_steadystate_y.cpp - -${MODEL_DIR}/wrapfunctions.cpp -) +set(SRC_LIST_LIB ${MODEL_DIR}/JSparse.cpp +${MODEL_DIR}/Jy.cpp +${MODEL_DIR}/dJydsigma.cpp +${MODEL_DIR}/dJydy.cpp +${MODEL_DIR}/dwdp.cpp +${MODEL_DIR}/dwdx.cpp +${MODEL_DIR}/dxdotdp.cpp +${MODEL_DIR}/dydx.cpp +${MODEL_DIR}/sigmay.cpp +${MODEL_DIR}/w.cpp +${MODEL_DIR}/x0.cpp +${MODEL_DIR}/xdot.cpp +${MODEL_DIR}/y.cpp + ${MODEL_DIR}/wrapfunctions.cpp) add_library(${PROJECT_NAME} ${SRC_LIST_LIB}) add_library(model ALIAS ${PROJECT_NAME}) -# This option can be helpful when using the Intel compiler and compilation of -# wrapfunctions.cpp fails due to insufficient memory. -option(ENABLE_WRAPFUNCTIONS_OPTIMIZATIONS "Enable compiler optimizations for wrapfunctions.cpp?" ON) -if(NOT ENABLE_WRAPFUNCTIONS_OPTIMIZATIONS) - set_source_files_properties(wrapfunctions.cpp PROPERTIES COMPILE_FLAGS -O0) -endif() +# Some special functions require boost +# +# TODO: set some flag during code generation whether the given model requires +# boost. for now, try to find it, add include directories and link against it. +# let the compiler/linker error if it is required but not found +find_package(Boost) target_include_directories(${PROJECT_NAME} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") -target_link_libraries(${PROJECT_NAME} - PUBLIC Upstream::amici -) +target_link_libraries( + ${PROJECT_NAME} + PUBLIC Upstream::amici + PRIVATE $<$:Boost::boost>) -set(SRC_LIST_EXE main.cpp) - -add_executable(simulate_${PROJECT_NAME} ${SRC_LIST_EXE}) +if(NOT "${AMICI_PYTHON_BUILD_EXT_ONLY}") + set(SRC_LIST_EXE main.cpp) + add_executable(simulate_${PROJECT_NAME} ${SRC_LIST_EXE}) + target_link_libraries(simulate_${PROJECT_NAME} ${PROJECT_NAME}) +endif() -target_link_libraries(simulate_${PROJECT_NAME} ${PROJECT_NAME}) +# 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}) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 --coverage") + string(APPEND CMAKE_CXX_FLAGS_DEBUG " --coverage") + string(APPEND CMAKE_EXE_LINKER_FLAGS_DEBUG " --coverage") endif() -## SWIG +# SWIG option(ENABLE_SWIG "Build swig/python library?" ON) if(ENABLE_SWIG) - if(NOT(${CMAKE_VERSION} VERSION_LESS 3.8)) - add_subdirectory(swig) - else() - message(WARNING "Unable to build SWIG interface, upgrade CMake to >=3.8.") - endif() + add_subdirectory(swig) endif() - # include(GNUInstallDirs) -install(TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME}Targets - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} - PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} -) -export(EXPORT ${PROJECT_NAME}Targets FILE ${PROJECT_NAME}Config.cmake - NAMESPACE Upstream:: - ) +install( + TARGETS ${PROJECT_NAME} + EXPORT ${PROJECT_NAME}Targets + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + INCLUDES + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +export( + EXPORT ${PROJECT_NAME}Targets + FILE ${PROJECT_NAME}Config.cmake + NAMESPACE Upstream::) # - diff --git a/deps/AMICI/models/model_steadystate/model_steadystate_JSparse.cpp b/deps/AMICI/models/model_steadystate/JSparse.cpp similarity index 100% rename from deps/AMICI/models/model_steadystate/model_steadystate_JSparse.cpp rename to deps/AMICI/models/model_steadystate/JSparse.cpp diff --git a/deps/AMICI/models/model_steadystate/model_steadystate_Jy.cpp b/deps/AMICI/models/model_steadystate/Jy.cpp similarity index 100% rename from deps/AMICI/models/model_steadystate/model_steadystate_Jy.cpp rename to deps/AMICI/models/model_steadystate/Jy.cpp diff --git a/deps/AMICI/models/model_steadystate/model_steadystate_dJydsigma.cpp b/deps/AMICI/models/model_steadystate/dJydsigma.cpp similarity index 100% rename from deps/AMICI/models/model_steadystate/model_steadystate_dJydsigma.cpp rename to deps/AMICI/models/model_steadystate/dJydsigma.cpp diff --git a/deps/AMICI/models/model_steadystate/model_steadystate_dJydy.cpp b/deps/AMICI/models/model_steadystate/dJydy.cpp similarity index 100% rename from deps/AMICI/models/model_steadystate/model_steadystate_dJydy.cpp rename to deps/AMICI/models/model_steadystate/dJydy.cpp diff --git a/deps/AMICI/models/model_steadystate/model_steadystate_dwdp.cpp b/deps/AMICI/models/model_steadystate/dwdp.cpp similarity index 90% rename from deps/AMICI/models/model_steadystate/model_steadystate_dwdp.cpp rename to deps/AMICI/models/model_steadystate/dwdp.cpp index 20e991668..154db2a72 100644 --- a/deps/AMICI/models/model_steadystate/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) { +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) { dwdp[0] = x[2]; } diff --git a/deps/AMICI/models/model_steadystate/model_steadystate_dwdx.cpp b/deps/AMICI/models/model_steadystate/dwdx.cpp similarity index 86% rename from deps/AMICI/models/model_steadystate/model_steadystate_dwdx.cpp rename to deps/AMICI/models/model_steadystate/dwdx.cpp index 34b1f15eb..d447f2140 100644 --- a/deps/AMICI/models/model_steadystate/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) { +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) { dwdx[0] = x[0]*2.0; dwdx[1] = p[3]; } diff --git a/deps/AMICI/models/model_steadystate/model_steadystate_dxdotdp.cpp b/deps/AMICI/models/model_steadystate/dxdotdp.cpp similarity index 100% rename from deps/AMICI/models/model_steadystate/model_steadystate_dxdotdp.cpp rename to deps/AMICI/models/model_steadystate/dxdotdp.cpp diff --git a/deps/AMICI/models/model_steadystate/model_steadystate_dydx.cpp b/deps/AMICI/models/model_steadystate/dydx.cpp similarity index 100% rename from deps/AMICI/models/model_steadystate/model_steadystate_dydx.cpp rename to deps/AMICI/models/model_steadystate/dydx.cpp diff --git a/deps/AMICI/models/model_steadystate/model_steadystate.h b/deps/AMICI/models/model_steadystate/model_steadystate.h index c009c86ca..b61649f9c 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) 2bd39716102b9842354964196889d40d2eb2731c */ +/* Generated by amiwrap (R2017b) 223c5075608273b17f304556d0e8ccb41233bd21 */ #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); -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); +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 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); -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); +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 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); @@ -45,6 +45,7 @@ class Model_model_steadystate : public amici::Model_ODE { 0, 0, 0, + 0, 1, 2, 2, @@ -52,6 +53,9 @@ class Model_model_steadystate : public amici::Model_ODE { 0, 0, {}, + 0, + 0, + 0, 9, 2, 2 @@ -67,7 +71,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 "2bd39716102b9842354964196889d40d2eb2731c"; }; + std::string getAmiciCommit() const override { return "223c5075608273b17f304556d0e8ccb41233bd21"; }; 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); @@ -106,7 +110,7 @@ class Model_model_steadystate : public amici::Model_ODE { void fdeltaqB(double *deltaqB, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const int ip, const int ie, const realtype *xdot, const realtype *xdot_old, const realtype *xB) override { } - void fdeltasx(double *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) override { + void fdeltasx(double *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) override { } void fdeltax(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) override { @@ -121,18 +125,18 @@ class Model_model_steadystate : public amici::Model_ODE { void fdrzdx(double *drzdx, const int ie, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h) override { } - void fdsigmaydp(double *dsigmaydp, const realtype t, const realtype *p, const realtype *k, const int ip) override { + void fdsigmaydp(double *dsigmaydp, const realtype t, const realtype *p, const realtype *k, const realtype *y, const int ip) override { } 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) override { - dwdp_model_steadystate(dwdp, t, x, p, k, h, w, tcl, stcl); + 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 fdwdx(realtype *dwdx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl) override { - dwdx_model_steadystate(dwdx, t, x, p, k, h, w, tcl); + 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 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 { @@ -152,14 +156,14 @@ class Model_model_steadystate : public amici::Model_ODE { void fdzdx(double *dzdx, const int ie, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h) override { } - void froot(realtype *root, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h) override { + void froot(realtype *root, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl) override { } void frz(double *rz, const int ie, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h) override { } - void fsigmay(double *sigmay, const realtype t, const realtype *p, const realtype *k) override { - sigmay_model_steadystate(sigmay, t, p, k); + void fsigmay(double *sigmay, const realtype t, const realtype *p, const realtype *k, const realtype *y) override { + sigmay_model_steadystate(sigmay, t, p, k, y); } void fsigmaz(double *sigmaz, const realtype t, const realtype *p, const realtype *k) override { @@ -168,7 +172,7 @@ class Model_model_steadystate : public amici::Model_ODE { void fsrz(double *srz, 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 fstau(double *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) override { + void fstau(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) override { } void fsx0(realtype *sx0, const realtype t,const realtype *x0, const realtype *p, const realtype *k, const int ip) override { @@ -177,8 +181,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) override { - w_model_steadystate(w, t, x, p, k, h, tcl); + 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 fx0(realtype *x0, const realtype t, const realtype *p, const realtype *k) override { diff --git a/deps/AMICI/models/model_steadystate/model_steadystate_sigmay.cpp b/deps/AMICI/models/model_steadystate/sigmay.cpp similarity index 87% rename from deps/AMICI/models/model_steadystate/model_steadystate_sigmay.cpp rename to deps/AMICI/models/model_steadystate/sigmay.cpp index 8e9ebc441..d895acd6c 100644 --- a/deps/AMICI/models/model_steadystate/model_steadystate_sigmay.cpp +++ b/deps/AMICI/models/model_steadystate/sigmay.cpp @@ -10,7 +10,7 @@ namespace amici { namespace model_model_steadystate{ -void sigmay_model_steadystate(double *sigmay, const realtype t, const realtype *p, const realtype *k) { +void sigmay_model_steadystate(double *sigmay, const realtype t, const realtype *p, const realtype *k, const realtype *y) { sigmay[0] = 1.0; sigmay[1] = 1.0; sigmay[2] = 1.0; diff --git a/deps/AMICI/models/model_steadystate/swig/CMakeLists.txt b/deps/AMICI/models/model_steadystate/swig/CMakeLists.txt index af0f64a76..eb9fe681c 100644 --- a/deps/AMICI/models/model_steadystate/swig/CMakeLists.txt +++ b/deps/AMICI/models/model_steadystate/swig/CMakeLists.txt @@ -1,28 +1,19 @@ -cmake_minimum_required(VERSION 3.8) # swig_add_library - -if(POLICY CMP0078) - cmake_policy(SET CMP0078 OLD) -endif(POLICY CMP0078) -if(POLICY CMP0074) - # Use package_ROOT environment variables - cmake_policy(SET CMP0074 NEW) -endif(POLICY CMP0074) -if(POLICY CMP0086) - cmake_policy(SET CMP0086 NEW) -endif(POLICY CMP0086) +cmake_minimum_required(VERSION 3.15) +if(DEFINED ENV{SWIG}) + set(SWIG_EXECUTABLE $ENV{SWIG}) +endif() find_package(SWIG REQUIRED) include(${SWIG_USE_FILE}) -if(${CMAKE_VERSION} VERSION_LESS "3.12.0") - find_package(PythonLibs REQUIRED) - include_directories(${PYTHON_INCLUDE_DIRS}) - set(Python3_LIBRARIES ${PYTHON_LIBRARIES}) -else() - find_package (Python3 COMPONENTS Development) - include_directories(${Python3_INCLUDE_DIRS}) +if(DEFINED ENV{PYTHON_EXECUTABLE}) + set(Python3_EXECUTABLE $ENV{PYTHON_EXECUTABLE}) endif() +# We don't need "Interpreter" here, but without that, FindPython3 will +# ignore the Python version selected via $Python3_EXECUTABLE +find_package(Python3 COMPONENTS Interpreter Development) +include_directories(${Python3_INCLUDE_DIRS}) set(SWIG_LIBRARY_NAME _${PROJECT_NAME}) set(CMAKE_SWIG_FLAGS "") @@ -30,17 +21,38 @@ set_source_files_properties(${PROJECT_NAME}.i PROPERTIES CPLUSPLUS ON) # swig does not use INTERFACE_INCLUDE_DIRS of linked libraries, so add manually get_target_property(AMICI_INCLUDE_DIRS Upstream::amici INTERFACE_INCLUDE_DIRECTORIES) -include_directories(${AMICI_INCLUDE_DIRS} ..) +include_directories(${AMICI_INCLUDE_DIRS} .. ${AMICI_INCLUDE_DIRS}/../swig) -swig_add_library(${PROJECT_NAME} +swig_add_library(${SWIG_LIBRARY_NAME} TYPE MODULE LANGUAGE python SOURCES ${PROJECT_NAME}.i) -swig_link_libraries(${PROJECT_NAME} + +set_target_properties(${SWIG_LIBRARY_NAME} + PROPERTIES + SWIG_USE_TARGET_INCLUDE_DIRECTORIES TRUE + PREFIX "" +) + +# Python extension suffix +execute_process( + COMMAND ${Python3_EXECUTABLE} -c + "import sysconfig; print(sysconfig.get_config_var('EXT_SUFFIX'))" + OUTPUT_VARIABLE PY_EXT_SUFFIX OUTPUT_STRIP_TRAILING_WHITESPACE) +if(NOT "${PY_EXT_SUFFIX}" STREQUAL "") + message(STATUS "Python extension suffix is ${PY_EXT_SUFFIX}") + set_target_properties(${SWIG_LIBRARY_NAME} PROPERTIES SUFFIX "${PY_EXT_SUFFIX}" ) +endif() + + +swig_link_libraries(${SWIG_LIBRARY_NAME} ${Python3_LIBRARIES} model) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.py + $ DESTINATION .) + # configure module setup script set(SETUP_PY_IN ${Amici_DIR}/model_setup.template.py) set(SETUP_PY_OUT ${CMAKE_CURRENT_BINARY_DIR}/setup.py) diff --git a/deps/AMICI/models/model_steadystate/swig/model_steadystate.i b/deps/AMICI/models/model_steadystate/swig/model_steadystate.i index f8f6865da..e96f7db31 100644 --- a/deps/AMICI/models/model_steadystate/swig/model_steadystate.i +++ b/deps/AMICI/models/model_steadystate/swig/model_steadystate.i @@ -10,5 +10,13 @@ using namespace amici; %} +// Make model module accessible from the model +%feature("pythonappend") amici::generic_model::getModel %{ + if '.' in __name__: + import sys + val.module = sys.modules['.'.join(__name__.split('.')[:-1])] +%} + + // Process symbols in header %include "wrapfunctions.h" diff --git a/deps/AMICI/models/model_steadystate/model_steadystate_w.cpp b/deps/AMICI/models/model_steadystate/w.cpp similarity index 90% rename from deps/AMICI/models/model_steadystate/model_steadystate_w.cpp rename to deps/AMICI/models/model_steadystate/w.cpp index c968a93c4..948d4529c 100644 --- a/deps/AMICI/models/model_steadystate/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) { +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) { w[0] = p[3]*x[2]; w[1] = x[0]*x[0]; } diff --git a/deps/AMICI/models/model_steadystate/model_steadystate_x0.cpp b/deps/AMICI/models/model_steadystate/x0.cpp similarity index 100% rename from deps/AMICI/models/model_steadystate/model_steadystate_x0.cpp rename to deps/AMICI/models/model_steadystate/x0.cpp diff --git a/deps/AMICI/models/model_steadystate/model_steadystate_xdot.cpp b/deps/AMICI/models/model_steadystate/xdot.cpp similarity index 100% rename from deps/AMICI/models/model_steadystate/model_steadystate_xdot.cpp rename to deps/AMICI/models/model_steadystate/xdot.cpp diff --git a/deps/AMICI/models/model_steadystate/model_steadystate_y.cpp b/deps/AMICI/models/model_steadystate/y.cpp similarity index 100% rename from deps/AMICI/models/model_steadystate/model_steadystate_y.cpp rename to deps/AMICI/models/model_steadystate/y.cpp diff --git a/deps/AMICI/pytest.ini b/deps/AMICI/pytest.ini index 315ef3eef..3868c80b1 100644 --- a/deps/AMICI/pytest.ini +++ b/deps/AMICI/pytest.ini @@ -1,13 +1,23 @@ [pytest] -addopts = -vv +addopts = -vv --strict-markers filterwarnings = + error + # 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 + ignore:Conservation laws for non-constant species in combination with parameterized stoichiometric coefficients are not currently supported and will be turned off.:UserWarning + ignore:Support for PEtab2.0 is experimental!:UserWarning # hundreds of SBML <=5.17 warnings 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 norecursedirs = .git amici_models build doc documentation matlab models ThirdParty amici sdist examples diff --git a/deps/AMICI/python/CMakeLists.txt b/deps/AMICI/python/CMakeLists.txt index 203e3103e..e94a0a2a0 100644 --- a/deps/AMICI/python/CMakeLists.txt +++ b/deps/AMICI/python/CMakeLists.txt @@ -1,41 +1,31 @@ -if(${CMAKE_VERSION} VERSION_LESS "3.12.0") - if(DEFINED ENV{PYTHON_EXECUTABLE}) - set( PYTHON_EXECUTABLE $ENV{PYTHON_EXECUTABLE} ) - endif() - find_package(PythonInterp 3.7 REQUIRED) - set(Python3_EXECUTABLE ${PYTHON_EXECUTABLE}) -else() - find_package (Python3 COMPONENTS Interpreter) +if(DEFINED ENV{PYTHON_EXECUTABLE}) + set(Python3_EXECUTABLE $ENV{PYTHON_EXECUTABLE}) endif() - - -add_custom_target(install-python - COMMENT "Installing AMICI base python package" - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/sdist - COMMAND ${Python3_EXECUTABLE} setup.py install --prefix= --user) - - -# Create python wheel -# Note that we have to run build_ext explicitely before bdist_wheel, otherwise the swig-generated -# amici.py will not be added to the module build folder, because it does not yet exist at the -# build_py stage -add_custom_target(python-wheel - COMMENT "Creating wheel for AMICI base python package" - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/sdist - COMMAND ${Python3_EXECUTABLE} setup.py build_ext - COMMAND ${Python3_EXECUTABLE} setup.py bdist_wheel --dist-dir=${CMAKE_CURRENT_BINARY_DIR} -) - - -add_custom_target(python-sdist - COMMENT "Creating sdist for AMICI base python package" - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/sdist - COMMAND ${Python3_EXECUTABLE} -m pip install build - COMMAND ${Python3_EXECUTABLE} -m build --sdist --outdir=${CMAKE_CURRENT_BINARY_DIR} -) - - -add_custom_command( - OUTPUT always_rebuild - COMMAND cmake -E echo - ) +find_package(Python3 COMPONENTS Interpreter) + +add_custom_target( + install-python + COMMENT "Installing AMICI base python package" + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/sdist + COMMAND ${Python3_EXECUTABLE} setup.py install --prefix= --user) + +# Create python wheel Note that we have to run build_ext explicitely before +# bdist_wheel, otherwise the swig-generated amici.py will not be added to the +# module build folder, because it does not yet exist at the build_py stage +add_custom_target( + python-wheel + COMMENT "Creating wheel for AMICI base python package" + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/sdist + COMMAND ${Python3_EXECUTABLE} setup.py build_ext + COMMAND ${Python3_EXECUTABLE} setup.py bdist_wheel + --dist-dir=${CMAKE_CURRENT_BINARY_DIR}) + +add_custom_target( + python-sdist + COMMENT "Creating sdist for AMICI base python package" + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/sdist + COMMAND ${Python3_EXECUTABLE} -m pip install build + COMMAND ${Python3_EXECUTABLE} -m build --sdist + --outdir=${CMAKE_CURRENT_BINARY_DIR}) + +add_custom_command(OUTPUT always_rebuild COMMAND cmake -E echo) diff --git a/deps/AMICI/python/amici/MANIFEST.template.in b/deps/AMICI/python/amici/MANIFEST.template.in deleted file mode 100644 index eb3b1b450..000000000 --- a/deps/AMICI/python/amici/MANIFEST.template.in +++ /dev/null @@ -1 +0,0 @@ -include *.cpp *.h diff --git a/deps/AMICI/python/amici/__init__.py b/deps/AMICI/python/amici/__init__.py deleted file mode 100644 index 40d696642..000000000 --- a/deps/AMICI/python/amici/__init__.py +++ /dev/null @@ -1,390 +0,0 @@ -""" -AMICI ------ - -The AMICI Python module provides functionality for importing SBML or PySB -models and turning them into C++ Python extensions. - -:var amici_path: - absolute root path of the amici repository or Python package -:var amiciSwigPath: - absolute path of the amici swig directory -:var amiciSrcPath: - absolute path of the amici source directory -:var amiciModulePath: - absolute root path of the amici module -:var hdf5_enabled: - boolean indicating if amici was compiled with hdf5 support -:var has_clibs: - boolean indicating if this is the full package with swig interface or - the raw package without -""" - -import importlib -import os -import re -import sys -from contextlib import suppress, contextmanager -from types import ModuleType as ModelModule -from typing import Any, Dict, Optional, Union, Sequence, List - - -def _get_amici_path(): - """ - Determine package installation path, or, if used directly from git - repository, get repository root - """ - basedir = os.path.dirname(os.path.dirname(os.path.dirname(__file__))) - if os.path.exists(os.path.join(basedir, '.git')): - return os.path.abspath(basedir) - return os.path.dirname(__file__) - - -def _get_commit_hash(): - """Get commit hash from file""" - basedir = os.path.dirname(os.path.dirname(os.path.dirname(amici_path))) - commitfile = next( - ( - file for file in [ - os.path.join(basedir, '.git', 'FETCH_HEAD'), - os.path.join(basedir, '.git', 'ORIG_HEAD'), ] - if os.path.isfile(file) - ), - None - ) - - if commitfile: - with open(commitfile) as f: - return str(re.search(r'^([\w]*)', f.read().strip()).group()) - return 'unknown' - - -def _imported_from_setup() -> bool: - """Check whether this module is imported from `setup.py`""" - - from inspect import getouterframes, currentframe - - # in case we are imported from setup.py, this will be the AMICI package - # root directory (otherwise it is most likely the Python library directory, - # we are not interested in) - package_root = os.path.realpath(os.path.dirname(os.path.dirname(__file__))) - - for frame in getouterframes(currentframe(), context=0): - # Need to compare the full path, in case a user tries to import AMICI - # from a module `*setup.py`. Will still cause trouble if some package - # requires the AMICI extension during its installation, but seems - # unlikely... - frame_path = os.path.realpath(os.path.expanduser(frame.filename)) - if frame_path == os.path.join(package_root, 'setup.py'): - return True - - return False - - -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 - - -# Initialize AMICI paths -amici_path = _get_amici_path() -amiciSwigPath = os.path.join(amici_path, 'swig') -amiciSrcPath = os.path.join(amici_path, 'src') -amiciModulePath = os.path.dirname(__file__) - -has_clibs = any([os.path.isfile(os.path.join(amici_path, wrapper)) - for wrapper in ['amici.py', 'amici_without_hdf5.py']]) - -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]] - -# Get version number from file -with open(os.path.join(amici_path, 'version.txt')) as f: - __version__ = f.read().strip() - -__commit__ = _get_commit_hash() - -# Import SWIG module and swig-dependent submodules if required and available -if not _imported_from_setup(): - if has_clibs: - from . import amici - from .amici import * - - # These module require the swig interface and other dependencies - from .numpy import ReturnDataView, ExpDataView - from .pandas import * - - # These modules don't require the swig interface - from .sbml_import import SbmlImporter, assignmentRules2observables - from .ode_export import ODEModel, ODEExporter - - try: - # Requires Python>=3.8 - from typing import Protocol - - class ModelModule(Protocol): - """Enable Python static type checking for AMICI-generated model - modules""" - def getModel(self) -> amici.Model: - pass - except ImportError: - pass - -hdf5_enabled = 'readSolverSettingsFromHDF5' in dir() - - -def _get_ptr(obj: Union[AmiciModel, AmiciExpData, AmiciSolver, AmiciReturnData] - ) -> Union['amici.Model', 'amici.ExpData', 'amici.Solver', - 'amici.ReturnData']: - """ - Convenience wrapper that returns the smart pointer pointee, if applicable - - :param obj: - Potential smart pointer - - :returns: - Non-smart pointer - """ - if isinstance(obj, (amici.ModelPtr, amici.ExpDataPtr, amici.SolverPtr, - amici.ReturnDataPtr)): - return obj.get() - return obj - - -def runAmiciSimulation( - model: AmiciModel, - solver: AmiciSolver, - edata: Optional[AmiciExpData] = None -) -> 'numpy.ReturnDataView': - """ - Convenience wrapper around :py:func:`amici.amici.runAmiciSimulation` - (generated by swig) - - :param model: - Model instance - -` :param solver: - Solver instance, must be generated from - :py:meth:`amici.amici.Model.getSolver` - - :param edata: - ExpData instance (optional) - - :returns: - ReturnData object with simulation results - """ - with _capture_cstdout(): - rdata = amici.runAmiciSimulation(_get_ptr(solver), _get_ptr(edata), - _get_ptr(model)) - return numpy.ReturnDataView(rdata) - - -def ExpData(*args) -> 'amici.ExpData': - """ - Convenience wrapper for :py:class:`amici.amici.ExpData` constructors - - :param args: arguments - - :returns: ExpData Instance - """ - if isinstance(args[0], ReturnDataView): - return amici.ExpData(_get_ptr(args[0]['ptr']), *args[1:]) - elif isinstance(args[0], (amici.ExpData, amici.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.ExpData(_get_ptr(args[0]), *args[1:]) - elif isinstance(args[0], (amici.Model, amici.ModelPtr)): - return amici.ExpData(_get_ptr(args[0])) - else: - return amici.ExpData(*args) - - -def runAmiciSimulations( - model: AmiciModel, - solver: AmiciSolver, - edata_list: AmiciExpDataVector, - failfast: bool = True, - num_threads: int = 1, -) -> List['numpy.ReturnDataView']: - """ - Convenience wrapper for loops of amici.runAmiciSimulation - - :param model: Model instance - :param solver: Solver instance, must be generated from Model.getSolver() - :param edata_list: list of ExpData instances - :param failfast: returns as soon as an integration failure is encountered - :param num_threads: number of threads to use (only used if compiled - with openmp) - - :returns: list of simulation results - """ - with _capture_cstdout(): - edata_ptr_vector = amici.ExpDataPtrVector(edata_list) - rdata_ptr_list = amici.runAmiciSimulations(_get_ptr(solver), - edata_ptr_vector, - _get_ptr(model), - failfast, - num_threads) - return [numpy.ReturnDataView(r) for r in rdata_ptr_list] - - -def readSolverSettingsFromHDF5( - file: str, - solver: AmiciSolver, - location: Optional[str] = 'solverSettings' -) -> None: - """ - Convenience wrapper for :py:func:`amici.readSolverSettingsFromHDF5` - - :param file: hdf5 filename - :param solver: Solver instance to which settings will be transferred - :param location: location of solver settings in hdf5 file - """ - amici.readSolverSettingsFromHDF5(file, _get_ptr(solver), location) - - -def writeSolverSettingsToHDF5( - solver: AmiciSolver, - file: Union[str, object], - location: Optional[str] = 'solverSettings' -) -> None: - """ - Convenience wrapper for :py:func:`amici.amici.writeSolverSettingsToHDF5` - - :param file: hdf5 filename, can also be object created by - :py:func:`amici.amici.createOrOpenForWriting` - :param solver: Solver instance from which settings will stored - :param location: location of solver settings in hdf5 file - """ - amici.writeSolverSettingsToHDF5(_get_ptr(solver), file, location) - - -# Values are suffixes of `get[...]` and `set[...]` `amici.Model` methods. -# If either the getter or setter is not named with this pattern, then the value -# is a tuple where the first and second elements are the getter and setter -# methods, respectively. -model_instance_settings = [ - 'AddSigmaResiduals', - 'AlwaysCheckFinite', - 'FixedParameters', - 'InitialStates', - 'InitialStateSensitivities', - 'MinimumSigmaResiduals', - ('nMaxEvent', 'setNMaxEvent'), - 'Parameters', - 'ParameterList', - 'ParameterScale', # getter returns a SWIG object - 'ReinitializationStateIdxs', - 'ReinitializeFixedParameterInitialStates', - 'StateIsNonNegative', - 'SteadyStateSensitivityMode', - ('t0', 'setT0'), - 'Timepoints', -] - - -def get_model_settings( - model: AmiciModel, -) -> Dict[str, Any]: - """Get model settings that are set independently of the compiled model. - - :param model: The AMICI model instance. - - :returns: Keys are AMICI model attributes, values are attribute values. - """ - settings = {} - for setting in model_instance_settings: - getter = setting[0] if isinstance(setting, tuple) else f'get{setting}' - settings[setting] = getattr(model, getter)() - # TODO `amici.Model.getParameterScale` returns a SWIG object instead - # of a Python list/tuple. - if setting == 'ParameterScale': - settings[setting] = tuple(settings[setting]) - return settings - - -def set_model_settings( - model: AmiciModel, - settings: Dict[str, Any], -) -> None: - """Set model settings. - - :param model: The AMICI model instance. - :param settings: Keys are callable attributes (setters) of an AMICI model, - values are provided to the setters. - """ - for setting, value in settings.items(): - setter = setting[1] if isinstance(setting, tuple) else f'set{setting}' - getattr(model, setter)(value) - - -class add_path: - """Context manager for temporarily changing PYTHONPATH""" - - def __init__(self, path: str): - self.path: str = path - - def __enter__(self): - if self.path: - sys.path.insert(0, self.path) - - def __exit__(self, exc_type, exc_value, traceback): - try: - sys.path.remove(self.path) - except ValueError: - pass - - -def import_model_module(module_name: str, - module_path: Optional[str] = None) -> ModelModule: - """ - Import Python module of an AMICI model - - :param module_name: - Name of the python package of the model - :param module_path: - Absolute or relative path of the package directory - :return: - The model module - """ - - # ensure we will find the newly created module - importlib.invalidate_caches() - - if not os.path.isdir(module_path): - raise ValueError(f"module_path '{module_path}' is not a directory.") - - module_path = os.path.abspath(module_path) - - # module already loaded? - if module_name in sys.modules: - # if a module with that name is already in sys.modules, we remove it, - # along with all other modules from that package. otherwise, there - # will be trouble if two different models with the same name are to - # be imported. - del sys.modules[module_name] - # collect first, don't delete while iterating - to_unload = {loaded_module_name for loaded_module_name in - sys.modules.keys() if - loaded_module_name.startswith(f"{module_name}.")} - for m in to_unload: - del sys.modules[m] - - with add_path(module_path): - return importlib.import_module(module_name) diff --git a/deps/AMICI/python/amici/__init__.template.py b/deps/AMICI/python/amici/__init__.template.py deleted file mode 100644 index 5c42765ec..000000000 --- a/deps/AMICI/python/amici/__init__.template.py +++ /dev/null @@ -1,16 +0,0 @@ -"""AMICI-generated module for model TPL_MODELNAME""" - -import amici - -# Ensure we are binary-compatible, see #556 -if 'TPL_AMICI_VERSION' != amici.__version__: - raise RuntimeError('Cannot use model TPL_MODELNAME, generated with AMICI ' - 'version TPL_AMICI_VERSION, together with AMICI version' - f' {amici.__version__} which is present in your ' - 'PYTHONPATH. Install the AMICI package matching the ' - 'model version or regenerate the model with the AMICI ' - 'currently in your path.') - -from TPL_MODELNAME._TPL_MODELNAME import * - -__version__ = 'TPL_PACKAGE_VERSION' diff --git a/deps/AMICI/python/amici/__main__.py b/deps/AMICI/python/amici/__main__.py deleted file mode 100644 index dac523027..000000000 --- a/deps/AMICI/python/amici/__main__.py +++ /dev/null @@ -1,25 +0,0 @@ -"""Package-level entrypoint""" - -from . import __version__, compiledWithOpenMP, has_clibs, hdf5_enabled -import os -import sys - -def print_info(): - """Displays information on the current AMICI installation. - - Useful for verifying package installation of submitting bug reports""" - features = [] - - if has_clibs: - features.append("extensions") - - if compiledWithOpenMP(): - features.append("OpenMP") - - if hdf5_enabled: - features.append("HDF5") - - print(f"AMICI ({sys.platform}) version {__version__} ({','.join(features)})") - -if __name__ == '__main__': - print_info() diff --git a/deps/AMICI/python/amici/constants.py b/deps/AMICI/python/amici/constants.py deleted file mode 100644 index d772e9588..000000000 --- a/deps/AMICI/python/amici/constants.py +++ /dev/null @@ -1,29 +0,0 @@ -""" -Constants ------------ -This module provides a central place to define native python enums and -constants that are used in multiple other modules -""" - -import enum - - -class SymbolId(str, enum.Enum): - """ - Defines the different fields in the symbol dict to which sbml entities - get parsed to. - - .. note:: This class inherits from str enabling direct comparison to - strings, which means that the species symbols can be accessed as - symbols['species'], which is convenient for debugging and symbols[ - SymbolId.SPECIES], which is how the field should be accessed - programmatically. - """ - SPECIES = 'species' - PARAMETER = 'parameter' - FIXED_PARAMETER = 'fixed_parameter' - OBSERVABLE = 'observable' - EXPRESSION = 'expression' - SIGMAY = 'sigmay' - LLHY = 'llhy' - EVENT = 'event' diff --git a/deps/AMICI/python/amici/custom_commands.py b/deps/AMICI/python/amici/custom_commands.py deleted file mode 100644 index d4c25a348..000000000 --- a/deps/AMICI/python/amici/custom_commands.py +++ /dev/null @@ -1,328 +0,0 @@ -"""Custom setuptools commands for AMICI installation""" - -import glob -import os -import subprocess -import sys -from shutil import copyfile -from typing import Dict, List, Tuple - -from amici.swig import fix_typehints -from amici.setuptools import generate_swig_interface_files -from setuptools.command.build_clib import build_clib -from setuptools.command.build_ext import build_ext -from setuptools.command.develop import develop -from setuptools.command.install import install -from setuptools.command.install_lib import install_lib -from setuptools.command.sdist import sdist - -# typehints -Library = Tuple[str, Dict[str, List[str]]] - - -class AmiciInstall(install): - """Custom install 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 initialize_options(self): - install.initialize_options(self) - self.no_clibs = False - - def finalize_options(self): - if self.no_clibs: - self.no_clibs = True - install.finalize_options(self) - - -def compile_parallel(self, sources, output_dir=None, macros=None, - include_dirs=None, debug=0, extra_preargs=None, - extra_postargs=None, depends=None): - """Parallelized version of distutils.ccompiler.compile""" - - macros, objects, extra_postargs, pp_opts, build = \ - self._setup_compile(output_dir, macros, include_dirs, sources, - depends, extra_postargs) - cc_args = self._get_cc_args(pp_opts, debug, extra_preargs) - - # parallel compilation - num_threads = 1 - if 'AMICI_PARALLEL_COMPILE' in os.environ: - max_threads = int(os.environ['AMICI_PARALLEL_COMPILE']) - num_threads = min(len(objects), max_threads) - num_threads = max(1, num_threads) - - def _single_compile(obj): - try: - src, ext = build[obj] - except KeyError: - return - self._compile(obj, src, ext, cc_args, extra_postargs, pp_opts) - - if num_threads > 1: - import multiprocessing.pool - # convert to list, imap is evaluated on-demand - list(multiprocessing.pool.ThreadPool(num_threads).imap( - _single_compile, objects)) - else: - for obj in objects: - _single_compile(obj) - - return objects - - -class AmiciBuildCLib(build_clib): - """Custom build_clib""" - - def run(self): - print("running AmiciBuildCLib") - - # Always force recompilation. The way setuptools/distutils check for - # whether sources require recompilation is not reliable and may lead - # to crashes or wrong results. We rather compile once too often... - self.force = True - - build_clib.run(self) - - def build_libraries(self, libraries: List[Library]): - print("running AmiciBuildCLib.build_libraries") - - no_clibs = 'develop' in self.distribution.command_obj \ - and self.get_finalized_command('develop').no_clibs - no_clibs |= 'install' in self.distribution.command_obj \ - and self.get_finalized_command('install').no_clibs - - if no_clibs: - return - - # Override for parallel compilation - import distutils.ccompiler - distutils.ccompiler.CCompiler.compile = compile_parallel - - # Work-around for compiler-specific build options - set_compiler_specific_library_options( - libraries, self.compiler.compiler_type) - - # Monkey-patch setuptools, to force recompilation of library sources - # --force does not work as expected - - # need full import here, not module-level imported build_clib - import setuptools.command.build_clib - # the patched function may return anything but `([], [])` to trigger - # recompilation - setuptools.command.build_clib.newer_pairwise_group = lambda *_: None - - build_clib.build_libraries(self, libraries) - - -class AmiciDevelop(develop): - """Custom develop to build clibs""" - - # Passing --no-clibs allows to install the Python-only part of AMICI - user_options = develop.user_options + [ - ('no-clibs', None, "Don't build AMICI C++ extension"), - ] - - def initialize_options(self): - develop.initialize_options(self) - self.no_clibs = False - - def finalize_options(self): - if self.no_clibs: - self.no_clibs = True - develop.finalize_options(self) - - def run(self): - print("running AmiciDevelop") - - if not self.no_clibs: - self.get_finalized_command('build_clib').run() - - develop.run(self) - - -class AmiciInstallLib(install_lib): - """Custom install to allow preserving of debug symbols""" - - def run(self): - """strip debug symbols - - Returns: - - """ - print("running AmiciInstallLib") - - if 'ENABLE_AMICI_DEBUGGING' in os.environ \ - and os.environ['ENABLE_AMICI_DEBUGGING'] == 'TRUE' \ - and sys.platform == 'darwin': - search_dir = os.path.join(os.getcwd(), self.build_dir, 'amici') - for file in os.listdir(search_dir): - if file.endswith('.so'): - subprocess.run(['dsymutil', os.path.join(search_dir, file), - '-o', - os.path.join(search_dir, file + '.dSYM')]) - - # Continue with the actual installation - install_lib.run(self) - - -class AmiciBuildExt(build_ext): - """Custom build_ext to allow keeping otherwise temporary static libs""" - - def build_extension(self, ext): - # Work-around for compiler-specific build options - set_compiler_specific_extension_options( - ext, self.compiler.compiler_type) - - build_ext.build_extension(self, ext) - - def run(self): - """Copy the generated clibs to the extensions folder to be included in - the wheel - """ - - print("running AmiciBuildExt") - - no_clibs = 'develop' in self.distribution.command_obj \ - and self.get_finalized_command('develop').no_clibs - no_clibs |= 'install' in self.distribution.command_obj \ - and self.get_finalized_command('install').no_clibs - - if no_clibs: - # Nothing to build - return - - if not self.dry_run and self.distribution.has_c_libraries(): - # get the previously built static libraries - build_clib = self.get_finalized_command('build_clib') - libraries = build_clib.get_library_names() or [] - - # Module build directory where we want to copy the generated - # libs to - if self.inplace == 0: - build_dir = self.build_lib - else: - build_dir = os.getcwd() - target_dir = os.path.join(build_dir, 'amici', 'libs') - self.mkpath(target_dir) - - # Copy the generated libs - for lib in libraries: - libfilenames = glob.glob( - f"{build_clib.build_clib}{os.sep}*{lib}.*") - assert len(libfilenames) == 1, \ - f"Found unexpected number of files: {libfilenames}" - src = libfilenames[0] - dest = os.path.join(target_dir, os.path.basename(src)) - print(f"copying {src} -> {dest}") - copyfile(src, dest) - - swig_outdir = os.path.join(os.path.abspath(build_dir), "amici") - generate_swig_interface_files(swig_outdir=swig_outdir) - swig_py_module_path = os.path.join(swig_outdir, 'amici.py') - print("updating typehints") - fix_typehints(swig_py_module_path, swig_py_module_path) - - # Always force recompilation. The way setuptools/distutils check for - # whether sources require recompilation is not reliable and may lead - # to crashes or wrong results. We rather compile once too often... - self.force = True - - # Continue with the actual extension building - build_ext.run(self) - - -class AmiciSDist(sdist): - """Customized creation of source distribution""" - - def run(self): - """Setuptools entry-point""" - - print("running AmiciSDist") - - save_git_version() - - sdist.run(self) - - -def save_git_version(): - """Create file with extended version string - - This requires git. We assume that whoever creates the sdist will work - inside a valid git repository. - - Returns: - - """ - with open(os.path.join("amici", "git_version.txt"), "w") as f: - try: - cmd = ['git', 'describe', '--abbrev=4', '--dirty=-dirty', - '--always', '--tags'] - subprocess.run(cmd, stdout=f) - except Exception as e: - print(e) - - -def set_compiler_specific_library_options( - libraries: List[Library], - compiler_type: str) -> None: - """Set compiler-specific library options. - - C/C++-libraries for setuptools/distutils are provided as dict containing - entries for 'sources', 'macros', 'cflags', etc. - As we don't know the compiler type at the stage of calling - ``setuptools.setup`` and as there is no other apparent way to set - compiler-specific options, we elsewhere extend the dict with additional - fields ${original_field}_${compiler_class}, and add the additional - compiler-specific options here, at a stage when the compiler has been - determined by distutils. - - Arguments: - libraries: - List of libraries as passed as ``libraries`` argument to - ``setuptools.setup`` and ``setuptools.build_ext.build_extension``. - This is modified in place. - compiler_type: - Compiler type, as defined in - ``distutils.ccompiler.compiler.compiler_class``, (e.g. 'unix', - 'msvc', 'mingw32'). - """ - - for lib in libraries: - for field in ['cflags', 'sources', 'macros']: - try: - lib[1][field] += lib[1][f'{field}_{compiler_type}'] - print(f"Changed {field} for {lib[0]} with {compiler_type} " - f"to {lib[1][field]}") - except KeyError: - # No compiler-specific options set - pass - - -def set_compiler_specific_extension_options( - ext: 'setuptools.Extension', - compiler_type: str) -> None: - """Set compiler-specific extension build options. - - Same game as in ``set_compiler_specific_library_options``, except that - here we look for compiler-specific class attributes. - - Arguments: - ext: setuptools/distutils extension object - compiler_type: Compiler type - """ - for attr in ['extra_compile_args', 'extra_link_args']: - try: - new_value = getattr(ext, attr) + \ - getattr(ext, f'{attr}_{compiler_type}') - setattr(ext, attr, new_value) - print(f"Changed {attr} for {compiler_type} to {new_value}") - except AttributeError: - # No compiler-specific options set - pass - diff --git a/deps/AMICI/python/amici/cxxcodeprinter.py b/deps/AMICI/python/amici/cxxcodeprinter.py deleted file mode 100644 index 541101027..000000000 --- a/deps/AMICI/python/amici/cxxcodeprinter.py +++ /dev/null @@ -1,214 +0,0 @@ -"""C++ code generation""" -import re -from typing import List, Optional, Tuple, Dict - -import sympy as sp -from sympy.printing.cxx import CXX11CodePrinter - - -class AmiciCxxCodePrinter(CXX11CodePrinter): - """C++ code printer""" - - def __init__(self): - super().__init__() - - def doprint(self, expr: sp.Expr, assign_to: Optional[str] = None) -> str: - try: - code = super().doprint(expr, assign_to) - code = re.sub(r'(^|\W)M_PI(\W|$)', r'\1amici::pi\2', code) - - return code - except TypeError as e: - raise ValueError( - f'Encountered unsupported function in expression "{expr}": ' - f'{e}!' - ) - - def _get_sym_lines_array( - self, - equations: sp.Matrix, - variable: str, - indent_level: int - ) -> List[str]: - """ - Generate C++ code for assigning symbolic terms in symbols to C++ array - `variable`. - - :param equations: - vectors of symbolic expressions - - :param variable: - name of the C++ array to assign to - - :param indent_level: - indentation level (number of leading blanks) - - :return: - C++ code as list of lines - """ - return [ - ' ' * indent_level + f'{variable}[{index}] = ' - f'{self.doprint(math)};' - for index, math in enumerate(equations) - if math not in [0, 0.0] - ] - - def _get_sym_lines_symbols( - self, symbols: sp.Matrix, - equations: sp.Matrix, - variable: str, - indent_level: int - ) -> List[str]: - """ - Generate C++ code for where array elements are directly replaced with - their corresponding macro symbol - - :param symbols: - vectors of symbols that equations are assigned to - - :param equations: - vectors of expressions - - :param variable: - name of the C++ array to assign to, only used in comments - - :param indent_level: - indentation level (number of leading blanks) - - :return: - C++ code as list of lines - """ - return [ - f'{" " * indent_level}{sym} = {self.doprint(math)};' - f' // {variable}[{index}]'.replace('\n', - '\n' + ' ' * indent_level) - for index, (sym, math) in enumerate(zip(symbols, equations)) - 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{self.doprint(rownames[row])}' \ - f'_d{self.doprint(colnames[col])}' - 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 - - -def get_switch_statement(condition: str, cases: Dict[int, List[str]], - indentation_level: Optional[int] = 0, - indentation_step: Optional[str] = ' ' * 4): - """ - Generate code for switch statement - - :param condition: - Condition for switch - - :param cases: - Cases as dict with expressions as keys and statement as - list of strings - - :param indentation_level: - indentation level - - :param indentation_step: - indentation whitespace per level - - :return: - Code for switch expression as list of strings - - """ - lines = [] - - if not cases: - return lines - - for expression, statements in cases.items(): - if statements: - lines.append((indentation_level + 1) * indentation_step - + f'case {expression}:') - for statement in statements: - lines.append((indentation_level + 2) * indentation_step - + statement) - lines.append((indentation_level + 2) * indentation_step + 'break;') - - if lines: - lines.insert(0, indentation_level * indentation_step - + f'switch({condition}) {{') - lines.append(indentation_level * indentation_step + '}') - - return lines - diff --git a/deps/AMICI/python/amici/gradient_check.py b/deps/AMICI/python/amici/gradient_check.py deleted file mode 100644 index b4225a7ea..000000000 --- a/deps/AMICI/python/amici/gradient_check.py +++ /dev/null @@ -1,303 +0,0 @@ -""" -Finite Difference Check ------------------------ -This module provides functions to automatically check correctness of amici -computed sensitivities using finite difference approximations -""" - -from . import ( - runAmiciSimulation, SensitivityOrder, AMICI_SUCCESS, SensitivityMethod, - Model, Solver, ExpData, ReturnData, ParameterScaling) -import numpy as np -import copy - -from typing import Callable, Optional, List, Sequence - - -def check_finite_difference( - x0: Sequence[float], - model: Model, - solver: Solver, - edata: ExpData, - ip: int, - fields: List[str], - atol: Optional[float] = 1e-4, - rtol: Optional[float] = 1e-4, - epsilon: Optional[float] = 1e-3 -) -> None: - """ - Checks the computed sensitivity based derivatives against a finite - difference approximation. - - :param x0: - parameter value at which to check finite difference approximation - - :param model: - amici model - - :param solver: - amici solver - - :param edata: - exp data - - :param ip: - parameter index - - :param fields: - rdata fields for which to check the gradient - - :param atol: - absolute tolerance for comparison - - :param rtol: - relative tolerance for comparison - - :param epsilon: - finite difference step-size - - """ - og_sensitivity_order = solver.getSensitivityOrder() - og_parameters = model.getParameters() - og_plist = model.getParameterList() - if edata: - og_eplist = edata.plist - - # sensitivity - p = copy.deepcopy(x0) - plist = [ip] - - model.setParameters(p) - model.setParameterList(plist) - if edata: - edata.plist = plist - - # simulation with gradient - if int(og_sensitivity_order) < int(SensitivityOrder.first): - solver.setSensitivityOrder(SensitivityOrder.first) - rdata = runAmiciSimulation(model, solver, edata) - if rdata['status'] != AMICI_SUCCESS: - raise AssertionError(f"Simulation failed (status {rdata['status']}") - - # finite difference - solver.setSensitivityOrder(SensitivityOrder.none) - - pf = copy.deepcopy(x0) - pb = copy.deepcopy(x0) - pscale = model.getParameterScale()[ip] - if x0[ip] == 0 or pscale != int(ParameterScaling.none): - pf[ip] += epsilon / 2 - pb[ip] -= epsilon / 2 - else: - pf[ip] *= 1 + epsilon / 2 - pb[ip] /= 1 + epsilon / 2 - - # forward: - model.setParameters(pf) - rdataf = runAmiciSimulation(model, solver, edata) - if rdataf['status'] != AMICI_SUCCESS: - raise AssertionError(f"Simulation failed (status {rdataf['status']}") - - # backward: - model.setParameters(pb) - rdatab = runAmiciSimulation(model, solver, edata) - if rdatab['status'] != AMICI_SUCCESS: - raise AssertionError(f"Simulation failed (status {rdatab['status']}") - - for field in fields: - sensi_raw = rdata[f's{field}'] - fd = (rdataf[field] - rdatab[field]) / (pf[ip] - pb[ip]) - if len(sensi_raw.shape) == 1: - sensi = sensi_raw[0] - elif len(sensi_raw.shape) == 2: - sensi = sensi_raw[:, 0] - elif len(sensi_raw.shape) == 3: - sensi = sensi_raw[:, 0, :] - else: - raise NotImplementedError() - - _check_close(sensi, fd, atol=atol, rtol=rtol, field=field, ip=ip) - - solver.setSensitivityOrder(og_sensitivity_order) - model.setParameters(og_parameters) - model.setParameterList(og_plist) - if edata: - edata.plist = og_eplist - - -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, - check_least_squares: bool = True, - skip_zero_pars: bool = False -) -> None: - """ - Finite differences check for likelihood gradient. - - :param model: - amici model - - :param solver: - amici solver - - :param edata: - exp data - - :param atol: - absolute tolerance for comparison - - :param rtol: - relative tolerance for comparison - - :param epsilon: - finite difference step-size - - :param check_least_squares: - whether to check least squares related values. - - :param skip_zero_pars: - whether to perform FD checks for parameters that are zero - - """ - p = np.array(model.getParameters()) - - og_sens_order = solver.getSensitivityOrder() - - if int(og_sens_order) < int(SensitivityOrder.first): - solver.setSensitivityOrder(SensitivityOrder.first) - rdata = runAmiciSimulation(model, solver, edata) - solver.setSensitivityOrder(og_sens_order) - - if rdata['status'] != AMICI_SUCCESS: - raise AssertionError(f"Simulation failed (status {rdata['status']}") - - fields = [] - - if solver.getSensitivityMethod() == SensitivityMethod.forward and \ - solver.getSensitivityOrder() <= SensitivityOrder.first: - fields.append('x') - - leastsquares_applicable = \ - solver.getSensitivityMethod() == SensitivityMethod.forward \ - and edata is not None - - if 'ssigmay' in rdata.keys() \ - and rdata['ssigmay'] is not None \ - and rdata['ssigmay'].any() and not model.getAddSigmaResiduals(): - leastsquares_applicable = False - - if check_least_squares and leastsquares_applicable: - fields += ['res', 'y'] - - _check_results(rdata, 'FIM', np.dot(rdata['sres'].T, rdata['sres']), - atol=1e-8, rtol=1e-4) - _check_results(rdata, 'sllh', -np.dot(rdata['res'].T, rdata['sres']), - atol=1e-8, rtol=1e-4) - - if edata is not None: - fields.append('llh') - - for ip, pval in enumerate(p): - if pval == 0.0 and skip_zero_pars: - continue - check_finite_difference(p, model, solver, edata, ip, fields, - atol=atol, rtol=rtol, epsilon=epsilon) - - -def _check_close( - result: np.array, - expected: np.array, - atol: float, - rtol: float, - field: str, - ip: Optional[int] = None, - verbose: Optional[bool] = True, -) -> None: - """ - Compares computed values against expected values and provides rich - output information. - - :param result: - computed values - - :param expected: - expected values - - :param field: - rdata field for which the gradient is checked, only for error reporting - - :param atol: - absolute tolerance for comparison - - :param rtol: - relative tolerance for comparison - - :param ip: - parameter index, for more informative output - - :param verbose: - produce a more verbose error message in case of unmatched expectations - """ - close = np.isclose(result, expected, atol=atol, rtol=rtol, equal_nan=True) - if close.all(): - return - - if ip is None: - index_str = '' - check_type = 'Regression check' - else: - index_str = f'at index ip={ip} ' - check_type = 'FD check' - - lines = [f'{check_type} failed for {field} {index_str}for ' - f'{close.size - close.sum()} indices:'] - if verbose: - for idx in np.argwhere(~close): - idx = tuple(idx) - lines.append( - f"\tat {idx}: Expected {expected[idx]}, got {result[idx]}") - adev = np.abs(result - expected) - rdev = np.abs((result - expected) / (expected + atol)) - lines.append(f'max(adev): {adev.max()}, max(rdev): {rdev.max()}') - - raise AssertionError("\n".join(lines)) - - -def _check_results( - rdata: ReturnData, - field: str, - expected: np.array, - atol: float, - rtol: float - ) -> None: - """ - Checks whether rdata[field] agrees with expected according to provided - tolerances. - - :param rdata: - simulation results as returned by - :meth:`amici.amici.runAmiciSimulation` - - :param field: - name of the field to check - - :param expected: - expected values - - :param atol: - absolute tolerance for comparison - - :param rtol: - relative tolerance for comparison - """ - - result = rdata[field] - if type(result) is float: - result = np.array(result) - - _check_close(result=result, expected=expected, - atol=atol, rtol=rtol, field=field) diff --git a/deps/AMICI/python/amici/import_utils.py b/deps/AMICI/python/amici/import_utils.py deleted file mode 100644 index bf1a3947b..000000000 --- a/deps/AMICI/python/amici/import_utils.py +++ /dev/null @@ -1,556 +0,0 @@ -"""Miscellaneous functions related to model import, independent of any specific - model format""" - -from typing import ( - Dict, Union, Optional, Callable, Sequence, Tuple, Iterable, Any -) -import sympy as sp -import enum -import itertools as itt - -from toposort import toposort -from sympy.logic.boolalg import BooleanAtom -from sympy.functions.elementary.piecewise import ExprCondPair - -SymbolDef = Dict[sp.Symbol, Union[Dict[str, sp.Expr], sp.Expr]] - - -class ObservableTransformation(str, enum.Enum): - """ - Different modes of observable transformation. - """ - LOG10 = 'log10' - LOG = 'log' - LIN = 'lin' - - -def noise_distribution_to_observable_transformation( - noise_distribution: Union[str, Callable] -) -> ObservableTransformation: - """ - Parse noise distribution string and extract observable transformation - - :param noise_distribution: - see :func:`noise_distribution_to_cost_function` - - :return: - observable transformation - """ - if isinstance(noise_distribution, str): - if noise_distribution.startswith('log-'): - return ObservableTransformation.LOG - if noise_distribution.startswith('log10-'): - return ObservableTransformation.LOG10 - - return ObservableTransformation.LIN - - -def noise_distribution_to_cost_function( - noise_distribution: Union[str, Callable] -) -> Callable[[str], str]: - """ - Parse noise distribution string to a cost function definition amici can - work with. - - The noise distributions listed in the following are supported. :math:`m` - denotes the measurement, :math:`y` the simulation, and :math:`\\sigma` a - distribution scale parameter - (currently, AMICI only supports a single distribution parameter). - - - `'normal'`, `'lin-normal'`: A normal distribution: - - .. math:: - \\pi(m|y,\\sigma) = \\frac{1}{\\sqrt{2\\pi}\\sigma}\\ - exp\\left(-\\frac{(m-y)^2}{2\\sigma^2}\\right) - - - `'log-normal'`: A log-normal distribution (i.e. log(m) is - normally distributed): - - .. math:: - \\pi(m|y,\\sigma) = \\frac{1}{\\sqrt{2\\pi}\\sigma m}\\ - exp\\left(-\\frac{(\\log m - \\log y)^2}{2\\sigma^2}\\right) - - - `'log10-normal'`: A log10-normal distribution (i.e. log10(m) is - normally distributed): - - .. math:: - \\pi(m|y,\\sigma) = \\frac{1}{\\sqrt{2\\pi}\\sigma m \\log(10)}\\ - exp\\left(-\\frac{(\\log_{10} m - \\log_{10} y)^2}{2\\sigma^2}\\right) - - - `'laplace'`, `'lin-laplace'`: A laplace distribution: - - .. math:: - \\pi(m|y,\\sigma) = \\frac{1}{2\\sigma} - \\exp\\left(-\\frac{|m-y|}{\\sigma}\\right) - - - `'log-laplace'`: A log-Laplace distribution (i.e. log(m) is Laplace - distributed): - - .. math:: - \\pi(m|y,\\sigma) = \\frac{1}{2\\sigma m} - \\exp\\left(-\\frac{|\\log m - \\log y|}{\\sigma}\\right) - - - `'log10-laplace'`: A log10-Laplace distribution (i.e. log10(m) is - Laplace distributed): - - .. math:: - \\pi(m|y,\\sigma) = \\frac{1}{2\\sigma m \\log(10)} - \\exp\\left(-\\frac{|\\log_{10} m - \\log_{10} y|}{\\sigma}\\right) - - - `'binomial'`, `'lin-binomial'`: A (continuation of a discrete) binomial - distribution, parameterized via the success probability - :math:`p=\\sigma`: - - .. math:: - \\pi(m|y,\\sigma) = \\operatorname{Heaviside}(y-m) \\cdot - \\frac{\\Gamma(y+1)}{\\Gamma(m+1) \\Gamma(y-m+1)} - \\sigma^m (1-\\sigma)^{(y-m)} - - - `'negative-binomial'`, `'lin-negative-binomial'`: A (continuation of a - discrete) negative binomial distribution, with with `mean = y`, - parameterized via success probability `p`: - - .. math:: - - \\pi(m|y,\\sigma) = \\frac{\\Gamma(m+r)}{\\Gamma(m+1) \\Gamma(r)} - (1-\\sigma)^m \\sigma^r - - where - - .. math:: - r = \\frac{1-\\sigma}{\\sigma} y - - The distributions above are for a single data point. - For a collection :math:`D=\\{m_i\\}_i` of data points and corresponding - simulations :math:`Y=\\{y_i\\}_i` and noise parameters - :math:`\\Sigma=\\{\\sigma_i\\}_i`, AMICI assumes independence, - i.e. the full distributions is - - .. math:: - \\pi(D|Y,\\Sigma) = \\prod_i\\pi(m_i|y_i,\\sigma_i) - - AMICI uses the logarithm :math:`\\log(\\pi(m|y,\\sigma)`. - - In addition to the above mentioned distributions, it is also possible to - pass a function taking a symbol string and returning a log-distribution - string with variables '{str_symbol}', 'm{str_symbol}', 'sigma{str_symbol}' - for y, m, sigma, respectively. - - :param noise_distribution: An identifier specifying a noise model. - Possible values are - - {`'normal'`, `'lin-normal'`, `'log-normal'`, `'log10-normal'`, - `'laplace'`, `'lin-laplace'`, `'log-laplace'`, `'log10-laplace'`, - `'binomial'`, `'lin-binomial'`, `'negative-binomial'`, - `'lin-negative-binomial'`, ``} - - For the meaning of the values see above. - - :return: A function that takes a strSymbol and then creates a cost - function string (negative log-likelihood) from it, which can be - sympified. - """ - - if isinstance(noise_distribution, Callable): - return noise_distribution - - if noise_distribution in ['normal', 'lin-normal']: - y_string = '0.5*log(2*pi*{sigma}**2) + 0.5*(({y} - {m}) / {sigma})**2' - elif noise_distribution == 'log-normal': - y_string = '0.5*log(2*pi*{sigma}**2*{m}**2) ' \ - '+ 0.5*((log({y}) - log({m})) / {sigma})**2' - elif noise_distribution == 'log10-normal': - y_string = '0.5*log(2*pi*{sigma}**2*{m}**2*log(10)**2) ' \ - '+ 0.5*((log({y}, 10) - log({m}, 10)) / {sigma})**2' - elif noise_distribution in ['laplace', 'lin-laplace']: - y_string = 'log(2*{sigma}) + Abs({y} - {m}) / {sigma}' - elif noise_distribution == 'log-laplace': - y_string = 'log(2*{sigma}*{m}) + Abs(log({y}) - log({m})) / {sigma}' - elif noise_distribution == 'log10-laplace': - y_string = 'log(2*{sigma}*{m}*log(10)) ' \ - '+ Abs(log({y}, 10) - log({m}, 10)) / {sigma}' - elif noise_distribution in ['binomial', 'lin-binomial']: - # Binomial noise model parameterized via success probability p - y_string = '- log(Heaviside({y} - {m})) - loggamma({y}+1) ' \ - '+ loggamma({m}+1) + loggamma({y}-{m}+1) ' \ - '- {m} * log({sigma}) - ({y} - {m}) * log(1-{sigma})' - elif noise_distribution in ['negative-binomial', 'lin-negative-binomial']: - # Negative binomial noise model of the number of successes m - # (data) before r=(1-sigma)/sigma * y failures occur, - # with mean number of successes y (simulation), - # parameterized via success probability p = sigma. - r = '{y} * (1-{sigma}) / {sigma}' - y_string = f'- loggamma({{m}}+{r}) + loggamma({{m}}+1) ' \ - f'+ loggamma({r}) - {r} * log(1-{{sigma}}) ' \ - f'- {{m}} * log({{sigma}})' - else: - raise ValueError( - f"Cost identifier {noise_distribution} not recognized.") - - def nllh_y_string(str_symbol): - y, m, sigma = _get_str_symbol_identifiers(str_symbol) - return y_string.format(y=y, m=m, sigma=sigma) - - return nllh_y_string - - -def _get_str_symbol_identifiers(str_symbol: str) -> tuple: - """Get identifiers for simulation, measurement, and sigma.""" - y, m, sigma = f"{str_symbol}", f"m{str_symbol}", f"sigma{str_symbol}" - return y, m, sigma - - -def smart_subs_dict(sym: sp.Expr, - subs: SymbolDef, - field: Optional[str] = None, - reverse: bool = True) -> sp.Expr: - """ - Subsitutes expressions completely flattening them out. Requires - sorting of expressions with toposort. - - :param sym: - Symbolic expression in which expressions will be substituted - - :param subs: - Substitutions - - :param field: - Field of substitution expressions in subs.values(), if applicable - - :param reverse: - Whether ordering in subs should be reversed. Note that substitution - requires the reverse order of what is required for evaluation. - - :return: - Substituted symbolic expression - """ - s = [ - (eid, expr[field] if field is not None else expr) - for eid, expr in subs.items() - ] - if reverse: - s.reverse() - for substitution in s: - # note that substitution may change free symbols, so we have to do - # this recursively - if substitution[0] in sym.free_symbols: - sym = sym.subs(*substitution) - return sym - - -def smart_subs(element: sp.Expr, old: sp.Symbol, new: sp.Expr) -> sp.Expr: - """ - Optimized substitution that checks whether anything needs to be done first - - :param element: - substitution target - - :param old: - to be substituted - - :param new: - subsitution value - - :return: - substituted expression - """ - - if old in element.free_symbols: - return element.subs(old, new) - - return element - - -def toposort_symbols(symbols: SymbolDef, - field: Optional[str] = None) -> SymbolDef: - """ - Topologically sort symbol definitions according to their interdependency - - :param symbols: - symbol definitions - - :param field: - field of definition.values() that is used to compute interdependency - - :return: - ordered symbol definitions - """ - sorted_symbols = toposort({ - identifier: { - s for s in ( - definition[field] if field is not None else definition - ).free_symbols - if s in symbols - } - for identifier, definition - in symbols.items() - }) - return { - s: symbols[s] - for symbol_group in sorted_symbols - for s in symbol_group - } - - -def _parse_special_functions(sym: sp.Expr, toplevel: bool = True) -> sp.Expr: - """ - Recursively checks the symbolic expression for functions which have be - to parsed in a special way, such as piecewise functions - - :param sym: - symbolic expressions - - :param toplevel: - as this is called recursively, are we in the top level expression? - """ - args = tuple(arg if arg.__class__.__name__ == 'piecewise' - and sym.__class__.__name__ == 'piecewise' - else _parse_special_functions(arg, False) - for arg in sym.args) - - fun_mappings = { - 'times': sp.Mul, - 'xor': sp.Xor, - 'abs': sp.Abs, - 'min': sp.Min, - 'max': sp.Max, - 'ceil': sp.functions.ceiling, - 'floor': sp.functions.floor, - 'factorial': sp.functions.factorial, - 'arcsin': sp.functions.asin, - 'arccos': sp.functions.acos, - 'arctan': sp.functions.atan, - 'arccot': sp.functions.acot, - 'arcsec': sp.functions.asec, - 'arccsc': sp.functions.acsc, - 'arcsinh': sp.functions.asinh, - 'arccosh': sp.functions.acosh, - 'arctanh': sp.functions.atanh, - 'arccoth': sp.functions.acoth, - 'arcsech': sp.functions.asech, - 'arccsch': sp.functions.acsch, - } - - if sym.__class__.__name__ in fun_mappings: - # c++ doesnt like mixing int and double for arguments of those - # functions - if sym.__class__.__name__ in ['min', 'max']: - args = tuple([ - sp.Float(arg) if arg.is_number else arg - for arg in sym.args - ]) - else: - args = sym.args - return fun_mappings[sym.__class__.__name__](*args) - - elif sym.__class__.__name__ == 'piecewise' \ - or isinstance(sym, sp.Piecewise): - if isinstance(sym, sp.Piecewise): - # this is sympy piecewise, can't be nested - denested_args = args - else: - # this is sbml piecewise, can be nested - denested_args = _denest_piecewise(args) - return _parse_piecewise_to_heaviside(denested_args) - - if sym.__class__.__name__ == 'plus' and not sym.args: - return sp.Float(0.0) - - if isinstance(sym, (sp.Function, sp.Mul, sp.Add)): - sym._args = args - - elif toplevel and isinstance(sym, BooleanAtom): - # Replace boolean constants by numbers so they can be differentiated - # must not replace in Piecewise function. Therefore, we only replace - # it the complete expression consists only of a Boolean value. - sym = sp.Float(int(bool(sym))) - - return sym - - -def _denest_piecewise( - args: Sequence[Union[sp.Expr, sp.logic.boolalg.Boolean, bool]] -) -> Tuple[Union[sp.Expr, sp.logic.boolalg.Boolean, bool]]: - """ - Denest piecewise functions that contain piecewise as condition - - :param args: - Arguments to the piecewise function - - :return: - Arguments where conditions no longer contain piecewise functions and - the conditional dependency is flattened out - """ - args_out = [] - for coeff, cond in grouper(args, 2, True): - # handling of this case is explicitely disabled in - # _parse_special_functions as keeping track of coeff/cond - # arguments is tricky. Simpler to just parse them out here - if coeff.__class__.__name__ == 'piecewise': - coeff = _parse_special_functions(coeff, False) - - # we can have conditions that are piecewise function - # returning True or False - if cond.__class__.__name__ == 'piecewise': - # this keeps track of conditional that the previous - # piece was picked - previous_was_picked = sp.false - # recursively denest those first - for sub_coeff, sub_cond in grouper( - _denest_piecewise(cond.args), 2, True - ): - # flatten the individual pieces - pick_this = sp.And( - sp.Not(previous_was_picked), sub_cond - ) - if sub_coeff == sp.true: - args_out.extend([coeff, pick_this]) - previous_was_picked = pick_this - - else: - args_out.extend([coeff, cond]) - # cut off last condition as that's the default - return tuple(args_out[:-1]) - - -def _parse_piecewise_to_heaviside(args: Iterable[sp.Expr]) -> sp.Expr: - """ - Piecewise functions cannot be transformed into C++ right away, but AMICI - has a special interface for Heaviside functions, so we transform them. - - :param args: - symbolic expressions for arguments of the piecewise function - """ - # how many condition-expression pairs will we have? - formula = sp.Float(0.0) - not_condition = sp.Float(1.0) - - if all(isinstance(arg, ExprCondPair) for arg in args): - # sympy piecewise - grouped_args = args - else: - # smbl piecewise - grouped_args = grouper(args, 2, True) - - for coeff, trigger in grouped_args: - if isinstance(coeff, BooleanAtom): - coeff = sp.Float(int(bool(coeff))) - - if trigger == sp.true: - return formula + coeff * not_condition - - if trigger == sp.false: - continue - - tmp = _parse_heaviside_trigger(trigger) - formula += coeff * sp.simplify(not_condition * tmp) - not_condition *= (1-tmp) - - return formula - - -def _parse_heaviside_trigger(trigger: sp.Expr) -> sp.Expr: - """ - Recursively translates a boolean trigger function into a real valued - root function - - :param trigger: - :return: real valued root function expression - """ - if trigger.is_Relational: - root = trigger.args[0] - trigger.args[1] - _check_unsupported_functions(root, 'sympy.Expression') - - # normalize such that we always implement <, - # this ensures that we can correctly evaluate the condition if - # simulation starts at H(0). This is achieved by translating - # conditionals into Heaviside functions H that is implemented as unit - # step with H(0) = 1 - if isinstance(trigger, sp.core.relational.StrictLessThan): - # x < y => x - y < 0 => r < 0 - return 1 - sp.Heaviside(root) - if isinstance(trigger, sp.core.relational.LessThan): - # x <= y => not(y < x) => not(y - x < 0) => not -r < 0 - return sp.Heaviside(-root) - if isinstance(trigger, sp.core.relational.StrictGreaterThan): - # y > x => y - x < 0 => -r < 0 - return 1 - sp.Heaviside(-root) - if isinstance(trigger, sp.core.relational.GreaterThan): - # y >= x => not(x < y) => not(x - y < 0) => not r < 0 - return sp.Heaviside(root) - - # or(x,y) = not(and(not(x),not(y)) - if isinstance(trigger, sp.Or): - return 1-sp.Mul(*[1-_parse_heaviside_trigger(arg) - for arg in trigger.args]) - - if isinstance(trigger, sp.And): - return sp.Mul(*[_parse_heaviside_trigger(arg) - for arg in trigger.args]) - - raise RuntimeError( - 'AMICI can not parse piecewise/event trigger functions with argument ' - f'{trigger}.' - ) - - -def grouper(iterable: Iterable, n: int, - fillvalue: Any = None) -> Iterable[Tuple[Any]]: - """ - Collect data into fixed-length chunks or blocks - - grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx" - - :param iterable: - any iterable - - :param n: - chunk length - - :param fillvalue: - padding for last chunk if length < n - - :return: itertools.zip_longest of requested chunks - """ - args = [iter(iterable)] * n - return itt.zip_longest(*args, fillvalue=fillvalue) - - -def _check_unsupported_functions(sym: sp.Expr, - expression_type: str, - full_sym: Optional[sp.Expr] = None): - """ - Recursively checks the symbolic expression for unsupported symbolic - functions - - :param sym: - symbolic expressions - - :param expression_type: - type of expression, only used when throwing errors - - :param full sym: - outermost symbolic expression in recursive checks, only used for errors - """ - if full_sym is None: - full_sym = sym - - # note that sp.functions.factorial, sp.functions.ceiling, - # sp.functions.floor applied to numbers should be simplified out and - # thus pass this test - unsupported_functions = ( - sp.functions.factorial, sp.functions.ceiling, sp.functions.floor, - sp.functions.sec, sp.functions.csc, sp.functions.cot, - sp.functions.asec, sp.functions.acsc, sp.functions.acot, - sp.functions.acsch, sp.functions.acoth, - sp.Mod, sp.core.function.UndefinedFunction - ) - - if isinstance(sym.func, unsupported_functions) \ - or isinstance(sym, unsupported_functions): - raise RuntimeError(f'Encountered unsupported expression ' - f'"{sym.func}" of type ' - f'"{type(sym.func)}" as part of a ' - f'{expression_type}: "{full_sym}"!') - for arg in list(sym.args): - _check_unsupported_functions(arg, expression_type) diff --git a/deps/AMICI/python/amici/logging.py b/deps/AMICI/python/amici/logging.py deleted file mode 100644 index eae753b29..000000000 --- a/deps/AMICI/python/amici/logging.py +++ /dev/null @@ -1,202 +0,0 @@ -""" -Logging -------- -This module provides custom logging functionality for other amici modules -""" - -import logging -import platform -import socket -import amici -import os -import warnings -import time -import functools - -from inspect import getouterframes, currentframe - -LOG_LEVEL_ENV_VAR = 'AMICI_LOG' -BASE_LOGGER_NAME = 'amici' -# Supported values for LOG_LEVEL_ENV_VAR -NAMED_LOG_LEVELS = {'NOTSET': logging.NOTSET, - 'DEBUG': logging.DEBUG, - 'INFO': logging.INFO, - 'WARNING': logging.WARNING, - 'ERROR': logging.ERROR, - 'CRITICAL': logging.CRITICAL} - -from typing import Optional, Callable, Union - - -def _setup_logger(level: Optional[int] = logging.WARNING, - console_output: Optional[bool] = True, - file_output: Optional[bool] = False, - capture_warnings: Optional[bool] = True) -> logging.Logger: - """ - Set up a new logging.Logger for AMICI logging - - :param level: - Logging level, typically using a constant like logging.INFO or - logging.DEBUG - - :param console_output: - 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) - - :param capture_warnings: - Capture warnings from Python's warnings module if True (default) - - :return: - A :class:`logging.Logger` object for AMICI logging. Note that other - AMICI modules - should use a logger specific to their namespace instead by calling - :func:`get_logger`. - """ - log = logging.getLogger(BASE_LOGGER_NAME) - - # Logging level can be overridden with environment variable - if LOG_LEVEL_ENV_VAR in os.environ: - try: - level = int(os.environ[LOG_LEVEL_ENV_VAR]) - except ValueError: - # Try parsing as a name - level_name = os.environ[LOG_LEVEL_ENV_VAR] - if level_name in NAMED_LOG_LEVELS.keys(): - level = NAMED_LOG_LEVELS[level_name] - else: - raise ValueError(f'Environment variable {LOG_LEVEL_ENV_VAR} ' - f'contains an invalid value "{level_name}".' - f' If set, its value must be one of ' - f'{", ".join(NAMED_LOG_LEVELS.keys())}' - f' (case-sensitive) or an integer log level.') - - log.setLevel(level) - - # Remove default logging handler - log.handlers = [] - - log_fmt = logging.Formatter('%(asctime)s.%(msecs).3d - %(name)s - ' - '%(levelname)s - %(message)s', - datefmt='%Y-%m-%d %H:%M:%S') - - if console_output: - stream_handler = logging.StreamHandler() - stream_handler.setFormatter(log_fmt) - log.addHandler(stream_handler) - - if file_output: - file_handler = logging.FileHandler(file_output) - file_handler.setFormatter(log_fmt) - log.addHandler(file_handler) - - log.info('Logging started on AMICI version %s', amici.__version__) - - log.debug('OS Platform: %s', platform.platform()) - log.debug('Python version: %s', platform.python_version()) - log.debug('Hostname: %s', socket.getfqdn()) - - logging.captureWarnings(capture_warnings) - - return log - - -def set_log_level(logger: logging.Logger, log_level: Union[int, bool]) -> None: - if log_level is not None and log_level is not False: - if isinstance(log_level, bool): - log_level = logging.DEBUG - elif not isinstance(log_level, int): - raise ValueError('log_level must be a boolean, integer or None') - - if logger.getEffectiveLevel() != log_level: - logger.debug('Changing log_level from %d to %d' % ( - logger.getEffectiveLevel(), log_level)) - logger.setLevel(log_level) - - -def get_logger(logger_name: Optional[str] = BASE_LOGGER_NAME, - log_level: Optional[int] = None, - **kwargs) -> logging.Logger: - """ - Returns (if extistant) or creates an AMICI logger - - If the AMICI base logger has already been set up, this method will - return it or any of its descendant loggers without overriding the - settings - i.e. any values supplied as kwargs will be ignored. - - :param logger_name: - Get a logger for a specific namespace, typically __name__ - for code outside of classes or self.__module__ inside a class - - :param log_level: - Override the default or preset log level for the requested logger. - None or False uses the default or preset value. True evaluates to - logging.DEBUG. Any integer is used directly. - - :param console_output: - Set up a default console log handler if True (default). Only used when - the AMICI logger hasn't been set up yet. - - :param file_output: - Supply a filename to copy all log output to that file, or set to - False to disable (default). Only used when the AMICI logger hasn't - been set up yet. - - :param capture_warnings: - Capture warnings from Python's warnings module if True (default). - Only used when the AMICI logger hasn't been set up yet.. - - :return: - A logging.Logger object with the requested name - """ - if BASE_LOGGER_NAME not in logging.Logger.manager.loggerDict.keys(): - _setup_logger(**kwargs) - elif kwargs: - warnings.warn('AMICI logger already exists, ignoring keyword ' - 'arguments to setup_logger') - - logger = logging.getLogger(logger_name) - - set_log_level(logger, log_level) - - return logger - - -def log_execution_time(description: str, logger: logging.Logger) -> Callable: - """ - Parameterized function decorator that enables automatic execution time - tracking - - :param description: - Description of what the decorated function does - - :param logger: - Logger to which execution timing will be printed - """ - def decorator_timer(func): - @functools.wraps(func) - def wrapper_timer(*args, **kwargs): - - # append pluses to indicate recursion level - recursion_level = sum( - frame.function == 'wrapper_timer' - and frame.filename == __file__ - for frame in getouterframes(currentframe(), context=0) - ) - - recursion = '' - if recursion_level > 1: - recursion = '+' * (recursion_level - 1) - - tstart = time.perf_counter() - rval = func(*args, **kwargs) - tend = time.perf_counter() - spacers = ' ' * max(54 - len(description) - len(logger.name) - - len(recursion), 0) - logger.info(f'Finished {description}{spacers}' - f'{recursion} ({(tend - tstart):.2E}s)') - return rval - return wrapper_timer - return decorator_timer diff --git a/deps/AMICI/python/amici/numpy.py b/deps/AMICI/python/amici/numpy.py deleted file mode 100644 index a2c593df9..000000000 --- a/deps/AMICI/python/amici/numpy.py +++ /dev/null @@ -1,310 +0,0 @@ -""" -C++ object views ----------------- -This module provides views on C++ objects for efficient access. -""" - -import numpy as np -import copy -import collections - -from . import ExpDataPtr, ReturnDataPtr, ExpData, ReturnData -from typing import Union, List, Dict, Iterator - - -class SwigPtrView(collections.abc.Mapping): - """ - Interface class to expose std::vector and scalar members of - swig wrapped C++ objects as numpy array attributes and fields. This - class is memory efficient as copies of the underlying C++ objects is - only created when respective fields are accessed for the first time. - Cached copies are used for all subsequent calls. - - :ivar _swigptr: pointer to the c++ object - :ivar _field_names: names of members that will be exposed as numpy arrays - :ivar _field_dimensions: dimensions of numpy arrays - :ivar _cache: dictionary with cached values - """ - - _swigptr = None - _field_names: List[str] = [] - _field_dimensions: Dict[str, List[int]] = dict() - - def __getitem__(self, item: str) -> Union[np.ndarray, float]: - """ - Access to field names, copies data from C++ object into numpy - array, reshapes according to field dimensions and stores values in - cache. - - :param item: field name - :return: value - """ - if self._swigptr is None: - raise NotImplementedError('Cannot get items from abstract class.') - - if item == 'ptr': - return self._swigptr - - if item in self._cache: - return self._cache[item] - - if item == 'id': - return getattr(self._swigptr, item) - - if item not in self._field_names: - self.__missing__(item) - - value = field_as_numpy( - self._field_dimensions, item, self._swigptr - ) - self._cache[item] = value - return value - - def __missing__(self, key: str) -> None: - """ - Default behaviour for missing keys - - :param key: field name - """ - raise KeyError(f'Unknown field name {key}.') - - def __getattr__(self, item) -> Union[np.ndarray, float]: - """ - Attribute accessor for field names - - :param item: field name - - :returns: value - """ - return self.__getitem__(item) - - def __init__(self, swigptr): - """ - Constructor - - :param swigptr: pointer to the C++ object - """ - self._swigptr = swigptr - self._cache = dict() - super(SwigPtrView, self).__init__() - - def __len__(self) -> int: - """ - Returns the number of available keys/fields - - :returns: length of _field_names - """ - return len(self._field_names) - - def __iter__(self) -> Iterator: - """ - Create an iterator of the keys/fields - - :returns: iterator over _field_names - """ - return iter(self._field_names) - - def __copy__(self): - """ - Create a shallow copy - - :return: SwigPtrView shallow copy - """ - other = SwigPtrView(self._swigptr) - other._field_names = self._field_names - other._field_dimensions = self._field_dimensions - other._cache = self._cache - return other - - def __contains__(self, item) -> bool: - """ - Faster implementation of __contains__ that avoids copy of the field - - :param item: item to check for - - :returns: whether item is available as key - """ - return item in self._field_names - - def __deepcopy__(self, memo): - """ - Create a deep copy - - :param memo: dict with id-to-object mapping - - :returns: SwigPtrView deep copy - """ - other = SwigPtrView(self._swigptr) - other._field_names = copy.deepcopy(self._field_names) - other._field_dimensions = copy.deepcopy(self._field_dimensions) - other._cache = copy.deepcopy(self._cache) - return other - - -class ReturnDataView(SwigPtrView): - """ - Interface class for C++ Return Data objects that avoids possibly costly - copies of member data. - """ - - _field_names = [ - 'ts', 'x', 'x0', 'x_ss', 'sx', 'sx0', 'sx_ss', 'y', 'sigmay', - 'sy', 'ssigmay', 'z', 'rz', 'sigmaz', 'sz', 'srz', - 'ssigmaz', 'sllh', 's2llh', 'J', 'xdot', 'status', 'llh', - 'chi2', 'res', 'sres', 'FIM', 'w', - 'preeq_wrms', 'preeq_t', 'preeq_numlinsteps', 'preeq_numsteps', - 'preeq_numstepsB', 'preeq_status', 'preeq_cpu_time', - 'preeq_cpu_timeB', 'posteq_wrms', 'posteq_t', 'posteq_numlinsteps', - 'posteq_numsteps', 'posteq_numstepsB', 'posteq_status', - 'posteq_cpu_time', 'posteq_cpu_timeB', 'numsteps', 'numrhsevals', - 'numerrtestfails', 'numnonlinsolvconvfails', 'order', 'cpu_time', - 'numstepsB', 'numrhsevalsB', 'numerrtestfailsB', - 'numnonlinsolvconvfailsB', 'cpu_timeB' - ] - - def __init__(self, rdata: Union[ReturnDataPtr, ReturnData]): - """ - Constructor - - :param rdata: pointer to the ReturnData instance - """ - if not isinstance(rdata, (ReturnDataPtr, ReturnData)): - raise TypeError(f'Unsupported pointer {type(rdata)}, must be' - f'amici.ExpDataPtr!') - self._field_dimensions = { - 'ts': [rdata.nt], - 'x': [rdata.nt, rdata.nx], - 'x0': [rdata.nx], - 'x_ss': [rdata.nx], - 'sx': [rdata.nt, rdata.nplist, rdata.nx], - 'sx0': [rdata.nplist, rdata.nx], - 'sx_ss': [rdata.nplist, rdata.nx], - - # observables - 'y': [rdata.nt, rdata.ny], - 'sigmay': [rdata.nt, rdata.ny], - 'sy': [rdata.nt, rdata.nplist, rdata.ny], - 'ssigmay': [rdata.nt, rdata.nplist, rdata.ny], - - # event observables - 'z': [rdata.nmaxevent, rdata.nz], - 'rz': [rdata.nmaxevent, rdata.nz], - 'sigmaz': [rdata.nmaxevent, rdata.nz], - 'sz': [rdata.nmaxevent, rdata.nplist, rdata.nz], - 'srz': [rdata.nmaxevent, rdata.nplist, rdata.nz], - 'ssigmaz': [rdata.nmaxevent, rdata.nplist, rdata.nz], - - # objective function - 'sllh': [rdata.nplist], - 's2llh': [rdata.np, rdata.nplist], - - 'res': [rdata.nt * rdata.nytrue * - (2 if rdata.sigma_res else 1)], - 'sres': [rdata.nt * rdata.nytrue * - (2 if rdata.sigma_res else 1), rdata.nplist], - 'FIM': [rdata.nplist, rdata.nplist], - - # diagnosis - 'J': [rdata.nx_solver, rdata.nx_solver], - 'w': [rdata.nt, rdata.nw], - 'xdot': [rdata.nx_solver], - 'preeq_numlinsteps': [rdata.newton_maxsteps, 2], - 'preeq_numsteps': [1, 3], - 'preeq_status': [1, 3], - 'posteq_numlinsteps': [rdata.newton_maxsteps, 2], - 'posteq_numsteps': [1, 3], - 'posteq_status': [1, 3], - 'numsteps': [rdata.nt], - 'numrhsevals': [rdata.nt], - 'numerrtestfails': [rdata.nt], - 'numnonlinsolvconvfails': [rdata.nt], - 'order': [rdata.nt], - 'numstepsB': [rdata.nt], - 'numrhsevalsB': [rdata.nt], - 'numerrtestfailsB': [rdata.nt], - 'numnonlinsolvconvfailsB': [rdata.nt], - } - super(ReturnDataView, self).__init__(rdata) - - def __getitem__(self, item: str) -> Union[np.ndarray, ReturnDataPtr, - ReturnData, float]: - """ - Custom getitem implementation shim to map `t` to `ts` - - :param item: field/attribute key - - :returns: self[item] - """ - if item == 't': - item = 'ts' - return super(ReturnDataView, self).__getitem__(item) - - -class ExpDataView(SwigPtrView): - """ - Interface class for C++ Exp Data objects that avoids possibly costly - copies of member data. - """ - - _field_names = [ - 'observedData', 'observedDataStdDev', 'observedEvents', - 'observedEventsStdDev', 'fixedParameters', - 'fixedParametersPreequilibration', - 'fixedParametersPresimulation' - ] - - def __init__(self, edata: Union[ExpDataPtr, ExpData]): - """ - Constructor - - :param edata: pointer to the ExpData instance - """ - if not isinstance(edata, (ExpDataPtr, ExpData)): - raise TypeError(f'Unsupported pointer {type(edata)}, must be' - f'amici.ExpDataPtr!') - self._field_dimensions = { # observables - 'observedData': [edata.nt(), edata.nytrue()], - 'observedDataStdDev': [edata.nt(), edata.nytrue()], - - # event observables - 'observedEvents': [edata.nmaxevent(), edata.nztrue()], - 'observedEventsStdDev': [edata.nmaxevent(), edata.nztrue()], - - # fixed parameters - 'fixedParameters': [len(edata.fixedParameters)], - 'fixedParametersPreequilibration': [ - len(edata.fixedParametersPreequilibration)], - 'fixedParametersPresimulation': [ - len(edata.fixedParametersPreequilibration)], - } - edata.observedData = edata.getObservedData() - edata.observedDataStdDev = edata.getObservedDataStdDev() - edata.observedEvents = edata.getObservedEvents() - edata.observedEventsStdDev = edata.getObservedEventsStdDev() - super(ExpDataView, self).__init__(edata) - - -def field_as_numpy(field_dimensions: Dict[str, List[int]], - field: str, data: SwigPtrView) -> Union[np.ndarray, - float, - None]: - """ - Convert data object field to numpy array with dimensions according to - specified field dimensions - - :param field_dimensions: dimension specifications - dict({field: list([dim1, dim2, ...])}) - :param data: object with fields - :param field: Name of field - - :returns: Field Data as numpy array with dimensions according to - specified field dimensions - """ - attr = getattr(data, field) - if field in field_dimensions.keys(): - if len(attr) == 0: - return None - else: - return np.array(attr).reshape(field_dimensions[field]) - else: - return float(attr) diff --git a/deps/AMICI/python/amici/ode_export.py b/deps/AMICI/python/amici/ode_export.py deleted file mode 100644 index f48c778c2..000000000 --- a/deps/AMICI/python/amici/ode_export.py +++ /dev/null @@ -1,3748 +0,0 @@ -""" -C++ Export ----------- -This module provides all necessary functionality specify an ODE 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 sympy as sp -import numpy as np -import re -import shutil -import subprocess -import sys -import os -import copy -import numbers -import logging -import itertools -import contextlib - -try: - import pysb -except ImportError: - pysb = None - -from typing import ( - Callable, Optional, Union, List, Dict, Tuple, SupportsFloat, Sequence, - Set, Any -) -from dataclasses import dataclass -from string import Template -from sympy.matrices.immutable import ImmutableDenseMatrix -from sympy.matrices.dense import MutableDenseMatrix -from sympy.logic.boolalg import BooleanAtom -from itertools import chain -from .cxxcodeprinter import AmiciCxxCodePrinter, get_switch_statement - -from . import ( - amiciSwigPath, amiciSrcPath, amiciModulePath, __version__, __commit__, - sbml_import -) -from .logging import get_logger, log_execution_time, set_log_level -from .constants import SymbolId -from .import_utils import smart_subs_dict, toposort_symbols, \ - ObservableTransformation - -# 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') - - -@dataclass -class _FunctionInfo: - """Information on a model-specific generated C++ function - - :ivar arguments: argument list of the 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 - """ - arguments: str = '' - return_type: str = 'void' - assume_pow_positivity: bool = False - sparse: bool = False - generate_body: bool = True - body: str = '' - - -# 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 - ), - 'root': - _FunctionInfo( - 'realtype *root, const realtype t, const realtype *x, ' - 'const realtype *p, const realtype *k, const realtype *h' - ), - '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', - 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', - assume_pow_positivity=True, sparse=True - ), - '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', - 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', - 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', - 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 *dtcldp', - ), - 'dsigmaydp': - _FunctionInfo( - 'realtype *dsigmaydp, const realtype t, const realtype *p, ' - 'const realtype *k, const int ip', - ), - 'sigmay': - _FunctionInfo( - 'realtype *sigmay, 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', - 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 *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' - ), - 'w': - _FunctionInfo( - 'realtype *w, const realtype t, const realtype *x, ' - 'const realtype *p, const realtype *k, ' - 'const realtype *h, const realtype *tcl', - 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', - 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' - ), - 'total_cl': - _FunctionInfo('realtype *total_cl, const realtype *x_rdata'), - - 'x_solver': - _FunctionInfo('realtype *x_solver, const realtype *x_rdata') -} - -# 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 -] -# 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) -> 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 - - :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 - ) - - -class ModelQuantity: - """ - Base class for model components - """ - def __init__(self, - identifier: sp.Symbol, - name: str, - value: Union[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)}') - 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 State(ModelQuantity): - """ - 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 - - """ - - _dt: Union[sp.Expr, None] = None - _conservation_law: Union[sp.Expr, None] = None - - 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(State, self).__init__(identifier, name, init) - self._dt = cast_to_sym(dt, 'dt') - self._conservation_law = None - - def set_conservation_law(self, - law: sp.Expr) -> None: - """ - Sets the conservation law of a state. If the 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, sp.Expr): - raise TypeError(f'conservation law must have type sympy.Expr, ' - 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.Symbol]: - """ - 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) - - -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): - """ - 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) - """ - super(ConservationLaw, self).__init__(identifier, name, value) - - -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: Union[sp.Symbol, None] = None - - def __init__(self, - identifier: sp.Symbol, - name: str, - value: sp.Expr, - measurement_symbol: Optional[sp.Symbol] = None, - transformation: Optional[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(Observable, self).__init__(identifier, name, value) - self._measurement_symbol = measurement_symbol - 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 - - -class SigmaY(ModelQuantity): - """ - A Standard Deviation SigmaY rescales the distance between simulations - and measurements when computing residuals or objective functions, - abbreviated by `sigmay` - """ - 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 - """ - super(SigmaY, self).__init__(identifier, name, value) - - -class Expression(ModelQuantity): - """ - An Expressions 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(Expression, self).__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(Parameter, self).__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(Constant, self).__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 - """ - super(LogLikelihood, self).__init__(identifier, name, value) - - -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 ODE 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: Union[sp.Expr, None], - event_observable: Union[sp.Expr, None]): - """ - 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 event_observable: - formula a potential observable linked to the event - (None for Heaviside functions, empty events without observable) - """ - super(Event, self).__init__(identifier, name, value) - # add the Event specific components - self._state_update = state_update - self._observable = event_observable - - 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() - - -# defines the type of some attributes in ODEModel -symbol_to_type = { - SymbolId.SPECIES: State, - SymbolId.PARAMETER: Parameter, - SymbolId.FIXED_PARAMETER: Constant, - SymbolId.OBSERVABLE: Observable, - SymbolId.SIGMAY: SigmaY, - SymbolId.LLHY: LogLikelihood, - SymbolId.EXPRESSION: Expression, - SymbolId.EVENT: Event -} - - -@log_execution_time('running smart_jacobian', logger) -def smart_jacobian(eq: sp.MutableDenseMatrix, - sym_var: sp.MutableDenseMatrix) -> sp.MutableDenseMatrix: - """ - 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 - """ - if min(eq.shape) and min(sym_var.shape) \ - and not smart_is_zero_matrix(eq) \ - and not smart_is_zero_matrix(sym_var) \ - and not sym_var.free_symbols.isdisjoint(eq.free_symbols): - return eq.jacobian(sym_var) - return sp.zeros(eq.shape[0], sym_var.shape[0]) - - -@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()) - - return x.nnz() == 0 - - -class ODEModel: - """ - Defines an Ordinary Differential Equation as set of ModelQuantities. - This class provides general purpose interfaces to ompute arbitrary - symbolic derivatives that are necessary for model simulation or - sensitivity computation - - :ivar _states: - list of state variables - - :ivar _observables: - list of observables - - :ivar _sigmays: - list of sigmays - - :ivar _parameters: - list of parameters - - :ivar _loglikelihoods: - list of loglikelihoods - - :ivar _expressions: - list of expressions instances - - :ivar _conservationlaws: - 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 names of symbolic identifiers of the symbolic variables - of the model - - :ivar _syms: - carries symbolic identifiers of the symbolic variables of the - model - - :ivar _strippedsyms: - carries symbolic identifiers that were stripped of additional class - information - - :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 - - :ivar _rowvals: - carries row values for sparsified variables. See - SUNMatrixContent_Sparse definition in - - :ivar _equation_prototype: - defines the attribute from which an equation should be generated via - list comprehension (see :meth:`ODEModel._generate_equation`) - - :ivar _variable_prototype: - defines the attribute from which a variable should be generated via - list comprehension (see :meth:`ODEModel._generate_symbol`) - - :ivar _value_prototype: - defines the attribute from which a value should be generated via - list comprehension (see :meth:`ODEModel._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 - ODEModel.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 - """ - - def __init__(self, verbose: Optional[Union[bool, int]] = False, - simplify: Optional[Callable] = sp.powsimp): - """ - Create a new ODEModel instance. - - :param verbose: - verbosity level for logging, True/False default to - ``logging.DEBUG``/``logging.ERROR`` - - :param simplify: - see :meth:`ODEModel._simplify` - """ - self._states: List[State] = [] - self._observables: List[Observable] = [] - self._sigmays: List[SigmaY] = [] - self._parameters: List[Parameter] = [] - self._constants: List[Constant] = [] - self._loglikelihoods: List[LogLikelihood] = [] - self._expressions: List[Expression] = [] - self._conservationlaws: List[ConservationLaw] = [] - self._events: List[Event] = [] - 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, - } - self._eqs: Dict[str, Union[sp.Matrix, List[sp.Matrix]]] = dict() - self._sparseeqs: Dict[str, Union[sp.Matrix, List[sp.Matrix]]] = dict() - self._vals: Dict[str, List[float]] = dict() - self._names: Dict[str, List[str]] = dict() - self._syms: Dict[str, Union[sp.Matrix, List[sp.Matrix]]] = dict() - self._strippedsyms: Dict[str, 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, str] = { - 'total_cl': '_conservationlaws', - 'x0': '_states', - 'y': '_observables', - 'Jy': '_loglikelihoods', - 'w': '_expressions', - 'root': '_events', - 'sigmay': '_sigmays' - } - self._variable_prototype: Dict[str, str] = { - 'tcl': '_conservationlaws', - 'x_rdata': '_states', - 'y': '_observables', - 'p': '_parameters', - 'k': '_constants', - 'w': '_expressions', - 'sigmay': '_sigmays', - 'h': '_events' - } - self._value_prototype: Dict[str, str] = { - 'p': '_parameters', - 'k': '_constants', - } - self._total_derivative_prototypes: \ - Dict[str, Dict[str, Union[str, List[str]]]] = { - 'sx_rdata': { - 'eq': 'x_rdata', - 'chainvars': ['x'], - 'var': 'p', - 'dxdz_name': 'sx', - }, - 'sroot': { - 'eq': 'root', - 'chainvars': ['x'], - 'var': 'p', - 'dxdz_name': 'sx', - } - } - - self._lock_total_derivative: List[str] = list() - self._simplify: Callable = 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++'] - - @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 - """ - - # 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(si.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 - 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 - )): - assert ix == species['index'] # check that no reordering occurred - # 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 ODE 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'] - else: - args += ['value'] - if symbol_name == SymbolId.EVENT: - args += ['state_update', 'event_observable'] - if symbol_name == SymbolId.OBSERVABLE: - args += ['transformation'] - - protos = [ - { - '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 proto in protos: - self.add_component(symbol_to_type[symbol_name](**proto)) - - # 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): - 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(from_sbml=True) - self._has_quadratic_nllh = all( - llh['dist'] in ['normal', 'lin-normal', 'log-normal', - 'log10-normal'] - for llh in si.symbols[SymbolId.LLHY].values() - ) - - 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. - """ - for comp_type in [Observable, Expression, Parameter, Constant, State, - LogLikelihood, SigmaY, ConservationLaw, Event]: - if isinstance(component, comp_type): - component_list = getattr( - self, f'_{type(component).__name__.lower()}s' - ) - if insert_first: - component_list.insert(0, component) - else: - component_list.append(component) - return - - raise ValueError(f'Invalid component type {type(component)}') - - def add_conservation_law(self, - state: sp.Symbol, - total_abundance: sp.Symbol, - state_expr: sp.Expr, - abundance_expr: sp.Expr) -> None: - """ - Adds a new conservation law to the model. A conservation law is defined - by the conserved quantity T = sum_i(a_i * x_i), where a_i are - coefficients and x_i are different state variables. - - :param state: - symbolic identifier of the state that should be replaced by - the conservation law (x_j) - - :param total_abundance: - symbolic identifier of the total abundance (T/a_j) - - :param state_expr: - symbolic algebraic formula that replaces the the state. This is - used to compute the numeric value of of `state` during simulations. - x_j = T/a_j - sum_i≠j(a_i * x_i)/a_j - - :param abundance_expr: - symbolic algebraic formula that computes the value of the - conserved quantity. This is used to update the numeric value for - `total_abundance` after (re-)initialization. - T/a_j = sum_i≠j(a_i * x_i)/a_j + x_j - """ - try: - ix = [ - s.get_id() - for s in self._states - ].index(state) - except ValueError: - raise ValueError(f'Specified state {state} was not found in the ' - f'model states.') - - state_id = self._states[ix].get_id() - - self.add_component( - Expression(state_id, str(state_id), state_expr), - insert_first=True - ) - - self.add_component( - ConservationLaw( - total_abundance, - f'total_{state_id}', - abundance_expr - ) - ) - - self._states[ix].set_conservation_law(state_expr) - - def get_observable_transformations(self) -> List[ObservableTransformation]: - """ - List of observable transformations - - :return: - list of transformations - """ - return [obs.trafo for obs in self._observables] - - def num_states_rdata(self) -> int: - """ - Number of states. - - :return: - number of state variable symbols - """ - return len(self.sym('x_rdata')) - - def num_states_solver(self) -> int: - """ - Number of states after applying conservation laws. - - :return: - number of state variable symbols - """ - return len(self.sym('x')) - - def num_cons_law(self) -> int: - """ - Number of conservation laws. - - :return: - number of conservation laws - """ - return self.num_states_rdata() - self.num_states_solver() - - def num_state_reinits(self) -> int: - """ - Number of solver states which would be reinitialized after - preequilibration - - :return: - number of state variable symbols with reinitialization - """ - reinit_states = self.eq('x0_fixedParameters') - solver_states = self.eq('x_solver') - return sum(ix in solver_states for ix in reinit_states) - - def num_obs(self) -> int: - """ - Number of Observables. - - :return: - number of observable symbols - """ - return len(self.sym('y')) - - def num_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, - stripped: Optional[bool] = False) -> sp.Matrix: - """ - Returns (and constructs if necessary) the identifiers for a symbolic - entity. - - :param name: - name of the symbolic variable - :param stripped: - should additional class information be stripped from the - symbolic variables (default=False) - - :return: - matrix of symbolic identifiers - """ - if name not in self._syms: - self._generate_symbol(name) - - if stripped and name in self._variable_prototype: - return self._strippedsyms[name] - else: - 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[float]: - """ - 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 ODE rhs and initial - conditions. - """ - return set(chain.from_iterable( - state.get_free_symbols() - for state in self._states - )) - - def _generate_symbol(self, name: str, *, from_sbml: bool = False) -> None: - """ - Generates the symbolic identifiers for a symbolic variable - - :param name: - name of the symbolic variable - - """ - if name in self._variable_prototype: - component = self._variable_prototype[name] - self._syms[name] = sp.Matrix([ - comp.get_id() - for comp in getattr(self, component) - ]) - # this gives us access to the "stripped" symbols that were - # generated by pysb (if compiling a pysb model). To ensure - # correctness of derivatives, the same assumptions as in pysb - # have to be used (currently no assumptions) - # NB if we are compiling a SBML model, then it will be the same - # as the "non-stripped" in order to preserve assumptions - self._strippedsyms[name] = self._syms[name] if from_sbml \ - else sp.Matrix([ - sp.Symbol(comp.get_name()) - for comp in getattr(self, component) - ]) - if name == 'y': - self._syms['my'] = sp.Matrix([ - comp.get_measurement_symbol() - for comp in getattr(self, component) - ]) - return - elif name == 'x': - self._syms[name] = sp.Matrix([ - state.get_id() - for state in self._states - if state._conservation_law is None - ]) - return - elif name == 'sx0': - self._syms[name] = sp.Matrix([ - f's{state.get_id()}_0' - for state in self._states - if state._conservation_law is None - ]) - 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._conservationlaws - ]) - 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 in sensi_functions: - length = self.eq(name).shape[0] - else: - length = len(self.eq(name)) - self._syms[name] = sp.Matrix([ - sp.Symbol(f'{name}{i}', real=True) for i in range(length) - ]) - - def generate_basic_variables(self, *, from_sbml: bool = False) -> None: - """ - Generates the symbolic identifiers for all variables in - ODEModel.variable_prototype - - :param from_sbml: - whether the model is generated from SBML - """ - # We need to process events and Heaviside functions in the ODE Model, - # before adding it to ODEExporter - self.parse_events() - - for var in self._variable_prototype: - if var not in self._syms: - self._generate_symbol(var, from_sbml=from_sbml) - - self._generate_symbol('x', from_sbml=from_sbml) - - def parse_events(self) -> None: - """ - This functions 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._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 `_get_unique_root` - 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._states[idx].get_id())) - + - free_symbols_expr.count(str(self._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) - match_deriv = re.match(r'd([\w]+)d([a-z]+)', name) - if match_deriv: - rownames = self.sym(match_deriv.group(1)) - colnames = self.sym(match_deriv.group(2)) - - 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 = re.match(r'd([\w_]+)d([a-z_]+)', - name.replace('dJydsigma', 'dJydsigmay')) - time_symbol = sp.Matrix([symbol_with_assumptions('t')]) - - if name in self._equation_prototype: - self._equation_from_component(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': - self._eqs[name] = sp.Matrix([ - s.get_dt() for s in self._states - if s._conservation_law is None - ]) - - elif name == 'x_rdata': - self._eqs[name] = sp.Matrix([ - state.get_id() - if state._conservation_law is None - else state._conservation_law - for state in self._states - ]) - - elif name == 'x_solver': - self._eqs[name] = sp.Matrix([ - state.get_id() - for state in self._states - if state._conservation_law is None - ]) - - elif name == 'sx_solver': - self._eqs[name] = sp.Matrix([ - self.sym('sx_rdata')[ix] - for ix, state in enumerate(self._states) - if state._conservation_law is None - ]) - - 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]) - for ip in range(self._eqs[name].shape[1]): - self._eqs[name][:, ip] += smart_multiply( - dx0_fixed_parametersdx, self.sym('sx0') - ) - - 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': - # not correctly parsed in regex - self._derivative('total_cl', 'x_rdata') - - 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 == 'dxdotdx_explicit': - # force symbols - self._derivative('xdot', 'x', name=name) - - elif name == 'dxdotdp_explicit': - # force symbols - self._derivative('xdot', 'p', name=name) - - 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._syms['w'], self._eqs['w']))) - tmp_xdot = smart_subs_dict(self._eqs['xdot'], w_sorted) - self._eqs[name] = ( - smart_multiply(self.eq('drootdx'), tmp_xdot) - + self.eq('drootdt') - ) - - 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 == 'ddeltaxdx': - self._eqs[name] = [ - smart_jacobian(self.eq('deltax')[ie], self.sym('x')) - for ie in range(self.num_events()) - ] - - elif name == 'ddeltaxdt': - self._eqs[name] = [ - smart_jacobian(self.eq('deltax')[ie], time_symbol) - for ie in range(self.num_events()) - ] - - elif name == 'ddeltaxdp': - self._eqs[name] = [ - smart_jacobian(self.eq('deltax')[ie], self.sym('p')) - for ie in range(self.num_events()) - ] - - elif name == 'stau': - self._eqs[name] = [ - -self.eq('sroot')[ie, :] / self.eq('drootdt_total')[ie] - for ie in range(self.num_events()) - ] - - elif name == 'deltasx': - event_eqs = [] - for ie, event in enumerate(self._events): - if event._state_update is not None: - # ====== chain rule for the state variables =============== - # get xdot with expressions back-substituted - tmp_eq = smart_multiply( - (self.sym('xdot_old') - self.sym('xdot')), - self.eq('stau')[ie]) - # construct an enhanced state sensitivity, which accounts - # for the time point sensitivity as well - tmp_dxdp = self.sym('sx') * sp.ones(1, self.num_par()) - tmp_dxdp += smart_multiply(self.sym('xdot'), - self.eq('stau')[ie]) - tmp_eq += smart_multiply(self.eq('ddeltaxdx')[ie], - tmp_dxdp) - # ====== chain rule for the time point ==================== - tmp_eq += smart_multiply(self.eq('ddeltaxdt')[ie], - self.eq('stau')[ie]) - # ====== partial derivative for the parameters ============ - tmp_eq += self.eq('ddeltaxdp')[ie] - else: - tmp_eq = smart_multiply( - (self.eq('xdot_old') - self.eq('xdot')), - self.eq('stau')[ie]) - - event_eqs.append(tmp_eq) - - self._eqs[name] = event_eqs - - elif name == 'xdot_old': - # force symbols - self._eqs[name] = self.sym(name) - - elif match_deriv: - self._derivative(match_deriv.group(1), match_deriv.group(2), name) - - else: - raise ValueError(f'Unknown equation {name}') - - if name == 'root': - # Events are processed after the ODE 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 self._simplify: - dec = log_execution_time(f'simplifying {name}', logger) - if isinstance(self._eqs[name], list): - self._eqs[name] = [dec(sub_eq.applyfunc)(self._simplify) - for sub_eq in self._eqs[name]] - else: - self._eqs[name] = \ - dec(self._eqs[name].applyfunc)(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) - and cv not in self._lock_total_derivative - and var is not 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 - - # this is the basic requirement check - needs_stripped_symbols = eq == 'xdot' and var != 'x' - - # partial derivative - sym_eq = self.eq(eq).transpose() if eq == 'Jy' else self.eq(eq) - if pysb is not None and needs_stripped_symbols: - needs_stripped_symbols = not any( - isinstance(sym, pysb.Component) - for sym in sym_eq.free_symbols - ) - - # now check whether we are working with energy_modeling branch - # where pysb class info is already stripped - # TODO: fixme as soon as energy_modeling made it to the main pysb - # branch - sym_var = self.sym(var, needs_stripped_symbols) - - 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 - whith 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 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): - 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] += smart_multiply(dydx, dxdz) - else: - self._eqs[name] += smart_multiply(dydx, 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) \ - 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) - 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_component(self, name: str, component: str) -> 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 getattr(self, component)] - ) - - def get_conservation_laws(self) -> List[Tuple[sp.Symbol, sp.Basic]]: - """Returns a list of states with conservation law set - - :return: - list of state identifiers - """ - return [ - (state.get_id(), state._conservation_law) - for state in self._states - if state._conservation_law is not None - ] - - 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: - component = self._value_prototype[name] - else: - raise ValueError(f'No values for {name}') - - self._vals[name] = [comp.get_val() - for comp in getattr(self, component)] - - 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: - component = self._variable_prototype[name] - elif name in self._equation_prototype: - component = self._equation_prototype[name] - else: - raise ValueError(f'No names for {name}') - - self._names[name] = [comp.get_name() - for comp in getattr(self, component)] - - 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]._conservation_law is not None - - 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 - - """ - return self._states[ix].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 - """ - # substitute 'w' expressions into root expressions now, to avoid - # rewriting '{model_name}_root.cpp' and '{model_name}_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_found = root_found.subs(w_sorted) - - 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, - event_observable=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)) - - 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 - """ - - # 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)) - - if heavisides: - # only apply subs if necessary - for heaviside_sympy, heaviside_amici in heavisides: - dxdt = dxdt.subs(heaviside_sympy, heaviside_amici) - - return dxdt - - -class ODEExporter: - """ - The ODEExporter class generates AMICI C++ files for ODE model as - defined in symbolic expressions. - - :ivar model: - ODE definition - - :ivar verbose: - more verbose output if True - - :ivar assume_pow_positivity: - if set to true, a special pow function is - used to avoid problems with state variables that may become negative - due to numerical errors - - compiler: distutils/setuptools compiler selection to build the - python extension - - :ivar functions: - carries C++ function signatures and other specifications - - :ivar model_name: - name of the model that will be used for compilation - - :ivar model_path: - path to the generated model specific files - - :ivar model_swig_path: - path to the generated swig files - - :ivar allow_reinit_fixpar_initcond: - indicates whether reinitialization of - initial states depending on fixedParameters is allowed for this model - - :ivar _build_hints: - If the given model uses special functions, this set contains hints for - model building. - - :ivar generate_sensitivity_code: - Specifies whether code for sensitivity computation is to be generated - """ - - def __init__( - self, - ode_model: ODEModel, - outdir: Optional[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' - ): - """ - Generate AMICI C++ files for the ODE provided to the constructor. - - :param ode_model: - ODE definition - - :param outdir: - see :meth:`amici.ode_export.ODEExporter.set_paths` - - :param verbose: - verbosity level for logging, True/False default to - logging.Error/logging.DEBUG - - :param assume_pow_positivity: - if set to true, a special pow function is - used to avoid problems with state variables that may become - negative due to numerical errors - - :param compiler: distutils/setuptools compiler selection to build the - python extension - - :param allow_reinit_fixpar_initcond: - see :class:`amici.ode_export.ODEExporter` - - :param generate_sensitivity_code specifies whether code required for - sensitivity computation will be generated - - :param model_name: - name of the model to be used during code generation - """ - set_log_level(logger, verbose) - - self.verbose: bool = logger.getEffectiveLevel() <= logging.DEBUG - self.assume_pow_positivity: bool = assume_pow_positivity - self.compiler: str = compiler - - self.model_path: str = '' - self.model_swig_path: str = '' - - self.set_name(model_name) - self.set_paths(outdir) - - # Signatures and properties of generated model functions (see - # include/amici/model.h for details) - self.model: ODEModel = ode_model - - # To only generate a subset of functions, apply subselection here - self.functions: Dict[str, _FunctionInfo] = copy.deepcopy(functions) - - self.allow_reinit_fixpar_initcond: bool = allow_reinit_fixpar_initcond - self._build_hints = set() - self.generate_sensitivity_code: bool = generate_sensitivity_code - - @log_execution_time('generating cpp code', logger) - def generate_model_code(self) -> None: - """ - Generates the native C++ code for the loaded model and a Matlab - script that can be run to compile a mex file from the C++ code - - - """ - with _monkeypatched(sp.Pow, '_eval_derivative', - _custom_pow_eval_derivative): - - self._prepare_model_folder() - self._generate_c_code() - self._generate_m_code() - - @log_execution_time('compiling cpp code', logger) - def compile_model(self) -> None: - """ - Compiles the generated code it into a simulatable module - - - """ - self._compile_c_code(compiler=self.compiler, - verbose=self.verbose) - - def _prepare_model_folder(self) -> None: - """ - Create model directory or remove all files if the output directory - already exists. - """ - os.makedirs(self.model_path, exist_ok=True) - - for file in os.listdir(self.model_path): - file_path = os.path.join(self.model_path, file) - if os.path.isfile(file_path): - os.remove(file_path) - - def _generate_c_code(self) -> None: - """ - Create C++ code files for the model based on ODEExporter.model - """ - for func_name, func_info in self.functions.items(): - if func_name in sensi_functions + sparse_sensi_functions and \ - not self.generate_sensitivity_code: - continue - - if func_info.generate_body: - dec = log_execution_time(f'writing {func_name}.cpp', logger) - dec(self._write_function_file)(func_name) - if func_name in sparse_functions and func_info.body: - self._write_function_index(func_name, 'colptrs') - self._write_function_index(func_name, 'rowvals') - - for name in self.model.sym_names(): - # only generate for those that have nontrivial implementation, - # check for both basic variables (not in functions) and function - # computed values - if (name in self.functions - and not self.functions[name].body - and name not in nobody_functions) \ - or (name not in self.functions and - len(self.model.sym(name)) == 0): - continue - self._write_index_files(name) - - self._write_wrapfunctions_cpp() - self._write_wrapfunctions_header() - self._write_model_header_cpp() - self._write_c_make_file() - self._write_swig_files() - self._write_module_setup() - - 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: - distutils/setuptools compiler selection to build the python - extension - - """ - - # 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}']) - - if compiler is not None: - script_args.extend([f'--compiler={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) - 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 - """ - - # Events are not yet implemented. Once this is done, the variable nz - # will have to be replaced by "self.model.nz()" - nz = 0 - - # Second order code is not yet implemented. Once this is done, - # those variables will have to be replaced by - # "self.model.true()", or the corresponding "model.self.o2flag" - nxtrue_rdata = self.model.num_states_rdata() - nytrue = self.model.num_obs() - o2flag = 0 - - lines = [ - '% This compile script was automatically created from' - ' Python SBML import.', - '% If mex compiler is set up within MATLAB, it can be run' - ' from MATLAB ', - '% in order to compile a mex-file from the Python' - ' generated C++ files.', - '', - f"modelName = '{self.model_name}';", - "amimodel.compileAndLinkModel(modelName, '', [], [], [], []);", - f"amimodel.generateMatlabWrapper({nxtrue_rdata}, " - f"{nytrue}, {self.model.num_par()}, " - f"{self.model.num_const()}, {nz}, {o2flag}, ...", - " [], ['simulate_' modelName '.m'], modelName, ...", - " 'lin', 1, 1);" - ] - - # write compile script (for mex) - compile_script = os.path.join(self.model_path, 'compileMexFile.m') - with open(compile_script, 'w') as fileout: - fileout.write('\n'.join(lines)) - - def _write_index_files(self, name: str) -> None: - """ - Write index file for a symbolic array. - - :param name: - key in self.model._syms for which the respective file should - be written - - """ - if name not in self.model.sym_names(): - raise ValueError(f'Unknown symbolic array: {name}') - - symbols = self.model.sparsesym(name) if name in sparse_functions \ - else self.model.sym(name).T - - # flatten multiobs - if isinstance(next(iter(symbols), None), list): - symbols = [symbol for obs in symbols for symbol in obs] - - lines = [] - for index, symbol in enumerate(symbols): - symbol_name = strip_pysb(symbol) - if str(symbol) == '0': - continue - if str(symbol_name) == '': - raise ValueError(f'{name} contains a symbol called ""') - lines.append(f'#define {symbol_name} {name}[{index}]') - - filename = os.path.join(self.model_path, f'{self.model_name}_{name}.h') - with open(filename, 'w') as fileout: - fileout.write('\n'.join(lines)) - - def _write_function_file(self, function: str) -> None: - """ - Generate equations and write the C++ code for the function - `function`. - - :param function: - name of the function to be written (see self.functions) - """ - - # first generate the equations to make sure we have everything we - # need in subsequent steps - if function in sparse_functions: - equations = self.model.sparseeq(function) - elif not self.allow_reinit_fixpar_initcond \ - and function == 'sx0_fixedParameters': - # Not required. Will create empty function body. - equations = sp.Matrix() - else: - equations = self.model.eq(function) - - # function header - lines = [ - '#include "amici/symbolic_functions.h"', - '#include "amici/defines.h"', - '#include "sundials/sundials_types.h"', - '', - '#include ', - '#include ', - '#include ', - '' - ] - - func_info = self.functions[function] - - # extract symbols that need definitions from signature - # don't add includes for files that won't be generated. - # Unfortunately we cannot check for `self.functions[sym].body` - # here since it may not have been generated yet. - for sym in re.findall( - r'const (?:realtype|double) \*([\w]+)[0]*(?:,|$)', - func_info.arguments - ): - if sym not in self.model.sym_names(): - continue - - if sym in sparse_functions: - iszero = smart_is_zero_matrix(self.model.sparseeq(sym)) - elif sym in self.functions: - iszero = smart_is_zero_matrix(self.model.eq(sym)) - else: - iszero = len(self.model.sym(sym)) == 0 - - if iszero: - continue - - lines.append(f'#include "{self.model_name}_{sym}.h"') - - # include return symbols - if function in self.model.sym_names() and \ - function not in non_unique_id_symbols: - lines.append(f'#include "{self.model_name}_{function}.h"') - - lines.extend([ - '', - 'namespace amici {', - f'namespace model_{self.model_name} {{', - '', - f'{func_info.return_type} {function}_{self.model_name}' - f'({func_info.arguments}){{' - ]) - - # function body - body = self._get_function_body(function, equations) - if self.assume_pow_positivity and func_info.assume_pow_positivity: - body = [re.sub(r'(^|\W)std::pow\(', r'\1amici::pos_pow(', line) - for line in body] - # execute this twice to catch cases where the ending ( would be the - # starting (^|\W) for the following match - body = [re.sub(r'(^|\W)std::pow\(', r'\1amici::pos_pow(', line) - for line in body] - - if not body: - return - - self.functions[function].body = body - - lines += body - lines.extend([ - '}', - '', - f'}} // namespace model_{self.model_name}', - '} // namespace amici\n', - ]) - - # check custom functions - for fun in CUSTOM_FUNCTIONS: - if 'include' in fun and any(fun['c++'] in line for line in lines): - if 'build_hint' in fun: - self._build_hints.add(fun['build_hint']) - lines.insert(0, fun['include']) - - # if not body is None: - filename = os.path.join(self.model_path, - f'{self.model_name}_{function}.cpp') - with open(filename, 'w') as fileout: - fileout.write('\n'.join(lines)) - - def _write_function_index(self, function: str, indextype: str) -> None: - """ - Generate equations and write the C++ code for the function - `function`. - - :param function: - name of the function to be written (see self.functions) - - :param indextype: - type of index {'colptrs', 'rowvals'} - """ - - if indextype == 'colptrs': - values = self.model.colptrs(function) - setter = 'indexptrs' - elif indextype == 'rowvals': - values = self.model.rowvals(function) - setter = 'indexvals' - else: - raise ValueError('Invalid value for indextype, must be colptrs or ' - f'rowvals: {indextype}') - - # function signature - if function in multiobs_functions: - signature = f'(SUNMatrixWrapper &{function}, int index)' - else: - signature = f'(SUNMatrixWrapper &{function})' - - lines = [ - '#include "amici/sundials_matrix_wrapper.h"', - '#include "sundials/sundials_types.h"', - '', - '#include ', - '#include ', - '', - 'namespace amici {', - f'namespace model_{self.model_name} {{', - '', - ] - - # Generate static array with indices - if len(values): - static_array_name = f"{function}_{indextype}_{self.model_name}_" - if function in multiobs_functions: - # list of index vectors - lines.append( - "static constexpr std::array, {len(values)}> " - f"{static_array_name} = {{{{" - ) - lines.extend([' {' - + ', '.join(map(str, index_vector)) + '}, ' - for index_vector in values]) - lines.append("}};") - else: - # single index vector - lines.extend([ - "static constexpr std::array {static_array_name} = {{", - ' ' + ', '.join(map(str, values)), - "};" - ]) - - lines.extend([ - '', - f'void {function}_{indextype}_{self.model_name}{signature}{{', - ]) - - if len(values): - if function in multiobs_functions: - lines.append( - f" {function}.set_{setter}" - f"(gsl::make_span({static_array_name}[index]));" - ) - else: - lines.append( - f" {function}.set_{setter}" - f"(gsl::make_span({static_array_name}));" - ) - - lines.extend([ - '}' - '', - f'}} // namespace model_{self.model_name}', - '} // namespace amici\n', - ]) - - filename = f'{self.model_name}_{function}_{indextype}.cpp' - filename = os.path.join(self.model_path, filename) - - with open(filename, 'w') as fileout: - fileout.write('\n'.join(lines)) - - def _get_function_body( - self, - function: str, - equations: sp.Matrix - ) -> List[str]: - """ - Generate C++ code for body of function `function`. - - :param function: - name of the function to be written (see self.functions) - - :param equations: - symbolic definition of the function body - - :return: - generated C++ code - """ - lines = [] - - if ( - len(equations) == 0 - or ( - isinstance(equations, (sp.Matrix, sp.ImmutableDenseMatrix)) - and min(equations.shape) == 0 - ) - ): - # dJydy is a list - return lines - - if not self.allow_reinit_fixpar_initcond and function in { - 'sx0_fixedParameters', - 'x0_fixedParameters', - }: - return lines - - if function == 'sx0_fixedParameters': - # here we only want to overwrite values where x0_fixedParameters - # was applied - - lines.extend([ - # Keep list of indices of fixed parameters occurring in x0 - " static const std::array _x0_fixedParameters_idxs = {", - " " - + ', '.join(str(x) - for x in self.model._x0_fixedParameters_idx), - " };", - "", - # Set all parameters that are to be reset to 0, so that the - # switch statement below only needs to handle non-zero entries - # (which usually reduces file size and speeds up - # compilation significantly). - " for(auto idx: reinitialization_state_idxs) {", - " if(std::find(_x0_fixedParameters_idxs.cbegin(), " - "_x0_fixedParameters_idxs.cend(), idx) != " - "_x0_fixedParameters_idxs.cend())\n" - " sx0_fixedParameters[idx] = 0.0;", - " }" - ]) - - cases = {} - for ipar in range(self.model.num_par()): - expressions = [] - for index, formula in zip( - self.model._x0_fixedParameters_idx, - equations[:, ipar] - ): - if not formula.is_zero: - expressions.extend([ - f'if(std::find(' - 'reinitialization_state_idxs.cbegin(), ' - f'reinitialization_state_idxs.cend(), {index}) != ' - 'reinitialization_state_idxs.cend())', - f' {function}[{index}] = ' - f'{self.model._code_printer.doprint(formula)};' - ]) - cases[ipar] = expressions - lines.extend(get_switch_statement('ip', cases, 1)) - - elif function == 'x0_fixedParameters': - for index, formula in zip( - self.model._x0_fixedParameters_idx, - equations - ): - 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)};' - ) - - elif function in event_functions: - cases = { - ie: self.model._code_printer._get_sym_lines_array( - equations[ie], function, 0) - for ie in range(self.model.num_events()) - if not smart_is_zero_matrix(equations[ie]) - } - lines.extend(get_switch_statement('ie', cases, 1)) - - elif function in event_sensi_functions: - outer_cases = {} - for ie, inner_equations in enumerate(equations): - inner_lines = [] - inner_cases = { - ipar: self.model._code_printer._get_sym_lines_array( - inner_equations[:, ipar], function, 0) - for ipar in range(self.model.num_par()) - if not smart_is_zero_matrix(inner_equations[:, ipar]) - } - inner_lines.extend(get_switch_statement( - 'ip', inner_cases, 0)) - outer_cases[ie] = copy.copy(inner_lines) - lines.extend(get_switch_statement('ie', outer_cases, 1)) - - elif function in sensi_functions: - cases = { - ipar: self.model._code_printer._get_sym_lines_array( - equations[:, ipar], function, 0) - for ipar in range(self.model.num_par()) - if not smart_is_zero_matrix(equations[:, ipar]) - } - lines.extend(get_switch_statement('ip', cases, 1)) - - elif function in multiobs_functions: - if function == 'dJydy': - cases = { - iobs: self.model._code_printer._get_sym_lines_array( - equations[iobs], function, 0) - for iobs in range(self.model.num_obs()) - if not smart_is_zero_matrix(equations[iobs]) - } - else: - cases = { - iobs: self.model._code_printer._get_sym_lines_array( - equations[:, iobs], function, 0) - for iobs in range(self.model.num_obs()) - if not smart_is_zero_matrix(equations[:, iobs]) - } - lines.extend(get_switch_statement('iy', cases, 1)) - - elif function in self.model.sym_names() \ - and function not in non_unique_id_symbols: - if function in sparse_functions: - symbols = self.model.sparsesym(function) - else: - symbols = self.model.sym(function, stripped=True) - lines += self.model._code_printer._get_sym_lines_symbols( - symbols, equations, function, 4) - - else: - lines += self.model._code_printer._get_sym_lines_array( - equations, function, 4) - - return [line for line in lines if line] - - def _write_wrapfunctions_cpp(self) -> None: - """ - Write model-specific 'wrapper' file (wrapfunctions.cpp). - """ - template_data = {'MODELNAME': self.model_name} - apply_template( - os.path.join(amiciSrcPath, 'wrapfunctions.template.cpp'), - os.path.join(self.model_path, 'wrapfunctions.cpp'), - template_data - ) - - def _write_wrapfunctions_header(self) -> None: - """ - Write model-specific header file (wrapfunctions.h). - """ - template_data = {'MODELNAME': str(self.model_name)} - apply_template( - os.path.join(amiciSrcPath, 'wrapfunctions.ODE_template.h'), - os.path.join(self.model_path, 'wrapfunctions.h'), - template_data - ) - - def _write_model_header_cpp(self) -> None: - """ - Write model-specific header and cpp file (MODELNAME.{h,cpp}). - """ - - tpl_data = { - 'MODELNAME': str(self.model_name), - 'NX_RDATA': str(self.model.num_states_rdata()), - 'NXTRUE_RDATA': str(self.model.num_states_rdata()), - 'NX_SOLVER': str(self.model.num_states_solver()), - 'NXTRUE_SOLVER': str(self.model.num_states_solver()), - 'NX_SOLVER_REINIT': str(self.model.num_state_reinits()), - 'NY': str(self.model.num_obs()), - 'NYTRUE': str(self.model.num_obs()), - 'NZ': '0', - 'NZTRUE': '0', - 'NEVENT': str(self.model.num_events()), - 'NOBJECTIVE': '1', - 'NW': str(len(self.model.sym('w'))), - 'NDWDP': str(len(self.model.sparsesym( - 'dwdp', force_generate=self.generate_sensitivity_code - ))), - 'NDWDX': str(len(self.model.sparsesym('dwdx'))), - 'NDWDW': str(len(self.model.sparsesym('dwdw'))), - 'NDXDOTDW': str(len(self.model.sparsesym('dxdotdw'))), - 'NDXDOTDP_EXPLICIT': str(len(self.model.sparsesym( - 'dxdotdp_explicit', - force_generate=self.generate_sensitivity_code - ))), - 'NDXDOTDX_EXPLICIT': str(len(self.model.sparsesym( - 'dxdotdx_explicit'))), - 'NDJYDY': 'std::vector{%s}' - % ','.join(str(len(x)) - for x in self.model.sparsesym('dJydy')), - 'UBW': str(self.model.num_states_solver()), - 'LBW': str(self.model.num_states_solver()), - 'NP': str(self.model.num_par()), - 'NK': str(self.model.num_const()), - 'O2MODE': 'amici::SecondOrderMode::none', - # using cxxcode 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( - self.model.val('k'))[1:-1], - 'PARAMETER_NAMES_INITIALIZER_LIST': - self._get_symbol_name_initializer_list('p'), - 'STATE_NAMES_INITIALIZER_LIST': - self._get_symbol_name_initializer_list('x_rdata'), - 'FIXED_PARAMETER_NAMES_INITIALIZER_LIST': - self._get_symbol_name_initializer_list('k'), - 'OBSERVABLE_NAMES_INITIALIZER_LIST': - self._get_symbol_name_initializer_list('y'), - 'OBSERVABLE_TRAFO_INITIALIZER_LIST': - '\n'.join( - f'ObservableScaling::{trafo}, // y[{idx}]' - for idx, trafo in enumerate( - self.model.get_observable_transformations() - ) - ), - 'EXPRESSION_NAMES_INITIALIZER_LIST': - self._get_symbol_name_initializer_list('w'), - 'PARAMETER_IDS_INITIALIZER_LIST': - self._get_symbol_id_initializer_list('p'), - 'STATE_IDS_INITIALIZER_LIST': - self._get_symbol_id_initializer_list('x_rdata'), - 'FIXED_PARAMETER_IDS_INITIALIZER_LIST': - self._get_symbol_id_initializer_list('k'), - 'OBSERVABLE_IDS_INITIALIZER_LIST': - self._get_symbol_id_initializer_list('y'), - 'EXPRESSION_IDS_INITIALIZER_LIST': - self._get_symbol_id_initializer_list('w'), - 'REINIT_FIXPAR_INITCOND': - 'true' if self.allow_reinit_fixpar_initcond else - 'false', - 'AMICI_VERSION_STRING': __version__, - 'AMICI_COMMIT_STRING': __commit__, - 'W_RECURSION_DEPTH': self.model._w_recursion_depth, - 'QUADRATIC_LLH': 'true' - if self.model._has_quadratic_nllh else 'false', - } - - for func_name, func_info in self.functions.items(): - if func_name in nobody_functions: - continue - - if not func_info.body: - tpl_data[f'{func_name.upper()}_DEF'] = '' - - if func_name in sensi_functions + sparse_sensi_functions and \ - not self.generate_sensitivity_code: - impl = '' - else: - impl = get_model_override_implementation( - func_name, self.model_name, nobody=True - ) - - tpl_data[f'{func_name.upper()}_IMPL'] = impl - - if func_name in sparse_functions: - for indexfield in ['colptrs', 'rowvals']: - if func_name in sparse_sensi_functions and \ - not self.generate_sensitivity_code: - impl = '' - else: - impl = get_sunindex_override_implementation( - func_name, self.model_name, indexfield, - nobody=True - ) - tpl_data[f'{func_name.upper()}_{indexfield.upper()}_DEF'] \ - = '' - tpl_data[f'{func_name.upper()}_{indexfield.upper()}_IMPL'] \ - = impl - continue - - tpl_data[f'{func_name.upper()}_DEF'] = \ - get_function_extern_declaration(func_name, self.model_name) - tpl_data[f'{func_name.upper()}_IMPL'] = \ - get_model_override_implementation(func_name, self.model_name) - 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_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_IMPL'] = \ - get_sunindex_override_implementation( - func_name, self.model_name, 'rowvals') - - if self.model.num_states_solver() == self.model.num_states_rdata(): - tpl_data['X_RDATA_DEF'] = '' - tpl_data['X_RDATA_IMPL'] = '' - - apply_template( - os.path.join(amiciSrcPath, 'model_header.ODE_template.h'), - os.path.join(self.model_path, f'{self.model_name}.h'), - tpl_data - ) - - apply_template( - os.path.join(amiciSrcPath, 'model.ODE_template.cpp'), - os.path.join(self.model_path, f'{self.model_name}.cpp'), - tpl_data - ) - - def _get_symbol_name_initializer_list(self, name: str) -> str: - """ - Get SBML name initializer list for vector of names for the given - model entity - - :param name: - any key present in self.model._syms - - :return: - Template initializer list of names - """ - return '\n'.join( - [ - f'"{symbol}", // {name}[{idx}]' - for idx, symbol in enumerate(self.model.name(name)) - ] - ) - - def _get_symbol_id_initializer_list(self, name: str) -> str: - """ - Get C++ initializer list for vector of names for the given model - entity - - :param name: - any key present in self.model._syms - - :return: - Template initializer list of ids - """ - return '\n'.join( - [ - f'"{strip_pysb(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 = [ - f + ' ' for f in os.listdir(self.model_path) - if f.endswith('.cpp') and f != 'main.cpp' - ] - - template_data = {'MODELNAME': self.model_name, - 'SOURCES': '\n'.join(sources), - 'AMICI_VERSION': __version__} - apply_template( - MODEL_CMAKE_TEMPLATE_FILE, - os.path.join(self.model_path, 'CMakeLists.txt'), - template_data - ) - - def _write_swig_files(self) -> None: - """ - Write SWIG interface files for this model. - """ - if not os.path.exists(self.model_swig_path): - os.makedirs(self.model_swig_path) - template_data = {'MODELNAME': self.model_name} - apply_template( - os.path.join(amiciSwigPath, 'modelname.template.i'), - os.path.join(self.model_swig_path, self.model_name + '.i'), - template_data - ) - shutil.copy(SWIG_CMAKE_TEMPLATE_FILE, - os.path.join(self.model_swig_path, 'CMakeLists.txt')) - - def _write_module_setup(self) -> None: - """ - Create a distutils setup.py file for compile the model module. - """ - - template_data = {'MODELNAME': self.model_name, - 'AMICI_VERSION': __version__, - 'PACKAGE_VERSION': '0.1.0'} - apply_template(os.path.join(amiciModulePath, 'setup.template.py'), - os.path.join(self.model_path, 'setup.py'), - template_data) - apply_template(os.path.join(amiciModulePath, 'MANIFEST.template.in'), - os.path.join(self.model_path, 'MANIFEST.in'), {}) - # write __init__.py for the model module - if not os.path.exists(os.path.join(self.model_path, self.model_name)): - os.makedirs(os.path.join(self.model_path, self.model_name)) - - apply_template( - os.path.join(amiciModulePath, '__init__.template.py'), - os.path.join(self.model_path, self.model_name, '__init__.py'), - template_data - ) - - def set_paths(self, output_dir: Optional[str] = None) -> None: - """ - Set output paths for the model and create if necessary - - :param output_dir: - relative or absolute path where the generated model - code is to be placed. If ``None``, this will default to - `amici-{self.model_name}` in the current working directory. - will be created if it does not exist. - - """ - if output_dir is None: - output_dir = os.path.join(os.getcwd(), - f'amici-{self.model_name}') - - self.model_path = os.path.abspath(output_dir) - self.model_swig_path = os.path.join(self.model_path, 'swig') - - def set_name(self, model_name: str) -> None: - """ - Sets the model name - - :param model_name: - name of the model (may only contain upper and lower case letters, - digits and underscores, and must not start with a digit) - - """ - if not is_valid_identifier(model_name): - raise ValueError( - f"'{model_name}' is not a valid model name. " - "Model name may only contain upper and lower case letters, " - "digits and underscores, and must not start with a digit.") - - self.model_name = model_name - - -class TemplateAmici(Template): - """ - Template format used in AMICI (see string.template for more details). - - :cvar delimiter: - delimiter that identifies template variables - - """ - delimiter = 'TPL_' - - -def apply_template(source_file: str, - target_file: str, - 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 strip_pysb(symbol: sp.Basic) -> sp.Basic: - """ - Strips pysb info from a :class:`pysb.Component` object - - :param symbol: - symbolic expression - - :return: - stripped expression - """ - # strip pysb type and transform into a flat sympy.Symbol. - # this ensures that the pysb type specific __repr__ is used when converting - # to string - if pysb and isinstance(symbol, pysb.Component): - return sp.Symbol(symbol.name, real=True) - else: - # in this case we will use sympy specific transform anyways - return symbol - - -def get_function_extern_declaration(fun: str, name: str) -> str: - """ - Constructs the extern function declaration for a given function - - :param fun: - function name - :param name: - model name - - :return: - c++ function definition string - """ - f = functions[fun] - return f'extern {f.return_type} {fun}_{name}({f.arguments});' - - -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, - 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 - """ - impl = '{return_type} f{fun}({signature}) override {{' - - if nobody: - impl += '}}\n' - else: - impl += '\n{ind8}{fun}_{name}({eval_signature});\n{ind4}}}\n' - - func_info = functions[fun] - - return impl.format( - ind4=' ' * 4, - ind8=' ' * 8, - fun=fun, - name=name, - signature=func_info.arguments, - eval_signature=remove_typedefs(func_info.arguments), - return_type=func_info.return_type - ) - - -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_typedefs(signature: str) -> str: - """ - Strips typedef info 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 - typedefs = [ - 'const realtype *', - 'const double *', - 'const realtype ', - 'double *', - 'realtype *', - 'const int ', - 'int ', - 'SUNMatrixContent_Sparse ', - 'gsl::span' - ] - - for typedef in typedefs: - signature = signature.replace(typedef, '') - - return signature - - -def is_valid_identifier(x: str) -> bool: - """ - Check whether `x` is a valid identifier for conditions, parameters, - observables... . Identifiers may only contain upper and lower case letters, - digits and underscores, and must not start with a digit. - - :param x: - string to check - - :return: - ``True`` if valid, ``False`` otherwise - """ - - return re.match(r'^[a-zA-Z_]\w*$', x) is not None - - -def generate_measurement_symbol(observable_id: Union[str, sp.Symbol]): - """ - Generates the appropriate measurement symbol for the provided observable - - :param observable_id: - symbol (or string representation) of the observable - - :return: - symbol for the corresponding measurement - """ - if not isinstance(observable_id, str): - observable_id = strip_pysb(observable_id) - return symbol_with_assumptions(f'm{observable_id}') - - -def generate_flux_symbol( - reaction_index: int, - name: Optional[str] = None -) -> sp.Symbol: - """ - Generate identifier symbol for a reaction flux. - This function will always return the same unique python object for a - given entity. - - :param reaction_index: - index of the reaction to which the flux corresponds - :param name: - an optional identifier of the reaction to which the flux corresponds - :return: - identifier symbol - """ - if name is not None: - return symbol_with_assumptions(name) - - return symbol_with_assumptions(f'flux_r{reaction_index}') - - -def symbol_with_assumptions(name: str): - """ - Central function to create symbols with consistent, canonical assumptions - - :param name: - name of the symbol - - :return: - symbol with canonical assumptions - """ - return sp.Symbol(name, real=True) - - -def cast_to_sym(value: Union[SupportsFloat, sp.Expr, BooleanAtom], - input_name: str) -> sp.Expr: - """ - Typecasts the value to sympy.Float if possible, and ensures the - value is a symbolic expression. - - :param value: - value to be cast - - :param input_name: - name of input variable - - :return: - typecast value - """ - if isinstance(value, (sp.RealNumber, numbers.Number)): - value = sp.Float(float(value)) - elif isinstance(value, BooleanAtom): - value = sp.Float(float(bool(value))) - - if not isinstance(value, sp.Expr): - raise TypeError(f"Couldn't cast {input_name} to sympy.Expr, was " - f"{type(value)}") - - return value - - -@contextlib.contextmanager -def _monkeypatched(obj: object, name: str, patch: Any): - """ - Temporarily monkeypatches an object. - - :param obj: - object to be patched - - :param name: - name of the attribute to be patched - - :param patch: - patched value - """ - pre_patched_value = getattr(obj, name) - setattr(obj, name, patch) - try: - yield object - finally: - setattr(obj, name, pre_patched_value) - - -def _custom_pow_eval_derivative(self, s): - """ - Custom Pow derivative that removes a removable singularity for - self.base == 0 and self.base.diff(s) == 0. This function is intended to - be monkeypatched into sp.Pow._eval_derivative. - - :param self: - sp.Pow class - - :param s: - variable with respect to which the derivative will be computed - """ - dbase = self.base.diff(s) - dexp = self.exp.diff(s) - part1 = sp.Pow(self.base, self.exp - 1) * self.exp * dbase - part2 = self * dexp * sp.log(self.base) - if self.base.is_nonzero or dbase.is_nonzero or part2.is_zero: - # first piece never applies or is zero anyways - return part1 + part2 - - return part1 + sp.Piecewise( - (self.base, sp.And(sp.Eq(self.base, 0), sp.Eq(dbase, 0))), - (part2, True) - ) diff --git a/deps/AMICI/python/amici/pandas.py b/deps/AMICI/python/amici/pandas.py deleted file mode 100644 index 740e32d41..000000000 --- a/deps/AMICI/python/amici/pandas.py +++ /dev/null @@ -1,743 +0,0 @@ -""" -Pandas Wrappers ---------------- -This module contains convenience wrappers that allow for easy interconversion -between C++ objects from :mod:`amici.amici` and pandas DataFrames -""" - -import pandas as pd -import numpy as np -import math -import copy - -from typing import List, Union, Optional, Dict, SupportsFloat -from .numpy import ExpDataView -import amici - -__all__ = [ - 'get_expressions_as_dataframe', - 'getEdataFromDataFrame', - 'getDataObservablesAsDataFrame', - 'getSimulationObservablesAsDataFrame', - 'getSimulationStatesAsDataFrame', - 'getResidualsAsDataFrame' -] - -ExpDatas = Union[ - List[amici.amici.ExpData], List[amici.ExpDataPtr], - amici.amici.ExpData, amici.ExpDataPtr -] -ReturnDatas = Union[ - List[amici.ReturnDataView], amici.ReturnDataView -] - -AmiciModel = Union[amici.ModelPtr, amici.Model] - - -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` - - :param edata_list: - list of instances or single instance - - :return: - list of instance(s) - """ - if isinstance(edata_list, (amici.amici.ExpData, amici.ExpDataPtr)): - return [edata_list] - else: - return edata_list - - -def _process_rdata_list(rdata_list: ReturnDatas) -> List[amici.ReturnDataView]: - """ - Maps single instances of :class:`amici.ReturnData` to lists of - :class:`amici.ReturnData` - - :param rdata_list: - list of instances or single instance - - :return: - list of instance(s) - """ - if isinstance(rdata_list, amici.ReturnDataView): - return [rdata_list] - else: - return rdata_list - - -def getDataObservablesAsDataFrame( - model: AmiciModel, - edata_list: ExpDatas, - by_id: Optional[bool] = False) -> pd.DataFrame: - """ - Write Observables from experimental data as DataFrame. - - :param model: - Model instance. - - :param edata_list: - list of ExpData instances with experimental data. - May also be a single ExpData instance. - - :param by_id: - If True, uses observable ids as column names in the generated - DataFrame, otherwise the possibly more descriptive observable names - are used. - - :return: - pandas DataFrame with conditions/timepoints as rows and observables as - columns. - """ - edata_list = _process_edata_list(edata_list) - - # list of all column names using either ids or names - cols = _get_extended_observable_cols(model, by_id=by_id) - - # aggregate records - dicts = [] - for edata in edata_list: - npdata = ExpDataView(edata) - for i_time, timepoint in enumerate(edata.getTimepoints()): - datadict = { - 'time': timepoint, - 'datatype': 'data' - } - # add observables and noises - for i_obs, obs in enumerate(_get_names_or_ids( - model, 'Observable', by_id=by_id)): - datadict[obs] = npdata['observedData'][i_time, i_obs] - datadict[obs + '_std'] = \ - npdata['observedDataStdDev'][i_time, i_obs] - - # add conditions - _fill_conditions_dict(datadict, model, edata, by_id=by_id) - - dicts.append(datadict) - - return pd.DataFrame.from_records(dicts, columns=cols) - - -def getSimulationObservablesAsDataFrame( - model: amici.Model, - edata_list: ExpDatas, - rdata_list: ReturnDatas, - by_id: Optional[bool] = False -) -> pd.DataFrame: - """ - Write Observables from simulation results as DataFrame. - - :param model: - Model instance. - - :param edata_list: - list of ExpData instances with experimental data. - May also be a single ExpData instance. - - :param rdata_list: - list of ReturnData instances corresponding to ExpData. - May also be a single ReturnData instance. - - :param by_id: - If True, ids are used as identifiers, otherwise the possibly more - descriptive names. - - :return: - pandas DataFrame with conditions/timepoints as rows and observables as - columns. - """ - edata_list = _process_edata_list(edata_list) - rdata_list = _process_rdata_list(rdata_list) - - # list of all column names using either names or ids - cols = _get_extended_observable_cols(model, by_id=by_id) - - # aggregate records - dicts = [] - for edata, rdata in zip(edata_list, rdata_list): - for i_time, timepoint in enumerate(rdata['t']): - datadict = { - 'time': timepoint, - 'datatype': 'simulation', - } - # append simulations - for i_obs, obs in enumerate(_get_names_or_ids( - model, 'Observable', by_id=by_id)): - datadict[obs] = rdata['y'][i_time, i_obs] - datadict[obs + '_std'] = rdata['sigmay'][i_time, i_obs] - - # use edata to fill conditions columns - _fill_conditions_dict(datadict, model, edata, by_id=by_id) - - # append to dataframe - dicts.append(datadict) - - return pd.DataFrame.from_records(dicts, columns=cols) - - -def getSimulationStatesAsDataFrame( - model: amici.Model, - edata_list: ExpDatas, - rdata_list: ReturnDatas, - by_id: Optional[bool] = False) -> pd.DataFrame: - """ - Get model state according to lists of ReturnData and ExpData. - - :param model: - Model instance. - - :param edata_list: - list of ExpData instances with experimental data. - May also be a single ExpData instance. - - :param rdata_list: - list of ReturnData instances corresponding to ExpData. - May also be a single ReturnData instance. - - :param by_id: - If True, ids are used as identifiers, otherwise the possibly more - descriptive names. - - :return: pandas DataFrame with conditions/timepoints as rows and - state variables as columns. - """ - edata_list = _process_edata_list(edata_list) - rdata_list = _process_rdata_list(rdata_list) - - # get conditions and state column names by name or id - cols = _get_state_cols(model, by_id=by_id) - - # aggregate records - dicts = [] - for edata, rdata in zip(edata_list, rdata_list): - for i_time, timepoint in enumerate(rdata['t']): - datadict = { - 'time': timepoint, - } - - # append states - for i_state, state in enumerate( - _get_names_or_ids(model, 'State', by_id=by_id)): - datadict[state] = rdata['x'][i_time, i_state] - - # use data to fill condition columns - _fill_conditions_dict(datadict, model, edata, by_id=by_id) - - # append to dataframe - dicts.append(datadict) - - return pd.DataFrame.from_records(dicts, columns=cols) - - -def get_expressions_as_dataframe( - model: amici.Model, - edata_list: ExpDatas, - rdata_list: ReturnDatas, - by_id: Optional[bool] = False) -> pd.DataFrame: - """ - Get values of model expressions from lists of ReturnData as DataFrame. - - :param model: - Model instance. - - :param edata_list: - list of ExpData instances with experimental data. - May also be a single ExpData instance. - - :param rdata_list: - list of ReturnData instances corresponding to ExpData. - May also be a single ReturnData instance. - - :param by_id: - If True, ids are used as identifiers, otherwise the possibly more - descriptive names. - - :return: pandas DataFrame with conditions/timepoints as rows and - model expressions as columns. - """ - edata_list = _process_edata_list(edata_list) - rdata_list = _process_rdata_list(rdata_list) - - # get conditions and state column names by name or id - cols = _get_expression_cols(model, by_id=by_id) - - # aggregate records - dicts = [] - for edata, rdata in zip(edata_list, rdata_list): - for i_time, timepoint in enumerate(rdata['t']): - datadict = { - 'time': timepoint, - } - - # append expressions - for i_expr, expr in enumerate( - _get_names_or_ids(model, 'Expression', by_id=by_id)): - datadict[expr] = rdata['w'][i_time, i_expr] - - # use data to fill condition columns - _fill_conditions_dict(datadict, model, edata, by_id=by_id) - - # append to dataframe - dicts.append(datadict) - - return pd.DataFrame.from_records(dicts, columns=cols) - - -def getResidualsAsDataFrame(model: amici.Model, - edata_list: ExpDatas, - rdata_list: ReturnDatas, - by_id: Optional[bool] = False) -> pd.DataFrame: - """ - Convert a list of ReturnData and ExpData to pandas DataFrame with - residuals. - - :param model: - Model instance. - - :param edata_list: - list of ExpData instances with experimental data. May also be a - single ExpData instance. - - :param rdata_list: - list of ReturnData instances corresponding to ExpData. May also be a - single ReturnData instance. - - :param by_id: bool, optional (default = False) - If True, ids are used as identifiers, otherwise the possibly more - descriptive names. - - :return: - pandas DataFrame with conditions and residuals. - """ - edata_list = _process_edata_list(edata_list) - rdata_list = _process_rdata_list(rdata_list) - - # create observable and simulation dataframes - df_edata = getDataObservablesAsDataFrame( - model, edata_list, by_id=by_id) - df_rdata = getSimulationObservablesAsDataFrame( - model, edata_list, rdata_list, by_id=by_id) - - # get all column names using names or ids - cols = _get_observable_cols(model, by_id=by_id) - - # aggregate records - dicts = [] - for row in df_rdata.index: - datadict = { - 'time': df_rdata.loc[row]['time'], - 't_presim': df_rdata.loc[row]['t_presim'] - } - - # iterate over observables - for obs in _get_names_or_ids(model, 'Observable', by_id=by_id): - # compute residual and append to dict - datadict[obs] = abs( - (df_edata.loc[row][obs] - df_rdata.loc[row][obs]) / - df_rdata.loc[row][obs + '_std']) - - # iterate over fixed parameters - for par in _get_names_or_ids(model, 'FixedParameter', by_id=by_id): - # fill in conditions - datadict[par] = df_rdata.loc[row][par] - datadict[par + '_preeq'] = df_rdata.loc[row][par + '_preeq'] - datadict[par + '_presim'] = df_rdata.loc[row][par + '_presim'] - - # append to dataframe - dicts.append(datadict) - - return pd.DataFrame.from_records(dicts, columns=cols) - - -def _fill_conditions_dict(datadict: Dict[str, float], - model: AmiciModel, - edata: amici.amici.ExpData, - by_id: bool) -> Dict[str, float]: - """ - Helper function that fills in condition parameters from model and - edata. - - :param datadict: - dictionary in which condition parameters will be inserted - as key value pairs. - - :param model: - Model instance. - - :param edata: - ExpData instance. - - :param by_id: - If True, ids are used as identifiers, otherwise the possibly more - descriptive names. - - :return: - dictionary with filled condition parameters. - - """ - datadict['condition_id'] = edata.id - datadict['t_presim'] = edata.t_presim - - for i_par, par in enumerate( - _get_names_or_ids(model, 'FixedParameter', by_id=by_id)): - if len(edata.fixedParameters): - datadict[par] = edata.fixedParameters[i_par] - else: - datadict[par] = model.getFixedParameters()[i_par] - - if len(edata.fixedParametersPreequilibration): - datadict[par + '_preeq'] = \ - edata.fixedParametersPreequilibration[i_par] - else: - datadict[par + '_preeq'] = np.nan - - if len(edata.fixedParametersPresimulation): - datadict[par + '_presim'] = \ - edata.fixedParametersPresimulation[i_par] - else: - datadict[par + '_presim'] = np.nan - return datadict - - -def _get_extended_observable_cols(model: AmiciModel, - by_id: bool) -> List[str]: - """ - Construction helper for extended observable dataframe headers. - - :param model: - Model instance. - - :param by_id: - If True, ids are used as identifiers, otherwise the possibly more - descriptive names. - - :return: - column names as list. - """ - return \ - ['condition_id', 'time', 'datatype', 't_presim'] + \ - _get_names_or_ids(model, 'FixedParameter', by_id=by_id) + \ - [name + '_preeq' for name in - _get_names_or_ids(model, 'FixedParameter', by_id=by_id)] + \ - [name + '_presim' for name in - _get_names_or_ids(model, 'FixedParameter', by_id=by_id)] + \ - _get_names_or_ids(model, 'Observable', by_id=by_id) + \ - [name + '_std' for name in - _get_names_or_ids(model, 'Observable', by_id=by_id)] - - -def _get_observable_cols(model: AmiciModel, - by_id: bool) -> List[str]: - """ - Construction helper for observable dataframe headers. - - :param model: - Model instance. - - :param by_id: - If True, ids are used as identifiers, otherwise the possibly more - descriptive names. - - :return: - column names as list. - """ - return \ - ['condition_id', 'time', 't_presim'] + \ - _get_names_or_ids(model, 'FixedParameter', by_id=by_id) + \ - [name + '_preeq' for name in - _get_names_or_ids(model, 'FixedParameter', by_id=by_id)] + \ - [name + '_presim' for name in - _get_names_or_ids(model, 'FixedParameter', 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]: - """ - Construction helper for state dataframe headers. - - :param model: - Model instance. - - :param by_id: - If True, ids are used as identifiers, otherwise the possibly more - descriptive names. - - :return: - column names as list. - """ - return \ - ['condition_id', 'time', 't_presim'] + \ - _get_names_or_ids(model, 'FixedParameter', by_id=by_id) + \ - [name + '_preeq' for name in - _get_names_or_ids(model, 'FixedParameter', by_id=by_id)] + \ - [name + '_presim' for name in - _get_names_or_ids(model, 'FixedParameter', 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]: - """Construction helper for expression dataframe headers. - - :param model: - Model instance. - - :param by_id: - If True, ids are used as identifiers, otherwise the possibly more - descriptive names. - - :return: - column names as list. - """ - return \ - ['condition_id', 'time', 't_presim'] + \ - _get_names_or_ids(model, 'FixedParameter', by_id=by_id) + \ - [name + '_preeq' for name in - _get_names_or_ids(model, 'FixedParameter', by_id=by_id)] + \ - [name + '_presim' for name in - _get_names_or_ids(model, 'FixedParameter', 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]: - """ - Obtains a unique list of identifiers for the specified variable. - First tries model.getVariableNames and then uses model.getVariableIds. - - :param model: - Model instance. - - :param variable: - variable name. - - :param by_id: - If True, ids are used as identifiers, otherwise first the possibly - more descriptive names are used. - - :return: - column names as list. - """ - # check whether variable type permitted - variable_options = [ - 'Parameter', 'FixedParameter', 'Observable', 'State', 'Expression' - ] - if variable not in variable_options: - raise ValueError('Variable must be in ' + str(variable_options)) - - # extract attributes - names = list(getattr(model, f'get{variable}Names')()) - ids = list(getattr(model, f'get{variable}Ids')()) - - # find out if model has names and ids - has_names = getattr(model, f'has{variable}Names')() - has_ids = getattr(model, f'has{variable}Ids')() - - # extract labels - if not by_id and has_names and len(set(names)) == len(names): - # use variable names - return names - elif has_ids: - # use variable ids - return ids - else: - # unable to create unique labels - if by_id: - msg = f"Model {variable} ids are not set." - else: - msg = f"Model {variable} names are not unique and " \ - f"{variable} ids are not set." - raise ValueError(msg) - - -def _get_specialized_fixed_parameters( - model: AmiciModel, - condition: Union[Dict[str, SupportsFloat], pd.Series], - overwrite: Union[Dict[str, SupportsFloat], pd.Series], - by_id: bool -) -> List[float]: - """ - Copies values in condition and overwrites them according to key - value pairs specified in overwrite. - - :param model: - Model instance. - :param condition: - fixedParameter values. - :param overwrite: - dict specifying which values in condition are to be replaced. - :param by_id: - bool - If True, ids are used as identifiers, otherwise the possibly more - descriptive names. - - :return: - overwritten FixedParameter as list. - """ - cond = copy.deepcopy(condition) - for field in overwrite: - cond[field] = overwrite[field] - return [float(cond[name]) for name in _get_names_or_ids( - model, 'FixedParameter', by_id=by_id)] - - -def constructEdataFromDataFrame( - df: pd.DataFrame, - model: AmiciModel, - condition: pd.Series, - by_id: Optional[bool] = False -) -> amici.amici.ExpData: - """ - Constructs an ExpData instance according to the provided Model - and DataFrame. - - :param df: - pd.DataFrame with Observable Names/Ids as columns. - Standard deviations may be specified by appending '_std' as suffix. - - :param model: - Model instance. - - :param condition: - pd.Series with FixedParameter Names/Ids as columns. - Preequilibration conditions may be specified by appending - '_preeq' as suffix. Presimulation conditions may be specified by - appending '_presim' as suffix. - - :param by_id: - Indicate whether in the arguments, column headers are based on ids or - names. This should correspond to the way `df` and `condition` was - created in the first place. - - :return: - ExpData instance. - """ - # initialize edata - edata = amici.ExpData(model.get()) - - # timepoints - df = df.sort_values(by='time', ascending=True) - edata.setTimepoints(df['time'].values.astype(float)) - - # get fixed parameters from condition - overwrite_preeq = {} - overwrite_presim = {} - for par in list(_get_names_or_ids(model, 'FixedParameter', by_id=by_id)): - if par + '_preeq' in condition.keys() \ - and not math.isnan(condition[par + '_preeq'].astype(float)): - overwrite_preeq[par] = condition[par + '_preeq'].astype(float) - if par + '_presim' in condition.keys() \ - and not math.isnan(condition[par + '_presim'].astype(float)): - overwrite_presim[par] = condition[par + '_presim'].astype(float) - - # fill in fixed parameters - edata.fixedParameters = condition[ - _get_names_or_ids(model, 'FixedParameter', by_id=by_id) - ].astype(float).values - - # fill in preequilibration parameters - if any([overwrite_preeq[key] != condition[key] for key in - overwrite_preeq]): - edata.fixedParametersPreequilibration = \ - _get_specialized_fixed_parameters( - model, condition, overwrite_preeq, by_id=by_id) - elif len(overwrite_preeq): - edata.fixedParametersPreequilibration = copy.deepcopy( - edata.fixedParameters - ) - - # fill in presimulation parameters - if any([overwrite_presim[key] != condition[key] for key in - overwrite_presim.keys()]): - edata.fixedParametersPresimulation = _get_specialized_fixed_parameters( - model, condition, overwrite_presim, by_id=by_id - ) - elif len(overwrite_presim.keys()): - edata.fixedParametersPresimulation = copy.deepcopy( - edata.fixedParameters - ) - - # fill in presimulation time - if 't_presim' in condition.keys(): - edata.t_presim = float(condition['t_presim']) - - # fill in data and stds - for obs_index, obs in enumerate( - _get_names_or_ids(model, 'Observable', by_id=by_id)): - if obs in df.keys(): - edata.setObservedData(df[obs].values.astype(float), obs_index) - if obs + '_std' in df.keys(): - edata.setObservedDataStdDev( - df[obs + '_std'].values.astype(float), obs_index - ) - - return edata - - -def getEdataFromDataFrame( - model: AmiciModel, - df: pd.DataFrame, - by_id: Optional[bool] = False -) -> List[amici.amici.ExpData]: - """ - Constructs a ExpData instances according to the provided Model and - DataFrame. - - :param df: - dataframe with Observable Names/Ids, FixedParameter Names/Ids - and time as columns. Standard deviations may be specified by - appending '_std' as suffix. Preequilibration fixedParameters may be - specified by appending '_preeq' as suffix. Presimulation - fixedParameters may be specified by appending '_presim' as suffix. - Presimulation time may be specified as 't_presim' column. - - :param model: - Model instance. - - :param by_id: - Whether the column names in `df` are based on ids or names, - corresponding to how the dataframe was created in the first place. - - :return: - list of ExpData instances. - """ - edata_list = [] - - # aggregate features that define a condition - - # fixed parameters - condition_parameters = _get_names_or_ids(model, 'FixedParameter', - by_id=by_id) - # preeq and presim parameters - for par in _get_names_or_ids(model, 'FixedParameter', by_id=by_id): - if par + '_preeq' in df.columns: - condition_parameters.append(par + '_preeq') - if par + '_presim' in df.columns: - condition_parameters.append(par + '_presim') - # presimulation time - if 't_presim' in df.columns: - condition_parameters.append('t_presim') - # drop duplicates to create final conditions - conditions = df[condition_parameters].drop_duplicates() - - # iterate over conditions - for ir, row in conditions.iterrows(): - # subselect rows that match condition - selected = np.ones((len(df),), dtype=bool) - for par_label, par in row.iteritems(): - if math.isnan(par): - selected = selected & np.isnan( - df[par_label].astype(float).values - ) - else: - selected = selected & (df[par_label] == par) - edata_df = df[selected] - - edata_list.append( - constructEdataFromDataFrame(edata_df, model, row, by_id=by_id) - ) - - return edata_list diff --git a/deps/AMICI/python/amici/parameter_mapping.py b/deps/AMICI/python/amici/parameter_mapping.py deleted file mode 100644 index 23e98e0bf..000000000 --- a/deps/AMICI/python/amici/parameter_mapping.py +++ /dev/null @@ -1,381 +0,0 @@ -""" -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 numbers -from typing import Any, Dict, List, Union -from collections.abc import Sequence - -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 - - -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): - for mapping in self.parameter_mappings: - yield mapping - - def __getitem__(self, item): - return self.parameter_mappings[item] - - 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 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. - """ - 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): - """Replace parameter IDs in mapping dicts by values from - problem_parameters where necessary""" - if isinstance(value, str): - # estimated parameter - # (condition table overrides must have been handled already, - # e.g. by the PEtab parameter mapping) - return problem_parameters[value] - 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) - for key, val in map_preeq_fix.items()} - map_sim_fix = {key: _get_par(key, val) - for key, val in map_sim_fix.items()} - map_sim_var = {key: _get_par(key, val) - 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 = parameters - - 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 not 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 not 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]) diff --git a/deps/AMICI/python/amici/petab_import.py b/deps/AMICI/python/amici/petab_import.py deleted file mode 100644 index 4f8ad93c9..000000000 --- a/deps/AMICI/python/amici/petab_import.py +++ /dev/null @@ -1,782 +0,0 @@ -""" -PEtab Import ------------- -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 _collections import OrderedDict -from itertools import chain -from typing import List, Dict, Union, Optional, Tuple - -import amici -import libsbml -import pandas as pd -import petab -import sympy as sp -from amici.logging import get_logger, log_execution_time, set_log_level -from petab.C import * - -try: - from amici.petab_import_pysb import PysbPetabProblem, import_model_pysb -except ModuleNotFoundError: - # pysb not available - PysbPetabProblem = None - import_model_pysb = None - -logger = get_logger(__name__, logging.WARNING) - -# ID of model parameter that is to be added to SBML model to indicate -# preequilibration -PREEQ_INDICATOR_ID = 'preequilibration_indicator' - - -def get_fixed_parameters( - sbml_model: 'libsbml.Model', - condition_df: Optional[pd.DataFrame] = None, - const_species_to_parameters: bool = False) -> List[str]: - """ - Determine, set and return fixed model parameters. - - Parameters specified in `condition_df` are turned into constants. - Only global SBML parameters are considered. Local parameters are ignored. - - :param condition_df: - PEtab condition table. If provided, the respective parameters - will be turned into AMICI constant parameters. - - :param sbml_model: - libsbml.Model instance - - :param const_species_to_parameters: - If `True`, species which are marked constant within the SBML model - will be turned into constant parameters *within* the given - `sbml_model`. - - :return: - List of IDs of parameters which are to be considered constant. - """ - - # 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 - if condition_df is not None: - fixed_parameters = list(condition_df.columns) - # get rid of conditionName column - try: - fixed_parameters.remove(CONDITION_NAME) - except ValueError: - pass - - logger.debug(f'Condition table: {condition_df.shape}') - - # remove overridden parameters (`object`-type columns) - fixed_parameters = [p for p in fixed_parameters - if condition_df[p].dtype != 'O' - and sbml_model.getParameter(p) is not None] - # must be unique - if len(fixed_parameters) != len(set(fixed_parameters)): - raise AssertionError( - 'len(fixed_parameters) != len(set(fixed_parameters))') - else: - fixed_parameters = [] - - # Others are optional - if const_species_to_parameters: - # Turn species which are marked constant in the SBML model into - # parameters - constant_species = constant_species_to_parameters(sbml_model) - - logger.debug("Constant species converted to parameters: " - + str(len(constant_species))) - logger.info("Non-constant species " - + str(len(sbml_model.getListOfSpecies()))) - - # ... and append them to the list of fixed_parameters - for species in constant_species: - if species not in fixed_parameters: - fixed_parameters.append(species) - - # Ensure mentioned parameters exist in the model. Remove additional ones - # from list - for fixed_parameter in fixed_parameters[:]: - # check global parameters - if not sbml_model.getParameter(fixed_parameter) \ - and not sbml_model.getSpecies(fixed_parameter): - logger.warning(f"Parameter or species '{fixed_parameter}'" - " provided in condition table but not present in" - " model. Ignoring.") - fixed_parameters.remove(fixed_parameter) - - if condition_df is None: - return fixed_parameters - - # 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 condition_df - if 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}") - - return 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 constant_species_to_parameters(sbml_model: 'libsbml.Model') -> List[str]: - """ - Convert constant species in the SBML model to constant parameters. - - This can be used e.g. for setting up models with condition-specific - constant species for PEtab, since there it is not possible to specify - constant species in the condition table. - - :param sbml_model: - SBML Model - - :return: - List of IDs of SBML species that have been turned into constants - """ - transformables = [] - for species in sbml_model.getListOfSpecies(): - if not species.getConstant() and not species.getBoundaryCondition(): - continue - - transformables.append(species.getId()) - - return species_to_parameters(transformables, sbml_model) - - -def import_petab_problem( - petab_problem: petab.Problem, - model_output_dir: str = None, - model_name: str = None, - force_compile: bool = False, - **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 SBML 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 kwargs: - Additional keyword arguments to be passed to - :meth:`amici.sbml_import.SbmlImporter.sbml2amici`. - - :return: - The imported model. - """ - # generate folder and model name if necessary - if model_output_dir is None: - if PysbPetabProblem and isinstance(petab_problem, PysbPetabProblem): - raise ValueError("Parameter `model_output_dir` is required.") - - model_output_dir = \ - _create_model_output_dir_name(petab_problem.sbml_model) - else: - model_output_dir = os.path.abspath(model_output_dir) - - if PysbPetabProblem and isinstance(petab_problem, PysbPetabProblem): - if model_name is None: - model_name = petab_problem.pysb_model.name - else: - raise ValueError( - "Argument model_name currently not allowed for pysb models") - elif model_name is None: - model_name = _create_model_name(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 PysbPetabProblem and isinstance(petab_problem, PysbPetabProblem): - import_model_pysb( - petab_problem, - model_output_dir=model_output_dir, - **kwargs) - else: - import_model_sbml( - sbml_model=petab_problem.sbml_model, - condition_table=petab_problem.condition_df, - observable_table=petab_problem.observable_df, - measurement_table=petab_problem.measurement_df, - model_name=model_name, - model_output_dir=model_output_dir, - **kwargs) - - # import model - model_module = amici.import_model_module(model_name, model_output_dir) - model = model_module.getModel() - - logger.info(f"Successfully loaded model {model_name} " - f"from {model_output_dir}.") - - return model - - -def _create_model_output_dir_name(sbml_model: 'libsbml.Model') -> str: - """ - 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 = os.path.abspath("amici_models") - - # create base directory - if not os.path.exists(BASE_DIR): - os.makedirs(BASE_DIR) - - # try sbml model id - sbml_model_id = sbml_model.getId() - if sbml_model_id: - model_output_dir = os.path.join(BASE_DIR, sbml_model_id) - else: - # create random folder name - model_output_dir = tempfile.mkdtemp(dir=BASE_DIR) - - return model_output_dir - - -def _create_model_name(folder: str) -> 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) -> 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, 'libsbml.Model'], - condition_table: Optional[Union[str, pd.DataFrame]] = None, - observable_table: Optional[Union[str, pd.DataFrame]] = None, - measurement_table: Optional[Union[str, pd.DataFrame]] = None, - model_name: Optional[str] = None, - model_output_dir: Optional[str] = None, - verbose: Optional[Union[bool, int]] = True, - allow_reinit_fixpar_initcond: bool = True, - **kwargs) -> amici.SbmlImporter: - """ - Create AMICI model from PEtab problem - - :param sbml_model: - PEtab SBML model or SBML file name. - - :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). - - :param observable_table: - PEtab observable table. - - :param measurement_table: - PEtab measurement table. - - :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.ode_export.ODEExporter`. Must be enabled if initial - states are to be reset after preequilibration. - - :param kwargs: - Additional keyword arguments to be passed to - :meth:`amici.sbml_import.SbmlImporter.sbml2amici`. - - :return: - The created :class:`amici.sbml_import.SbmlImporter` instance. - """ - - set_log_level(logger, verbose) - - logger.info(f"Importing model ...") - - # Get PEtab tables - observable_df = petab.get_observable_df(observable_table) - # to determine fixed parameters - condition_df = petab.get_condition_df(condition_table) - - if observable_df is None: - raise NotImplementedError("PEtab import without observables table " - "is currently not supported.") - - # Model name from SBML ID or filename - if model_name is None: - if isinstance(sbml_model, libsbml.Model): - model_name = sbml_model.getId() - else: - 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(), model_name) - - logger.info(f"Model name is '{model_name}'.\n" - f"Writing model code to '{model_output_dir}'.") - - # Load model - if isinstance(sbml_model, str): - # from file - sbml_reader = libsbml.SBMLReader() - sbml_doc = sbml_reader.readSBMLFromFile(sbml_model) - sbml_model = sbml_doc.getModel() - else: - # Create a copy, because it will be modified by SbmlImporter - sbml_doc = sbml_model.getSBMLDocument().clone() - sbml_model = sbml_doc.getModel() - - show_model_info(sbml_model) - - sbml_importer = amici.SbmlImporter(sbml_model) - sbml_model = sbml_importer.sbml - - allow_n_noise_pars = \ - not petab.lint.observable_table_has_nontrivial_noise_formula( - observable_df - ) - if measurement_table is not None and \ - petab.lint.measurement_table_has_timepoint_specific_mappings( - measurement_table, - 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 observable_df is not None: - observables, noise_distrs, sigmas = \ - get_observation_model(observable_df) - - logger.info(f'Observables: {len(observables)}') - logger.info(f'Sigmas: {len(sigmas)}') - - if not 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).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': - output_parameters[sym] = None - logger.debug(f"Adding output parameters to model: {output_parameters}") - for par in output_parameters.keys(): - petab.add_global_parameter(sbml_model, par) - # - - # 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_${startOrCompartmentID}. - # feels dirty and should be changed (see also #924) - # - initial_states = [col for col in condition_df - if sbml_model.getSpecies(col) is not None] - initial_sizes = [col for col in condition_df - if sbml_model.getCompartment(col) is not None] - fixed_parameters = [] - if len(initial_states) or len(initial_sizes): - # 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("Adding initial assignments for " - f"{initial_sizes + initial_states}") - for assignee_id in chain(initial_sizes, 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(sbml_model=sbml_model, condition_df=condition_df)) - - 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) - - 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 - - # Replace observableIds occurring in error model definition - for observable_id, formula in sigmas.items(): - repl = sp.sympify(formula).subs( - observable_id, observables[observable_id]['formula']) - 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('--flatten', dest='flatten', default=False, - action='store_true', - help='Flatten measurement specific overrides of ' - 'observable and noise parameters') - - # 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) - - # First check for valid PEtab - 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, - verbose=args.verbose) - - -if __name__ == '__main__': - main() diff --git a/deps/AMICI/python/amici/petab_import_pysb.py b/deps/AMICI/python/amici/petab_import_pysb.py deleted file mode 100644 index 3d33906e5..000000000 --- a/deps/AMICI/python/amici/petab_import_pysb.py +++ /dev/null @@ -1,374 +0,0 @@ -""" -PySB-PEtab Import ------------------ -Import a model in the PySB-adapted :mod:`petab` -(https://github.com/PEtab-dev/PEtab) format into AMICI. -""" - -import logging -import os -from itertools import chain -from typing import List, Dict, Union, Optional, Tuple, Iterable - -import libsbml -import petab -import pysb -import sympy as sp -from petab.C import (CONDITION_NAME, OBSERVABLE_TRANSFORMATION, LIN, - OBSERVABLE_FORMULA, NOISE_FORMULA, FORMAT_VERSION, - PARAMETER_FILE, SBML_FILES, CONDITION_FILES, - MEASUREMENT_FILES, VISUALIZATION_FILES, OBSERVABLE_FILES) - -from . import petab_import -from .logging import get_logger, log_execution_time, set_log_level - -logger = get_logger(__name__, logging.WARNING) - - -class PysbPetabProblem(petab.Problem): - """Representation of a PySB-model-based PEtab problem - - This class extends :class:`petab.Problem` with a PySB model. - The model is augmented with the observation model based on the PEtab - observable table. - For now, a dummy SBML model is created which allows used the existing - SBML-PEtab API. - - :ivar pysb_model: - PySB model instance from of this PEtab problem. - - """ - - def __init__(self, pysb_model: 'pysb.Model' = None, *args, **kwargs): - """ - Constructor - - :param pysb_model: PySB model instance for this PEtab problem - :param args: See :meth:`petab.Problem.__init__` - :param kwargs: See :meth:`petab.Problem.__init__` - """ - flatten = kwargs.pop('flatten', False) - super().__init__(*args, **kwargs) - if flatten: - petab.flatten_timepoint_specific_output_overrides(self) - - self.pysb_model: 'pysb.Model' = pysb_model - self._add_observation_model() - - if self.pysb_model is not None: - self.sbml_document, self.sbml_model = \ - create_dummy_sbml( - self.pysb_model, - observable_ids=self.observable_df.index.values - if self.observable_df is not None else None - ) - - def _add_observation_model(self): - """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 - self.pysb_model.components if - isinstance(comp, sp.Symbol)} - for formula in [*self.observable_df[OBSERVABLE_FORMULA], - *self.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, _export=False) - self.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(self.observable_df.index, - self.observable_df[OBSERVABLE_FORMULA], - self.observable_df[NOISE_FORMULA]): - obs_symbol = sp.sympify(observable_formula, locals=local_syms) - if observable_id in self.pysb_model.expressions.keys(): - obs_expr = self.pysb_model.expressions[observable_id] - else: - obs_expr = pysb.Expression(observable_id, obs_symbol, - _export=False) - self.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, _export=False) - self.pysb_model.add_component(sigma_expr) - local_syms[sigma_id] = sigma_expr - - @staticmethod - def from_files(condition_file: str = None, - measurement_file: Union[str, Iterable[str]] = None, - parameter_file: Union[str, List[str]] = None, - visualization_files: Union[str, Iterable[str]] = None, - observable_files: Union[str, Iterable[str]] = None, - pysb_model_file: str = None, - flatten: bool = False) -> 'PysbPetabProblem': - """ - Factory method to load model and tables from files. - - :param condition_file: - PEtab condition table - - :param measurement_file: - PEtab measurement table - - :param parameter_file: - PEtab parameter table - - :param visualization_files: - PEtab visualization tables - - :param observable_files: - PEtab observables tables - - :param pysb_model_file: - PySB model file - - :param flatten: - Flatten the petab problem - - :return: - Petab Problem - """ - - condition_df = measurement_df = parameter_df = visualization_df = None - observable_df = None - - if condition_file: - condition_df = petab.conditions.get_condition_df(condition_file) - - if measurement_file: - # If there are multiple tables, we will merge them - measurement_df = petab.core.concat_tables( - measurement_file, petab.measurements.get_measurement_df) - - if parameter_file: - parameter_df = petab.parameters.get_parameter_df(parameter_file) - - if visualization_files: - # If there are multiple tables, we will merge them - visualization_df = petab.core.concat_tables( - visualization_files, petab.core.get_visualization_df) - - if observable_files: - # If there are multiple tables, we will merge them - observable_df = petab.core.concat_tables( - observable_files, petab.observables.get_observable_df) - from amici.pysb_import import pysb_model_from_path - return PysbPetabProblem( - pysb_model=pysb_model_from_path( - pysb_model_file=pysb_model_file), - condition_df=condition_df, - measurement_df=measurement_df, - parameter_df=parameter_df, - observable_df=observable_df, - visualization_df=visualization_df, - flatten=flatten - ) - - @staticmethod - def from_yaml(yaml_config: Union[Dict, str], - flatten: bool = False) -> 'PysbPetabProblem': - """ - Factory method to load model and tables as specified by YAML file. - - NOTE: The PySB model is currently expected in the YAML file under - ``sbml_files``. - - :param yaml_config: - PEtab configuration as dictionary or YAML file name - - :param flatten: - Flatten the petab problem - - :return: - Petab Problem - """ - from petab.yaml import (load_yaml, is_composite_problem, - assert_single_condition_and_sbml_file) - if isinstance(yaml_config, str): - path_prefix = os.path.dirname(yaml_config) - yaml_config = load_yaml(yaml_config) - else: - path_prefix = "" - - if is_composite_problem(yaml_config): - raise ValueError('petab.Problem.from_yaml() can only be used for ' - 'yaml files comprising a single model. ' - 'Consider using ' - 'petab.CompositeProblem.from_yaml() instead.') - - if yaml_config[FORMAT_VERSION] != petab.__format_version__: - raise ValueError("Provided PEtab files are of unsupported version" - f"{yaml_config[FORMAT_VERSION]}. Expected " - f"{petab.__format_version__}.") - - problem0 = yaml_config['problems'][0] - - assert_single_condition_and_sbml_file(problem0) - - if isinstance(yaml_config[PARAMETER_FILE], list): - parameter_file = [ - os.path.join(path_prefix, f) - for f in yaml_config[PARAMETER_FILE] - ] - else: - parameter_file = os.path.join( - path_prefix, yaml_config[PARAMETER_FILE]) - - return PysbPetabProblem.from_files( - pysb_model_file=os.path.join( - path_prefix, problem0[SBML_FILES][0]), - measurement_file=[os.path.join(path_prefix, f) - for f in problem0[MEASUREMENT_FILES]], - condition_file=os.path.join( - path_prefix, problem0[CONDITION_FILES][0]), - parameter_file=parameter_file, - visualization_files=[ - os.path.join(path_prefix, f) - for f in problem0.get(VISUALIZATION_FILES, [])], - observable_files=[ - os.path.join(path_prefix, f) - for f in problem0.get(OBSERVABLE_FILES, [])], - flatten=flatten - ) - - -def create_dummy_sbml( - pysb_model: 'pysb.Model', - observable_ids: Optional[Iterable[str]] = None -) -> Tuple['libsbml.Model', 'libsbml.SBMLDocument']: - """Create SBML dummy model for to use PySB models with PEtab. - - Model must at least contain PEtab problem parameter and noise parameters - for observables. - - :param pysb_model: PySB model - :param observable_ids: Observable IDs - :return: A dummy SBML model and document. - """ - - import libsbml - - document = libsbml.SBMLDocument(3, 1) - dummy_sbml_model = document.createModel() - dummy_sbml_model.setTimeUnits("second") - dummy_sbml_model.setExtentUnits("mole") - dummy_sbml_model.setSubstanceUnits('mole') - - # mandatory if there are species - c = dummy_sbml_model.createCompartment() - c.setId('dummy_compartment') - c.setConstant(False) - - # parameters are required for parameter mapping - for parameter in pysb_model.parameters: - p = dummy_sbml_model.createParameter() - p.setId(parameter.name) - p.setConstant(True) - p.setValue(0.0) - - # noise parameters are required for every observable - for observable_id in observable_ids: - p = dummy_sbml_model.createParameter() - p.setId(f"noiseParameter1_{observable_id}") - p.setConstant(True) - p.setValue(0.0) - - # pysb observables and expressions are required in case they occur in - # the observableFormula or noiseFormula. - # as this code is only temporary and not performance-critical, we just add - # all of them. we just need an sbml entity with the same ID. sbml species - # seem to be the simplest, as parameters would interfere with parameter - # mapping later on - for component in chain(pysb_model.expressions, pysb_model.observables): - s = dummy_sbml_model.createSpecies() - s.setId(component.name) - s.setInitialAmount(0.0) - s.setHasOnlySubstanceUnits(False) - s.setBoundaryCondition(False) - s.setCompartment('dummy_compartment') - s.setConstant(False) - - return document, dummy_sbml_model - - -@log_execution_time('Importing PEtab model', logger) -def import_model_pysb( - petab_problem: PysbPetabProblem, - model_output_dir: Optional[str] = None, - verbose: Optional[Union[bool, int]] = True, - **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 kwargs: - Additional keyword arguments to be passed to - :meth:`amici.pysb_import.pysb2amici`. - """ - - set_log_level(logger, verbose) - - logger.info(f"Importing model ...") - - observable_table = petab_problem.observable_df - pysb_model = petab_problem.pysb_model - - # For pysb, we only allow parameters in the condition table - # those must be pysb model parameters (either natively, or output - # parameters from measurement or condition table that have been added in - # PysbPetabProblem) - model_parameters = [p.name for p in pysb_model.parameters] - for x in petab_problem.condition_df.columns: - if x == CONDITION_NAME: - continue - - if x not in model_parameters: - raise NotImplementedError( - "For PySB PEtab import, only model parameters, but no states " - "or compartments are allowed in the condition table." - f"Offending column: {x}" - ) - - constant_parameters = petab_import.get_fixed_parameters( - petab_problem.sbml_model, petab_problem.condition_df) - - if observable_table is None: - observables = None - sigmas = None - else: - observables = [expr.name for expr in pysb_model.expressions - if expr.name in observable_table.index] - - sigmas = {obs_id: f"{obs_id}_sigma" for obs_id in observables} - - noise_distrs = petab_import.petab_noise_distributions_to_amici( - observable_table) - - - from amici.pysb_import import pysb2amici - pysb2amici(pysb_model, model_output_dir, verbose=True, - observables=observables, - sigmas=sigmas, - constant_parameters=constant_parameters, - noise_distributions=noise_distrs, - **kwargs) diff --git a/deps/AMICI/python/amici/petab_objective.py b/deps/AMICI/python/amici/petab_objective.py deleted file mode 100644 index b1da55d86..000000000 --- a/deps/AMICI/python/amici/petab_objective.py +++ /dev/null @@ -1,754 +0,0 @@ -""" -PEtab Objective ---------------- -Functionality related to running simulations or evaluating the objective -function as defined by a PEtab problem -""" - -import copy -import logging -import numbers -from typing import (List, Sequence, Optional, Dict, Tuple, Union, Any, - Collection, Iterator) - -import amici -from amici.sbml_import import get_species_initial -import libsbml -import numpy as np -import pandas as pd -import petab -import sympy as sp -from petab.C import * # noqa: F403 - -from . import AmiciModel, AmiciExpData -from .logging import get_logger, log_execution_time -from .petab_import import PREEQ_INDICATOR_ID -from .parameter_mapping import ( - fill_in_parameters, ParameterMappingForCondition, ParameterMapping) - -logger = get_logger(__name__) - - -# string constant definitions -LLH = 'llh' -SLLH = 'sllh' -FIM = 'fim' -S2LLH = 's2llh' -RES = 'res' -SRES = 'sres' -RDATAS = 'rdatas' - - -@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, -) -> Dict[str, Any]: - """Simulate PEtab model. - - :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 `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 `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. - - :return: - Dictionary of - - * cost function value (LLH), - * const function sensitivity w.r.t. parameters (SLLH), - (**NOTE**: Sensitivities are computed for the scaled parameters) - * list of `ReturnData` (RDATAS), - - 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() - - # Get parameters - if problem_parameters is None: - # Use PEtab nominal values as default - problem_parameters = {t.Index: getattr(t, NOMINAL_VALUE) for t in - petab_problem.parameter_df.itertuples()} - scaled_parameters = False - - # 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) - - # Log results - sim_cond = petab_problem.get_simulation_conditions_from_measurement_df() - for i, rdata in enumerate(rdatas): - logger.debug(f"Condition: {sim_cond.iloc[i, :].values}, status: " - f"{rdata['status']}, llh: {rdata['llh']}") - - return { - LLH: llh, - RDATAS: rdatas - } - - -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 `create_parameter_mapping`. - :param simulation_conditions: - Result of `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, Dict], - scaled_parameters: bool, - amici_model: AmiciModel, -) -> ParameterMapping: - """Generate AMICI specific parameter mapping. - - :param petab_problem: - PEtab problem - :param simulation_conditions: - Result of `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. - - :return: - List of the parameter mappings. - """ - if simulation_conditions is None: - simulation_conditions = \ - petab_problem.get_simulation_conditions_from_measurement_df() - - # 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.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.") - - prelim_parameter_mapping = \ - petab_problem.get_optimization_to_simulation_parameter_mapping( - 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 - ) - ) - - 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 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: - 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. - - species_in_condition_table = [ - col for col in petab_problem.condition_df - if petab_problem.sbml_model.getSpecies(col) is not None] - - if species_in_condition_table: - # 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 - - def _set_initial_concentration(condition_id, species_id, init_par_id, - par_map, scale_map): - value = petab.to_float_if_float( - petab_problem.condition_df.loc[condition_id, species_id]) - if pd.isna(value): - value = get_species_initial( - petab_problem.sbml_model.getSpecies(species_id) - ) - try: - value = float(value) - except (ValueError, TypeError): - if sp.nsimplify(value).is_Atom: - # Get rid of multiplication with one - value = sp.nsimplify(value) - else: - raise NotImplementedError( - "Cannot handle non-trivial expressions for " - f"species initial for {species_id}: {value}") - # this should be a parameter ID - value = str(value) - logger.debug(f'The species {species_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.loc[ - value, PARAMETER_SCALE] - - for species_id in species_in_condition_table: - # for preequilibration - init_par_id = f'initial_{species_id}_preeq' - if condition.get(PREEQUILIBRATION_CONDITION_ID): - condition_id = condition[PREEQUILIBRATION_CONDITION_ID] - _set_initial_concentration( - condition_id, species_id, init_par_id, condition_map_preeq, - condition_scale_map_preeq) - 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_{species_id}_sim' - _set_initial_concentration( - condition_id, species_id, init_par_id, condition_map_sim, - condition_scale_map_sim) - - ########################################################################## - # 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 `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() - - edatas = [] - for _, condition in simulation_conditions.iterrows(): - # Create amici.ExpData for each simulation - edata = create_edata_for_condition( - condition=condition, - amici_model=amici_model, - petab_problem=petab_problem, - observable_ids=observable_ids, - ) - edatas.append(edata) - - return edatas - - -def create_edata_for_condition( - condition: Union[Dict, pd.Series], - 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: - pandas.DataFrame row with preequilibrationConditionId and - simulationConditionId. - :param amici_model: - AMICI model - :param petab_problem: - Underlying PEtab problem - :param observable_ids: - List of observable IDs - - :return: - ExpData instance. - """ - # extract measurement table rows for condition - measurement_df = petab.get_rows_for_condition( - measurement_df=petab_problem.measurement_df, condition=condition) - - 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 - species_in_condition_table = [ - col for col in petab_problem.condition_df - if not pd.isna(petab_problem.condition_df.loc[ - condition[SIMULATION_CONDITION_ID], col]) - and petab_problem.sbml_model.getSpecies(col) is not None - ] - if condition.get(PREEQUILIBRATION_CONDITION_ID) \ - and species_in_condition_table: - state_ids = amici_model.getStateIds() - state_idx_reinitalization = [state_ids.index(s) - for s in species_in_condition_table] - 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"{species_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 - `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`. - """ - - df = pd.DataFrame(columns=list(measurement_df.columns)) - - simulation_conditions = petab.get_simulation_conditions( - measurement_df) - - observable_ids = model.getObservableIds() - - # 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['t']) - - # 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 - - # append to dataframe - df = df.append(row_sim, ignore_index=True) - - return df - - -def rdatas_to_simulation_df( - rdatas: Sequence[amici.ReturnData], - model: AmiciModel, - measurement_df: pd.DataFrame) -> pd.DataFrame: - """Create a PEtab simulation dataframe from amici.ReturnDatas. - - See ``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/amici/petab_simulate.py b/deps/AMICI/python/amici/petab_simulate.py deleted file mode 100644 index 54058897b..000000000 --- a/deps/AMICI/python/amici/petab_simulate.py +++ /dev/null @@ -1,110 +0,0 @@ -""" -PEtab Simulate --------------- -Functionality related to the use of AMICI for simulation with PEtab's -Simulator class. - -Use cases: - -- generate data for use with PEtab's plotting methods -- generate synthetic data -""" - -import inspect -import sys -from typing import Callable - -import pandas as pd - -from amici import SensitivityMethod_none -from amici import AmiciModel -from amici.petab_import import import_petab_problem -from amici.petab_objective import (simulate_petab, - rdatas_to_measurement_df, - RDATAS) -import 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/amici/plotting.py b/deps/AMICI/python/amici/plotting.py deleted file mode 100644 index d2917de9f..000000000 --- a/deps/AMICI/python/amici/plotting.py +++ /dev/null @@ -1,90 +0,0 @@ -""" -Plotting --------- -Plotting related functions -""" -from . import ReturnDataView, Model - -import matplotlib.pyplot as plt -from matplotlib.axes import Axes -from typing import Optional, Iterable - - -def plotStateTrajectories( - rdata: ReturnDataView, - state_indices: Optional[Iterable[int]] = None, - ax: Optional[Axes] = None, - model: Model = None -) -> None: - """ - Plot state trajectories - - :param rdata: - AMICI simulation results as returned by - :func:`amici.amici.runAmiciSimulation` - - :param state_indices: - Indices of states for which trajectories are to be plotted - - :param ax: - matplotlib Axes instance to plot into - - :param model: - amici model instance - """ - 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 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') - - -def plotObservableTrajectories( - rdata: ReturnDataView, - observable_indices: Optional[Iterable[int]] = None, - ax: Optional[Axes] = None, - model: Model = None -) -> None: - """ - Plot observable trajectories - - :param rdata: - AMICI simulation results as returned by - :func:`amici.amici.runAmiciSimulation` - - :param observable_indices: - Indices of observables for which trajectories are to be plotted - - :param ax: - matplotlib Axes instance to plot into - - :param model: - amici model instance - """ - 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 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') diff --git a/deps/AMICI/python/amici/pysb_import.py b/deps/AMICI/python/amici/pysb_import.py deleted file mode 100644 index 0d38391e9..000000000 --- a/deps/AMICI/python/amici/pysb_import.py +++ /dev/null @@ -1,1322 +0,0 @@ -""" -PySB Import ------------- -This module provides all necessary functionality to import a model specified -in the :class:`pysb.core.Model` format. -""" - -from .ode_export import ( - ODEExporter, ODEModel, State, Constant, Parameter, Observable, SigmaY, - Expression, LogLikelihood, generate_measurement_symbol -) -from .import_utils import ( - noise_distribution_to_cost_function, _get_str_symbol_identifiers, - noise_distribution_to_observable_transformation, _parse_special_functions -) -import logging -from .logging import get_logger, log_execution_time, set_log_level - -import sympy as sp -import numpy as np -import itertools -import os -import sys - -from typing import ( - List, Union, Dict, Tuple, Set, Iterable, Any, Callable, Optional -) - -CL_Prototype = Dict[str, Dict[str, Any]] -ConservationLaw = Dict[str, Union[str, sp.Basic]] - -import pysb.bng -import pysb -import pysb.pattern - -logger = get_logger(__name__, logging.ERROR) - - -def pysb2amici( - model: pysb.Model, - output_dir: str = 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, - assume_pow_positivity: bool = False, - compiler: str = None, - compute_conservation_laws: bool = True, - compile: bool = True, - simplify: Callable = lambda x: sp.powsimp(x, deep=True), - generate_sensitivity_code: bool = True, -): - r""" - Generate AMICI C++ files for the provided model. - - .. warning:: - **PySB models with Compartments** - - When importing a PySB model with ``pysb.Compartment``\ s, BioNetGen - scales reaction fluxes with the compartment size. Instead of using the - respective symbols, the compartment size Parameter or Expression is - evaluated when generating equations. This may lead to unexpected - results if the compartment size parameter is changed for AMICI - simulations. - - :param model: - pysb model, :attr:`pysb.Model.name` will determine the name of the - generated module - - :param output_dir: - see :meth:`amici.ode_export.ODEExporter.set_paths` - - :param observables: - list of :class:`pysb.core.Expression` or :class:`pysb.core.Observable` - names in the provided model that should be mapped to observables - - :param sigmas: - dict of :class:`pysb.core.Expression` names that should be mapped to - sigmas - - :param noise_distributions: - dict with names of observable Expressions as keys and a noise type - identifier, or a callable generating a custom noise formula string - (see :py:func:`amici.import_utils.noise_distribution_to_cost_function` - ). If nothing is passed for some observable id, a normal model is - assumed as default. - - :param constant_parameters: - list of :class:`pysb.core.Parameter` names that should be mapped as - fixed parameters - - :param verbose: verbosity level for logging, True/False default to - :attr:`logging.DEBUG`/:attr:`logging.ERROR` - - :param assume_pow_positivity: - if set to ``True``, a special pow function is used to avoid problems - with state variables that may become negative due to numerical - errors - - :param compiler: - distutils/setuptools compiler selection to build the python - extension - - :param compute_conservation_laws: - if set to ``True``, conservation laws are automatically computed and - applied such that the state-jacobian of the ODE right-hand-side has - full rank. This option should be set to ``True`` when using the Newton - algorithm to compute steadystates - - :param compile: - If ``True``, build the python module for the generated model. If false, - just generate the source code. - - :param simplify: - see :attr:`amici.ODEModel._simplify` - - :param generate_sensitivity_code: - if set to ``False``, code for sensitivity computation will not be - generated - """ - if observables is None: - observables = [] - if constant_parameters is None: - constant_parameters = [] - - if sigmas is None: - sigmas = {} - - set_log_level(logger, verbose) - ode_model = ode_model_from_pysb_importer( - model, constant_parameters=constant_parameters, - observables=observables, sigmas=sigmas, - noise_distributions=noise_distributions, - compute_conservation_laws=compute_conservation_laws, - simplify=simplify, - verbose=verbose, - ) - exporter = ODEExporter( - ode_model, - outdir=output_dir, - model_name=model.name, - verbose=verbose, - assume_pow_positivity=assume_pow_positivity, - compiler=compiler, - generate_sensitivity_code=generate_sensitivity_code - ) - exporter.generate_model_code() - - if compile: - exporter.compile_model() - - -@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, - compute_conservation_laws: bool = True, - simplify: Callable = sp.powsimp, - verbose: Union[int, bool] = False, -) -> ODEModel: - """ - Creates an :class:`amici.ODEModel` instance from a :class:`pysb.Model` - instance. - - :param model: - see :func:`amici.pysb_import.pysb2amici` - - :param constant_parameters: - see :func:`amici.pysb_import.pysb2amici` - - :param observables: - see :func:`amici.pysb_import.pysb2amici` - - :param sigmas: - dict with names of observable Expressions as keys and names of sigma - Expressions as value sigma - - :param noise_distributions: - see :func:`amici.pysb_import.pysb2amici` - - :param compute_conservation_laws: - see :func:`amici.pysb_import.pysb2amici` - - :param simplify: - see :attr:`amici.ODEModel._simplify` - - :param verbose: verbosity level for logging, True/False default to - :attr:`logging.DEBUG`/:attr:`logging.ERROR` - - :return: - New ODEModel instance according to pysbModel - """ - - ode = ODEModel(verbose=verbose, simplify=simplify) - - if constant_parameters is None: - constant_parameters = [] - - if observables is None: - observables = [] - - if sigmas is None: - sigmas = {} - - pysb.bng.generate_equations(model, verbose=verbose) - - _process_pysb_species(model, ode) - _process_pysb_parameters(model, ode, constant_parameters) - if compute_conservation_laws: - _process_pysb_conservation_laws(model, ode) - _process_pysb_observables(model, ode, observables, sigmas, - noise_distributions) - _process_pysb_expressions(model, ode, observables, sigmas, - noise_distributions) - ode._has_quadratic_nllh = not noise_distributions or all( - noise_distr in ['normal', 'lin-normal', 'log-normal', 'log10-normal'] - for noise_distr in noise_distributions.values() - ) - - ode.generate_basic_variables() - - return ode - - -@log_execution_time('processing PySB species', logger) -def _process_pysb_species(pysb_model: pysb.Model, - ode_model: ODEModel) -> None: - """ - Converts pysb Species into States and adds them to the ODEModel instance - - :param pysb_model: - pysb model instance - - :param ode_model: - ODEModel instance - """ - xdot = sp.Matrix(pysb_model.odes) - - for ix, specie in enumerate(pysb_model.species): - init = sp.sympify('0.0') - for ic in pysb_model.odes.model.initials: - if pysb.pattern.match_complex_pattern( - ic.pattern, specie, exact=True): - # we don't want to allow expressions in initial conditions - if ic.value in pysb_model.expressions: - init = pysb_model.expressions[ic.value.name].expand_expr() - else: - init = ic.value - - ode_model.add_component( - State( - sp.Symbol(f'__s{ix}'), - f'{specie}', - init, - xdot[ix] - ) - ) - logger.debug(f'Finished Processing PySB species ') - - -@log_execution_time('processing PySB parameters', logger) -def _process_pysb_parameters(pysb_model: pysb.Model, - ode_model: ODEModel, - constant_parameters: List[str]) -> None: - """ - Converts pysb parameters into Parameters or Constants and adds them to - the ODEModel instance - - :param pysb_model: - pysb model - - :param constant_parameters: - list of Parameters that should be constants - - :param ode_model: - ODEModel instance - """ - for par in pysb_model.parameters: - if par.name in constant_parameters: - comp = Constant - else: - comp = Parameter - - ode_model.add_component( - comp(par, f'{par.name}', par.value) - ) - - -@log_execution_time('processing PySB expressions', logger) -def _process_pysb_expressions( - pysb_model: pysb.Model, - ode_model: ODEModel, - observables: List[str], - sigmas: Dict[str, str], - noise_distributions: Optional[Dict[str, Union[str, Callable]]] = None, -) -> None: - r""" - Converts pysb expressions/observables into Observables (with - corresponding standard deviation SigmaY and LogLikelihood) or - Expressions and adds them to the ODEModel instance - - :param pysb_model: - pysb model - - :param observables: - list of names of :class`pysb.Expression`\ s or - :class:`pysb.Observable`\ s that are to be mapped to ODEModel - observables - - :param sigmas: - dict with names of observable pysb.Expressions/pysb.Observables - names as keys and names of sigma pysb.Expressions as values - - :param noise_distributions: - see :func:`amici.pysb_import.pysb2amici` - - :param ode_model: - ODEModel instance - """ - # we no longer expand expressions here. pysb/bng guarantees that - # they are ordered according to their dependency and we can - # evaluate them sequentially without reordering. Important to make - # sure that observables are processed first though. - for expr in pysb_model.expressions: - _add_expression(expr, expr.name, expr.expr, - pysb_model, ode_model, observables, sigmas, - noise_distributions) - - -def _add_expression( - sym: sp.Symbol, - name: str, - expr: sp.Basic, - pysb_model: pysb.Model, - ode_model: ODEModel, - observables: List[str], - sigmas: Dict[str, str], - noise_distributions: Optional[Dict[str, Union[str, Callable]]] = None, -): - """ - Adds expressions to the ODE model given and adds observables/sigmas if - appropriate - - :param sym: - symbol how the expression is referenced in the model - - :param name: - name of the expression - - :param expr: - symbolic expression that the symbol refers to - - :param pysb_model: - see :py:func:`_process_pysb_expressions` - - :param observables: - see :py:func:`_process_pysb_expressions` - - :param sigmas: - see :py:func:`_process_pysb_expressions` - - :param noise_distributions: - see :py:func:`amici.pysb_import.pysb2amici` - - :param ode_model: - see :py:func:`_process_pysb_expressions` - """ - ode_model.add_component( - Expression(sym, name, _parse_special_functions(expr)) - ) - - if name in observables: - noise_dist = noise_distributions.get(name, 'normal') \ - if noise_distributions else 'normal' - - y = sp.Symbol(f'{name}') - trafo = noise_distribution_to_observable_transformation(noise_dist) - obs = Observable(y, name, sym, transformation=trafo) - ode_model.add_component(obs) - - sigma_name, sigma_value = _get_sigma_name_and_value( - pysb_model, name, sigmas - ) - - sigma = sp.Symbol(sigma_name) - ode_model.add_component(SigmaY(sigma, f'{sigma_name}', sigma_value)) - - - cost_fun_str = noise_distribution_to_cost_function(noise_dist)(name) - my = generate_measurement_symbol(obs.get_id()) - cost_fun_expr = sp.sympify(cost_fun_str, - locals=dict(zip( - _get_str_symbol_identifiers(name), - (y, my, sigma)))) - ode_model.add_component( - LogLikelihood( - sp.Symbol(f'llh_{name}'), - f'llh_{name}', - cost_fun_expr - ) - ) - - -def _get_sigma_name_and_value( - 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 - available sets default values - - :param pysb_model: - pysb model - - :param obs_name: - name of the observable - - :param sigmas: - dict of :class:`pysb.core.Expression` names that should be mapped to - sigmas - - :return: - tuple containing symbolic identifier and formula for the specified - observable - """ - if obs_name in sigmas: - sigma_name = sigmas[obs_name] - try: - # find corresponding Expression instance - sigma_expr = next(x for x in pysb_model.expressions - if x.name == sigma_name) - except StopIteration: - raise ValueError(f'value of sigma {obs_name} is not a ' - f'valid expression.') - sigma_value = sigma_expr.expand_expr() - else: - sigma_name = f'sigma_{obs_name}' - sigma_value = sp.sympify(1.0) - - return sigma_name, sigma_value - - -@log_execution_time('processing PySB observables', logger) -def _process_pysb_observables( - pysb_model: pysb.Model, - ode_model: ODEModel, - observables: List[str], - sigmas: Dict[str, str], - noise_distributions: Optional[Dict[str, Union[str, Callable]]] = None, -) -> None: - """ - Converts :class:`pysb.core.Observable` into - :class:`ODEModel.Expressions` and adds them to the ODEModel instance - - :param pysb_model: - pysb model - - :param ode_model: - ODEModel instance - - :param observables: - list of names of pysb.Expressions or pysb.Observables that are to be - mapped to ODEModel observables - - :param sigmas: - dict with names of observable pysb.Expressions/pysb.Observables - names as keys and names of sigma pysb.Expressions as values - - :param noise_distributions: - see :func:`amici.pysb_import.pysb2amici` - """ - # only add those pysb observables that occur in the added - # Observables as expressions - for obs in pysb_model.observables: - _add_expression(obs, obs.name, obs.expand_obs(), - pysb_model, ode_model, observables, sigmas, - noise_distributions) - - -@log_execution_time('computing PySB conservation laws', logger) -def _process_pysb_conservation_laws(pysb_model: pysb.Model, - ode_model: ODEModel) -> None: - """ - Removes species according to conservation laws to ensure that the - jacobian has full rank - - :param pysb_model: - pysb model - - :param ode_model: - ODEModel instance - """ - - monomers_without_conservation_law = set() - for rule in pysb_model.rules: - monomers_without_conservation_law |= \ - _get_unconserved_monomers(rule, pysb_model) - - monomers_without_conservation_law |= \ - _compute_monomers_with_fixed_initial_conditions(pysb_model) - - cl_prototypes = _generate_cl_prototypes( - monomers_without_conservation_law, pysb_model, ode_model - ) - conservation_laws = _construct_conservation_from_prototypes( - cl_prototypes, pysb_model - ) - _add_conservation_for_constant_species(ode_model, conservation_laws) - - _flatten_conservation_laws(conservation_laws) - - for cl in conservation_laws: - ode_model.add_conservation_law(**cl) - - -def _compute_monomers_with_fixed_initial_conditions( - pysb_model: pysb.Model) -> Set[str]: - """ - Computes the set of monomers in a model with species that have fixed - initial conditions - - :param pysb_model: pysb model - - :return: - set of monomer names with fixed initial conditions - """ - monomers_with_fixed_initial_conditions = set() - - for monomer in pysb_model.monomers: - # check if monomer has an initial condition that is fixed (means - # that corresponding state is constant and all conservation - # laws are broken) - if any([ - ic.fixed # true or false - for ic in pysb_model.initials - if monomer.name in extract_monomers(ic.pattern) - ]): - monomers_with_fixed_initial_conditions |= {monomer.name} - - return monomers_with_fixed_initial_conditions - - -def _generate_cl_prototypes(excluded_monomers: Iterable[str], - pysb_model: pysb.Model, - ode_model: ODEModel) -> CL_Prototype: - """ - Constructs a dict that contains preprocessed information for the - construction of conservation laws - - :param excluded_monomers: - list of monomer names for which no prototypes - should be computed - - :param pysb_model: - pysb model - - :param ode_model: - ODEModel instance - - :return: - dict('monomer.name':{'possible_indices': ..., 'target_indices': ...} - """ - cl_prototypes = dict() - - _compute_possible_indices(cl_prototypes, pysb_model, ode_model, - excluded_monomers) - _compute_dependency_idx(cl_prototypes) - _compute_target_index(cl_prototypes, ode_model) - - return cl_prototypes - - -def _compute_possible_indices(cl_prototypes: CL_Prototype, - pysb_model: pysb.Model, - ode_model: ODEModel, - excluded_monomers: Iterable[str]) -> None: - """ - Computes viable choices for target_index, ie species that could be - removed and replaced by an algebraic expression according to the - conservation law - - :param cl_prototypes: - dict in which possible indices will be written - - :param pysb_model: - pysb model - - :param ode_model: - ODEModel instance - - :param excluded_monomers: - monomers for which no conservation laws will be - computed - """ - for monomer in pysb_model.monomers: - if monomer.name not in excluded_monomers: - compartments = [ - str(mp.compartment) # string based comparison as - # compartments are not hashable - for cp in pysb_model.species - for mp in cp.monomer_patterns - if mp.monomer.name == monomer.name - ] - - if len(set(compartments)) > 1: - raise ValueError('Conservation laws involving species in ' - 'multiple compartments are currently not ' - 'supported! Please run pysb2amici with ' - 'compute_conservation_laws=False') - # TODO: implement this, multiply species by the volume of - # their respective compartment and allow total_cl to depend - # on parameters + constants and update the respective symbolic - # derivative accordingly - - prototype = dict() - prototype['possible_indices'] = [ - ix - for ix, specie in enumerate(pysb_model.species) - if monomer.name in extract_monomers(specie) - and not ode_model.state_is_constant(ix) - ] - - prototype['species_count'] = len( - prototype['possible_indices'] - ) - - if prototype['possible_indices']: - cl_prototypes[monomer.name] = prototype - - -def _compute_dependency_idx(cl_prototypes: CL_Prototype) -> None: - """ - Compute connecting species, this allows us to efficiently compute - whether the respective conservation law would induce a cyclic dependency. - Adds a 'dependency_idx' field to the prototype dict that - itself is a dict where keys correspond to indexes that, when used as - target index yield dependencies on conservation laws of monomers in - the respective values - - :param cl_prototypes: - dict in which possible indices will be written - """ - # - for monomer_i, prototype_i in cl_prototypes.items(): - if 'dependency_idx' not in prototype_i: - prototype_i['dependency_idx'] = dict() - - for monomer_j, prototype_j in cl_prototypes.items(): - if monomer_i == monomer_j: - continue - - if 'dependency_idx' not in prototype_j: - prototype_j['dependency_idx'] = dict() - - idx_overlap = set(prototype_i['possible_indices']).intersection( - set(prototype_j['possible_indices']) - ) - if len(idx_overlap) == 0: - continue - - for idx in idx_overlap: - if idx not in prototype_i['dependency_idx']: - prototype_i['dependency_idx'][idx] = set() - - if idx not in prototype_j['dependency_idx']: - prototype_j['dependency_idx'][idx] = set() - - prototype_i['dependency_idx'][idx] |= {monomer_j} - prototype_j['dependency_idx'][idx] |= {monomer_i} - - -def _compute_target_index(cl_prototypes: CL_Prototype, - ode_model: ODEModel) -> None: - """ - Computes the target index for every monomer - - :param cl_prototypes: - dict that contains possible indices for every monomer - - :param ode_model: - ODEModel instance - """ - possible_indices = list(set(list(itertools.chain(*[ - cl_prototypes[monomer]['possible_indices'] - for monomer in cl_prototypes - ])))) - - # Note: currently this function is supposed to also count appearances in - # expressions. However, expressions are currently still empty as they - # are also populated from conservation laws. In case there are many - # state heavy expressions in the model (should not be the case for mass - # action kinetics). This may lead to suboptimal results and could improved. - # As this would require substantial code shuffling, this will only be - # fixed if this becomes an actual problem - appearance_counts = ode_model.get_appearance_counts(possible_indices) - - # in this initial guess we ignore the cost of having cyclic dependencies - # between conservation laws - for monomer in cl_prototypes: - prototype = cl_prototypes[monomer] - # extract monomer specific appearance counts - prototype['appearance_counts'] = \ - [ - appearance_counts[possible_indices.index(idx)] - for idx in prototype['possible_indices'] - ] - # select target index as possible index with minimal appearance count - if len(prototype['appearance_counts']) == 0: - raise RuntimeError(f'Failed to compute conservation law for ' - f'monomer {monomer}') - - idx = np.argmin(prototype['appearance_counts']) - - # remove entries from possible indices and appearance counts so we - # do not consider them again in later iterations - prototype['target_index'] = prototype['possible_indices'].pop(idx) - prototype['appearance_count'] = prototype['appearance_counts'].pop(idx) - - # this is only an approximation as the effective species count - # of other conservation laws may also be affected by the chosen - # target index. As long as the number of unique monomers in - # multimers has a low upper bound and the species count does not - # vary too much across conservation laws, this approximation - # should be fine - prototype['fillin'] = \ - prototype['appearance_count'] * prototype['species_count'] - - # we might end up with the same index for multiple monomers, so loop until - # we have a set of unique target indices - while not _cl_prototypes_are_valid(cl_prototypes): - _greedy_target_index_update(cl_prototypes) - - -def _cl_prototypes_are_valid(cl_prototypes: CL_Prototype) -> bool: - """ - Checks consistency of cl_prototypes by asserting that target indices - are unique and there are no cyclic dependencies - - :param cl_prototypes: - dict that contains dependency and target indexes for - every monomer - """ - # target indices are unique - if len(cl_prototypes) != len(set(_get_target_indices(cl_prototypes))): - return False - # conservation law dependencies are cycle free - if any( - _cl_has_cycle(monomer, cl_prototypes) - for monomer in cl_prototypes - ): - return False - - return True - - -def _cl_has_cycle(monomer: str, cl_prototypes: CL_Prototype) -> bool: - """ - Checks whether monomer has a conservation law that is part of a - cyclic dependency - - :param monomer: - name of monomer for which conservation law is to be checked - - :param cl_prototypes: - dict that contains dependency and target indexes for every monomer - - :return: - boolean indicating whether the conservation law is cyclic - """ - - prototype = cl_prototypes[monomer] - - if prototype['target_index'] not in prototype['dependency_idx']: - return False - - visited = [monomer] - root = monomer - return any( - _is_in_cycle( - connecting_monomer, - cl_prototypes, - visited, - root - ) - for connecting_monomer in prototype['dependency_idx'][ - prototype['target_index'] - ] - ) - - -def _is_in_cycle(monomer: str, - cl_prototypes: CL_Prototype, - visited: List[str], - root: str) -> bool: - """ - Recursively checks for cycles in conservation law dependencies via - Depth First Search - - :param monomer: - current location in cl dependency graph - - :param cl_prototypes: - dict that contains dependency and target indexes for - every monomer - - :param visited: - history of visited monomers with conservation laws - - :param root: - monomer at which the cycle search was started - - :return: - boolean indicating whether the specified monomer is part of a cyclic - conservation law - - """ - if monomer == root: - return True # we found a cycle and root is part of it - - if monomer in visited: - return False # we found a cycle but root is not part of it - - visited.append(monomer) - - prototype = cl_prototypes[monomer] - - if prototype['target_index'] not in prototype['dependency_idx']: - return False - - return any( - _is_in_cycle( - connecting_monomer, - cl_prototypes, - visited, - root - ) - for connecting_monomer in prototype['dependency_idx'][ - prototype['target_index'] - ] - ) - - -def _greedy_target_index_update(cl_prototypes: CL_Prototype) -> None: - """ - Computes unique target indices for conservation laws from possible - indices such that expected fill in in symbolic derivatives is minimized - - :param cl_prototypes: - dict that contains possible indices and non-unique target indices - for every monomer - """ - - target_indices = _get_target_indices(cl_prototypes) - - for monomer, prototype in cl_prototypes.items(): - if target_indices.count(prototype['target_index']) > 1 or \ - _cl_has_cycle(monomer, cl_prototypes): - # compute how much fillin the next best target_index would yield - - # we exclude already existing target indices to avoid that - # updating the target index removes uniqueness from already unique - # target indices, this may slightly reduce chances of finding a - # solution but prevents infinite loops - for target_index in list(set(target_indices)): - try: - local_idx = prototype['possible_indices'].index( - target_index - ) - except ValueError: - local_idx = None - - if local_idx: - del prototype['possible_indices'][local_idx] - del prototype['appearance_counts'][local_idx] - - if len(prototype['possible_indices']) == 0: - prototype['diff_fillin'] = -1 - continue - - idx = np.argmin(prototype['appearance_counts']) - - prototype['local_index'] = idx - prototype['alternate_target_index'] = \ - prototype['possible_indices'][idx] - prototype['alternate_appearance_count'] = \ - prototype['appearance_counts'][idx] - - prototype['alternate_fillin'] = \ - prototype['alternate_appearance_count'] \ - * prototype['species_count'] - - prototype['diff_fillin'] = \ - prototype['alternate_fillin'] - prototype['fillin'] - else: - prototype['diff_fillin'] = -1 - - if all( - prototype['diff_fillin'] == -1 - for prototype in cl_prototypes.values() - ): - raise RuntimeError('Could not compute a valid set of conservation ' - 'laws for this model!') - - # this puts prototypes with high diff_fillin last - cl_prototypes = sorted( - cl_prototypes.items(), key=lambda kv: kv[1]['diff_fillin'] - ) - cl_prototypes = { - proto[0]: proto[1] - for proto in cl_prototypes - } - - for monomer in cl_prototypes: - prototype = cl_prototypes[monomer] - # we check that we - # A) have an alternative index computed, i.e. that - # that monomer originally had a non-unique target_index - # B) that the target_index still is not unique or part of a cyclic - # dependency. due to the sorting, this will always be the monomer - # with the highest diff_fillin (note that the target index counts - # are recomputed on the fly) - - if prototype['diff_fillin'] > -1 \ - and ( - _get_target_indices(cl_prototypes).count( - prototype['target_index'] - ) > 1 - or _cl_has_cycle(monomer, cl_prototypes) - ): - prototype['fillin'] = prototype['alternate_fillin'] - prototype['target_index'] = prototype['alternate_target_index'] - prototype['appearance_count'] = \ - prototype['alternate_appearance_count'] - - del prototype['possible_indices'][prototype['local_index']] - del prototype['appearance_counts'][prototype['local_index']] - - -def _get_target_indices( - cl_prototypes: CL_Prototype) -> List[List[int]]: - """ - Computes the list target indices for the current - conservation law prototype - - :param cl_prototypes: - dict that contains target indices for every monomer - - :return: - List of lists of target indices - """ - return [ - prototype['target_index'] for prototype in cl_prototypes.values() - ] - - -def _construct_conservation_from_prototypes( - cl_prototypes: CL_Prototype, - pysb_model: pysb.Model -) -> List[ConservationLaw]: - """ - Computes the algebraic expression for the total amount of a given - monomer - - :param cl_prototypes: - see return of :func:`_generate_cl_prototypes` - - :param pysb_model: - pysb model - - :return: - list of dicts describing conservation laws - """ - conservation_laws = [] - for monomer_name in cl_prototypes: - target_index = cl_prototypes[monomer_name]['target_index'] - - # T = sum_i(a_i * x_i) - # x_j = (T - sum_i≠j(a_i * x_i))/a_j - # law: sum_i≠j(a_i * x_i))/a_j - # state: x_j - target_expression = sum( - sp.Symbol(f'__s{ix}') - * extract_monomers(specie).count(monomer_name) - for ix, specie in enumerate(pysb_model.species) - if ix != target_index - ) / extract_monomers(pysb_model.species[ - target_index - ]).count(monomer_name) - # normalize by the stoichiometry of the target species - target_state = sp.Symbol(f'__s{target_index}') - # = x_j - - total_abundance = sp.Symbol(f'tcl__s{target_index}') - # = T/a_j - - state_expr = total_abundance - target_expression - # x_j = T/a_j - sum_i≠j(a_i * x_i)/a_j - - abundance_expr = target_expression + target_state - # T/a_j = sum_i≠j(a_i * x_i)/a_j + x_j - - conservation_laws.append({ - 'state': target_state, - 'total_abundance': total_abundance, - 'state_expr': state_expr, - 'abundance_expr': abundance_expr, - }) - - return conservation_laws - - -def _add_conservation_for_constant_species( - ode_model: ODEModel, - conservation_laws: List[ConservationLaw] -) -> None: - """ - Computes the algebraic expression for the total amount of a given - monomer - - :param ode_model: - ODEModel instance to which the conservation laws will be added - - :param conservation_laws: - see return of :func:`_construct_conservation_from_prototypes` - - """ - - for ix in range(ode_model.num_states_rdata()): - if ode_model.state_is_constant(ix): - target_state = sp.Symbol(f'__s{ix}') - total_abundance = sp.Symbol(f'tcl__s{ix}') - - conservation_laws.append({ - 'state': target_state, - 'total_abundance': total_abundance, - 'state_expr': total_abundance, - 'abundance_expr': target_state, - }) - - -def _flatten_conservation_laws( - conservation_laws: List[ConservationLaw]) -> None: - """ - Flatten the conservation laws such that the state_expr not longer - depend on any states that are replaced by conservation laws - - :param conservation_laws: - see return of :func:`_construct_conservation_from_prototypes` - """ - conservation_law_subs = \ - _get_conservation_law_subs(conservation_laws) - - while len(conservation_law_subs): - for cl in conservation_laws: - if _sub_matches_cl( - conservation_law_subs, - cl['state_expr'], - cl['state'] - ): - # this optimization is done by subs anyways, but we dont - # want to recompute the subs if we did not change anything - valid_subs = _select_valid_cls( - conservation_law_subs, - cl['state'] - ) - if len(valid_subs) > 0: - cl['state_expr'] = cl['state_expr'].subs(valid_subs) - conservation_law_subs = \ - _get_conservation_law_subs(conservation_laws) - - -def _select_valid_cls(subs: Iterable[Tuple[sp.Symbol, sp.Basic]], - state: sp.Symbol) -> List[Tuple[sp.Symbol, sp.Basic]]: - """ - Subselect substitutions such that we do not end up with conservation - laws that are self-referential - - :param subs: - substitutions in tuple format - - :param state: - target symbolic state to which substitutions will be applied - - :return: - list of valid substitutions - """ - return [ - sub - for sub in subs - if str(state) not in [str(symbol) for symbol in sub[1].free_symbols] - ] - - -def _sub_matches_cl(subs: Iterable[Tuple[sp.Symbol, sp.Basic]], - state_expr: sp.Basic, - state: sp.Basic) -> bool: - """ - Checks whether any of the substitutions in subs will be applied to - state_expr - - :param subs: - substitutions in tuple format - - :param state_expr: - target symbolic expressions in which substitutions will be applied - - :param state: target symbolic state to which substitutions will - be applied - - :return: - boolean indicating positive match - """ - - sub_symbols = set( - sub[0] - for sub in subs - if str(state) not in [ - str(symbol) for symbol in sub[1].free_symbols - ] - ) - - return len(sub_symbols.intersection(state_expr.free_symbols)) > 0 - - -def _get_conservation_law_subs( - conservation_laws: List[ConservationLaw] -) -> List[Tuple[sp.Symbol, sp.Basic]]: - """ - Computes a list of (state, law) tuples for conservation laws that still - appear in other conservation laws - - :param conservation_laws: - see return of :func:`_flatten_conservation_laws` - - :return: - list of tuples containing substitution rules to be used with sympy - subs - """ - free_symbols_cl = _conservation_law_variables(conservation_laws) - return [ - (cl['state'], cl['state_expr']) for cl in conservation_laws - if cl['state'] in free_symbols_cl - ] - - -def _conservation_law_variables( - conservation_laws: List[ConservationLaw]) -> Set[sp.Symbol]: - """ - Construct the set of all free variables from a list of conservation laws - - :param conservation_laws: - list of conservation laws - - :return: - free variables in conservation laws - """ - variables = set() - for cl in conservation_laws: - variables |= cl['state_expr'].free_symbols - return variables - - -def has_fixed_parameter_ic(specie: pysb.core.ComplexPattern, - pysb_model: pysb.Model, - ode_model: ODEModel) -> bool: - """ - Wrapper to interface - :meth:`ode_export.ODEModel.state_has_fixed_parameter_initial_condition` - from a pysb specie/model arguments - - :param specie: - pysb species - - :param pysb_model: - pysb model - - :param ode_model: - ODE model - - :return: - ``False`` if the species does not have an initial condition at all. - Otherwise the return value of - :meth:`ode_export.ODEModel.state_has_fixed_parameter_initial_condition` - """ - # ComplexPatterns are not hashable, so we have to compare by string - ic_index = next( - ( - ic - for ic, condition in enumerate(pysb_model.initials) - if pysb.pattern.match_complex_pattern(condition[0], - specie, exact=True) - ), - None - ) - if ic_index is None: - return False - else: - return ode_model.state_has_fixed_parameter_initial_condition( - ic_index - ) - - -def extract_monomers( - complex_patterns: Union[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. - - :param complex_patterns: - (list of) complex pattern(s) - - :return: - list of monomer names - """ - if not isinstance(complex_patterns, list): - complex_patterns = [complex_patterns] - return [ - mp.monomer.name - for cp in complex_patterns - if cp is not None - for mp in cp.monomer_patterns - ] - - -def _get_unconserved_monomers(rule: pysb.Rule, - pysb_model: pysb.Model) -> Set[str]: - """ - Constructs the set of monomer names for which the specified rule changes - the stoichiometry of the monomer in the specified model. - - :param rule: - the pysb rule - - :param pysb_model: - pysb model - - :return: - set of monomer names for which the stoichiometry is not conserved - """ - unconserved_monomers = set() - - if not rule.delete_molecules \ - and len(rule.product_pattern.complex_patterns) == 0: - # if delete_molecules is not True but we have a degradation rule, - # we have to actually go through the reactions that are created by - # the rule - for reaction in [r for r in pysb_model.reactions - if rule.name in r['rule']]: - unconserved_monomers |= _get_changed_stoichiometries( - [pysb_model.species[ix] for ix in reaction['reactants']], - [pysb_model.species[ix] for ix in reaction['products']] - ) - else: - # otherwise we can simply extract all information for the rule - # itself, which is computationally much more efficient - unconserved_monomers |= _get_changed_stoichiometries( - rule.reactant_pattern.complex_patterns, - rule.product_pattern.complex_patterns - ) - - return unconserved_monomers - - -def _get_changed_stoichiometries( - reactants: Union[pysb.ComplexPattern, List[pysb.ComplexPattern]], - products: Union[pysb.ComplexPattern, List[pysb.ComplexPattern]] -) -> Set[str]: - """ - Constructs the set of monomer names which have different - stoichiometries in reactants and products. - - :param reactants: - (list of) complex pattern(s) - :param products: - (list of) complex pattern(s) - - :returns: - set of monomer name for which the stoichiometry changed - """ - - changed_stoichiometries = set() - - reactant_monomers = extract_monomers( - reactants - ) - - product_monomers = extract_monomers( - products - ) - - for monomer in set(reactant_monomers + product_monomers): - if reactant_monomers.count(monomer) != product_monomers.count(monomer): - changed_stoichiometries.add(monomer) - - return changed_stoichiometries - - -def pysb_model_from_path(pysb_model_file: str) -> 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 - :return: The pysb Model instance - """ - - pysb_model_module_name = \ - os.path.splitext(os.path.split(pysb_model_file)[-1])[0] - - import importlib.util - spec = importlib.util.spec_from_file_location( - pysb_model_module_name, pysb_model_file) - module = importlib.util.module_from_spec(spec) - sys.modules[pysb_model_module_name] = module - spec.loader.exec_module(module) - - return module.model diff --git a/deps/AMICI/python/amici/sbml_import.py b/deps/AMICI/python/amici/sbml_import.py deleted file mode 100644 index b86e822be..000000000 --- a/deps/AMICI/python/amici/sbml_import.py +++ /dev/null @@ -1,1962 +0,0 @@ -""" -SBML Import ------------ -This module provides all necessary functionality to import a model specified -in the `Systems Biology Markup Language (SBML) `_. -""" - - -import sympy as sp -import libsbml as sbml -import re -import math -import itertools as itt -import warnings -import logging -import copy -from typing import ( - Dict, List, Callable, Any, Iterable, Union, Optional, -) - -from .import_utils import ( - smart_subs, smart_subs_dict, toposort_symbols, - _get_str_symbol_identifiers, noise_distribution_to_cost_function, - noise_distribution_to_observable_transformation, _parse_special_functions, - _check_unsupported_functions, -) -from .ode_export import ( - ODEExporter, ODEModel, generate_measurement_symbol, - symbol_with_assumptions -) -from .constants import SymbolId -from .logging import get_logger, log_execution_time, set_log_level -from . import has_clibs - - -class SBMLException(Exception): - pass - - -SymbolicFormula = Dict[sp.Symbol, sp.Expr] - - -default_symbols = { - symbol: {} for symbol in SymbolId -} - -ConservationLaw = Dict[str, Union[str, sp.Expr]] - -logger = get_logger(__name__, logging.ERROR) - - -class SbmlImporter: - """ - Class to generate AMICI C++ files for a model provided in the Systems - Biology Markup Language (SBML). - - :ivar show_sbml_warnings: - indicates whether libSBML warnings should be - displayed - - :ivar symbols: - dict carrying symbolic definitions - - :ivar sbml_reader: - - The libSBML sbml reader - - .. warning:: - Not storing this may result in a segfault. - - :ivar sbml_doc: - document carrying the sbml definition - - .. warning:: - Not storing this may result in a segfault. - - :ivar sbml: - SBML model to import - - :ivar compartments: - dict of compartment ids and compartment volumes - - :ivar stoichiometric_matrix: - stoichiometric matrix of the model - - :ivar flux_vector: - reaction kinetic laws - - :ivar flux_ids: - identifiers for elements of flux_vector - - :ivar _local_symbols: - model symbols for sympy to consider during sympification - see `locals`argument in `sympy.sympify` - - :ivar species_assignment_rules: - Assignment rules for species. - Key is symbolic identifier and value is assignment value - - :ivar compartment_assignment_rules: - Assignment rules for compartments. - Key is symbolic identifier and value is assignment value - - :ivar parameter_assignment_rules: - assignment rules for parameters, these parameters are not permissible - for sensitivity analysis - - :ivar initial_assignments: - initial assignments for parameters, these parameters are not - permissible for sensitivity analysis - - :ivar sbml_parser_settings: - sets behaviour of SBML Formula parsing - - """ - - def __init__(self, - sbml_source: Union[str, sbml.Model], - show_sbml_warnings: bool = False, - from_file: bool = True) -> None: - """ - Create a new Model instance. - - :param sbml_source: - Either a path to SBML file where the model is specified, - or a model string as created by sbml.sbmlWriter( - ).writeSBMLToString() or an instance of `libsbml.Model`. - - :param show_sbml_warnings: - Indicates whether libSBML warnings should be displayed. - - :param from_file: - Whether `sbml_source` is a file name (True, default), or an SBML - string - """ - if isinstance(sbml_source, sbml.Model): - self.sbml_doc: sbml.Document = sbml_source.getSBMLDocument() - else: - self.sbml_reader: sbml.SBMLReader = sbml.SBMLReader() - if from_file: - sbml_doc = self.sbml_reader.readSBMLFromFile(sbml_source) - else: - sbml_doc = self.sbml_reader.readSBMLFromString(sbml_source) - self.sbml_doc = sbml_doc - - self.show_sbml_warnings: bool = show_sbml_warnings - - # process document - self._process_document() - - 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._local_symbols: Dict[str, Union[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._reset_symbols() - - # http://sbml.org/Software/libSBML/5.18.0/docs/python-api/classlibsbml_1_1_l3_parser_settings.html#abcfedd34efd3cae2081ba8f42ea43f52 - # 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 - ) - - def _process_document(self) -> None: - """ - Validate and simplify document. - """ - # Ensure we got a valid SBML model, otherwise further processing - # might lead to undefined results - self.sbml_doc.validateSBML() - _check_lib_sbml_errors(self.sbml_doc, self.show_sbml_warnings) - - # apply several model simplifications that make our life substantially - # easier - if self.sbml_doc.getModel().getNumFunctionDefinitions(): - convert_config = sbml.SBMLFunctionDefinitionConverter()\ - .getDefaultProperties() - self.sbml_doc.convert(convert_config) - - convert_config = sbml.SBMLLocalParameterConverter().\ - getDefaultProperties() - self.sbml_doc.convert(convert_config) - - # If any of the above calls produces an error, this will be added to - # the SBMLError log in the sbml document. Thus, it is sufficient to - # check the error log just once after all conversion/validation calls. - _check_lib_sbml_errors(self.sbml_doc, self.show_sbml_warnings) - - def _reset_symbols(self) -> None: - """ - Reset the symbols attribute to default values - """ - self.symbols = copy.deepcopy(default_symbols) - self._local_symbols = dict() - - def sbml2amici(self, - model_name: str = None, - output_dir: str = None, - observables: Dict[str, Dict[str, str]] = None, - constant_parameters: Iterable[str] = None, - sigmas: Dict[str, Union[str, float]] = None, - noise_distributions: Dict[str, Union[str, Callable]] = None, - verbose: Union[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: Callable = lambda x: sp.powsimp(x, deep=True), - log_as_log10: bool = True, - generate_sensitivity_code: bool = True, - **kwargs) -> None: - """ - Generate and compile AMICI C++ files for the model provided to the - constructor. - - The resulting model can be imported as a regular Python module (if - `compile=True`), or used from Matlab or C++ as described in the - documentation of the respective AMICI interface. - - Note that this generates model ODEs for changes in concentrations, not - amounts unless the `hasOnlySubstanceUnits` attribute has been - defined for a particular species. - - Sensitivity analysis for local parameters is enabled by creating - global parameters _{reactionId}_{localParameterName}. - - :param model_name: - name of the model/model directory - - :param output_dir: - see :meth:`amici.ode_export.ODEExporter.set_paths` - - :param observables: - dictionary( observableId:{'name':observableName - (optional), 'formula':formulaString)}) to be added to the model - - :param constant_parameters: - list of SBML Ids identifying constant parameters - - :param sigmas: - dictionary(observableId: sigma value or (existing) parameter name) - - :param noise_distributions: - dictionary(observableId: noise type). - If nothing is passed for some observable id, a normal model is - assumed as default. Either pass a noise type identifier, or a - callable generating a custom noise string. - - :param verbose: - verbosity level for logging, ``True``/``False`` default to - ``logging.Error``/``logging.DEBUG`` - - :param assume_pow_positivity: - if set to ``True``, a special pow function is - used to avoid problems with state variables that may become - negative due to numerical errors - - :param compiler: - distutils/setuptools compiler selection to build the - python extension - - :param allow_reinit_fixpar_initcond: - see :class:`amici.ode_export.ODEExporter` - - :param compile: - If ``True``, compile the generated Python package, - if ``False``, just generate code. - - :param compute_conservation_laws: - if set to ``True``, conservation laws are automatically computed - and applied such that the state-jacobian of the ODE - right-hand-side has full rank. This option should be set to - ``True`` when using the newton algorithm to compute steadystate - sensitivities. - - :param simplify: - see :attr:`ODEModel._simplify` - - :param log_as_log10: - If ``True``, log in the SBML model will be parsed as ``log10`` - (default), if ``False``, log will be parsed as natural logarithm - ``ln`` - - :param generate_sensitivity_code: - If ``False``, the code required for sensitivity computation will - not be generated - - """ - set_log_level(logger, verbose) - - if 'constantParameters' in kwargs: - logger.warning('Use of `constantParameters` as argument name ' - 'is deprecated and will be removed in a future ' - 'version. Please use `constant_parameters` as ' - 'argument name.') - - if constant_parameters is not None: - raise ValueError('Cannot specify constant parameters using ' - 'both `constantParameters` and ' - '`constant_parameters` as argument names.') - - constant_parameters = kwargs.pop('constantParameters', []) - - elif constant_parameters is None: - constant_parameters = [] - constant_parameters = list(constant_parameters) - - if sigmas is None: - sigmas = {} - - if noise_distributions is None: - noise_distributions = {} - - if model_name is None: - model_name = kwargs.pop('modelName', None) - if model_name is None: - raise ValueError('Missing argument: `model_name`') - else: - logger.warning('Use of `modelName` as argument name is ' - 'deprecated and will be removed in a future' - ' version. Please use `model_name` as ' - 'argument name.') - else: - if 'modelName' in kwargs: - raise ValueError('Cannot specify model name using both ' - '`modelName` and `model_name` as argument ' - 'names.') - - if len(kwargs): - raise ValueError(f'Unknown arguments {kwargs.keys()}.') - - self._reset_symbols() - self.sbml_parser_settings.setParseLog( - sbml.L3P_PARSE_LOG_AS_LOG10 if log_as_log10 else - sbml.L3P_PARSE_LOG_AS_LN - ) - self._process_sbml(constant_parameters) - if self.symbols.get(SymbolId.EVENT, False): - if compute_conservation_laws: - logger.warning( - 'Conservation laws are currently not supported for models ' - 'with events, and will be turned off.' - ) - compute_conservation_laws = False - - self._process_observables(observables, sigmas, noise_distributions) - self._replace_compartments_with_volumes() - - self._clean_reserved_symbols() - self._process_time() - - ode_model = ODEModel(verbose=verbose, simplify=simplify) - ode_model.import_from_sbml_importer( - self, compute_cls=compute_conservation_laws) - exporter = ODEExporter( - ode_model, - model_name=model_name, - outdir=output_dir, - verbose=verbose, - assume_pow_positivity=assume_pow_positivity, - compiler=compiler, - allow_reinit_fixpar_initcond=allow_reinit_fixpar_initcond, - generate_sensitivity_code=generate_sensitivity_code - ) - exporter.generate_model_code() - - if compile: - if not has_clibs: - warnings.warn('AMICI C++ extensions have not been built. ' - 'Generated model code, but unable to compile.') - exporter.compile_model() - - @log_execution_time('importing SBML', logger) - def _process_sbml(self, constant_parameters: List[str] = None) -> None: - """ - Read parameters, species, reactions, and so on from SBML model - - :param constant_parameters: - SBML Ids identifying constant parameters - """ - self.check_support() - self._gather_locals() - self._process_parameters(constant_parameters) - self._process_compartments() - self._process_species() - self._process_reactions() - self._process_rules() - self._process_initial_assignments() - self._process_species_references() - self._process_events() - - def check_support(self) -> None: - """ - Check whether all required SBML features are supported. - Also ensures that the SBML contains at least one reaction, or rate - rule, or assignment rule, to produce change in the system over time. - """ - - # Check for required but unsupported SBML extensions - if self.sbml_doc.getLevel() != 3 \ - and hasattr(self.sbml, 'all_elements_from_plugins') \ - and self.sbml.all_elements_from_plugins.getSize(): - raise SBMLException('SBML extensions are currently not supported!') - - if self.sbml_doc.getLevel() == 3: - # the "required" attribute is only available in SBML Level 3 - for i_plugin in range(self.sbml.getNumPlugins()): - plugin = self.sbml.getPlugin(i_plugin) - if plugin.getPackageName() in ('layout',): - # 'layout' plugin does not have the 'required' attribute - continue - if hasattr(plugin, 'getRequired') and not plugin.getRequired(): - # if not "required", this has no impact on model - # simulation, and we can safely ignore it - continue - # Check if there are extension elements. If not, we can safely - # ignore the enabled package - if plugin.getListOfAllElements(): - raise SBMLException( - f'Required SBML extension {plugin.getPackageName()} ' - f'is currently not supported!') - - if any(not rule.isAssignment() and not isinstance( - self.sbml.getElementBySId(rule.getVariable()), - (sbml.Compartment, sbml.Species, sbml.Parameter) - ) for rule in self.sbml.getListOfRules()): - raise SBMLException('Algebraic rules are currently not supported, ' - 'and rate rules are only supported for ' - 'species, compartments, and parameters.') - - if any(not (rule.isAssignment() or rule.isRate()) - and isinstance( - self.sbml.getElementBySId(rule.getVariable()), - (sbml.Compartment, sbml.Species, sbml.Parameter) - ) for rule in self.sbml.getListOfRules()): - raise SBMLException('Only assignment and rate rules are ' - 'currently supported for compartments, ' - 'species, and parameters!') - - if any(r.getFast() for r in self.sbml.getListOfReactions()): - raise SBMLException('Fast reactions are currently not supported!') - - # Check events for unsupported functionality - self.check_event_support() - - def check_event_support(self) -> None: - """ - Check possible events in the model, as AMICI does currently not support - - * delays in events - * priorities of events - * events fired at initial time - - Furthermore, event triggers are optional (e.g., if an event is fired at - initial time, no trigger function is necessary). - In this case, warn that this event will have no effect. - """ - for event in self.sbml.getListOfEvents(): - event_id = event.getId() - # Check for delays in events - delay = event.getDelay() - if delay is not None: - try: - delay_time = float(self._sympy_from_sbml_math(delay)) - if delay_time != 0: - raise ValueError - # `TypeError` would be raised in the above `float(...)` - # if the delay is not a fixed time - except (TypeError, ValueError): - raise SBMLException('Events with execution delays are ' - 'currently not supported in AMICI.') - # Check for priorities - if event.getPriority() is not None: - raise SBMLException(f'Event {event_id} has a priority ' - 'specified. This is currently not ' - 'supported in AMICI.') - - # check trigger - trigger_sbml = event.getTrigger() - if trigger_sbml is None: - logger.warning(f'Event {event_id} trigger has no trigger, ' - 'so will be skipped.') - continue - if trigger_sbml.getMath() is None: - logger.warning(f'Event {event_id} trigger has no trigger ' - 'expression, so a dummy trigger will be set.') - if not trigger_sbml.getInitialValue(): - # True: event not executed if triggered at time == 0 - # (corresponding to AMICI default). Raise if set to False. - raise SBMLException( - f'Event {event_id} has a trigger that has an initial ' - 'value of False. This is currently not supported in AMICI.' - ) - - if not trigger_sbml.getPersistent(): - raise SBMLException( - f'Event {event_id} has a non-persistent trigger.' - 'This is currently not supported in AMICI.' - ) - - @log_execution_time('gathering local SBML symbols', logger) - def _gather_locals(self) -> None: - """ - Populate self.local_symbols with all model entities. - - This is later used during sympifications to avoid sympy builtins - shadowing model entities as well as to avoid possibly costly - symbolic substitutions - """ - self._gather_base_locals() - self._gather_dependent_locals() - - def _gather_base_locals(self): - """ - Populate self.local_symbols with pure symbol definitions that do not - depend on any other symbol. - """ - - special_symbols_and_funs = { - # oo is sympy infinity - 'INF': sp.oo, - 'NaN': sp.nan, - 'rem': sp.Mod, - 'time': symbol_with_assumptions('time'), - # SBML L3 explicitly defines this value, which is not equal - # to the most recent SI definition. - 'avogadro': sp.Float(6.02214179e23), - 'exponentiale': sp.E, - } - for s, v in special_symbols_and_funs.items(): - self.add_local_symbol(s, v) - - for c in itt.chain(self.sbml.getListOfSpecies(), - self.sbml.getListOfParameters(), - self.sbml.getListOfCompartments()): - if not c.isSetId(): - continue - - self.add_local_symbol(c.getId(), _get_identifier_symbol(c)) - - for x_ref in _get_list_of_species_references(self.sbml): - if not x_ref.isSetId(): - continue - if x_ref.isSetStoichiometry() and not \ - self.is_assignment_rule_target(x_ref): - value = sp.Float(x_ref.getStoichiometry()) - else: - value = _get_identifier_symbol(x_ref) - - ia_sym = self._get_element_initial_assignment(x_ref.getId()) - if ia_sym is not None: - value = ia_sym - - self.add_local_symbol(x_ref.getId(), value) - - for r in self.sbml.getListOfReactions(): - for e in itt.chain(r.getListOfReactants(), r.getListOfProducts()): - if isinstance(e, sbml.SpeciesReference): - continue - - if not (e.isSetId() and e.isSetStoichiometry()) or \ - self.is_assignment_rule_target(e): - continue - - self.add_local_symbol(e.getId(), - sp.Float(e.getStoichiometry())) - - def _gather_dependent_locals(self): - """ - Populate self.local_symbols with symbol definitions that may depend on - other symbol definitions. - """ - for r in self.sbml.getListOfReactions(): - if not r.isSetId(): - continue - self.add_local_symbol( - r.getId(), - self._sympy_from_sbml_math(r.getKineticLaw()) - ) - - def add_local_symbol(self, key: str, value: sp.Expr): - """ - Add local symbols with some sanity checking for duplication which - would indicate redefinition of internals, which SBML permits, - but we don't. - - :param key: - local symbol key - - :param value: - local symbol value - """ - if key in itt.chain(self._local_symbols.keys(), - ['True', 'False', 'pi']): - raise SBMLException( - f'AMICI tried to add a local symbol {key} with value {value}, ' - f'but {key} was already instantiated with ' - f'{self._local_symbols[key]}. This means either that there ' - f'are multiple SBML element with SId {key}, which is ' - f'invalid SBML, or that the employed SId {key} is a special ' - 'reserved symbol in AMICI. This can be fixed by renaming ' - f'the element with SId {key}.' - ) - self._local_symbols[key] = value - - @log_execution_time('processing SBML compartments', logger) - def _process_compartments(self) -> None: - """ - Get compartment information, stoichiometric matrix and fluxes from - SBML model. - """ - compartments = self.sbml.getListOfCompartments() - self.compartments = {} - for comp in compartments: - init = sp.Float(1.0) - - if comp.isSetVolume(): - init = self._sympy_from_sbml_math(comp.getVolume()) - - ia_sym = self._get_element_initial_assignment(comp.getId()) - if ia_sym is not None: - init = ia_sym - - self.compartments[_get_identifier_symbol(comp)] = init - - @log_execution_time('processing SBML species', logger) - def _process_species(self) -> None: - """ - Get species information from SBML model. - """ - if self.sbml.isSetConversionFactor(): - conversion_factor = symbol_with_assumptions( - self.sbml.getConversionFactor() - ) - else: - conversion_factor = 1.0 - - for s in self.sbml.getListOfSpecies(): - if self.is_assignment_rule_target(s): - continue - self.symbols[SymbolId.SPECIES][_get_identifier_symbol(s)] = { - 'name': s.getName() if s.isSetName() else s.getId(), - 'compartment': _get_species_compartment_symbol(s), - 'constant': s.getConstant() or s.getBoundaryCondition(), - 'amount': s.getHasOnlySubstanceUnits(), - 'conversion_factor': symbol_with_assumptions( - s.getConversionFactor() - ) - if s.isSetConversionFactor() - else conversion_factor, - 'index': len(self.symbols[SymbolId.SPECIES]), - } - - self._convert_event_assignment_parameter_targets_to_species() - self._process_species_initial() - self._process_rate_rules() - - @log_execution_time('processing SBML species initials', logger) - def _process_species_initial(self): - """ - Extract initial values and initial assignments from species - """ - for species_variable in self.sbml.getListOfSpecies(): - initial = get_species_initial(species_variable) - - species_id = _get_identifier_symbol(species_variable) - # If species_id is a target of an AssignmentRule, species will be - # None, but we don't have to account for the initial definition - # of the species itself and SBML doesn't permit AssignmentRule - # targets to have InitialAssignments. - species = self.symbols[SymbolId.SPECIES].get(species_id, None) - - ia_initial = self._get_element_initial_assignment( - species_variable.getId() - ) - if ia_initial is not None: - if species and species['amount'] \ - and 'compartment' in species: - ia_initial *= self.compartments.get( - species['compartment'], species['compartment'] - ) - initial = ia_initial - if species: - species['init'] = initial - - # don't assign this since they need to stay in order - sorted_species = toposort_symbols(self.symbols[SymbolId.SPECIES], - 'init') - for species in self.symbols[SymbolId.SPECIES].values(): - species['init'] = smart_subs_dict(species['init'], - sorted_species, - 'init') - - @log_execution_time('processing SBML rate rules', logger) - def _process_rate_rules(self): - """ - Process rate rules for species, compartments and parameters. - Compartments and parameters with rate rules are implemented as species. - Note that, in the case of species, rate rules may describe the change - in amount, not concentration, of a species. - """ - rules = self.sbml.getListOfRules() - # compartments with rules are replaced with constants in the relevant - # equations during the _replace_in_all_expressions call inside - # _process_rules - for rule in rules: - if rule.getTypeCode() != sbml.SBML_RATE_RULE: - continue - - variable = symbol_with_assumptions(rule.getVariable()) - formula = self._sympy_from_sbml_math(rule) - if formula is None: - continue - - # Species rules are processed first, to avoid processing - # compartments twice (as compartments with rate rules are - # implemented as species). - ia_init = self._get_element_initial_assignment(rule.getVariable()) - if variable in self.symbols[SymbolId.SPECIES]: - init = self.symbols[SymbolId.SPECIES][variable]['init'] - name = None - - if variable in self.compartments: - init = self.compartments[variable] - name = str(variable) - del self.compartments[variable] - - elif variable in self.symbols[SymbolId.PARAMETER]: - init = self._sympy_from_sbml_math( - self.symbols[SymbolId.PARAMETER][variable]['value'], - ) - name = self.symbols[SymbolId.PARAMETER][variable]['name'] - del self.symbols[SymbolId.PARAMETER][variable] - - # parameter with initial assignment, cannot use - # self.initial_assignments as it is not filled at this - # point - elif ia_init is not None: - init = ia_init - par = self.sbml.getElementBySId(rule.getVariable()) - name = par.getName() if par.isSetName() else par.getId() - - self.add_d_dt(formula, variable, init, name) - - def add_d_dt( - self, - d_dt: sp.Expr, - variable: sp.Symbol, - variable0: Union[float, sp.Expr], - name: str, - ) -> None: - """ - Creates or modifies species, to implement rate rules for - compartments and species, respectively. - - :param d_dt: - The rate rule (or, right-hand side of an ODE). - - :param variable: - The subject of the rate rule. - - :param variable0: - The initial value of the variable. - - :param name: - Species name, only applicable if this function generates a new - species - """ - if variable in self.symbols[SymbolId.SPECIES]: - # only update dt if species was already generated - self.symbols[SymbolId.SPECIES][variable]['dt'] = d_dt - else: - # update initial values - for species_id, species in self.symbols[SymbolId.SPECIES].items(): - variable0 = smart_subs(variable0, species_id, species['init']) - - for species in self.symbols[SymbolId.SPECIES].values(): - species['init'] = smart_subs(species['init'], - variable, variable0) - - # add compartment/parameter species - self.symbols[SymbolId.SPECIES][variable] = { - 'name': name, - 'init': variable0, - 'amount': False, - 'conversion_factor': 1.0, - 'constant': False, - 'index': len(self.symbols[SymbolId.SPECIES]), - 'dt': d_dt, - } - - @log_execution_time('processing SBML parameters', logger) - def _process_parameters(self, - constant_parameters: List[str] = None) -> None: - """ - Get parameter information from SBML model. - - :param constant_parameters: - SBML Ids identifying constant parameters - """ - - if constant_parameters is None: - constant_parameters = [] - - # Ensure specified constant parameters exist in the model - for parameter in constant_parameters: - if not self.sbml.getParameter(parameter): - raise KeyError('Cannot make %s a constant parameter: ' - 'Parameter does not exist.' % parameter) - - fixed_parameters = [ - parameter - for parameter in self.sbml.getListOfParameters() - if parameter.getId() in constant_parameters - ] - for parameter in fixed_parameters: - if self._get_element_initial_assignment(parameter.getId()) is not \ - None or self.is_assignment_rule_target(parameter) or \ - self.is_rate_rule_target(parameter): - raise SBMLException( - f'Cannot turn parameter {parameter.getId()} into a ' - 'constant/fixed parameter since it either has an ' - 'initial assignment or is the target of an assignment or ' - 'rate rule.' - ) - - 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 not self.is_assignment_rule_target(parameter) - ] - - loop_settings = { - SymbolId.PARAMETER: {'var': parameters, 'name': 'parameter'}, - SymbolId.FIXED_PARAMETER: {'var': fixed_parameters, - 'name': 'fixed_parameter'} - } - - for partype, settings in loop_settings.items(): - for par in settings['var']: - self.symbols[partype][_get_identifier_symbol(par)] = { - 'name': par.getName() if par.isSetName() else par.getId(), - 'value': par.getValue() - } - - @log_execution_time('processing SBML reactions', logger) - def _process_reactions(self): - """ - Get reactions from SBML model. - """ - reactions = self.sbml.getListOfReactions() - # nr (number of reactions) should have a minimum length of 1. This is - # to ensure that, if there are no reactions, the stoichiometric matrix - # and flux vector multiply to a zero vector with dimensions (nx, 1). - nr = max(1, len(reactions)) - nx = len(self.symbols[SymbolId.SPECIES]) - # stoichiometric matrix - self.stoichiometric_matrix = sp.SparseMatrix(sp.zeros(nx, nr)) - self.flux_vector = sp.zeros(nr, 1) - # Use reaction IDs as IDs for flux expressions (note that prior to SBML - # level 3 version 2 the ID attribute was not mandatory and may be - # unset) - self.flux_ids = [ - f"flux_{reaction.getId()}" if reaction.isSetId() - else f"flux_r{reaction_idx}" - for reaction_idx, reaction in enumerate(reactions) - ] or ['flux_r0'] - - reaction_ids = [ - reaction.getId() for reaction in reactions - if reaction.isSetId() - ] - - for reaction_index, reaction in enumerate(reactions): - for element_list, sign in [(reaction.getListOfReactants(), -1.0), - (reaction.getListOfProducts(), 1.0)]: - for element in element_list: - stoichiometry = self._get_element_stoichiometry( - element - ) - sbml_species = self.sbml.getSpecies(element.getSpecies()) - if self.is_assignment_rule_target(sbml_species): - continue - species_id = _get_identifier_symbol(sbml_species) - species = self.symbols[SymbolId.SPECIES][species_id] - - if species['constant']: - continue - - # Division by species compartment size (to find the - # rate of change in species concentration) now occurs - # in the `dx_dt` method in "ode_export.py", which also - # accounts for possibly variable compartments. - self.stoichiometric_matrix[species['index'], - reaction_index] += \ - sign * stoichiometry * species['conversion_factor'] - - if reaction.isSetId(): - sym_math = self._local_symbols[reaction.getId()] - else: - sym_math = self._sympy_from_sbml_math(reaction.getKineticLaw()) - - self.flux_vector[reaction_index] = sym_math - if any([ - str(symbol) in reaction_ids - for symbol in self.flux_vector[reaction_index].free_symbols - ]): - raise SBMLException( - 'Kinetic laws involving reaction ids are currently' - ' not supported!' - ) - - @log_execution_time('processing SBML rules', logger) - def _process_rules(self) -> None: - """ - Process Rules defined in the SBML model. - """ - for rule in self.sbml.getListOfRules(): - # rate rules are processed in _process_species - if rule.getTypeCode() == sbml.SBML_RATE_RULE: - continue - - sbml_var = self.sbml.getElementBySId(rule.getVariable()) - sym_id = symbol_with_assumptions(rule.getVariable()) - formula = self._sympy_from_sbml_math(rule) - if formula is None: - continue - - if isinstance(sbml_var, sbml.Species): - self.species_assignment_rules[sym_id] = formula - - elif isinstance(sbml_var, sbml.Compartment): - self.compartment_assignment_rules[sym_id] = formula - self.compartments[sym_id] = formula - - elif isinstance(sbml_var, sbml.Parameter): - self.parameter_assignment_rules[sym_id] = formula - - self.symbols[SymbolId.EXPRESSION][sym_id] = { - 'name': str(sym_id), - 'value': formula - } - - self.symbols[SymbolId.EXPRESSION] = toposort_symbols( - self.symbols[SymbolId.EXPRESSION], 'value' - ) - - # expressions must not occur in definition of x0 - for species in self.symbols[SymbolId.SPECIES].values(): - species['init'] = self._make_initial( - smart_subs_dict(species['init'], - self.symbols[SymbolId.EXPRESSION], - 'value') - ) - - def _process_time(self) -> None: - """ - Convert time_symbol into cpp variable. - """ - sbml_time_symbol = symbol_with_assumptions('time') - amici_time_symbol = symbol_with_assumptions('t') - self.amici_time_symbol = amici_time_symbol - - self._replace_in_all_expressions(sbml_time_symbol, amici_time_symbol) - - def _convert_event_assignment_parameter_targets_to_species(self): - """ - Convert parameters that are targets of event assignments to species. - - This is for the convenience of only implementing event assignments for - "species". - """ - parameter_targets = \ - _collect_event_assignment_parameter_targets(self.sbml) - for parameter_target in parameter_targets: - # Parameter rate rules already exist as species. - if parameter_target in self.symbols[SymbolId.SPECIES]: - continue - if parameter_target in self.parameter_assignment_rules: - raise SBMLException( - 'AMICI does not currently support models with SBML events ' - 'that affect parameters that are also the target of ' - 'assignment rules.' - ) - parameter_def = None - for symbol_id in {SymbolId.PARAMETER, SymbolId.FIXED_PARAMETER}: - if parameter_target in self.symbols[symbol_id]: - # `parameter_target` should only exist in one of the - # `symbol_id` dictionaries. - if parameter_def is not None: - raise AssertionError( - 'Unexpected error. The parameter target of an ' - 'event assignment was processed twice.' - ) - parameter_def = \ - self.symbols[symbol_id].pop(parameter_target) - if parameter_def is None: - raise AssertionError( - 'Unexpected error. The parameter target of an event ' - 'assignment could not be found.' - ) - # Fixed parameters are added as species such that they can be - # targets of events. - self.symbols[SymbolId.SPECIES][parameter_target] = { - 'name': parameter_def['name'], - 'init': sp.Float(parameter_def['value']), - # '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), - } - - @log_execution_time('processing SBML events', logger) - def _process_events(self) -> None: - """Process SBML events.""" - events = self.sbml.getListOfEvents() - - def get_empty_bolus_value() -> sp.Float: - """ - Used in the event update vector for species that are not affected - by the event. - """ - return sp.Symbol('AMICI_EMTPY_BOLUS') - - # Used to update species concentrations when an event affects a - # compartment. - concentration_species_by_compartment = { - symbol_with_assumptions(c.getId()): [] - for c in self.sbml.getListOfCompartments() - } - for species, species_def in self.symbols[SymbolId.SPECIES].items(): - if ( - # Species is a concentration - not species_def.get('amount', True) and - # Species has a compartment - 'compartment' in species_def - ): - concentration_species_by_compartment[ - species_def['compartment'] - ].append(species) - - for ievent, event in enumerate(events): - # get the event id (which is optional unfortunately) - event_id = event.getId() - if event_id is None or event_id == '': - event_id = f'event_{ievent}' - event_sym = sp.Symbol(event_id) - - # get and parse the trigger function - trigger_sbml = event.getTrigger() - 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() - for event_assignment in event_assignments: - variable_sym = \ - symbol_with_assumptions(event_assignment.getVariable()) - if event_assignment.getMath() is None: - # Ignore event assignments with no change in value. - continue - formula = self._sympy_from_sbml_math(event_assignment) - try: - # Try to find the species in the state vector. - index = state_vector.index(variable_sym) - bolus[index] = formula - except ValueError: - raise SBMLException( - '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.' - ) - try: - # Try working with the formula now to detect errors - # here instead of at multiple points downstream. - _ = formula - variable_sym - except TypeError: - raise SBMLException( - 'Could not process event assignment for ' - f'{str(variable_sym)}. AMICI only allows symbolic ' - 'expressions as event assignments.' - ) - if variable_sym in concentration_species_by_compartment: - compartment_event_assignments.add(variable_sym) - - for comp, assignment in \ - self.compartment_assignment_rules.items(): - if variable_sym not in assignment.free_symbols: - continue - compartment_event_assignments.add(comp) - - # 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 species_sym in concentration_species_by_compartment[ - compartment_sym - ]: - # If the species was not affected by an event assignment - # then the old value should be updated. - if ( - bolus[state_vector.index(species_sym)] - == get_empty_bolus_value() - ): - species_value = species_sym - # else the species was affected by an event assignment, - # hence the updated value should be updated further. - else: - species_value = bolus[state_vector.index(species_sym)] - # New species value is old amount / new volume. - bolus[state_vector.index(species_sym)] = ( - species_value * compartment_sym / formula - ) - - # Subtract the current species value from each species with an - # update, as the bolus will be added on to the current species - # value during simulation. - for index in range(len(bolus)): - if bolus[index] != get_empty_bolus_value(): - bolus[index] -= state_vector[index] - bolus[index] = bolus[index].subs(get_empty_bolus_value(), - sp.Float(0.0)) - - self.symbols[SymbolId.EVENT][event_sym] = { - 'name': event_id, - 'value': trigger, - 'state_update': sp.MutableDenseMatrix(bolus), - 'event_observable': None, - } - - @log_execution_time('processing SBML observables', logger) - def _process_observables( - self, - observables: Optional[Dict[str, Dict[str, str]]] = None, - sigmas: Optional[Dict[str, Union[str, float]]] = None, - noise_distributions: Optional[Dict[str, str]] = None - ) -> None: - """ - Perform symbolic computations required for observable and objective - function evaluation. - - :param observables: - dictionary(observableId: {'name':observableName - (optional), 'formula':formulaString)}) - to be added to the model - - :param sigmas: - dictionary(observableId: sigma value or (existing) - parameter name) - - :param noise_distributions: - dictionary(observableId: noise type) - See :py:func:`sbml2amici`. - """ - - if sigmas is None or observables is None: - sigmas = {} - else: - # Ensure no non-existing observableIds have been specified - # (no problem here, but usually an upstream bug) - unknown_ids = set(sigmas.keys()) - set(observables.keys()) - if unknown_ids: - raise ValueError( - f"Sigma provided for unknown observableIds: " - f"{unknown_ids}.") - - if noise_distributions is None or observables is None: - noise_distributions = {} - else: - # Ensure no non-existing observableIds have been specified - # (no problem here, but usually an upstream bug) - unknown_ids = set(noise_distributions.keys()) - \ - set(observables.keys()) - if unknown_ids: - raise ValueError( - f"Noise distribution provided for unknown observableIds: " - f"{unknown_ids}.") - - # add user-provided observables or make all species, and compartments - # with assignment rules, observable - if observables: - self.symbols[SymbolId.OBSERVABLE] = { - symbol_with_assumptions(obs): { - 'name': definition.get('name', f'y{iobs}'), - # Replace logX(.) by log(., X) since sympy cannot parse the - # former. - 'value': self._sympy_from_sbml_math( - definition['formula'] - ), - 'transformation': - noise_distribution_to_observable_transformation( - noise_distributions.get(obs, 'normal') - ) - } - for iobs, (obs, definition) in enumerate(observables.items()) - } - elif observables is None: - self._generate_default_observables() - - self._process_log_likelihood(sigmas, noise_distributions) - - def _generate_default_observables(self): - """ - Generate default observables from species, compartments and - (initial) assignment rules. - """ - self.symbols[SymbolId.OBSERVABLE] = { - symbol_with_assumptions(f'y{species_id}'): { - 'name': specie['name'], - 'value': species_id - } - for ix, (species_id, specie) - in enumerate(self.symbols[SymbolId.SPECIES].items()) - } - - for variable, formula in itt.chain( - self.parameter_assignment_rules.items(), - self.initial_assignments.items(), - self.compartment_assignment_rules.items(), - self.species_assignment_rules.items(), - self.compartments.items() - ): - symbol = symbol_with_assumptions(f'y{variable}') - # Assignment rules take precedence over compartment volume - # definitions, so they need to be evaluated first. - # Species assignment rules always overwrite. - if symbol in self.symbols[SymbolId.OBSERVABLE] \ - and variable not in self.species_assignment_rules: - continue - self.symbols[SymbolId.OBSERVABLE][symbol] = { - 'name': str(variable), 'value': formula - } - - def _process_log_likelihood(self, - sigmas: Dict[str, Union[str, float]], - noise_distributions: Dict[str, str]): - """ - Perform symbolic computations required for objective function - evaluation. - - :param sigmas: - See :py:func:`SBMLImporter._process_observables` - - :param noise_distributions: - See :py:func:`SBMLImporter._process_observables` - """ - - for obs_id, obs in self.symbols[SymbolId.OBSERVABLE].items(): - obs['measurement_symbol'] = generate_measurement_symbol(obs_id) - - self.symbols[SymbolId.SIGMAY] = { - symbol_with_assumptions(f'sigma_{obs_id}'): { - 'name': f'sigma_{obs["name"]}', - 'value': self._sympy_from_sbml_math( - sigmas.get(str(obs_id), '1.0') - ) - } - for obs_id, obs in self.symbols[SymbolId.OBSERVABLE].items() - } - - self.symbols[SymbolId.LLHY] = {} - for (obs_id, obs), (sigma_id, sigma) in zip( - self.symbols[SymbolId.OBSERVABLE].items(), - self.symbols[SymbolId.SIGMAY].items() - ): - symbol = symbol_with_assumptions(f'J{obs_id}') - dist = noise_distributions.get(str(obs_id), 'normal') - cost_fun = noise_distribution_to_cost_function(dist)(obs_id) - value = sp.sympify(cost_fun, locals=dict(zip( - _get_str_symbol_identifiers(obs_id), - (obs_id, obs['measurement_symbol'], sigma_id) - ))) - self.symbols[SymbolId.LLHY][symbol] = { - 'name': f'J{obs["name"]}', - 'value': value, - 'dist': dist, - } - - @log_execution_time('processing SBML initial assignments', logger) - def _process_initial_assignments(self): - """ - Accounts for initial assignments of parameters and species - references. Initial assignments for species and compartments are - processed in :py:func:`amici.SBMLImporter._process_initial_species` and - :py:func:`amici.SBMLImporter._process_compartments` respectively. - """ - for ia in self.sbml.getListOfInitialAssignments(): - identifier = _get_identifier_symbol(ia) - if identifier in itt.chain(self.symbols[SymbolId.SPECIES], - self.compartments): - continue - - sym_math = self._get_element_initial_assignment(ia.getId()) - if sym_math is None: - continue - - sym_math = self._make_initial(smart_subs_dict( - sym_math, self.symbols[SymbolId.EXPRESSION], 'value' - )) - self.initial_assignments[_get_identifier_symbol(ia)] = sym_math - - # sort and flatten - self.initial_assignments = toposort_symbols(self.initial_assignments) - for ia_id, ia in self.initial_assignments.items(): - self.initial_assignments[ia_id] = smart_subs_dict( - ia, self.initial_assignments - ) - - for identifier, sym_math in list(self.initial_assignments.items()): - self._replace_in_all_expressions(identifier, sym_math) - - @log_execution_time('processing SBML species references', logger) - def _process_species_references(self): - """ - Replaces species references that define anything but stoichiometries. - - Species references for stoichiometries are processed in - :py:func:`amici.SBMLImporter._process_reactions`. - """ - # doesnt look like there is a better way to get hold of those lists: - species_references = _get_list_of_species_references(self.sbml) - for species_reference in species_references: - if hasattr(species_reference, 'getStoichiometryMath') and \ - species_reference.getStoichiometryMath() is not None: - raise SBMLException('StoichiometryMath is currently not ' - 'supported for species references.') - if species_reference.getId() == '': - continue - - stoich = self._get_element_stoichiometry(species_reference) - self._replace_in_all_expressions( - _get_identifier_symbol(species_reference), - self._sympy_from_sbml_math(stoich) - ) - - def _make_initial(self, sym_math: Union[sp.Expr, None, float] - ) -> Union[sp.Expr, None, float]: - """ - Transforms an expression to its value at the initial time point by - replacing species by their initial values. - - :param sym_math: - symbolic expression - :return: - transformed expression - """ - - if not isinstance(sym_math, sp.Expr): - return sym_math - - for species_id, species in self.symbols[SymbolId.SPECIES].items(): - 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)) - - return sym_math - - def process_conservation_laws(self, ode_model) -> None: - """ - Find conservation laws in reactions and species. - - :param ode_model: - ODEModel object with basic definitions - - :returns volume_updates_solver: - List (according to reduced stoichiometry) with updates for the - stoichiometric matrix accounting for compartment volumes - """ - conservation_laws = [] - - # So far, only conservation laws for constant species are supported - species_solver = _add_conservation_for_constant_species( - ode_model, conservation_laws - ) - - # Check, whether species_solver is empty now. As currently, AMICI - # cannot handle ODEs without species, CLs must switched in this case - if len(species_solver) == 0: - conservation_laws = [] - species_solver = list(range(ode_model.num_states_rdata())) - - # prune out species from stoichiometry and - self.stoichiometric_matrix = \ - self.stoichiometric_matrix[species_solver, :] - - # add the found CLs to the ode_model - for cl in conservation_laws: - ode_model.add_conservation_law(**cl) - - def _replace_compartments_with_volumes(self): - """ - Replaces compartment symbols in expressions with their respective - (possibly variable) volumes. - """ - for comp, vol in self.compartments.items(): - if comp in self.symbols[SymbolId.SPECIES]: - # for comps with rate rules volume is only initial - for species in self.symbols[SymbolId.SPECIES].values(): - if isinstance(species['init'], sp.Expr): - species['init'] = smart_subs(species['init'], - comp, vol) - continue - self._replace_in_all_expressions(comp, vol) - - def _replace_in_all_expressions(self, - old: sp.Symbol, - new: sp.Expr, - replace_identifiers=False) -> None: - """ - Replace 'old' by 'new' in all symbolic expressions. - - :param old: - symbolic variables to be replaced - - :param new: - replacement symbolic variables - """ - fields = [ - 'stoichiometric_matrix', 'flux_vector', - ] - for field in fields: - if field in dir(self): - self.__setattr__(field, smart_subs( - self.__getattribute__(field), old, new - )) - - dictfields = [ - 'compartment_assignment_rules', 'parameter_assignment_rules', - 'initial_assignments' - ] - for dictfield in dictfields: - d = getattr(self, dictfield) - - # replace identifiers - if old in d and replace_identifiers: - d[new] = d[old] - del d[old] - - if dictfield == 'initial_assignments': - tmp_new = self._make_initial(new) - else: - tmp_new = new - - # replace values - for k in d: - d[k] = smart_subs(d[k], old, tmp_new) - - # replace in identifiers - if replace_identifiers: - for symbol in [SymbolId.EXPRESSION, SymbolId.SPECIES]: - # completely recreate the dict to keep ordering consistent - if old not in self.symbols[symbol]: - continue - self.symbols[symbol] = { - smart_subs(k, old, new): v - for k, v in self.symbols[symbol].items() - } - - for symbol in [SymbolId.OBSERVABLE, SymbolId.LLHY, - SymbolId.SIGMAY]: - if old not in self.symbols[symbol]: - continue - self.symbols[symbol][new] = self.symbols[symbol][old] - del self.symbols[symbol][old] - - # replace in values - for symbol in [SymbolId.OBSERVABLE, SymbolId.LLHY, SymbolId.SIGMAY, - SymbolId.EXPRESSION, SymbolId.EVENT]: - if not self.symbols.get(symbol, None): - continue - for element in self.symbols[symbol].values(): - element['value'] = smart_subs(element['value'], old, new) - - # replace in event state updates (boluses) - if self.symbols.get(SymbolId.EVENT, False): - for event in self.symbols[SymbolId.EVENT].values(): - for index in range(len(event['state_update'])): - event['state_update'][index] = \ - smart_subs(event['state_update'][index], old, new) - - if SymbolId.SPECIES in self.symbols: - for species in self.symbols[SymbolId.SPECIES].values(): - species['init'] = smart_subs(species['init'], - old, self._make_initial(new)) - - fields = ['dt'] - if replace_identifiers: - fields.append('compartment') - - for field in ['dt']: - if field in species: - species[field] = smart_subs(species[field], old, new) - - # Initial compartment volume may also be specified with an assignment - # 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)) - for c, v in self.compartments.items()} - - def _clean_reserved_symbols(self) -> None: - """ - Remove all reserved symbols from self.symbols - """ - reserved_symbols = ['x', 'k', 'p', 'y', 'w', 'h', 't', - 'AMICI_EMPTY_BOLUS'] - for sym in reserved_symbols: - old_symbol = symbol_with_assumptions(sym) - new_symbol = symbol_with_assumptions(f'amici_{sym}') - self._replace_in_all_expressions(old_symbol, new_symbol, - replace_identifiers=True) - for symbols_ids, symbols in self.symbols.items(): - if old_symbol in symbols: - # reconstitute the whole dict in order to keep the ordering - self.symbols[symbols_ids] = { - new_symbol if k is old_symbol else k: v - for k, v in symbols.items() - } - - def _sympy_from_sbml_math(self, var_or_math: [sbml.SBase, str] - ) -> Union[sp.Expr, float, None]: - """ - Sympify Math of SBML variables with all sanity checks and - transformations - - :param var_or_math: - SBML variable that has a getMath() function or math string - :return: - sympfified symbolic expression - """ - if isinstance(var_or_math, sbml.SBase): - math_string = sbml.formulaToL3StringWithSettings( - var_or_math.getMath(), - self.sbml_parser_settings - ) - ele_name = var_or_math.element_name - else: - math_string = var_or_math - ele_name = 'string' - math_string = replace_logx(math_string) - try: - try: - formula = sp.sympify(_parse_logical_operators( - math_string - ), locals=self._local_symbols) - except TypeError as err: - if str(err) == 'BooleanAtom not allowed in this context.': - formula = sp.sympify(_parse_logical_operators( - math_string - ), locals={'true': sp.Float(1.0), 'false': sp.Float(0.0), - **self._local_symbols}) - else: - raise - except (sp.SympifyError, TypeError, ZeroDivisionError) as err: - raise SBMLException(f'{ele_name} "{math_string}" ' - 'contains an unsupported expression: ' - f'{err}.') - - if isinstance(formula, sp.Expr): - formula = _parse_special_functions_sbml(formula) - _check_unsupported_functions_sbml(formula, - expression_type=ele_name) - return formula - - def _get_element_initial_assignment(self, - element_id: str) -> Union[sp.Expr, - None]: - """ - Extract value of sbml variable according to its initial assignment - - :param element_id: - sbml variable name - :return: - - """ - assignment = self.sbml.getInitialAssignment( - element_id - ) - if assignment is None: - return None - sym = self._sympy_from_sbml_math(assignment) - # this is an initial assignment so we need to use - # initial conditions - sym = self._make_initial(sym) - return sym - - def _get_element_stoichiometry(self, - ele: sbml.SBase) -> sp.Expr: - """ - Computes the stoichiometry of a reactant or product of a reaction - - :param ele: - reactant or product - :return: - symbolic variable that defines stoichiometry - """ - if ele.isSetId(): - sym = self._get_element_initial_assignment(ele.getId()) - if sym is not None: - return sym - - if self.is_assignment_rule_target(ele): - return _get_identifier_symbol(ele) - - if ele.isSetStoichiometry(): - return sp.Float(ele.getStoichiometry()) - - return sp.Float(1.0) - - def is_assignment_rule_target(self, element: sbml.SBase) -> bool: - """ - Checks if an element has a valid assignment rule in the specified - model. - - :param element: - SBML variable - - :return: - boolean indicating truth of function name - """ - a = self.sbml.getAssignmentRuleByVariable(element.getId()) - if a is None or self._sympy_from_sbml_math(a) is None: - return False - - return True - - def is_rate_rule_target(self, element: sbml.SBase) -> bool: - """ - Checks if an element has a valid assignment rule in the specified - model. - - :param element: - SBML variable - - :return: - boolean indicating truth of function name - """ - a = self.sbml.getRateRuleByVariable(element.getId()) - if a is None or self._sympy_from_sbml_math(a) is None: - return False - - return True - - -def _check_lib_sbml_errors(sbml_doc: sbml.SBMLDocument, - show_warnings: bool = False) -> None: - """ - Checks the error log in the current self.sbml_doc. - - :param sbml_doc: - SBML document - - :param show_warnings: - display SBML warnings - """ - num_warning = sbml_doc.getNumErrors(sbml.LIBSBML_SEV_WARNING) - num_error = sbml_doc.getNumErrors(sbml.LIBSBML_SEV_ERROR) - num_fatal = sbml_doc.getNumErrors(sbml.LIBSBML_SEV_FATAL) - - if num_warning + num_error + num_fatal: - for i_error in range(0, sbml_doc.getNumErrors()): - error = sbml_doc.getError(i_error) - # we ignore any info messages for now - if error.getSeverity() >= sbml.LIBSBML_SEV_ERROR \ - or (show_warnings and - error.getSeverity() >= sbml.LIBSBML_SEV_WARNING): - logger.error(f'libSBML {error.getCategoryAsString()} ' - f'({error.getSeverityAsString()}):' - f' {error.getMessage()}') - - if num_error + num_fatal: - raise SBMLException( - 'SBML Document failed to load (see error messages above)' - ) - - -def _parse_event_trigger(trigger: sp.Expr) -> sp.Expr: - """ - Recursively translates a boolean trigger function into a real valued - root function - - :param trigger: - :return: real valued root function expression - """ - # Events can be defined without trigger, i.e., the event will never fire. - # In this case, set a dummy trigger: - if trigger is None: - return sp.Float(1.0) - if trigger.is_Relational: - root = trigger.args[0] - trigger.args[1] - _check_unsupported_functions_sbml(root, 'sympy.Expression') - - # convert relational expressions into trigger functions - if isinstance(trigger, sp.core.relational.LessThan) or \ - isinstance(trigger, sp.core.relational.StrictLessThan): - # y < x or y <= x - return -root - if isinstance(trigger, sp.core.relational.GreaterThan) or \ - isinstance(trigger, sp.core.relational.StrictGreaterThan): - # y >= x or y > x - return root - - # or(x,y): any of {x,y} is > 0: sp.Max(x, y) - if isinstance(trigger, sp.Or): - return sp.Max(*[_parse_event_trigger(arg) for arg in trigger.args]) - # and(x,y): all out of {x,y} are > 0: sp.Min(x, y) - if isinstance(trigger, sp.And): - return sp.Min(*[_parse_event_trigger(arg) for arg in trigger.args]) - - raise SBMLException( - 'AMICI can not parse piecewise/event trigger functions with argument ' - f'{trigger}.' - ) - - -def _parse_logical_operators(math_str: Union[str, float, None] - ) -> Union[str, float, None]: - """ - Parses a math string in order to replace logical operators by a form - parsable for sympy - - :param math_str: - str with mathematical expression - :param math_str: - parsed math_str - """ - if not isinstance(math_str, str): - return math_str - - if ' xor(' in math_str or ' Xor(' in math_str: - raise SBMLException('Xor is currently not supported as logical ' - 'operation.') - - return (math_str.replace('&&', '&')).replace('||', '|') - - -def assignmentRules2observables(sbml_model: sbml.Model, - filter_function: Callable = lambda *_: True): - """ - Turn assignment rules into observables. - - :param sbml_model: - Model to operate on - - :param filter_function: - Callback function taking assignment variable as input and returning - ``True``/``False`` to indicate if the respective rule should be - turned into an observable. - - :return: - A dictionary(observableId:{ - 'name': observableName, - 'formula': formulaString - }) - """ - observables = {} - for p in sbml_model.getListOfParameters(): - parameter_id = p.getId() - if filter_function(p): - observables[parameter_id] = { - 'name': p.getName() if p.isSetName() else p.getId(), - 'formula': sbml_model.getAssignmentRuleByVariable( - parameter_id - ).getFormula() - } - - for parameter_id in observables: - sbml_model.removeRuleByVariable(parameter_id) - sbml_model.removeParameter(parameter_id) - - return observables - - -def _add_conservation_for_constant_species( - ode_model: ODEModel, - conservation_laws: List[ConservationLaw] -) -> List[int]: - """ - Adds constant species to conservations laws - - :param ode_model: - ODEModel object with basic definitions - - :param conservation_laws: - List of already known conservation laws - - :returns species_solver: - List of species indices which remain later in the ODE solver - """ - - # decide which species to keep in stoichiometry - species_solver = list(range(ode_model.num_states_rdata())) - - # iterate over species, find constant ones - for ix in reversed(range(ode_model.num_states_rdata())): - if ode_model.state_is_constant(ix): - # dont use sym('x') here since conservation laws need to be - # added before symbols are generated - target_state = ode_model._states[ix].get_id() - total_abundance = symbol_with_assumptions(f'tcl_{target_state}') - conservation_laws.append({ - 'state': target_state, - 'total_abundance': total_abundance, - 'state_expr': total_abundance, - 'abundance_expr': target_state, - }) - # mark species to delete from stoichiometric matrix - species_solver.pop(ix) - - return species_solver - - -def _get_species_compartment_symbol(species: sbml.Species) -> sp.Symbol: - """ - Generate compartment symbol for the compartment of a specific species. - This function will always return the same unique python object for a - given species name. - - :param species: - sbml species - :return: - compartment symbol - """ - return symbol_with_assumptions(species.getCompartment()) - - -def _get_identifier_symbol(var: sbml.SBase) -> sp.Symbol: - """ - Generate identifier symbol for a sbml variable. - This function will always return the same unique python object for a - given entity. - - :param var: - sbml variable - :return: - identifier symbol - """ - return symbol_with_assumptions(var.getId()) - - -def get_species_initial(species: sbml.Species) -> sp.Expr: - """ - Extract the initial concentration from a given species - - :param species: - species index - - :return: - initial species concentration - """ - if species.isSetInitialConcentration(): - conc = species.getInitialConcentration() - if species.getHasOnlySubstanceUnits(): - return sp.Float(conc) * _get_species_compartment_symbol(species) - else: - return sp.Float(conc) - - if species.isSetInitialAmount(): - amt = species.getInitialAmount() - if math.isnan(amt): - return sp.Float(0.0) - - if species.getHasOnlySubstanceUnits(): - return sp.Float(amt) - else: - return sp.Float(amt) / _get_species_compartment_symbol(species) - - return sp.Float(0.0) - - -def _get_list_of_species_references(sbml_model: sbml.Model) \ - -> List[sbml.SpeciesReference]: - """ - Extracts list of species references as SBML doesn't provide a native - function for this. - - :param sbml_model: - SBML model instance - - :return: - ListOfSpeciesReferences - """ - return [ - reference - for element in sbml_model.all_elements - if isinstance(element, sbml.ListOfSpeciesReferences) - for reference in element - ] - - -def replace_logx(math_str: Union[str, float, None]) -> Union[str, float, None]: - """ - Replace logX(.) by log(., X) since sympy cannot parse the former - - :param math_str: - string for sympification - - :return: - sympifiable string - """ - if not isinstance(math_str, str): - return math_str - - 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() - 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(_get_identifier_symbol( - sbml_parameters[sbml_parameter_ids.index(target_id)] - )) - return targets - - -def _check_unsupported_functions_sbml(sym: sp.Expr, - expression_type: str, - full_sym: Optional[sp.Expr] = None): - try: - _check_unsupported_functions(sym, expression_type, full_sym) - except RuntimeError as err: - raise SBMLException(str(err)) - - -def _parse_special_functions_sbml(sym: sp.Expr, - toplevel: bool = True) -> sp.Expr: - try: - return _parse_special_functions(sym, toplevel) - except RuntimeError as err: - raise SBMLException(str(err)) diff --git a/deps/AMICI/python/amici/setup.template.py b/deps/AMICI/python/amici/setup.template.py deleted file mode 100644 index 990b5ea23..000000000 --- a/deps/AMICI/python/amici/setup.template.py +++ /dev/null @@ -1,176 +0,0 @@ -"""AMICI model package setup""" - -import os -import sys -from typing import List - -from amici import amici_path, hdf5_enabled, compiledWithOpenMP -from amici.custom_commands import (set_compiler_specific_extension_options, - compile_parallel) -from amici.setuptools import (get_blas_config, - get_hdf5_config, - add_coverage_flags_if_required, - add_debug_flags_if_required, - add_openmp_flags, - ) -from setuptools import find_packages, setup, Extension -from setuptools.command.build_ext import build_ext - - -class ModelBuildExt(build_ext): - """Custom build_ext""" - - def build_extension(self, ext): - # Work-around for compiler-specific build options - set_compiler_specific_extension_options( - ext, self.compiler.compiler_type) - - - # Monkey-patch compiler instance method for parallel compilation - # except for Windows, where this seems to be incompatible with - # providing swig files. Not investigated further... - if sys.platform != 'win32': - import setuptools._distutils.ccompiler - self.compiler.compile = compile_parallel.__get__( - self.compiler, setuptools._distutils.ccompiler.CCompiler) - - build_ext.build_extension(self, ext) - - def find_swig(self) -> str: - """Find SWIG executable - - Overrides horribly outdated distutils function.""" - - from amici.swig import find_swig - return find_swig() - - -def get_model_sources() -> List[str]: - """Get list of source files for the amici base library""" - import glob - model_sources = glob.glob('*.cpp') - try: - model_sources.remove('main.cpp') - except ValueError: - pass - return model_sources - - -def get_amici_libs() -> List[str]: - """ - Get list of libraries for the amici base library - """ - return ['amici', 'sundials', 'suitesparse'] - - -def get_extension() -> Extension: - """Get setuptools extension object for this AMICI model package""" - - cxx_flags = [] - linker_flags = [] - - if compiledWithOpenMP(): - # Only build model with OpenMP support if AMICI base packages was built - # that way - add_openmp_flags(cxx_flags=cxx_flags, ldflags=linker_flags) - - add_coverage_flags_if_required(cxx_flags, linker_flags) - add_debug_flags_if_required(cxx_flags, linker_flags) - - h5pkgcfg = get_hdf5_config() - - blaspkgcfg = get_blas_config() - linker_flags.extend(blaspkgcfg.get('extra_link_args', [])) - - libraries = [*get_amici_libs(), *blaspkgcfg['libraries']] - if hdf5_enabled: - libraries.extend(['hdf5_hl_cpp', 'hdf5_hl', 'hdf5_cpp', 'hdf5']) - - sources = [os.path.join("swig", "TPL_MODELNAME.i"), *get_model_sources()] - - # compiler and linker flags for libamici - if 'AMICI_CXXFLAGS' in os.environ: - cxx_flags.extend(os.environ['AMICI_CXXFLAGS'].split(' ')) - if 'AMICI_LDFLAGS' in os.environ: - linker_flags.extend(os.environ['AMICI_LDFLAGS'].split(' ')) - - ext_include_dirs = [ - os.getcwd(), - os.path.join(amici_path, 'include'), - os.path.join(amici_path, "ThirdParty", "gsl"), - os.path.join(amici_path, "ThirdParty", "sundials", "include"), - os.path.join(amici_path, "ThirdParty", "SuiteSparse", "include"), - *h5pkgcfg['include_dirs'], - *blaspkgcfg['include_dirs'] - ] - - ext_library_dirs = [ - *h5pkgcfg['library_dirs'], - *blaspkgcfg['library_dirs'], - os.path.join(amici_path, 'libs') - ] - - # Build shared object - ext = Extension( - 'TPL_MODELNAME._TPL_MODELNAME', - sources=sources, - include_dirs=ext_include_dirs, - libraries=libraries, - library_dirs=ext_library_dirs, - swig_opts=[ - '-c++', '-modern', '-outdir', 'TPL_MODELNAME', - '-I%s' % os.path.join(amici_path, 'swig'), - '-I%s' % os.path.join(amici_path, 'include'), - ], - extra_compile_args=cxx_flags, - extra_link_args=linker_flags - ) - - # see `set_compiler_specific_extension_options` - ext.extra_compile_args_mingw32 = ['-std=c++14'] - ext.extra_compile_args_unix = ['-std=c++14'] - ext.extra_compile_args_msvc = ['/std:c++14'] - - return ext - - -# Change working directory to setup.py location -os.chdir(os.path.dirname(os.path.abspath(__file__))) - -MODEL_EXT = get_extension() - -CLASSIFIERS = [ - 'Development Status :: 3 - Alpha', - 'Intended Audience :: Science/Research', - 'Operating System :: POSIX :: Linux', - 'Operating System :: MacOS :: MacOS X', - 'Programming Language :: Python', - 'Programming Language :: C++', - 'Topic :: Scientific/Engineering :: Bio-Informatics', -] - -CMDCLASS = { - # For parallel compilation and custom swig finder - 'build_ext': ModelBuildExt, -} - -# Install -setup( - name='TPL_MODELNAME', - cmdclass=CMDCLASS, - version='TPL_PACKAGE_VERSION', - description='AMICI-generated module for model TPL_MODELNAME', - url='https://github.com/AMICI-dev/AMICI', - author='model-author-todo', - author_email='model-author-todo', - # license = 'BSD', - ext_modules=[MODEL_EXT], - packages=find_packages(), - install_requires=['amici==TPL_AMICI_VERSION'], - extras_require={'wurlitzer': ['wurlitzer']}, - python_requires='>=3.7', - package_data={}, - zip_safe=False, - include_package_data=True, - classifiers=CLASSIFIERS, -) diff --git a/deps/AMICI/python/amici/setuptools.py b/deps/AMICI/python/amici/setuptools.py deleted file mode 100644 index 7cfcf61fe..000000000 --- a/deps/AMICI/python/amici/setuptools.py +++ /dev/null @@ -1,284 +0,0 @@ -""" -setuptools ----------- -Helper functions for AMICI core and module package preparation -""" - -import os -import sys -import shlex -import subprocess - -from .swig import find_swig, get_swig_version - -try: - import pkgconfig # optional - - # pkgconfig python module might be installed without pkg-config binary - # being available - pkgconfig.exists('somePackageName') -except (ModuleNotFoundError, EnvironmentError): - pkgconfig = None - -from typing import Dict, List, Union, Tuple, Any - -PackageInfo = Dict[str, List[Union[str, Tuple[str, Any]]]] - - -def get_blas_config() -> PackageInfo: - """ - Find CBLAS-compatible BLAS - - :return: - blas related package information - """ - - blaspkgcfg = {'include_dirs': [], - 'library_dirs': [], - 'libraries': [], - 'define_macros': [], - 'extra_compile_args': [], - 'extra_link_args': [] - } - - # Check environment variables - if 'BLAS_CFLAGS' in os.environ: - blaspkgcfg['extra_compile_args'].extend( - shlex.split(os.environ['BLAS_CFLAGS']) - ) - - if 'BLAS_LIBS' in os.environ: - blaspkgcfg['extra_link_args'].extend( - shlex.split(os.environ['BLAS_LIBS']) - ) - - if 'BLAS_CFLAGS' in os.environ or 'BLAS_LIBS' in os.environ: - # If options have been provided by the user, we don't try to detect - # anything by ourselves - return blaspkgcfg - - # Try environment modules - # MKL - if 'MKLROOT' in os.environ: - if 'MKL_INC' in os.environ: - blaspkgcfg['extra_compile_args'].extend( - shlex.split(os.environ['MKL_INC']) - ) - if 'MKL_LIB' in os.environ: - blaspkgcfg['extra_link_args'].extend( - shlex.split(os.environ['MKL_LIB']) - ) - blaspkgcfg['define_macros'].append(('AMICI_BLAS_MKL', None), ) - return blaspkgcfg - - # Try pkgconfig - if pkgconfig: - for blas_name in ['cblas', 'openblas']: - if pkgconfig.exists(blas_name): - blaspkgcfg = pkgconfig.parse(blas_name) - blaspkgcfg['extra_compile_args'] = [ - pkgconfig.cflags(blas_name) - ] - blaspkgcfg['extra_link_args'] = [ - pkgconfig.libs(blas_name) - ] - - return blaspkgcfg - - # If none of the previous worked, fall back to libcblas in default paths - blaspkgcfg['libraries'] = ['cblas'] - - return blaspkgcfg - - -def get_hdf5_config() -> PackageInfo: - """ - Find HDF5 include dir and libs - - :return: - hdf5 related package information - """ - - h5pkgcfg = {'include_dirs': [], - 'library_dirs': [], - 'libraries': [], - 'define_macros': [] - } - hdf5_include_dir_found = False - hdf5_library_dir_found = False - - # try for hdf5 in standard locations - hdf5_include_dir_hints = [ - '/usr/include/hdf5/serial', - '/usr/local/include', - '/usr/include', # travis ubuntu xenial, centos - '/usr/local/Cellar/hdf5/1.10.2_1/include' # travis macOS - ] - hdf5_library_dir_hints = [ - '/usr/lib/x86_64-linux-gnu/', # travis ubuntu xenial - '/usr/lib/x86_64-linux-gnu/hdf5/serial', - '/usr/local/lib', - '/usr/lib64/', # CentOS - '/usr/local/Cellar/hdf5/1.10.2_1/lib' # travis macOS - ] - - # special treatment for conda environments - # as the conda library dir is provided first, we should also check for - # conda header files first - if 'CONDA_DIR' in os.environ: - hdf5_include_dir_hints.insert( - 0, os.path.join(os.environ['CONDA_DIR'], 'include')) - hdf5_library_dir_hints.insert( - 0, os.path.join(os.environ['CONDA_DIR'], 'lib')) - - # Check for Environment Modules variables - if 'HDF5_BASE' in os.environ: - hdf5_include_dir_hints.insert( - 0, os.path.join(os.environ['HDF5_BASE'], 'include')) - hdf5_library_dir_hints.insert( - 0, os.path.join(os.environ['HDF5_BASE'], 'lib')) - - for hdf5_include_dir_hint in hdf5_include_dir_hints: - hdf5_include_dir_found = os.path.isfile( - os.path.join(hdf5_include_dir_hint, 'hdf5.h')) - if hdf5_include_dir_found: - print(f"hdf5.h found in {hdf5_include_dir_hint}") - h5pkgcfg['include_dirs'] = [hdf5_include_dir_hint] - break - - for hdf5_library_dir_hint in hdf5_library_dir_hints: - # check for static or shared library - for lib_filename in ['libhdf5.a', 'libhdf5.so']: - hdf5_library_dir_found = os.path.isfile( - os.path.join(hdf5_library_dir_hint, lib_filename)) - if hdf5_library_dir_found: - print(f'{lib_filename} found in {hdf5_library_dir_hint}') - h5pkgcfg['library_dirs'] = [hdf5_library_dir_hint] - break - if hdf5_library_dir_found: - # break to not override hdf5_library_dir_found - break - - h5pkgcfg['found'] = hdf5_include_dir_found and hdf5_library_dir_found - if h5pkgcfg['found']: - return h5pkgcfg - - if pkgconfig: - try: - h5pkgcfg = pkgconfig.parse('hdf5') - except pkgconfig.PackageNotFoundError: - pass - # NOTE: Cannot use pkgconfig.exists('hdf5f'), since this is true - # although no libraries or include dirs are available - h5pkgcfg['found'] = 'include_dirs' in h5pkgcfg \ - and h5pkgcfg['include_dirs'] and \ - 'library_dirs' in h5pkgcfg \ - and h5pkgcfg['library_dirs'] - - return h5pkgcfg - - -def add_coverage_flags_if_required(cxx_flags: List[str], - linker_flags: List[str]) -> None: - """ - Add compiler and linker flags if gcov coverage requested - - :param cxx_flags: - list of existing cxx flags - - :param linker_flags: - list of existing linker flags - """ - if 'ENABLE_GCOV_COVERAGE' in os.environ and \ - os.environ['ENABLE_GCOV_COVERAGE'].upper() == 'TRUE': - print("ENABLE_GCOV_COVERAGE was set to TRUE." - " Building AMICI with coverage symbols.") - cxx_flags.extend(['-g', '-O0', '--coverage']) - linker_flags.extend(['--coverage', '-g']) - - -def add_debug_flags_if_required(cxx_flags: List[str], - linker_flags: List[str]) -> None: - """ - Add compiler and linker debug flags if requested - - Arguments: - :param cxx_flags: - list of existing cxx flags - - :param linker_flags: - list of existing linker flags - """ - if 'ENABLE_AMICI_DEBUGGING' in os.environ \ - and os.environ['ENABLE_AMICI_DEBUGGING'] == 'TRUE': - print("ENABLE_AMICI_DEBUGGING was set to TRUE." - " Building AMICI with debug symbols.") - cxx_flags.extend(['-g', '-O0', '-UNDEBUG']) - linker_flags.extend(['-g']) - - -def generate_swig_interface_files(swig_outdir: str = None, - with_hdf5: bool = None) -> None: - """ - Compile the swig python interface to amici - """ - - swig_exe = find_swig() - swig_version = get_swig_version(swig_exe) - - swig_args = [ - '-c++', - '-python', - '-py3', - '-threads', - '-Wall', - f'-Iamici{os.sep}swig', - f'-Iamici{os.sep}include', - ] - - print(f"Found SWIG version {swig_version}") - - # Are HDF5 includes available to generate the wrapper? - if with_hdf5 is None: - with_hdf5 = get_hdf5_config()['found'] - - if not with_hdf5: - swig_args.append('-DAMICI_SWIG_WITHOUT_HDF5') - - if swig_outdir is not None: - swig_args.extend(['-outdir', swig_outdir]) - - # Do we have -doxygen? - if swig_version >= (4, 0, 0): - swig_args.append('-doxygen') - - swig_cmd = [swig_exe, - *swig_args, - '-o', os.path.join("amici", "amici_wrap.cxx"), - os.path.join("amici", "swig", "amici.i")] - - print(f"Running SWIG: {' '.join(swig_cmd)}") - sp = subprocess.run(swig_cmd, stdout=subprocess.PIPE, - stderr=sys.stdout.buffer) - if not sp.returncode == 0: - raise AssertionError('Swigging AMICI failed:\n' - + sp.stdout.decode('utf-8')) - - -def add_openmp_flags(cxx_flags: List, ldflags: List) -> None: - """Add OpenMP flags to lists for compiler/linker flags (in-place)""" - - # Enable OpenMP support for Linux / OSX: - if sys.platform == 'linux': - print("Adding OpenMP flags...") - cxx_flags.insert(0, "-fopenmp") - ldflags.insert(0, "-fopenmp") - elif sys.platform == 'darwin': - if os.path.exists('/usr/local/lib/libomp.a'): - print("Adding OpenMP flags...") - cxx_flags[0:0] = ["-Xpreprocessor", "-fopenmp"] - ldflags[0:0] = ["-Xpreprocessor", "-fopenmp", "-lomp"] - else: - print("Not adding OpenMP flags, because /usr/local/lib/libomp.a" - " does not exist. To enable, run `brew install libomp` " - "or add flags manually.") diff --git a/deps/AMICI/python/amici/swig.py b/deps/AMICI/python/amici/swig.py deleted file mode 100644 index da189fd48..000000000 --- a/deps/AMICI/python/amici/swig.py +++ /dev/null @@ -1,145 +0,0 @@ -"""Functions for downloading/building/finding SWIG""" - -from typing import Tuple -import ast -import os -import subprocess -import re - - -def find_swig() -> str: - """Get name and version of SWIG executable - - We need version >=3.0. Probably we should try some default paths and names, - but this should do the trick for now. - - Debian/Ubuntu systems have swig3.0 ('swig' is older versions), - OSX has swig 3.0 as 'swig'. - """ - - candidates = ['swig4.0', 'swig3.0', 'swig'] - # Environment variable has priority - if 'SWIG' in os.environ: - candidates.insert(0, os.environ['SWIG']) - - for candidate in candidates: - if swig_works(candidate): - return candidate - - raise RuntimeError( - "Unable to find SWIG executable with default names. " - "Ensure you have SWIG installed, e.g. by " - "`sudo apt install swig` or `brew install swig`. " - "As non-root user, you can install SWIG using " - "https://github.com/AMICI-dev/AMICI/blob/master/scripts/" - "downloadAndBuildSwig.sh, or by following the " - "instructions at http://www.swig.org/Doc4.0/" - "SWIGDocumentation.html#Preface_installation. " - "If was not found despite being installed, set the SWIG" - " environment variable to the full path of the correct " - "executable." - ) - - -def swig_works(swig: str, verbose: bool = True) -> bool: - """Test if `swig` looks like a working SWIG executable.""" - - try: - # For python3.6 compatibility no `capture_output=True` - result = subprocess.run([swig, '-version'], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - except (FileNotFoundError, PermissionError): - if verbose: - print(f'Testing SWIG executable {swig}... FAILED.') - return False - - if verbose: - if result.returncode == 0: - print(f'Testing SWIG executable {swig}... SUCCEEDED.') - else: - print(f'Testing SWIG executable {swig}... FAILED.') - - return result.returncode == 0 - - -def get_swig_version(swig_exe: str) -> Tuple: - """Determine version of the given SWIG executable - - Returns: - Version tuple - """ - result = subprocess.run([swig_exe, '-version'], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - assert result.returncode == 0 - - version = re.sub(r'(?s).*Version\s+([\S]+).*', r'\1', - result.stdout.decode('utf-8')) - - return tuple(int(x) for x in version.split('.')) - - -class TypeHintFixer(ast.NodeTransformer): - """Replaces SWIG-generated C++ typehints by corresponding Python types""" - - mapping = { - 'void': None, - 'std::unique_ptr< amici::Solver >': 'amici.Solver', - 'amici::InternalSensitivityMethod': 'amici.InternalSensitivityMethod', - 'amici::InterpolationType': 'amici.InterpolationType', - 'amici::LinearMultistepMethod': 'amici.LinearMultistepMethod', - 'amici::LinearSolver': 'amici.LinearSolver', - 'amici::Model *': 'amici.Model', - 'amici::Model const *': 'amici.Model', - 'amici::NewtonDampingFactorMode': 'amici.NewtonDampingFactorMode', - 'amici::NonlinearSolverIteration': 'amici.NonlinearSolverIteration', - 'amici::RDataReporting': 'amici.RDataReporting', - 'amici::SensitivityMethod': 'amici.SensitivityMethod', - 'amici::SensitivityOrder': 'amici.SensitivityOrder', - 'amici::Solver *': 'amici.Solver', - 'amici::SteadyStateSensitivityMode': 'amici.SteadyStateSensitivityMode', - 'amici::realtype': 'float', - 'DoubleVector': 'numpy.ndarray', - 'IntVector': 'List[int]', - 'std::string': 'str', - 'std::string const &': 'str', - 'std::unique_ptr< amici::ExpData >': 'amici.ExpData', - 'std::unique_ptr< amici::ReturnData >': 'amici.ReturnData', - } - - def visit_FunctionDef(self, node): - # Has a return type annotation? - if node.returns: - node.returns.value = self._new_annot(node.returns.value) - - # Has arguments? - if node.args.args: - for arg in node.args.args: - if not arg.annotation: - continue - arg.annotation.value = self._new_annot(arg.annotation.value) - return node - - def _new_annot(self, old_annot): - return self.mapping.get(old_annot, old_annot) - - -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: - source = f.read() - parsed_source = ast.parse(source) - - # Change AST - fixer = TypeHintFixer() - parsed_source = fixer.visit(parsed_source) - - # AST -> file - with open(outfilename, 'w') as f: - f.write(ast.unparse(parsed_source)) diff --git a/deps/AMICI/python/amici/testing.py b/deps/AMICI/python/amici/testing.py deleted file mode 100644 index 83980e02e..000000000 --- a/deps/AMICI/python/amici/testing.py +++ /dev/null @@ -1,19 +0,0 @@ -"""Test support functions""" - -import sys -from tempfile import TemporaryDirectory - - -class TemporaryDirectoryWinSafe(TemporaryDirectory): - """TemporaryDirectory that will not raise if cleanup fails. - - If any extension was loaded from the temporary directory, cleanup would - otherwise fail on Windows with a ``PermissionError``. This class ignores - such failures. - """ - def cleanup(self): - try: - super().cleanup() - except PermissionError as e: - if sys.platform not in {'win32', 'cygwin'}: - raise e diff --git a/deps/AMICI/python/benchmark/benchmark_pysb.py b/deps/AMICI/python/benchmark/benchmark_pysb.py index add85092e..079041ed4 100644 --- a/deps/AMICI/python/benchmark/benchmark_pysb.py +++ b/deps/AMICI/python/benchmark/benchmark_pysb.py @@ -5,20 +5,20 @@ times are averages of N_REPEATS simulations at reference values. """ +import importlib import os -import pysb +import sys +import timeit + import amici -from amici.pysb_import import pysb2amici +import matplotlib.pyplot as plt import numpy as np import pandas as pd -import matplotlib.pyplot as plt -import importlib -import timeit -import sys - +import pysb +from amici.pysb_import import pysb2amici from pysb.simulator import ScipyOdeSimulator -sys.path.insert(0, os.path.join('..', 'tests')) +sys.path.insert(0, os.path.join("..", "tests")) from test_pysb import pysb_models simulation_times = dict() @@ -32,37 +32,41 @@ simulation_times[example] = dict() with amici.add_path(os.path.dirname(pysb.examples.__file__)): - with amici.add_path(os.path.join(os.path.dirname(__file__), '..', - 'tests', 'pysb_test_models')): - + with amici.add_path( + os.path.join( + os.path.dirname(__file__), "..", "tests", "pysb_test_models" + ) + ): pysb.SelfExporter.cleanup() # reset pysb pysb.SelfExporter.do_export = True module = importlib.import_module(example) pysb_model = module.model - pysb_model.name = pysb_model.name.replace('pysb.examples.', '') + pysb_model.name = pysb_model.name.replace("pysb.examples.", "") # avoid naming clash for custom pysb models - pysb_model.name += '_amici' + pysb_model.name += "_amici" # pysb part tspan = np.linspace(0, 100, 101) sim = ScipyOdeSimulator( pysb_model, tspan=tspan, - integrator_options={'rtol': rtol, 'atol': atol}, + integrator_options={"rtol": rtol, "atol": atol}, + ) + time_pysb = ( + timeit.Timer( + "pysb_simres = sim.run()", globals={"sim": sim} + ).timeit(number=N_REPEATS) + / N_REPEATS ) - time_pysb = timeit.Timer( - 'pysb_simres = sim.run()', - globals={'sim': sim} - ).timeit(number=N_REPEATS)/N_REPEATS - simulation_times[example]['pysb'] = time_pysb - print(f'PySB average simulation time {example}: {time_pysb}') + simulation_times[example]["pysb"] = time_pysb + print(f"PySB average simulation time {example}: {time_pysb}") # amici part outdir = pysb_model.name - if pysb_model.name in ['move_connected_amici']: + if pysb_model.name in ["move_connected_amici"]: compute_conservation_laws = False else: compute_conservation_laws = True @@ -71,11 +75,12 @@ pysb_model, outdir, compute_conservation_laws=compute_conservation_laws, - observables=list(pysb_model.observables.keys()) + observables=list(pysb_model.observables.keys()), ) - amici_model_module = amici.import_model_module(pysb_model.name, - outdir) + amici_model_module = amici.import_model_module( + pysb_model.name, outdir + ) model_pysb = amici_model_module.getModel() @@ -85,27 +90,33 @@ solver.setMaxSteps(int(1e6)) solver.setAbsoluteTolerance(atol) solver.setRelativeTolerance(rtol) - time_amici = timeit.Timer( - 'rdata = amici.runAmiciSimulation(model, solver)', - globals={'model': model_pysb, 'solver': solver, - 'amici': amici} - ).timeit(number=N_REPEATS)/N_REPEATS - simulation_times[example]['amici'] = time_amici - print(f'AMICI average simulation time {example}: {time_amici}') + time_amici = ( + timeit.Timer( + "rdata = amici.runAmiciSimulation(model, solver)", + globals={ + "model": model_pysb, + "solver": solver, + "amici": amici, + }, + ).timeit(number=N_REPEATS) + / N_REPEATS + ) + simulation_times[example]["amici"] = time_amici + print(f"AMICI average simulation time {example}: {time_amici}") times = pd.DataFrame(simulation_times) -ax = times.T.plot(kind='scatter', x='pysb', y='amici') -ax.set_xscale('log') -ax.set_yscale('log') -ax.set_aspect('equal') +ax = times.T.plot(kind="scatter", x="pysb", y="amici") +ax.set_xscale("log") +ax.set_yscale("log") +ax.set_aspect("equal") xy_min = np.min([ax.get_xlim()[0], ax.get_ylim()[0]]) xy_max = np.max([ax.get_xlim()[1], ax.get_ylim()[1]]) ax.set_xlim([xy_min, xy_max]) ax.set_ylim([xy_min, xy_max]) -ax.set_ylabel('simulation time AMICI [s]') -ax.set_xlabel('simulation time PySB [s]') -ax.plot([xy_min, xy_max], [xy_min, xy_max], 'k:') +ax.set_ylabel("simulation time AMICI [s]") +ax.set_xlabel("simulation time PySB [s]") +ax.plot([xy_min, xy_max], [xy_min, xy_max], "k:") plt.tight_layout() -plt.savefig('benchmark_pysb.eps') +plt.savefig("benchmark_pysb.eps") diff --git a/deps/AMICI/python/examples/example_constant_species/ExampleEquilibrationLogic.ipynb b/deps/AMICI/python/examples/example_constant_species/ExampleEquilibrationLogic.ipynb index f65005b1d..5f66ea4db 100644 --- a/deps/AMICI/python/examples/example_constant_species/ExampleEquilibrationLogic.ipynb +++ b/deps/AMICI/python/examples/example_constant_species/ExampleEquilibrationLogic.ipynb @@ -60,7 +60,10 @@ ], "source": [ "from IPython.display import Image\n", - "fig = Image(filename=('../../../documentation/gfx/steadystate_solver_workflow.png'))\n", + "\n", + "fig = Image(\n", + " filename=(\"../../../documentation/gfx/steadystate_solver_workflow.png\")\n", + ")\n", "fig" ] }, @@ -102,11 +105,11 @@ "import matplotlib.pyplot as plt\n", "\n", "# SBML model we want to import\n", - "sbml_file = 'model_constant_species.xml'\n", + "sbml_file = \"model_constant_species.xml\"\n", "\n", "# Name of the models that will also be the name of the python module\n", - "model_name = 'model_constant_species'\n", - "model_reduced_name = model_name + '_reduced'\n", + "model_name = \"model_constant_species\"\n", + "model_reduced_name = model_name + \"_reduced\"\n", "\n", "# Directories to which the generated model code is written\n", "model_output_dir = model_name\n", @@ -118,18 +121,41 @@ "sbml_model = sbml_doc.getModel()\n", "dir(sbml_doc)\n", "\n", - "print('Species: ', [s.getId() for s in sbml_model.getListOfSpecies()])\n", + "print(\"Species: \", [s.getId() for s in sbml_model.getListOfSpecies()])\n", "\n", - "print('\\nReactions:')\n", + "print(\"\\nReactions:\")\n", "for reaction in sbml_model.getListOfReactions():\n", - " reactants = ' + '.join(['%s %s'%(int(r.getStoichiometry()) if r.getStoichiometry() > 1 else '', r.getSpecies()) for r in reaction.getListOfReactants()])\n", - " products = ' + '.join(['%s %s'%(int(r.getStoichiometry()) if r.getStoichiometry() > 1 else '', r.getSpecies()) for r in reaction.getListOfProducts()])\n", - " reversible = '<' if reaction.getReversible() else ''\n", - " print('%3s: %10s %1s->%10s\\t\\t[%s]' % (reaction.getId(),\n", - " reactants,\n", - " reversible,\n", - " products,\n", - " libsbml.formulaToL3String(reaction.getKineticLaw().getMath())))" + " reactants = \" + \".join(\n", + " [\n", + " \"%s %s\"\n", + " % (\n", + " int(r.getStoichiometry()) if r.getStoichiometry() > 1 else \"\",\n", + " r.getSpecies(),\n", + " )\n", + " for r in reaction.getListOfReactants()\n", + " ]\n", + " )\n", + " products = \" + \".join(\n", + " [\n", + " \"%s %s\"\n", + " % (\n", + " int(r.getStoichiometry()) if r.getStoichiometry() > 1 else \"\",\n", + " r.getSpecies(),\n", + " )\n", + " for r in reaction.getListOfProducts()\n", + " ]\n", + " )\n", + " reversible = \"<\" if reaction.getReversible() else \"\"\n", + " print(\n", + " \"%3s: %10s %1s->%10s\\t\\t[%s]\"\n", + " % (\n", + " reaction.getId(),\n", + " reactants,\n", + " reversible,\n", + " products,\n", + " libsbml.formulaToL3String(reaction.getKineticLaw().getMath()),\n", + " )\n", + " )" ] }, { @@ -142,25 +168,29 @@ "sbml_importer = amici.SbmlImporter(sbml_file)\n", "\n", "# specify observables and constant parameters\n", - "constantParameters = ['synthesis_substrate', 'init_enzyme']\n", + "constantParameters = [\"synthesis_substrate\", \"init_enzyme\"]\n", "observables = {\n", - " 'observable_product': {'name': '', 'formula': 'product'},\n", - " 'observable_substrate': {'name': '', 'formula': 'substrate'},\n", + " \"observable_product\": {\"name\": \"\", \"formula\": \"product\"},\n", + " \"observable_substrate\": {\"name\": \"\", \"formula\": \"substrate\"},\n", "}\n", - "sigmas = {'observable_product': 1.0, 'observable_substrate': 1.0}\n", + "sigmas = {\"observable_product\": 1.0, \"observable_substrate\": 1.0}\n", "\n", "# import the model\n", - "sbml_importer.sbml2amici(model_reduced_name,\n", - " model_reduced_output_dir,\n", - " observables=observables,\n", - " constantParameters=constantParameters,\n", - " sigmas=sigmas)\n", - "sbml_importer.sbml2amici(model_name,\n", - " model_output_dir,\n", - " observables=observables,\n", - " constantParameters=constantParameters,\n", - " sigmas=sigmas,\n", - " compute_conservation_laws=False)" + "sbml_importer.sbml2amici(\n", + " model_reduced_name,\n", + " model_reduced_output_dir,\n", + " observables=observables,\n", + " constant_parameters=constantParameters,\n", + " sigmas=sigmas,\n", + ")\n", + "sbml_importer.sbml2amici(\n", + " model_name,\n", + " model_output_dir,\n", + " observables=observables,\n", + " constant_parameters=constantParameters,\n", + " sigmas=sigmas,\n", + " compute_conservation_laws=False,\n", + ")" ] }, { @@ -219,10 +249,14 @@ ], "source": [ "# import the models and run some test simulations\n", - "model_reduced_module = amici.import_model_module(model_reduced_name, os.path.abspath(model_reduced_output_dir))\n", + "model_reduced_module = amici.import_model_module(\n", + " model_reduced_name, os.path.abspath(model_reduced_output_dir)\n", + ")\n", "model_reduced = model_reduced_module.getModel()\n", "\n", - "model_module = amici.import_model_module(model_name, os.path.abspath(model_output_dir))\n", + "model_module = amici.import_model_module(\n", + " model_name, os.path.abspath(model_output_dir)\n", + ")\n", "model = model_module.getModel()\n", "\n", "\n", @@ -238,6 +272,7 @@ "\n", "# plot trajectories\n", "import amici.plotting\n", + "\n", "amici.plotting.plotStateTrajectories(rdata_reduced, model=model_reduced)\n", "amici.plotting.plotObservableTrajectories(rdata_reduced, model=model_reduced)\n", "\n", @@ -336,9 +371,9 @@ "solver.setMaxSteps(1000)\n", "rdata = amici.runAmiciSimulation(model, solver)\n", "\n", - "#np.set_printoptions(threshold=8, edgeitems=2)\n", + "# np.set_printoptions(threshold=8, edgeitems=2)\n", "for key, value in rdata.items():\n", - " print('%12s: ' % key, value)" + " print(\"%12s: \" % key, value)" ] }, { @@ -399,8 +434,10 @@ "# reduce maxsteps for integration\n", "solver.setMaxSteps(100)\n", "rdata = amici.runAmiciSimulation(model, solver)\n", - "print('Status of postequilibration:', rdata['posteq_status'])\n", - "print('Number of steps employed in postequilibration:', rdata['posteq_numsteps'])" + "print(\"Status of postequilibration:\", rdata[\"posteq_status\"])\n", + "print(\n", + " \"Number of steps employed in postequilibration:\", rdata[\"posteq_numsteps\"]\n", + ")" ] }, { @@ -437,8 +474,11 @@ "solver_reduced.setMaxSteps(100)\n", "rdata_reduced = amici.runAmiciSimulation(model_reduced, solver_reduced)\n", "\n", - "print('Status of postequilibration:', rdata_reduced['posteq_status'])\n", - "print('Number of steps employed in postequilibration:', rdata_reduced['posteq_numsteps'])" + "print(\"Status of postequilibration:\", rdata_reduced[\"posteq_status\"])\n", + "print(\n", + " \"Number of steps employed in postequilibration:\",\n", + " rdata_reduced[\"posteq_numsteps\"],\n", + ")" ] }, { @@ -496,9 +536,11 @@ } ], "source": [ - "# Call simulation with singular Jacobian and simulationFSA mode\n", + "# Call simulation with singular Jacobian and integrateIfNewtonFails mode\n", "model.setTimepoints(np.full(1, np.inf))\n", - "model.setSteadyStateSensitivityMode(amici.SteadyStateSensitivityMode.simulationFSA)\n", + "model.setSteadyStateSensitivityMode(\n", + " amici.SteadyStateSensitivityMode.integrateIfNewtonFails\n", + ")\n", "solver = model.getSolver()\n", "solver.setNewtonMaxSteps(10)\n", "solver.setSensitivityMethod(amici.SensitivityMethod.forward)\n", @@ -506,10 +548,12 @@ "solver.setMaxSteps(10000)\n", "rdata = amici.runAmiciSimulation(model, solver)\n", "\n", - "print('Status of postequilibration:', rdata['posteq_status'])\n", - "print('Number of steps employed in postequilibration:', rdata['posteq_numsteps'])\n", - "print('Computed state sensitivities:')\n", - "print(rdata['sx'][0,:,:])" + "print(\"Status of postequilibration:\", rdata[\"posteq_status\"])\n", + "print(\n", + " \"Number of steps employed in postequilibration:\", rdata[\"posteq_numsteps\"]\n", + ")\n", + "print(\"Computed state sensitivities:\")\n", + "print(rdata[\"sx\"][0, :, :])" ] }, { @@ -550,17 +594,21 @@ "source": [ "# Call simulation with singular Jacobian and newtonOnly mode (will fail)\n", "model.setTimepoints(np.full(1, np.inf))\n", - "model.setSteadyStateSensitivityMode(amici.SteadyStateSensitivityMode.newtonOnly)\n", + "model.setSteadyStateSensitivityMode(\n", + " amici.SteadyStateSensitivityMode.newtonOnly\n", + ")\n", "solver = model.getSolver()\n", "solver.setSensitivityMethod(amici.SensitivityMethod.forward)\n", "solver.setSensitivityOrder(amici.SensitivityOrder.first)\n", "solver.setMaxSteps(10000)\n", "rdata = amici.runAmiciSimulation(model, solver)\n", "\n", - "print('Status of postequilibration:', rdata['posteq_status'])\n", - "print('Number of steps employed in postequilibration:', rdata['posteq_numsteps'])\n", - "print('Computed state sensitivities:')\n", - "print(rdata['sx'][0,:,:])" + "print(\"Status of postequilibration:\", rdata[\"posteq_status\"])\n", + "print(\n", + " \"Number of steps employed in postequilibration:\", rdata[\"posteq_numsteps\"]\n", + ")\n", + "print(\"Computed state sensitivities:\")\n", + "print(rdata[\"sx\"][0, :, :])" ] }, { @@ -586,7 +634,9 @@ "source": [ "# Call postequilibration by setting an infinity timepoint\n", "model_reduced.setTimepoints(np.full(1, np.inf))\n", - "model.setSteadyStateSensitivityMode(amici.SteadyStateSensitivityMode.newtonOnly)\n", + "model.setSteadyStateSensitivityMode(\n", + " amici.SteadyStateSensitivityMode.newtonOnly\n", + ")\n", "solver_reduced = model_reduced.getSolver()\n", "solver_reduced.setNewtonMaxSteps(10)\n", "solver_reduced.setSensitivityMethod(amici.SensitivityMethod.forward)\n", @@ -594,10 +644,13 @@ "solver_reduced.setMaxSteps(1000)\n", "rdata_reduced = amici.runAmiciSimulation(model_reduced, solver_reduced)\n", "\n", - "print('Status of postequilibration:', rdata_reduced['posteq_status'])\n", - "print('Number of steps employed in postequilibration:', rdata_reduced['posteq_numsteps'])\n", - "print('Computed state sensitivities:')\n", - "print(rdata_reduced['sx'][0,:,:])" + "print(\"Status of postequilibration:\", rdata_reduced[\"posteq_status\"])\n", + "print(\n", + " \"Number of steps employed in postequilibration:\",\n", + " rdata_reduced[\"posteq_numsteps\"],\n", + ")\n", + "print(\"Computed state sensitivities:\")\n", + "print(rdata_reduced[\"sx\"][0, :, :])" ] }, { @@ -646,11 +699,13 @@ "source": [ "# Call adjoint postequilibration by setting an infinity timepoint\n", "# and create an edata object, which is needed for adjoint computation\n", - "edata = amici.ExpData(2, 0, 0, np.array([float('inf')]))\n", + "edata = amici.ExpData(2, 0, 0, np.array([float(\"inf\")]))\n", "edata.setObservedData([1.8] * 2)\n", - "edata.fixedParameters = np.array([3., 5.])\n", + "edata.fixedParameters = np.array([3.0, 5.0])\n", "\n", - "model_reduced.setSteadyStateSensitivityMode(amici.SteadyStateSensitivityMode.newtonOnly)\n", + "model_reduced.setSteadyStateSensitivityMode(\n", + " amici.SteadyStateSensitivityMode.newtonOnly\n", + ")\n", "solver_reduced = model_reduced.getSolver()\n", "solver_reduced.setNewtonMaxSteps(10)\n", "solver_reduced.setSensitivityMethod(amici.SensitivityMethod.adjoint)\n", @@ -658,10 +713,16 @@ "solver_reduced.setMaxSteps(1000)\n", "rdata_reduced = amici.runAmiciSimulation(model_reduced, solver_reduced, edata)\n", "\n", - "print('Status of postequilibration:', rdata_reduced['posteq_status'])\n", - "print('Number of steps employed in postequilibration:', rdata_reduced['posteq_numsteps'])\n", - "print('Number of backward steps employed in postequilibration:', rdata_reduced['posteq_numstepsB'])\n", - "print('Computed gradient:', rdata_reduced['sllh'])" + "print(\"Status of postequilibration:\", rdata_reduced[\"posteq_status\"])\n", + "print(\n", + " \"Number of steps employed in postequilibration:\",\n", + " rdata_reduced[\"posteq_numsteps\"],\n", + ")\n", + "print(\n", + " \"Number of backward steps employed in postequilibration:\",\n", + " rdata_reduced[\"posteq_numstepsB\"],\n", + ")\n", + "print(\"Computed gradient:\", rdata_reduced[\"sllh\"])" ] }, { @@ -691,17 +752,24 @@ ], "source": [ "# Call adjoint postequilibration with model with singular Jacobian\n", - "model.setSteadyStateSensitivityMode(amici.SteadyStateSensitivityMode.newtonOnly)\n", + "model.setSteadyStateSensitivityMode(\n", + " amici.SteadyStateSensitivityMode.newtonOnly\n", + ")\n", "solver = model.getSolver()\n", "solver.setNewtonMaxSteps(10)\n", "solver.setSensitivityMethod(amici.SensitivityMethod.adjoint)\n", "solver.setSensitivityOrder(amici.SensitivityOrder.first)\n", "rdata = amici.runAmiciSimulation(model, solver, edata)\n", "\n", - "print('Status of postequilibration:', rdata['posteq_status'])\n", - "print('Number of steps employed in postequilibration:', rdata['posteq_numsteps'])\n", - "print('Number of backward steps employed in postequilibration:', rdata['posteq_numstepsB'])\n", - "print('Computed gradient:', rdata['sllh'])" + "print(\"Status of postequilibration:\", rdata[\"posteq_status\"])\n", + "print(\n", + " \"Number of steps employed in postequilibration:\", rdata[\"posteq_numsteps\"]\n", + ")\n", + "print(\n", + " \"Number of backward steps employed in postequilibration:\",\n", + " rdata[\"posteq_numstepsB\"],\n", + ")\n", + "print(\"Computed gradient:\", rdata[\"sllh\"])" ] }, { @@ -720,11 +788,10 @@ "outputs": [], "source": [ "# create edata, with 3 timepoints and 2 observables:\n", - "edata = amici.ExpData(2, 0, 0,\n", - " np.array([0., 0.1, 1.]))\n", + "edata = amici.ExpData(2, 0, 0, np.array([0.0, 0.1, 1.0]))\n", "edata.setObservedData([1.8] * 6)\n", - "edata.fixedParameters = np.array([3., 5.])\n", - "edata.fixedParametersPreequilibration = np.array([0., 2.])\n", + "edata.fixedParameters = np.array([3.0, 5.0])\n", + "edata.fixedParametersPreequilibration = np.array([0.0, 2.0])\n", "edata.reinitializeFixedParameterInitialStates = True" ] }, @@ -764,8 +831,8 @@ "solver_reduced.setNewtonMaxSteps(10)\n", "rdata_reduced = amici.runAmiciSimulation(model_reduced, solver_reduced, edata)\n", "\n", - "amici.plotting.plotStateTrajectories(rdata_reduced, model = model_reduced)\n", - "amici.plotting.plotObservableTrajectories(rdata_reduced, model = model_reduced)" + "amici.plotting.plotStateTrajectories(rdata_reduced, model=model_reduced)\n", + "amici.plotting.plotObservableTrajectories(rdata_reduced, model=model_reduced)" ] }, { @@ -782,7 +849,7 @@ "outputs": [], "source": [ "# Change the last timepoint to an infinity timepoint.\n", - "edata.setTimepoints(np.array([0., 0.1, float('inf')]))\n", + "edata.setTimepoints(np.array([0.0, 0.1, float(\"inf\")]))\n", "\n", "# run the simulation\n", "rdata_reduced = amici.runAmiciSimulation(model_reduced, solver_reduced, edata)" @@ -844,10 +911,12 @@ ], "source": [ "# No postquilibration this time.\n", - "edata.setTimepoints(np.array([0., 0.1, 1.]))\n", + "edata.setTimepoints(np.array([0.0, 0.1, 1.0]))\n", "\n", "# create the solver object and run the simulation, singular Jacobian, enforce Newton solver for sensitivities\n", - "model.setSteadyStateSensitivityMode(amici.SteadyStateSensitivityMode.newtonOnly)\n", + "model.setSteadyStateSensitivityMode(\n", + " amici.SteadyStateSensitivityMode.newtonOnly\n", + ")\n", "solver = model.getSolver()\n", "solver.setNewtonMaxSteps(10)\n", "solver.setSensitivityMethod(amici.SensitivityMethod.forward)\n", @@ -855,8 +924,8 @@ "rdata = amici.runAmiciSimulation(model, solver, edata)\n", "\n", "for key, value in rdata.items():\n", - " if key[0:6] == 'preeq_':\n", - " print('%20s: ' % key, value)" + " if key[0:6] == \"preeq_\":\n", + " print(\"%20s: \" % key, value)" ] }, { @@ -883,7 +952,9 @@ ], "source": [ "# Singluar Jacobian, use simulation\n", - "model.setSteadyStateSensitivityMode(amici.SteadyStateSensitivityMode.simulationFSA)\n", + "model.setSteadyStateSensitivityMode(\n", + " amici.SteadyStateSensitivityMode.integrateIfNewtonFails\n", + ")\n", "solver = model.getSolver()\n", "solver.setNewtonMaxSteps(10)\n", "solver.setSensitivityMethod(amici.SensitivityMethod.forward)\n", @@ -891,8 +962,8 @@ "rdata = amici.runAmiciSimulation(model, solver, edata)\n", "\n", "for key, value in rdata.items():\n", - " if key[0:6] == 'preeq_':\n", - " print('%20s: ' % key, value)" + " if key[0:6] == \"preeq_\":\n", + " print(\"%20s: \" % key, value)" ] }, { @@ -924,8 +995,8 @@ "rdata_reduced = amici.runAmiciSimulation(model_reduced, solver_reduced, edata)\n", "\n", "for key, value in rdata_reduced.items():\n", - " if key[0:6] == 'preeq_':\n", - " print('%20s: ' % key, value)" + " if key[0:6] == \"preeq_\":\n", + " print(\"%20s: \" % key, value)" ] }, { @@ -975,9 +1046,9 @@ "rdata_reduced = amici.runAmiciSimulation(model_reduced, solver_reduced, edata)\n", "\n", "for key, value in rdata_reduced.items():\n", - " if key[0:6] == 'preeq_':\n", - " print('%20s: ' % key, value)\n", - "print('Gradient:', rdata_reduced['sllh'])" + " if key[0:6] == \"preeq_\":\n", + " print(\"%20s: \" % key, value)\n", + "print(\"Gradient:\", rdata_reduced[\"sllh\"])" ] }, { @@ -1010,9 +1081,9 @@ "rdata_reduced = amici.runAmiciSimulation(model_reduced, solver_reduced, edata)\n", "\n", "for key, value in rdata_reduced.items():\n", - " if key[0:6] == 'preeq_':\n", - " print('%20s: ' % key, value)\n", - "print('Gradient:', rdata_reduced['sllh'])" + " if key[0:6] == \"preeq_\":\n", + " print(\"%20s: \" % key, value)\n", + "print(\"Gradient:\", rdata_reduced[\"sllh\"])" ] }, { @@ -1041,14 +1112,16 @@ "solver_reduced = model_reduced.getSolver()\n", "solver_reduced.setNewtonMaxSteps(10)\n", "solver_reduced.setSensitivityMethod(amici.SensitivityMethod.adjoint)\n", - "solver_reduced.setSensitivityMethodPreequilibration(amici.SensitivityMethod.adjoint)\n", + "solver_reduced.setSensitivityMethodPreequilibration(\n", + " amici.SensitivityMethod.adjoint\n", + ")\n", "solver_reduced.setSensitivityOrder(amici.SensitivityOrder.first)\n", "rdata_reduced = amici.runAmiciSimulation(model_reduced, solver_reduced, edata)\n", "\n", "for key, value in rdata_reduced.items():\n", - " if key[0:6] == 'preeq_':\n", - " print('%20s: ' % key, value)\n", - "print('Gradient:', rdata_reduced['sllh'])" + " if key[0:6] == \"preeq_\":\n", + " print(\"%20s: \" % key, value)\n", + "print(\"Gradient:\", rdata_reduced[\"sllh\"])" ] }, { @@ -1089,9 +1162,9 @@ "rdata = amici.runAmiciSimulation(model, solver, edata)\n", "\n", "for key, value in rdata.items():\n", - " if key[0:6] == 'preeq_':\n", - " print('%20s: ' % key, value)\n", - "print('Gradient:', rdata['sllh'])" + " if key[0:6] == \"preeq_\":\n", + " print(\"%20s: \" % key, value)\n", + "print(\"Gradient:\", rdata[\"sllh\"])" ] }, { @@ -1135,7 +1208,9 @@ ], "source": [ "# Non-singular Jacobian, use simulaiton\n", - "model_reduced.setSteadyStateSensitivityMode(amici.SteadyStateSensitivityMode.simulationFSA)\n", + "model_reduced.setSteadyStateSensitivityMode(\n", + " amici.SteadyStateSensitivityMode.integrateIfNewtonFails\n", + ")\n", "solver_reduced = model_reduced.getSolver()\n", "solver_reduced.setNewtonMaxSteps(0)\n", "solver_reduced.setSensitivityMethod(amici.SensitivityMethod.forward)\n", @@ -1146,27 +1221,31 @@ "solver_reduced.setAbsoluteToleranceSteadyState(1e-3)\n", "solver_reduced.setRelativeToleranceSteadyStateSensi(1e-2)\n", "solver_reduced.setAbsoluteToleranceSteadyStateSensi(1e-3)\n", - "rdata_reduced_lax = amici.runAmiciSimulation(model_reduced, solver_reduced, edata)\n", + "rdata_reduced_lax = amici.runAmiciSimulation(\n", + " model_reduced, solver_reduced, edata\n", + ")\n", "\n", "# run with strict tolerances\n", "solver_reduced.setRelativeToleranceSteadyState(1e-12)\n", "solver_reduced.setAbsoluteToleranceSteadyState(1e-16)\n", "solver_reduced.setRelativeToleranceSteadyStateSensi(1e-12)\n", "solver_reduced.setAbsoluteToleranceSteadyStateSensi(1e-16)\n", - "rdata_reduced_strict = amici.runAmiciSimulation(model_reduced, solver_reduced, edata)\n", + "rdata_reduced_strict = amici.runAmiciSimulation(\n", + " model_reduced, solver_reduced, edata\n", + ")\n", "\n", "# compare ODE outputs\n", - "print('\\nODE solver steps, which were necessary to reach steady state:')\n", - "print('lax tolerances: ', rdata_reduced_lax['preeq_numsteps'])\n", - "print('strict tolerances: ', rdata_reduced_strict['preeq_numsteps'])\n", + "print(\"\\nODE solver steps, which were necessary to reach steady state:\")\n", + "print(\"lax tolerances: \", rdata_reduced_lax[\"preeq_numsteps\"])\n", + "print(\"strict tolerances: \", rdata_reduced_strict[\"preeq_numsteps\"])\n", "\n", - "print('\\nsimulation time corresponding to steady state:')\n", - "print(rdata_reduced_lax['preeq_t'])\n", - "print(rdata_reduced_strict['preeq_t'])\n", + "print(\"\\nsimulation time corresponding to steady state:\")\n", + "print(rdata_reduced_lax[\"preeq_t\"])\n", + "print(rdata_reduced_strict[\"preeq_t\"])\n", "\n", - "print('\\ncomputation time to reach steady state:')\n", - "print(rdata_reduced_lax['preeq_cpu_time'])\n", - "print(rdata_reduced_strict['preeq_cpu_time'])" + "print(\"\\ncomputation time to reach steady state:\")\n", + "print(rdata_reduced_lax[\"preeq_cpu_time\"])\n", + "print(rdata_reduced_strict[\"preeq_cpu_time\"])" ] } ], @@ -1186,7 +1265,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.2" + "version": "3.8.10" }, "toc": { "base_numbering": 1, diff --git a/deps/AMICI/python/examples/example_errors.ipynb b/deps/AMICI/python/examples/example_errors.ipynb new file mode 100644 index 000000000..5e07803d9 --- /dev/null +++ b/deps/AMICI/python/examples/example_errors.ipynb @@ -0,0 +1,1128 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "6d8e8890", + "metadata": {}, + "source": [ + "# Debugging simulation failures\n", + "\n", + "**Objective:** Demonstrate common simulation failures and give some hints for interpreting, debugging, and fixing them." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "87383e84", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib inline\n", + "import os\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.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", + "except ModuleNotFoundError:\n", + " # install `benchmark_models_petab` if necessary\n", + " %pip install -q -e \"git+https://github.com/Benchmarking-Initiative/Benchmark-Models-PEtab.git@master#subdirectory=src/python&egg=benchmark_models_petab\"\n", + " try:\n", + " import benchmark_models_petab\n", + " except ModuleNotFoundError:\n", + " print(\"** Please restart the kernel. **\")" + ] + }, + { + "cell_type": "markdown", + "id": "46500fb0", + "metadata": {}, + "source": [ + "## Overview\n", + "\n", + "In the following, we will simulate models contained in the [PEtab Benchmark Collection](https://github.com/Benchmarking-Initiative/Benchmark-Models-PEtab/) to demonstrate a number of simulation failures to analyze and fix them. We use the PEtab format, as it makes model import and simulation much easier, but everything illustrated here, also applies to plain SBML or PySB import.\n", + "\n", + "Note that, due to numerical issues, the examples below may not be fully reproducible on every system.\n", + "\n", + "If any simulation failures occur, they will be printed via Python logging. \n", + "\n", + "Programmatically, simulation success can be checked via `ReturnDataView.status`. In case of a successful simulation, and only then, this value corresponds to `amici.AMICI_SUCCESS`.\n", + "In case of a simulation error, all quantities in `ReturnData`/`ReturnDataView` will be reported up to the time of failure, the rest will be `NaN`. The likelihood and it's gradient will always be `NaN` in case of failure." + ] + }, + { + "cell_type": "markdown", + "id": "0ad882ac", + "metadata": {}, + "source": [ + "## `AMICI_TOO_MUCH_WORK` - `mxstep steps taken before reaching tout`\n", + "\n", + "Let's run a simulation:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ae3cb45e", + "metadata": {}, + "outputs": [], + "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", + ")\n", + "\n", + "np.random.seed(2991)\n", + "problem_parameters = dict(\n", + " zip(\n", + " petab_problem.x_free_ids,\n", + " petab_problem.sample_parameter_startpoints(n_starts=1)[0],\n", + " )\n", + ")\n", + "res = simulate_petab(\n", + " petab_problem=petab_problem,\n", + " amici_model=amici_model,\n", + " problem_parameters=problem_parameters,\n", + " scaled_parameters=True,\n", + ")\n", + "print(\n", + " \"Status:\",\n", + " [amici.simulation_status_to_str(rdata.status) for rdata in res[RDATAS]],\n", + ")\n", + "assert [\n", + " amici.simulation_status_to_str(rdata.status) for rdata in res[RDATAS]\n", + "] == [\n", + " \"AMICI_SUCCESS\",\n", + " \"AMICI_SUCCESS\",\n", + " \"AMICI_SUCCESS\",\n", + " \"AMICI_TOO_MUCH_WORK\",\n", + " \"AMICI_NOT_RUN\",\n", + " \"AMICI_NOT_RUN\",\n", + "]" + ] + }, + { + "cell_type": "markdown", + "id": "e75a6dcc", + "metadata": {}, + "source": [ + "**What happened?**\n", + "\n", + "AMICI failed to integrate the forward problem. The problem occurred for only one simulation condition, `condition_step_03_0`. The issue occurred at $t = 3031.8$, where the CVODES reached the maximum number of steps.\n", + "\n", + "**How to address?**\n", + "\n", + "The number of steps the solver has to take is closely related to the chosen error tolerance. More accurate results, more steps. Therefore, this problem can be solved in two ways:\n", + "\n", + "1. Increasing the maximum number of steps via [amici.Solver.setMaxSteps](https://amici.readthedocs.io/en/latest/generated/amici.amici.Solver.html#amici.amici.Solver.setMaxSteps). Note that this will increase the time required for simulation, and that simulation may still fail eventually. Sometimes it may be preferable to not increase this limit but rather fail fast. Also note that increasing the number of allowed steps increase RAM requirements (even if fewer steps are actually taken), so don't set this to ridiculously large values in order to avoid this error.\n", + "\n", + "2. Reducing the number of steps CVODES has to take. This is determined by the required error tolerance. There are various solver error tolerances than can be adjusted. The most relevant ones are those controlled via [amici.Solver.setRelativeTolerance()](https://amici.readthedocs.io/en/latest/generated/amici.amici.Solver.html#amici.amici.Solver.setRelativeTolerance) and [amici.Solver.setAbsoluteTolerance()](https://amici.readthedocs.io/en/latest/generated/amici.amici.Solver.html#amici.amici.Solver.setAbsoluteTolerance).\n", + "\n", + "So, let's fix that:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e9667f55", + "metadata": {}, + "outputs": [], + "source": [ + "# let's increase the allowed number of steps by 10x:\n", + "print(\"Increasing allowed number of steps ...\")\n", + "amici_solver = amici_model.getSolver()\n", + "amici_solver.setMaxSteps(10 * amici_solver.getMaxSteps())\n", + "\n", + "res = simulate_petab(\n", + " petab_problem=petab_problem,\n", + " amici_model=amici_model,\n", + " problem_parameters=problem_parameters,\n", + " scaled_parameters=True,\n", + " solver=amici_solver,\n", + ")\n", + "\n", + "print(\n", + " \"Status:\",\n", + " [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()\n", + "\n", + "\n", + "# let's relax the relative error tolerance by a factor of 50\n", + "print(\"Relaxing relative error tolerance ...\")\n", + "amici_solver = amici_model.getSolver()\n", + "amici_solver.setRelativeTolerance(50 * amici_solver.getRelativeTolerance())\n", + "\n", + "res = simulate_petab(\n", + " petab_problem=petab_problem,\n", + " amici_model=amici_model,\n", + " problem_parameters=problem_parameters,\n", + " scaled_parameters=True,\n", + " solver=amici_solver,\n", + ")\n", + "print(\n", + " \"Status:\",\n", + " [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.\")" + ] + }, + { + "cell_type": "markdown", + "id": "18fd3fa6", + "metadata": {}, + "source": [ + "## `Internal t = [...] and h = [...] are such that t + h = t on the next step`\n", + "\n", + "Let's run a simulation:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f78179dc", + "metadata": {}, + "outputs": [], + "source": [ + "petab_problem = benchmark_models_petab.get_problem(\"Crauste_CellSystems2017\")\n", + "amici_model = import_petab_problem(petab_problem, verbose=False)\n", + "\n", + "np.random.seed(1)\n", + "problem_parameters = dict(\n", + " zip(\n", + " petab_problem.x_free_ids,\n", + " petab_problem.sample_parameter_startpoints(n_starts=1)[0],\n", + " )\n", + ")\n", + "res = simulate_petab(\n", + " petab_problem=petab_problem,\n", + " amici_model=amici_model,\n", + " problem_parameters=problem_parameters,\n", + " scaled_parameters=True,\n", + ")\n", + "print(\n", + " \"Status:\",\n", + " [amici.simulation_status_to_str(rdata.status) for rdata in res[RDATAS]],\n", + ")\n", + "assert [\n", + " amici.simulation_status_to_str(rdata.status) for rdata in res[RDATAS]\n", + "] == [\"AMICI_TOO_MUCH_WORK\"]" + ] + }, + { + "cell_type": "markdown", + "id": "53e4b822", + "metadata": {}, + "source": [ + "**What happened?**\n", + "\n", + "The forward simulation failed because the AMICI solver exceeded the maximum number of steps. Unlike in the previous case of `mxstep steps taken before reaching tout` (see above), here we got several additional warnings that the current step size $h$ is numerically zero.\n", + "\n", + "**How to address?**\n", + "\n", + "The warning `Internal t = [...] and h = [...] are such that t + h = t on the next step` tells us that the solver is not able to move forward. The solver may be able to recover from that, but not always.\n", + "\n", + "Let's look at the state trajectories to see what's going on. Such a tiny step size is usually related to very fast dynamics. We repeat the simulation with additional timepoints before the point of failure:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6a6794d3", + "metadata": {}, + "outputs": [], + "source": [ + "# Create a copy of this simulation condition\n", + "edata = amici.ExpData(res[EDATAS][0])\n", + "edata.setTimepoints(np.linspace(0, 0.33011, 5000))\n", + "amici_solver = amici_model.getSolver()\n", + "rdata = amici.runAmiciSimulation(amici_model, amici_solver, edata)\n", + "\n", + "# Visualize state trajectories\n", + "plot_state_trajectories(rdata, model=amici_model)\n", + "plt.yscale(\"log\")" + ] + }, + { + "cell_type": "markdown", + "id": "a7fe6d08", + "metadata": {}, + "source": [ + "We can see a steep increase for `Pathogen` just before the error occurs. Let's zoom in:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6a645efb", + "metadata": {}, + "outputs": [], + "source": [ + "plt.plot(rdata.t, rdata.by_id(\"Pathogen\"))\n", + "plt.xlabel(\"time\")\n", + "plt.ylabel(\"Pathogen\");" + ] + }, + { + "cell_type": "markdown", + "id": "b85cd813", + "metadata": {}, + "source": [ + "The solver is unable to handle such a steep increase. There is not much we can do. Increasing the tolerances will let the solver proceed a bit further, but this is usually not enough. Most likely there is a problem in the model or in the choice of parameter values." + ] + }, + { + "cell_type": "markdown", + "id": "5afbd242", + "metadata": {}, + "source": [ + "## `the error test failed repeatedly or with |h| = hmin`\n", + "\n", + "Let's run a simulation:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb8910f2", + "metadata": {}, + "outputs": [], + "source": [ + "petab_problem = benchmark_models_petab.get_problem(\"Fujita_SciSignal2010\")\n", + "amici_model = import_petab_problem(petab_problem, verbose=False)\n", + "\n", + "np.random.seed(4920)\n", + "problem_parameters = dict(\n", + " zip(\n", + " petab_problem.x_free_ids,\n", + " petab_problem.sample_parameter_startpoints(n_starts=1)[0],\n", + " )\n", + ")\n", + "res = simulate_petab(\n", + " petab_problem=petab_problem,\n", + " amici_model=amici_model,\n", + " problem_parameters=problem_parameters,\n", + " scaled_parameters=True,\n", + ")\n", + "print(\n", + " \"Status:\",\n", + " [amici.simulation_status_to_str(rdata.status) for rdata in res[RDATAS]],\n", + ")\n", + "\n", + "assert [\n", + " amici.simulation_status_to_str(rdata.status) for rdata in res[RDATAS]\n", + "] == [\n", + " \"AMICI_SUCCESS\",\n", + " \"AMICI_ERR_FAILURE\",\n", + " \"AMICI_NOT_RUN\",\n", + " \"AMICI_NOT_RUN\",\n", + " \"AMICI_NOT_RUN\",\n", + " \"AMICI_NOT_RUN\",\n", + "]" + ] + }, + { + "cell_type": "markdown", + "id": "0b3a9904", + "metadata": {}, + "source": [ + "**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." + ] + }, + { + "cell_type": "markdown", + "id": "c16ac6c8", + "metadata": {}, + "source": [ + "**How to address?**\n", + "\n", + "The step size is computed based on the Jacobian. Inspecting `ReturnData.J` shows us that we have rather large values in the Jacobian:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "467c3d36", + "metadata": {}, + "outputs": [], + "source": [ + "rdata = res[RDATAS][1]\n", + "\n", + "# Show Jacobian as heatmap\n", + "plot_jacobian(rdata)\n", + "\n", + "print(f\"largest absolute Jacobian value: {np.max(np.abs(rdata.J)):.3g}\")" + ] + }, + { + "cell_type": "markdown", + "id": "1f6ec0e3", + "metadata": {}, + "source": [ + "In this case, the default relative error tolerance may be too high and lead too large absolute errors. \n", + "\n", + "Let's retry simulation using stricter tolerances:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "600ae826", + "metadata": {}, + "outputs": [], + "source": [ + "# set stricter relative error tolerance\n", + "amici_solver = amici_model.getSolver()\n", + "amici_solver.setRelativeTolerance(amici_solver.getRelativeTolerance() / 10)\n", + "\n", + "res = simulate_petab(\n", + " petab_problem=petab_problem,\n", + " amici_model=amici_model,\n", + " problem_parameters=problem_parameters,\n", + " scaled_parameters=True,\n", + " solver=amici_solver,\n", + ")\n", + "print(\n", + " \"Status:\",\n", + " [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.\")" + ] + }, + { + "cell_type": "markdown", + "id": "616710b6", + "metadata": {}, + "source": [ + "## `Cvode routine CVode returned a root after reinitialization`\n", + "\n", + "Let's run a simulation:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cec31332", + "metadata": {}, + "outputs": [], + "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", + ")\n", + "\n", + "np.random.seed(4)\n", + "problem_parameters = dict(\n", + " zip(\n", + " petab_problem.x_free_ids,\n", + " petab_problem.sample_parameter_startpoints(n_starts=1)[0],\n", + " )\n", + ")\n", + "res = simulate_petab(\n", + " petab_problem=petab_problem,\n", + " amici_model=amici_model,\n", + " problem_parameters=problem_parameters,\n", + " scaled_parameters=True,\n", + ")\n", + "print(\n", + " \"Status:\",\n", + " [amici.simulation_status_to_str(rdata.status) for rdata in res[RDATAS]],\n", + ")\n", + "assert [\n", + " amici.simulation_status_to_str(rdata.status) for rdata in res[RDATAS]\n", + "] == [\n", + " \"AMICI_ERROR\",\n", + " \"AMICI_NOT_RUN\",\n", + "]" + ] + }, + { + "cell_type": "markdown", + "id": "d4386603", + "metadata": {}, + "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`)." + ] + }, + { + "cell_type": "markdown", + "id": "02f2dd5d", + "metadata": {}, + "source": [ + "**How to address?**\n", + "\n", + "The error message already suggests a fix for this situation, so let's try increasing the relative tolerance:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3d1552e3", + "metadata": {}, + "outputs": [], + "source": [ + "amici_solver = amici_model.getSolver()\n", + "amici_solver.setRelativeTolerance(200 * amici_solver.getRelativeTolerance())\n", + "\n", + "np.random.seed(4)\n", + "problem_parameters = dict(\n", + " zip(\n", + " petab_problem.x_free_ids,\n", + " petab_problem.sample_parameter_startpoints(n_starts=1)[0],\n", + " )\n", + ")\n", + "res = simulate_petab(\n", + " petab_problem=petab_problem,\n", + " amici_model=amici_model,\n", + " problem_parameters=problem_parameters,\n", + " scaled_parameters=True,\n", + " solver=amici_solver,\n", + ")\n", + "print(\n", + " \"Status:\",\n", + " [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])" + ] + }, + { + "cell_type": "markdown", + "id": "86f4db29", + "metadata": {}, + "source": [ + "## `AMICI encountered a NaN / Inf value for [...]`\n", + "\n", + "Let's run a simulation:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d97349ff", + "metadata": {}, + "outputs": [], + "source": [ + "petab_problem = benchmark_models_petab.get_problem(\"Borghans_BiophysChem1997\")\n", + "amici_model = import_petab_problem(petab_problem, verbose=False)\n", + "\n", + "np.random.seed(18)\n", + "problem_parameters = dict(\n", + " zip(\n", + " petab_problem.x_free_ids,\n", + " petab_problem.sample_parameter_startpoints(n_starts=1)[0],\n", + " )\n", + ")\n", + "res = simulate_petab(\n", + " petab_problem=petab_problem,\n", + " amici_model=amici_model,\n", + " problem_parameters=problem_parameters,\n", + " scaled_parameters=True,\n", + ")\n", + "print(\n", + " \"Status:\",\n", + " [amici.simulation_status_to_str(rdata.status) for rdata in res[RDATAS]],\n", + ")\n", + "assert [\n", + " amici.simulation_status_to_str(rdata.status) for rdata in res[RDATAS]\n", + "] == [\"AMICI_FIRST_RHSFUNC_ERR\"]" + ] + }, + { + "cell_type": "markdown", + "id": "63641cff", + "metadata": {}, + "source": [ + "**What happened?**\n", + "\n", + "The forward simulation failed because AMICI encountered a `NaN` value when simulating condition `model1_data1`.\n", + "Then `NaN`s occurred in $\\dot x$ and $w$ (model expressions, such as reaction fluxes or assignment rules). Furthermore, the failure occurred at the first call, so at $t = t_0$ (here: $t = 0$).\n", + "\n", + "**How to address?**\n", + "\n", + "The `NaN` in $\\dot x$ is most likely a consequence of the one in $w$. (A subset of) the dependency tree looks something like:\n", + "\n", + "[![](https://mermaid.ink/img/pako:eNpdkrFuwyAQhl8FIWVL1MwMndKFtd1wBmJIg2IDwucCivLuxQp2zvaA_P1398Od7kFbpzRl9DdIfyM_p8aS8t3FnZHW2QGkheH8Er3wjHgZZK9Bh1kFAYyA6XXldBTpyIixBozsSHGAJSQSWwlccEa4bN3FSFu1KCIjOvmgh8GUF8y1yoGYDkZU-lBQ5SwyI-4y6PAnL52esl-B3a68pDZDDofPhfyKYEVLacSVERdGXFchzYAu59iBYweOHTh2qBAxvLupI3s9eJ41pndqGdOqvYXThv2G7xuOiBf7jHMzNsr41oyvzNgv0z3tdeilUWXzHlOooXDTvW4oK79KX-XYQUMb-yypcgT3nW1LGYRR7-noVVmhk5FlZ3vKrrIbFvVLGXChis9_j9jNUw?type=png)](https://mermaid.live/edit#pako:eNpdkrFuwyAQhl8FIWVL1MwMndKFtd1wBmJIg2IDwucCivLuxQp2zvaA_P1398Od7kFbpzRl9DdIfyM_p8aS8t3FnZHW2QGkheH8Er3wjHgZZK9Bh1kFAYyA6XXldBTpyIixBozsSHGAJSQSWwlccEa4bN3FSFu1KCIjOvmgh8GUF8y1yoGYDkZU-lBQ5SwyI-4y6PAnL52esl-B3a68pDZDDofPhfyKYEVLacSVERdGXFchzYAu59iBYweOHTh2qBAxvLupI3s9eJ41pndqGdOqvYXThv2G7xuOiBf7jHMzNsr41oyvzNgv0z3tdeilUWXzHlOooXDTvW4oK79KX-XYQUMb-yypcgT3nW1LGYRR7-noVVmhk5FlZ3vKrrIbFvVLGXChis9_j9jNUw)\n", + "\n", + "Always look for the most basic (furthest up) model quantities.\n", + "In cases where non-finite values occur in expressions further down, rerunning the simulation after calling `Model.setAlwaysCheckFinite(True)` may give some further hints on where the issue originates.\n", + "\n", + "The `NaN` in $w$ occurred for `flux_v7_v_6` (see error log), i.e., when computing the reaction flux for reaction `v7_v_6`. As $w$ only depends on $(t, p, k, x)$ and no non-finite values have been reported for those, the issue has to be in the respective flux equation.\n", + "\n", + "Let's look at that expression. This can either be done by inspecting the underlying SBML model (e.g., using COPASI), or by checking the generated model code:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fca95143", + "metadata": {}, + "outputs": [], + "source": [ + "# model source code location\n", + "model_src_dir = Path(amici_model.module.__file__).parents[1]\n", + "\n", + "# find the problematic expression in the model source code\n", + "!grep flux_v7_v_6 {model_src_dir}/w.cpp" + ] + }, + { + "cell_type": "markdown", + "id": "9f49a00a", + "metadata": {}, + "source": [ + "What could go wrong? We can obtain `NaN` from any of these symbols being `NaN`, or through division by zero.\n", + "\n", + "Let's let's check the denominator first: $$(A\\_state^2 + Kp^2)*(Kd^{n\\_par} + Z\\_state^{n\\_par})$$\n", + "\n", + "\n", + "`A_state` and `Z_state` are state variables, `Kd`, `K_p`, and `n_par` are parameters.\n", + "\n", + "As the error occurred at $t = t_0$, let's ensure the initial state is non-zero and finite:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0eec6fe8", + "metadata": {}, + "outputs": [], + "source": [ + "rdata = res[RDATAS][0]\n", + "edata = res[EDATAS][0]\n", + "# check initial states\n", + "x0 = dict(zip(amici_model.getStateIds(), rdata.x0))\n", + "print(f\"{x0=}\")" + ] + }, + { + "cell_type": "markdown", + "id": "050c4c38", + "metadata": {}, + "source": [ + "The initial states are fine - the first multiplicand is non-zero, as $x_0$ was non-zero. \n", + "\n", + "So let's check the parameter values occurring in the second multiplicand:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec724ce9", + "metadata": {}, + "outputs": [], + "source": [ + "# we have to account for the chosen parameter scale\n", + "from itertools import starmap\n", + "\n", + "unscaled_parameter = dict(\n", + " zip(\n", + " amici_model.getParameterIds(),\n", + " starmap(\n", + " amici.getUnscaledParameter, zip(edata.parameters, edata.pscale)\n", + " ),\n", + " )\n", + ")\n", + "print(dict((p, unscaled_parameter[p]) for p in (\"Kd\", \"Kp\", \"n_par\")))" + ] + }, + { + "cell_type": "markdown", + "id": "62d82971", + "metadata": {}, + "source": [ + "Considering that `n_par` occurrs as exponent, it's magnitude looks pretty high.\n", + "This term is very likely causing the problem - let's check:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9f3f8bdb", + "metadata": {}, + "outputs": [], + "source": [ + "print(\n", + " f\"{x0['Z_state']**unscaled_parameter['n_par'] + unscaled_parameter['Kd']**unscaled_parameter['n_par']=}\"\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "6616c2ff", + "metadata": {}, + "source": [ + "Indeed, no way we can fix this for the given model.\n", + "This was most likely an unrealistic parameter value, originating from a too high upper parameter bound for `n_par`.\n", + "Therefore, if this error occurs during optimization, a first step could be adapting the respective parameter bounds.\n", + "In other cases, this may be a result of unfortunate arrangement of model expressions, which can sometimes be solved by passing a suitable simplification function to the model import." + ] + }, + { + "cell_type": "markdown", + "id": "22cfbbbf", + "metadata": {}, + "source": [ + "\n", + "\n", + "## `Steady state sensitivity computation failed due to unsuccessful factorization of RHS Jacobian`\n", + "\n", + "Let's run a simulation:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b41a5017", + "metadata": {}, + "outputs": [], + "source": [ + "petab_problem = benchmark_models_petab.get_problem(\"Blasi_CellSystems2016\")\n", + "with suppress(KeyError):\n", + " del os.environ[\"AMICI_EXPERIMENTAL_SBML_NONCONST_CLS\"]\n", + "amici_model = import_petab_problem(\n", + " petab_problem,\n", + " verbose=False,\n", + " force_compile=True,\n", + " model_name=\"Blasi_CellSystems2016_1\",\n", + ")\n", + "\n", + "amici_solver = amici_model.getSolver()\n", + "amici_solver.setSensitivityMethod(amici.SensitivityMethod.forward)\n", + "amici_solver.setSensitivityOrder(amici.SensitivityOrder.first)\n", + "amici_model.setSteadyStateSensitivityMode(\n", + " amici.SteadyStateSensitivityMode.newtonOnly\n", + ")\n", + "\n", + "np.random.seed(2020)\n", + "problem_parameters = dict(\n", + " zip(\n", + " petab_problem.x_free_ids,\n", + " petab_problem.sample_parameter_startpoints(n_starts=1)[0],\n", + " )\n", + ")\n", + "res = simulate_petab(\n", + " petab_problem=petab_problem,\n", + " amici_model=amici_model,\n", + " problem_parameters=problem_parameters,\n", + " scaled_parameters=True,\n", + " solver=amici_solver,\n", + ")\n", + "print(\n", + " \"Status:\",\n", + " [amici.simulation_status_to_str(rdata.status) for rdata in res[RDATAS]],\n", + ")\n", + "\n", + "# hard to reproduce on GHA\n", + "if os.getenv(\"GITHUB_ACTIONS\") is None:\n", + " assert [\n", + " amici.simulation_status_to_str(rdata.status) for rdata in res[RDATAS]\n", + " ] == [\"AMICI_ERROR\"]" + ] + }, + { + "cell_type": "markdown", + "id": "82267e47", + "metadata": {}, + "source": [ + "**What happened?**\n", + "\n", + "AMICI failed to compute steady-state sensitivities, because it was not able to factorize the Jacobian.\n", + "\n", + "**How to address?**\n", + "\n", + "This is most likely a result of a singular Jacobian. Let's check the condition number:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8cd349b7", + "metadata": {}, + "outputs": [], + "source": [ + "rdata = res[RDATAS][0]\n", + "np.linalg.cond(rdata.J)" + ] + }, + { + "cell_type": "markdown", + "id": "da131d3e", + "metadata": {}, + "source": [ + "Indeed, the condition number shows that the Jacobian is numerically singular. If this happens consistently, it is usually due to conserved quantities in the model.\n", + "\n", + "There are two ways we can address that:\n", + "\n", + "1. Use numerical integration to compute sensitivities, for which a singular Jacobian is not an issue. This is, usually, slower, though.\n", + "2. Remove any conserved quantities.\n", + "\n", + "Let's try both approaches:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f82078e7", + "metadata": {}, + "outputs": [], + "source": [ + "# use numerical integration\n", + "amici_model.setSteadyStateSensitivityMode(\n", + " amici.SteadyStateSensitivityMode.integrationOnly\n", + ")\n", + "\n", + "res = simulate_petab(\n", + " petab_problem=petab_problem,\n", + " amici_model=amici_model,\n", + " problem_parameters=problem_parameters,\n", + " scaled_parameters=True,\n", + " solver=amici_solver,\n", + ")\n", + "print(\n", + " \"Status:\",\n", + " [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])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8d7be541", + "metadata": {}, + "outputs": [], + "source": [ + "# Remove conserved quantities - this requires re-importing the model\n", + "\n", + "# this is enabled by the `AMICI_EXPERIMENTAL_SBML_NONCONST_CLS` environment variable\n", + "os.environ[\"AMICI_EXPERIMENTAL_SBML_NONCONST_CLS\"] = \"1\"\n", + "amici_model = import_petab_problem(\n", + " petab_problem,\n", + " verbose=False,\n", + " # 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", + ")\n", + "del os.environ[\"AMICI_EXPERIMENTAL_SBML_NONCONST_CLS\"]\n", + "\n", + "amici_solver = amici_model.getSolver()\n", + "amici_solver.setSensitivityMethod(amici.SensitivityMethod.forward)\n", + "amici_solver.setSensitivityOrder(amici.SensitivityOrder.first)\n", + "\n", + "res = simulate_petab(\n", + " petab_problem=petab_problem,\n", + " amici_model=amici_model,\n", + " problem_parameters=problem_parameters,\n", + " scaled_parameters=True,\n", + " solver=amici_solver,\n", + ")\n", + "print(\n", + " \"Status:\",\n", + " [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])" + ] + }, + { + "cell_type": "markdown", + "id": "4b977c0b", + "metadata": {}, + "source": [ + "## `Steady state computation failed`\n", + "\n", + "Let's run a simulation:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "97f797dd", + "metadata": {}, + "outputs": [], + "source": [ + "petab_problem = benchmark_models_petab.get_problem(\"Brannmark_JBC2010\")\n", + "amici_model = import_petab_problem(\n", + " petab_problem,\n", + " verbose=False,\n", + ")\n", + "\n", + "amici_solver = amici_model.getSolver()\n", + "\n", + "\n", + "np.random.seed(1851)\n", + "problem_parameters = dict(\n", + " zip(\n", + " petab_problem.x_free_ids,\n", + " petab_problem.sample_parameter_startpoints(n_starts=1)[0],\n", + " )\n", + ")\n", + "res = simulate_petab(\n", + " petab_problem=petab_problem,\n", + " amici_model=amici_model,\n", + " problem_parameters=problem_parameters,\n", + " scaled_parameters=True,\n", + " solver=amici_solver,\n", + ")\n", + "\n", + "print(\n", + " \"Status:\",\n", + " [amici.simulation_status_to_str(rdata.status) for rdata in res[RDATAS]],\n", + ")\n", + "\n", + "# hard to reproduce on GHA\n", + "if os.getenv(\"GITHUB_ACTIONS\") is None:\n", + " assert [\n", + " amici.simulation_status_to_str(rdata.status) for rdata in res[RDATAS]\n", + " ] == [\n", + " \"AMICI_ERROR\",\n", + " \"AMICI_NOT_RUN\",\n", + " \"AMICI_NOT_RUN\",\n", + " \"AMICI_NOT_RUN\",\n", + " \"AMICI_NOT_RUN\",\n", + " \"AMICI_NOT_RUN\",\n", + " \"AMICI_NOT_RUN\",\n", + " \"AMICI_NOT_RUN\",\n", + " ]" + ] + }, + { + "cell_type": "markdown", + "id": "0713b830", + "metadata": {}, + "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", + "\n", + "This can also be seen in `ReturnDataView.preeq_status` (the three statuses corresponds to Newton \\#1, Simulation, Newton \\#2):" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ffdc8e82", + "metadata": {}, + "outputs": [], + "source": [ + "rdata = res[RDATAS][0]\n", + "list(map(amici.SteadyStateStatus, rdata.preeq_status.flatten()))" + ] + }, + { + "cell_type": "markdown", + "id": "189dd964", + "metadata": {}, + "source": [ + "**How to address?**\n", + "\n", + "There are several ways to address that:\n", + "\n", + "1. Stricter integration tolerances (preferred if affordable - higher accuracy, but generally slower)\n", + "\n", + "2. Looser steady-state tolerances (lower accuracy, generally faster)\n", + "\n", + "3. Increase the number of allowed steps for Newton's method\n", + "\n", + "Let's try all of them:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "28fada9f", + "metadata": {}, + "outputs": [], + "source": [ + "# Reduce relative tolerance for integration\n", + "amici_solver = amici_model.getSolver()\n", + "amici_solver.setRelativeTolerance(\n", + " 1 / 100 * amici_solver.getRelativeTolerance()\n", + ")\n", + "\n", + "res = simulate_petab(\n", + " petab_problem=petab_problem,\n", + " amici_model=amici_model,\n", + " problem_parameters=problem_parameters,\n", + " scaled_parameters=True,\n", + " solver=amici_solver,\n", + ")\n", + "print(\n", + " \"status:\",\n", + " [amici.simulation_status_to_str(rdata.status) for rdata in res[RDATAS]],\n", + ")\n", + "\n", + "rdata = res[RDATAS][0]\n", + "print(\n", + " f\"preeq_status={list(map(amici.SteadyStateStatus, rdata.preeq_status.flatten()))}\"\n", + ")\n", + "print(f\"{rdata.preeq_numsteps=}\")\n", + "\n", + "# hard to reproduce on GHA\n", + "if os.getenv(\"GITHUB_ACTIONS\") is None:\n", + " assert all(rdata.status == amici.AMICI_SUCCESS for rdata in res[RDATAS])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a6d34467", + "metadata": {}, + "outputs": [], + "source": [ + "# Increase relative steady state tolerance\n", + "for log10_relaxation_factor in range(1, 10):\n", + " print(f\"Relaxing tolerances by factor {10 ** log10_relaxation_factor}\")\n", + " amici_solver = amici_model.getSolver()\n", + " amici_solver.setRelativeToleranceSteadyState(\n", + " amici_solver.getRelativeToleranceSteadyState()\n", + " * 10**log10_relaxation_factor\n", + " )\n", + "\n", + " res = simulate_petab(\n", + " petab_problem=petab_problem,\n", + " amici_model=amici_model,\n", + " problem_parameters=problem_parameters,\n", + " scaled_parameters=True,\n", + " solver=amici_solver,\n", + " )\n", + " if all(rdata.status == amici.AMICI_SUCCESS for rdata in res[RDATAS]):\n", + " print(\n", + " f\"-> Succeeded with relative steady state tolerance {amici_solver.getRelativeToleranceSteadyState()}\\n\"\n", + " )\n", + " break\n", + " else:\n", + " print(\"-> Failed.\\n\")\n", + "\n", + "print(\n", + " \"status:\",\n", + " [amici.simulation_status_to_str(rdata.status) for rdata in res[RDATAS]],\n", + ")\n", + "\n", + "rdata = res[RDATAS][0]\n", + "print(\n", + " f\"preeq_status={list(map(amici.SteadyStateStatus, rdata.preeq_status.flatten()))}\"\n", + ")\n", + "print(f\"{rdata.preeq_numsteps=}\")\n", + "assert all(rdata.status == amici.AMICI_SUCCESS for rdata in res[RDATAS])" + ] + }, + { + "cell_type": "markdown", + "id": "6d1b1835", + "metadata": {}, + "source": [ + "That fixed the error, and took only a quarter of the number steps as the previous run, but at the cost of much lower accuracy." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "df1ee3fc", + "metadata": {}, + "outputs": [], + "source": [ + "# Let's try increasing the number of Newton steps\n", + "# (this is 0 by default, so the Newton solver wasn't used before,\n", + "# as can be seen from the 0 in `rdata.preeq_numsteps[0]`)\n", + "amici_solver = amici_model.getSolver()\n", + "amici_solver.setNewtonMaxSteps(10**4)\n", + "\n", + "res = simulate_petab(\n", + " petab_problem=petab_problem,\n", + " amici_model=amici_model,\n", + " problem_parameters=problem_parameters,\n", + " scaled_parameters=True,\n", + " solver=amici_solver,\n", + ")\n", + "print(\n", + " \"status:\",\n", + " [amici.simulation_status_to_str(rdata.status) for rdata in res[RDATAS]],\n", + ")\n", + "\n", + "rdata = res[RDATAS][0]\n", + "print(\n", + " f\"preeq_status={list(map(amici.SteadyStateStatus, rdata.preeq_status.flatten()))}\"\n", + ")\n", + "print(f\"{rdata.preeq_numsteps=}\")\n", + "# hard to reproduce on GHA\n", + "if os.getenv(\"GITHUB_ACTIONS\") is None:\n", + " assert [\n", + " amici.simulation_status_to_str(rdata.status) for rdata in res[RDATAS]\n", + " ] == [\n", + " \"AMICI_ERROR\",\n", + " \"AMICI_NOT_RUN\",\n", + " \"AMICI_NOT_RUN\",\n", + " \"AMICI_NOT_RUN\",\n", + " \"AMICI_NOT_RUN\",\n", + " \"AMICI_NOT_RUN\",\n", + " \"AMICI_NOT_RUN\",\n", + " \"AMICI_NOT_RUN\",\n", + " ]" + ] + }, + { + "cell_type": "markdown", + "id": "5c746982", + "metadata": {}, + "source": [ + "Increasing the maximum number of Newton steps doesn't seem to help here. The Jacobian was numerically singular and its factorization failed. This can be a result of conserved quantities in the model. Section [Steady state sensitivity computation failed due to unsuccessful factorization of RHS Jacobian](#unsuccessful_factorization) shows how to address that." + ] + } + ], + "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.6" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": true, + "sideBar": true, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": { + "height": "calc(100% - 180px)", + "left": "10px", + "top": "150px", + "width": "335.6px" + }, + "toc_section_display": true, + "toc_window_display": true + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/deps/AMICI/python/examples/example_jax/ExampleJax.ipynb b/deps/AMICI/python/examples/example_jax/ExampleJax.ipynb new file mode 100644 index 000000000..efda5b458 --- /dev/null +++ b/deps/AMICI/python/examples/example_jax/ExampleJax.ipynb @@ -0,0 +1,916 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "d4d2bc5c", + "metadata": {}, + "source": [ + "# Overview\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." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "b0a66e18", + "metadata": {}, + "outputs": [], + "source": [ + "import jax\n", + "import jax.numpy as jnp" + ] + }, + { + "cell_type": "markdown", + "id": "fb2fe897", + "metadata": {}, + "source": [ + "# 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)." + ] + }, + { + "cell_type": "markdown", + "id": "8552f123", + "metadata": {}, + "source": [ + "From the benchmark collection, we now import the Böhm model." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "9166e3bf", + "metadata": {}, + "outputs": [], + "source": [ + "import petab\n", + "\n", + "model_name = \"Boehm_JProteomeRes2014\"\n", + "yaml_file = f\"https://raw.githubusercontent.com/Benchmarking-Initiative/Benchmark-Models-PEtab/master/Benchmark-Models/{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": 3, + "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": 3, + "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": 4, + "id": "6ada3fb8", + "metadata": {}, + "outputs": [], + "source": [ + "from amici.petab_import import import_petab_problem\n", + "\n", + "amici_model = import_petab_problem(\n", + " petab_problem, force_compile=True, verbose=False\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "e2ef051a", + "metadata": {}, + "source": [ + "# 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)." + ] + }, + { + "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": 5, + "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", + " solver=amici_solver,\n", + " )" + ] + }, + { + "cell_type": "markdown", + "id": "6f6201e8", + "metadata": {}, + "source": [ + "Now we can use this base function to create two separate functions that compute the log-likelihood (`llh`) and its 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": 6, + "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": 7, + "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": 8, + "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": 9, + "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.exp(value)\n", + " if scale == petab.LOG\n", + " else jnp.power(10, value)\n", + " for value, scale in zip(parameters, parameter_scales)\n", + " )\n", + " )\n", + " return jax_objective(par_scaled)" + ] + }, + { + "cell_type": "markdown", + "id": "293e29fb", + "metadata": {}, + "source": [ + "# Testing\n", + "\n", + "We can now run the function to compute the log-likelihood and the gradient." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "b7e9ff3b", + "metadata": {}, + "outputs": [], + "source": [ + "parameters = dict(zip(petab_problem.x_free_ids, petab_problem.x_nominal_free))\n", + "scaled_parameters = petab_problem.scale_parameters(parameters)\n", + "scaled_parameters_np = np.asarray(list(scaled_parameters.values()))" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "fb3085a8", + "metadata": {}, + "outputs": [], + "source": [ + "llh_jax, sllh_jax = jax_objective_with_parameter_transform(\n", + " scaled_parameters_np\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(\n", + " petab_problem,\n", + " amici_model,\n", + " solver=amici_solver,\n", + " scaled_parameters=True,\n", + " scaled_gradients=True,\n", + " problem_parameters=scaled_parameters,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "2628db12", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
amicijaxrel_diff
llh-138.221997-138.222-2.135248e-08
\n", + "
" + ], + "text/plain": [ + " amici jax rel_diff\n", + "llh -138.221997 -138.222 -2.135248e-08" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import pandas as pd\n", + "\n", + "pd.DataFrame(\n", + " dict(\n", + " amici=r[\"llh\"],\n", + " jax=float(llh_jax),\n", + " rel_diff=(r[\"llh\"] - float(llh_jax)) / r[\"llh\"],\n", + " ),\n", + " index=(\"llh\",),\n", + ")" + ] + }, + { + "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", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
amicijaxrel_diff
Epo_degradation_BaF3-0.022045-0.0220344.645833e-04
k_exp_hetero-0.055323-0.0553238.646725e-08
k_exp_homo-0.005789-0.005801-2.013520e-03
k_imp_hetero-0.005414-0.0054031.973517e-03
k_imp_homo0.0000450.0000451.119566e-06
k_phos-0.007907-0.0077941.447768e-02
sd_pSTAT5A_rel-0.010784-0.010800-1.469604e-03
sd_pSTAT5B_rel-0.024037-0.024037-8.729860e-06
sd_rSTAT5A_rel-0.019191-0.0191862.829431e-04
\n", + "
" + ], + "text/plain": [ + " amici jax rel_diff\n", + "Epo_degradation_BaF3 -0.022045 -0.022034 4.645833e-04\n", + "k_exp_hetero -0.055323 -0.055323 8.646725e-08\n", + "k_exp_homo -0.005789 -0.005801 -2.013520e-03\n", + "k_imp_hetero -0.005414 -0.005403 1.973517e-03\n", + "k_imp_homo 0.000045 0.000045 1.119566e-06\n", + "k_phos -0.007907 -0.007794 1.447768e-02\n", + "sd_pSTAT5A_rel -0.010784 -0.010800 -1.469604e-03\n", + "sd_pSTAT5B_rel -0.024037 -0.024037 -8.729860e-06\n", + "sd_rSTAT5A_rel -0.019191 -0.019186 2.829431e-04" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "grad_amici = np.asarray(list(r[\"sllh\"].values()))\n", + "grad_jax = np.asarray(sllh_jax)\n", + "rel_diff = (grad_amici - grad_jax) / grad_jax\n", + "pd.DataFrame(\n", + " index=r[\"sllh\"].keys(),\n", + " data=dict(amici=grad_amici, jax=grad_jax, rel_diff=rel_diff),\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "4b00dcb2", + "metadata": {}, + "source": [ + "We see quite some differences in the gradient calculation, with over to 1% error for `k_phos`. 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", + " scaled_parameters_np\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 have now disappeared." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "25e8b301", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
amicijaxrel_diff
llh-138.221997-138.221997-0.0
\n", + "
" + ], + "text/plain": [ + " amici jax rel_diff\n", + "llh -138.221997 -138.221997 -0.0" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pd.DataFrame(\n", + " dict(\n", + " amici=r[\"llh\"],\n", + " jax=float(llh_jax),\n", + " rel_diff=(r[\"llh\"] - float(llh_jax)) / r[\"llh\"],\n", + " ),\n", + " index=(\"llh\",),\n", + ")" + ] + }, + { + "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", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
amicijaxrel_diff
Epo_degradation_BaF3-0.022045-0.022045-0.0
k_exp_hetero-0.055323-0.055323-0.0
k_exp_homo-0.005789-0.005789-0.0
k_imp_hetero-0.005414-0.005414-0.0
k_imp_homo0.0000450.0000450.0
k_phos-0.007907-0.007907-0.0
sd_pSTAT5A_rel-0.010784-0.010784-0.0
sd_pSTAT5B_rel-0.024037-0.024037-0.0
sd_rSTAT5A_rel-0.019191-0.019191-0.0
\n", + "
" + ], + "text/plain": [ + " amici jax rel_diff\n", + "Epo_degradation_BaF3 -0.022045 -0.022045 -0.0\n", + "k_exp_hetero -0.055323 -0.055323 -0.0\n", + "k_exp_homo -0.005789 -0.005789 -0.0\n", + "k_imp_hetero -0.005414 -0.005414 -0.0\n", + "k_imp_homo 0.000045 0.000045 0.0\n", + "k_phos -0.007907 -0.007907 -0.0\n", + "sd_pSTAT5A_rel -0.010784 -0.010784 -0.0\n", + "sd_pSTAT5B_rel -0.024037 -0.024037 -0.0\n", + "sd_rSTAT5A_rel -0.019191 -0.019191 -0.0" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "grad_amici = np.asarray(list(r[\"sllh\"].values()))\n", + "grad_jax = np.asarray(sllh_jax)\n", + "rel_diff = (grad_amici - grad_jax) / grad_jax\n", + "pd.DataFrame(\n", + " index=r[\"sllh\"].keys(),\n", + " data=dict(amici=grad_amici, jax=grad_jax, rel_diff=rel_diff),\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.11.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} 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 new file mode 100644 index 000000000..31a9fc172 --- /dev/null +++ b/deps/AMICI/python/examples/example_large_models/example_performance_optimization.ipynb @@ -0,0 +1,555 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "dbe0a770", + "metadata": {}, + "source": [ + "# Speeding up model import and simulation - with a focus on large models\n", + "\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", + "\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", + "\n", + "1. Generating the ODEs\n", + "2. Computing derivatives\n", + "3. Generating C++ code\n", + "4. Compiling the generated code\n", + "5. Simulating the model\n", + "\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", + "\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", + "Since many of the following demonstrations take quite some time to compute, this notebook mostly shows pre-generated results." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "411be08a", + "metadata": {}, + "outputs": [], + "source": [ + "from IPython.core.pylabtools import figsize, getfigs\n", + "import matplotlib.pyplot as plt\n", + "import pandas as pd\n", + "\n", + "plt.rcParams.update({\"font.size\": 12})" + ] + }, + { + "cell_type": "markdown", + "id": "bfd50810", + "metadata": {}, + "source": [ + "## Examples\n", + "\n", + "The demos below make use of the following models contained in the [PEtab benchmark collection](https://github.com/Benchmarking-Initiative/Benchmark-Models-PEtab) and other publications:\n", + "\n", + "| Model | # parameters | # states |\n", + "|----------------------------------------------------------------------------------------------------------------------------------------------------------|--------------|----------|\n", + "| [Chen_MSB2009](https://github.com/Benchmarking-Initiative/Benchmark-Models-PEtab/tree/master/Benchmark-Models/Chen_MSB2009) | 155 | 500 |\n", + "| [Froehlich_CellSystems2018](https://github.com/Benchmarking-Initiative/Benchmark-Models-PEtab/tree/master/Benchmark-Models/Froehlich_CellSystems2018) | 4231 | 1396 |\n", + "| [FröhlichGer2022 (RTKERK__base)](https://doi.org/10.1101/2022.02.17.480899) | 105 | 2272 |\n", + "| [hello_pysb](https://github.com/pysb/pysb/blob/master/pysb/examples/hello_pysb.py) | 4 | 3 |\n", + "\n", + "All data has been generated with AMICI v0.15.0 or v0.16.0 unless stated otherwise." + ] + }, + { + "cell_type": "markdown", + "id": "b772af14", + "metadata": {}, + "source": [ + "## Model import\n", + "\n", + "### Symbolic processing\n", + "\n", + "#### Parameters as constants\n", + "\n", + "By default, AMICI will generate sensitivity equations with respect to all model parameters. If it is clear upfront, that sensitivities with respect to certain parameters will not be required, their IDs can be passed 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) via the `constant_parameters` argument to not generate the respective equations. This will reduce CPU time and RAM requirements during import and simulation.\n", + "The PEtab import will automatically pass all parameters with `petab.ESTIMATE==False` as `constant_parameters` arguments.\n", + "\n", + "See also the following section for the case that no sensitivities are required at all.\n", + "\n", + "\n", + "#### Not generating sensivitiy 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", + "Example:\n", + "```bash\n", + "petab_yaml=\"https://raw.githubusercontent.com/Benchmarking-Initiative/Benchmark-Models-PEtab/master/Benchmark-Models/Froehlich_CellSystems2018/Froehlich_CellSystems2018.yaml\"\n", + "/usr/bin/time -v amici_import_petab -y \"$petab_yaml\" --no-compile\n", + "# vs.\n", + "/usr/bin/time -v amici_import_petab -y \"$petab_yaml\" --no-compile --no-sensitivities\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "4f3af02d", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "speedup: 1.25x\n" + ] + } + ], + "source": [ + "figsize(4, 4)\n", + "plt.bar([\"True\", \"False\"], [873.54, 697.85])\n", + "plt.xlabel(\"generate_sensitivity_code\")\n", + "plt.ylabel(\"Import time (s)\")\n", + "plt.title(\n", + " \"Import speed-up when not generating sensitivity code\\n(Froehlich_CellSystems2018)\"\n", + ")\n", + "plt.show()\n", + "\n", + "print(f\"speedup: {873.54/697.85:.2f}x\")" + ] + }, + { + "cell_type": "markdown", + "id": "490d8987", + "metadata": {}, + "source": [ + "#### Extracting common subexpressions\n", + "\n", + "For some models, the size of the generated model code can be significantly reduced by extracting common subexpressions. This can yield substantial reductions of compile times and RAM-requirements. Very large models might not compile without this option. Extracting common subexpressions can be enabled by setting an environment variable `AMICI_EXTRACT_CSE=1` before model import.\n", + "The downside is, that the generated model code becomes rather unreadable. The increase in import time when enabling this feature is usually <15%, the effect on code size and compile time is highly model dependent. Mostly models with tightly coupled ODEs, as obtained from complex rate laws or spatial discretizations of ODEs, seem to benefit. For models with mass action or similar kinetics, this option seems to not be helpful and rather increases compile time (e.g., for FröhlichGer2022, the compile time doubles).\n", + "\n", + "Benchmark result from [here](https://github.com/AMICI-dev/AMICI/pull/1852) (SBML import, `AMICI_IMPORT_NPROCS=2`, sequential compilation with clang14, `CFLAGS=-O2`):\n", + "\n", + "| | default | `AMICI_EXTRACT_CSE=1` |\n", + "|---------------------|----------------|-----------------------|\n", + "| import time | 160 min (100%) | 164 min (103%) |\n", + "| code size | 89 MB (100%) | 27 MB (30%) |\n", + "| compile time | 169 min (100%) | 90 min (53%) |\n", + "| compile RAM | 7.49 GB (100%) | 1.18 GB (16%) |\n", + "| simulation time (*) | 100% | 97% |\n", + "\n", + "(*) lowest out of 20 identical simulations using ASA" + ] + }, + { + "cell_type": "markdown", + "id": "21421f47", + "metadata": {}, + "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", + "\n", + "Impact for a large and a tiny model:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "49cd66c1", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Import time decreased by up to ~44%.\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Import time increased by at least ~774%.\n" + ] + } + ], + "source": [ + "df_import = pd.read_csv(\"results_import.tsv\", sep=\"\\t\")\n", + "for model_name, df in df_import.groupby(\"model_name\"):\n", + " plt.plot(df.nprocs, df.time)\n", + " plt.title(f\"Import time {model_name}\")\n", + " plt.xlabel(\"AMICI_IMPORT_NPROCS\")\n", + " plt.ylabel(\"Import time (s)\")\n", + " plt.ylim(ymin=0)\n", + " plt.show()\n", + "\n", + " import_times = df.sort_values(\"nprocs\")[\"time\"].values\n", + " percent_change = (\n", + " (import_times[0] - min(import_times[1:])) / import_times[0] * 100\n", + " )\n", + " if percent_change > 0:\n", + " print(f\"Import time decreased by up to ~{percent_change:.0f}%.\")\n", + " else:\n", + " print(f\"Import time increased by at least ~{-percent_change:.0f}%.\")" + ] + }, + { + "cell_type": "markdown", + "id": "6b938781", + "metadata": {}, + "source": [ + "#### (No) simplification of model expressions\n", + "\n", + "By default, AMICI will try to perform some basic simplification of model expressions. For complex model expressions, or for large models, this can become costly. It very much depends on the model expressions, whether the benefits outweigh the cost.\n", + "\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)." + ] + }, + { + "cell_type": "markdown", + "id": "c32065a1", + "metadata": {}, + "source": [ + "### Compilation\n", + "\n", + "#### Choice of compiler\n", + "\n", + "From own experience, `clang` seems to handle larger models, or more specifically, their large source files, better than `g++`, both in terms of memory requirement and compile time. You can use a different compiler by setting the `CC` and `CXX` environment variables to, e.g., `CC=clang`, `CXX=clang`." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "62cfce84", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Clang was ~10x as fast as g++.\n" + ] + } + ], + "source": [ + "figsize(8, 4)\n", + "compilation_time_s = [3022.453, 289.518]\n", + "labels = [\n", + " \"g++ (Ubuntu 12.2.0-3ubuntu1) 12.2.0\",\n", + " \"Ubuntu clang version 15.0.2-1\",\n", + "]\n", + "plt.bar(labels, compilation_time_s)\n", + "plt.ylim(ymin=0)\n", + "plt.title(\"Choice of compiler - FröhlichGer2022\")\n", + "plt.xlabel(\"Compiler\")\n", + "plt.ylabel(\"Walltime for compilation (s)\")\n", + "plt.show()\n", + "print(\n", + " f\"Clang was ~{compilation_time_s[0] / compilation_time_s[1]:.0f}x as fast as g++.\"\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "ee9e4b2a", + "metadata": {}, + "source": [ + "#### Parallel compilation\n", + "\n", + "It's possible to compile multiple source files in parallel by specifying the number of parallel processes via the `AMICI_PARALLEL_COMPILE` environment variable. This is also beneficial for small models.\n", + "Note, however, that for large models, this may require significant amounts of RAM. \n", + "\n", + "Example for a large and tiny model:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "779b773a", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "We were able to reduce compile time by up to ~45%.\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "We were able to reduce compile time by up to ~73%.\n" + ] + } + ], + "source": [ + "df_compile = pd.read_csv(\"results_compile.tsv\", sep=\"\\t\")\n", + "figsize(6, 4)\n", + "\n", + "for model_name, df in df_compile.groupby(\"model_name\"):\n", + " plt.plot(df.nprocs, df.time)\n", + " plt.title(f\"Compile time {model_name}\")\n", + " plt.xlabel(\"AMICI_PARALLEL_COMPILE\")\n", + " plt.ylabel(\"Compile time (s)\")\n", + " plt.ylim(ymin=0)\n", + " plt.show()\n", + "\n", + " compilation_time_s = df.sort_values(\"nprocs\")[\"time\"].values\n", + " print(\n", + " \"We were able to reduce compile time by up to \"\n", + " f\"~{(compilation_time_s[0] - min(compilation_time_s[1:])) / compilation_time_s[0] * 100:.0f}%.\"\n", + " )" + ] + }, + { + "cell_type": "markdown", + "id": "0f68459d", + "metadata": {}, + "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", + "\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", + "\n", + "These compiler flags should be set for both, AMICI installation installation and model compilation. \n", + "\n", + "For AMICI installation, e.g.,\n", + "```bash\n", + "CFLAGS=\"-O3 -march=native\" pip install amici\n", + "```\n", + "\n", + "For model compilation, flags can be passed via the `AMICI_CXXFLAGS` environment variable.\n", + "\n", + "\n", + "Example:\n", + "```bash\n", + "petab_yaml=\"https://raw.githubusercontent.com/Benchmarking-Initiative/Benchmark-Models-PEtab/master/Benchmark-Models/Chen_MSB2009/Chen_MSB2009.yaml\"\n", + "amici_import_petab -y \"${petab_yaml}\" --no-compile\n", + "cd Chen_MSB2009-amici0.16.0/\n", + "for cflags in \"-O0\" \"-O1\" \"-O2\" \"-O3\" \"-O3 -march=native\"\n", + " # this line only builds the model extension, and is normally performed automatically during import\n", + " do AMICI_PARALLEL_COMPILE=1 AMICI_CXXFLAGS=${cflags} /usr/bin/time -v python setup.py build_ext --force --build-lib .\n", + "done\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "b94b979b", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "compilation_time_s = [20.01, 24.62, 25.59, 25.63, 28.21]\n", + "labels = [\"-O0\", \"-O1\", \"-O2\", \"-O3\", \"-O3 -march=native\"]\n", + "figsize(9, 4)\n", + "plt.bar(labels, compilation_time_s)\n", + "plt.title(\"Impact of optimization flags on compilation\")\n", + "plt.xlabel(\"AMICI_CXXFLAGS\")\n", + "plt.ylabel(\"Walltime for compilation (s)\");" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "33ebf019", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjIAAAEXCAYAAAC3Ra5BAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/av/WaAAAACXBIWXMAAAsTAAALEwEAmpwYAAAi6UlEQVR4nO3de9xlZV338c9XkINyFh7kMDiKqIEmKqIpmaIpKokWCuQBDeOpqOypNPGQplKUiadQIyWBFCTIwEIQESQ0hEFUThqDgIAICHIKRJDf88e6btje3Id1D7Nnz5r5vF+v/dprXev0W2vvPfO7r+ta60pVIUmSNEQPmXQAkiRJy8pERpIkDZaJjCRJGiwTGUmSNFgmMpIkabBMZCRJ0mCZyEgrqSSvSHJVktuTPGXMx3p1ki8t47a/muR7K1NMPfb9+0mua9f2EUkqyWPHcaxJSbJNO781xrDv1yc560Fs/8Uk+y7PmLT6is+R0aogyRXAG6vqy5OOZUqSArarqqXLuP1lwJ9W1QnLOa7FwOXAQ6vqnuW572W1ImNK8lDgVuCZVfXtVvagPqvVTZLX0/3edumx7ruBx1bVa8Ydl1ZP1shIK69HARdNOohV0ObAOnhtpVWCiYxWOa3a+2tJPpjk5iTfT/KsVn5VkutHq7WTfDrJJ5KcmuS2JF9N8qiR5R9u292a5LwkvzqybI0kb0tyWdv2vCSLkpzZVvl2q97fa4Y4H5LkHUmubDEdmWTDJGsnuR1Yo21/2Szn+awk5ya5pb0/a2TZGUn+Jsk5Le4TkmzSFk/FdnOL7VemNxW0ppY/SHJpO6/3Jtk2ydfb/o5NslZb97lJrm7Te7V9Tr3uSnJGW/bSJOe37a9qf6mzgJjmO9/3ts/9tiRfSrLpDNfsccD3Ro71lRnWmStOkryufWY3JnlnkiuSvKAt2znJkrbtdUkOmemza+v+bpKlSW5KcmKSLadd/99r1//mJIcmySz7mfGYSRa3/aw5co3e1z7D25N8IV2z2mfatuemqxl7wLYj279xlhg+nBl+I0l2A94GTH0vvj19X5nldzAtjn2T/CDJj5O8fbZrqtVUVfnyNfgXcAXwgjb9euAe4A10ycD7gB8AhwJrAy8EbgPWa+t/us0/py3/MHDWyL5fAzwCWBP4M+BHwDpt2ZuBC4DHAwGeDDyiLSu6KvXZYv4dYCnwGGA94N+Ao0aWz7o9sAnwE+C1La592vzUsc8ArgGeCDwcOB74l7Zscdv3miP7e/20cy7gBGADYAfgLuC0FuuGwMXAvm3d5wJXzxDjBsAlwP8dWe9JdH9A/TJwHfDyPjH1PN/LgMcB67b5g2e5djMd675rPU+c2wO3A7sAawF/D9zN/d+9/wZe26bXo2u+mimGXYEfA0+l+859FDhzWjz/AWwEbAPcAOw2y75mPOb082zXZCmw7chn+D/AC9o1PRL45zmu0Rl0zUm/8Nn0+I28m/bdm2Vfs/4ORuL4p/a5Ppnuu/hLk/43x9fK87JGRquqy6vqn6vq58DngEXAe6rqrqr6EvAzYLRz539W1ZlVdRfwduBXkiwCqKp/qaobq+qeqvoA3X88j2/bvRF4R1V9rzrfrqobe8b4auCQqvp+Vd0OHAjsPfpX8BxeClxaVUe1uI4Gvgv8xsg6R1XVhVX1v8A7gVdlYR0//66qbq2qi4ALgS+1WG8BvgjM2gE5yUOAzwJnVNU/AlTVGVV1QVXdW1XfAY4Gfq1nLH3O95+r6n+q6k7gWGDHBZzrfeaJc0/gC1V1VlX9DPhLuv9op9wNPDbJplV1e1WdPcthXg0cXlXfbN+5A+m+c4tH1jm4qm6uqh8Ap89xPn2PCd01umzkM7ysqr5cXb+kf2WOz3Qu8/xG5tPnd/BXVXVndX2avk2X0EiATUtadV03Mn0nQFVNL1tvZP6qqYn2j+lNwJYASf48ySWtSeNmur9mp5otFtHVBCyLLYErR+avpPuLdvNl2HZq+61G5q+atuyh3B93H9Ov11zXb7qDgPWBP54qSPKMJKcnuSHJLcDvLSCePuf7o5HpO+aJb1bzxLklv/hduQMYTVz3o6sV+m5rqtl9lsP8wvm079yNy3g+fY8JD+4zndU8v5H59PkdLJfPVqsmExmps2hqIsl6dE0ZP2xt/W8BXgVsXFUbAbfQNSNB95/atst4zB/Sdeidsg1dk9h1M68+57ZT218zMr9o2rK76ZozxnqrYpK96Zp+9qyqu0cWfRY4EVhUVRsCn+D+6zhfTH3Od3mZK85rga2nVkyyLl2TCgBVdWlV7QP8H+BvgeOSPHyGY/zC+bR1HsEynM8CjrkQ/9veHzZS9siZVuzxG1noZ7uQ34FkIiM1L0myS7oOrO8Fzq6qq+hqFe6h66OwZpK/pOv7MeWTwHuTbJfOLyeZ+o/tOrp2/9kcDfy/JI9uydNfA5+rfrcfnwQ8LslvJ1kzXWfi7en6VUx5TZLtkzwMeA9wXGtquwG4d57Ylkm65918lK5PyQ3TFq8P3FRVP02yM/DbI8vmi6nP+S4vc8V5HPAb6Toer0XX/+O+TrhJXpNks6q6F7i5Fd87wzGOBt6QZMcka9N99t+oqisWGuwCjtlb++yuofsOrZHkd5g9YZ/vN3IdsLg1N87kwfwOJBMZqfks8C66JqWn0XVeBDgFOJmuU+SVwE/5xSabQ+j6Y3yJ7tkkn6LrlAjdf3JHpLvr5FUzHPNw4Ci6O3Yub/v+oz7Btn44u9N1rLyR7i/i3avqxyOrHUXXkflHdLcb/3Hb9g66pp+vtdie2eeYPe0BbAyclfvvXPpiW/YHwHuS3EbXt+TYkfOZM6ae57u8zBXnRXSf0TF0tTO3A9fTdUAF2A24KN1dZx8G9m59dn5Bdc87eiddJ+xr6ZKEvZcx3l7HXAa/S9eZ/Ua6Dt9fn2W9+X4j/9reb0zyzRm2X+bfgQQ+EE8iyafp7rp5x6RjWV7S3fL8L1X1yUnHsiprNQg30z1M7/IJhyOtlqyRkaQFSPIbSR7W+qH8Pd3t91dMNipp9WUiI0kLswddB9UfAtvRNeVYtS1NiE1LkiRpsKyRkSRJg9XnCaKDs+mmm9bixYsnHYYkSVpOzjvvvB9X1WbTy1fJRGbx4sUsWbJk0mFIkqTlJMn0p3sDNi1JkqQBM5GRJEmDZSIjSZIGy0RGkiQNlomMJEkaLBMZSZI0WCYykiRpsExkJEnSYJnISJKkwVoln+w7Tovf+p+TDmFQrjj4pZMOQZK0CrNGRpIkDZaJjCRJGiwTGUmSNFgmMpIkabBMZCRJ0mCZyEiSpMEykZEkSYNlIiNJkgbLREaSJA2WiYwkSRosExlJkjRYJjKSJGmwTGQkSdJgjT2RSbJGkvOT/Eebf3SSbyRZmuRzSdZq5Wu3+aVt+eKRfRzYyr+X5EXjjlmSJA3DiqiReRNwycj83wIfrKrHAj8B9mvl+wE/aeUfbOuRZHtgb2AHYDfgY0nWWAFxS5KkldxYE5kkWwMvBT7Z5gPsChzXVjkCeHmb3qPN05Y/v62/B3BMVd1VVZcDS4Gdxxm3JEkahnHXyHwIeAtwb5t/BHBzVd3T5q8GtmrTWwFXAbTlt7T17yufYZv7JNk/yZIkS2644YblfBqSJGllNLZEJsnuwPVVdd64jjGqqg6rqp2qaqfNNttsRRxSkiRN2Jpj3PezgZcleQmwDrAB8GFgoyRrtlqXrYFr2vrXAIuAq5OsCWwI3DhSPmV0G0mStBobW41MVR1YVVtX1WK6zrpfqapXA6cDe7bV9gVOaNMntnna8q9UVbXyvdtdTY8GtgPOGVfckiRpOMZZIzObvwCOSfI+4HzgU638U8BRSZYCN9ElP1TVRUmOBS4G7gEOqKqfr/iwJUnSymaFJDJVdQZwRpv+PjPcdVRVPwVeOcv2BwEHjS9CSZI0RD7ZV5IkDZaJjCRJGiwTGUmSNFgmMpIkabBMZCRJ0mCZyEiSpMEykZEkSYNlIiNJkgbLREaSJA2WiYwkSRosExlJkjRYJjKSJGmwTGQkSdJgmchIkqTBMpGRJEmDZSIjSZIGy0RGkiQNlomMJEkaLBMZSZI0WCYykiRpsExkJEnSYJnISJKkwTKRkSRJg2UiI0mSBstERpIkDZaJjCRJGiwTGUmSNFgmMpIkabDWnHQAUl+L3/qfkw5hUK44+KWTDkGSxq53IpNkY2BL4E7giqq6d2xRSZIk9TBnIpNkQ+AAYB9gLeAGYB1g8yRnAx+rqtPHHqUkSdIM5quROQ44EvjVqrp5dEGSpwGvTfKYqvrUmOKTJEma1ZyJTFX9+hzLzgPOW+4RSZIk9dTrrqUkz07y8Db9miSHJHnUeEOTJEmaW9/brz8O3JHkycCfAZfRNTlJkiRNTN+7lu6pqkqyB/APVfWpJPuNMzBJKw9vfV8Yb32XVpy+icxtSQ4EXgM8J8lDgIeOLyxJkqT59W1a2gu4C9ivqn4EbA28f2xRSZIk9TDfc2RSnR8Bh0yVV9UPaH1kptaZYdt1gDOBtdtxjquqdyV5NHAM8Ai6u55eW1U/S7J22+fTgBuBvarqiravA4H9gJ8Df1xVpzy405akYbBZb+Fs2lu9zNe0dHqS44ETWvICQJK1gF2AfYHTgU/PsO1dwK5VdXuShwJnJfki8KfAB6vqmCSfoEtQPt7ef1JVj02yN/C3wF5Jtgf2Bnage7Lwl5M8rqp+vuynLUnS/EwkF2YSSeR8TUu70dWCHJ3kh0kuTvJ94FK6p/1+qKo+PdOGrSbn9jb70PYqYFe6B+0BHAG8vE3v0eZpy5+fJK38mKq6q6ouB5YCOy/oLCVJ0ippvgfi/RT4GPCxVquyKXDn9Kf8zibJGnTNR48FDqW7bfvmqrqnrXI1sFWb3gq4qh33niS30DU/bQWcPbLb0W1Gj7U/sD/ANtts0yc8SZI0cH07+1JVd1fVtX2TmLbNz6tqR7rOwTsDT1hwhP2PdVhV7VRVO2222WbjOowkSVqJ9E5kHoyW/JwO/AqwUZKpmqCtgWva9DXAIoC2fEO6Tr/3lc+wjSRJWo2NLZFJslmSjdr0usCvA5fQJTR7ttX2BU5o0ye2edryr7S7oU4E9k6ydrvjaTvgnHHFLUmShqPvA/FoYyttV1VfbonJmlV12xybbAEc0frJPAQ4tqr+I8nFwDFJ3gecD0yNnP0p4KgkS4Gb6O5UoqouSnIscDFwD3CAdyxJkiTomcgk+V26jrSbANvSNe98Anj+bNtU1XeAp8xQ/n1muOuodSx+5Sz7Ogg4qE+skiRp9dG3aekA4NnArQBVdSnwf8YVlCRJUh99E5m7qupnUzOtM+4DnuYrSZK0IvVNZL6a5G3Aukl+HfhX4AvjC0uSJGl+fROZtwI3ABcA/xc4CXjHuIKSJEnqo1dn36q6F/in9pIkSVop9KqRSbJ7kvOT3JTk1iS3Jbl13MFJkiTNpe9zZD4E/CZwQXtInSRJ0sT17SNzFXChSYwkSVqZ9K2ReQtwUpKvAndNFVbVIWOJSpIkqYe+icxBwO3AOsBa4wtHkiSpv76JzJZV9cSxRiJJkrRAffvInJTkhWONRJIkaYH6JjK/D5yc5E5vv5YkSSuLvg/EW3/cgUiSJC3UnIlMkidU1XeTPHWm5VX1zfGEJUmSNL/5amT+FNgf+MAMywrYdblHJEmS1NOciUxV7d8mX1xVPx1dlmSdsUUlSZLUQ9/Ovl/vWSZJkrTCzNdH5pHAVsC6SZ4CpC3aAHjYmGOTJEma03x9ZF4EvB7Ymq6fzFQicyvwtvGFJUmSNL/5+sgcARyR5Leq6vgVFJMkSVIvvfrImMRIkqSVUd/OvpIkSSsdExlJkjRYfUe/JsmzgMWj21TVkWOISZIkqZdeiUySo4BtgW8BP2/FBZjISJKkielbI7MTsH1V1TiDkSRJWoi+fWQuBB45zkAkSZIWqm+NzKbAxUnOAe6aKqyql40lKkmSpB76JjLvHmcQkiRJy6JXIlNVX02yOfD0VnROVV0/vrAkSZLm16uPTJJXAecArwReBXwjyZ7jDEySJGk+fZuW3g48faoWJslmwJeB48YVmCRJ0nz63rX0kGlNSTcuYFtJkqSx6Fsjc3KSU4Cj2/xewEnjCUmSJKmfvp1935zkt4Bnt6LDqurz4wtLkiRpfr3HWqqq44HjxxiLJEnSgsyZyCQ5q6p2SXIb3dhK9y0Cqqo2GGt0kiRJc5gzkamqXdr7+ismHEmSpP76PkfmqD5l05YvSnJ6kouTXJTkTa18kySnJrm0vW/cypPkI0mWJvlOkqeO7Gvftv6lSfZd2ClKkqRVVd9bqHcYnUmyJvC0eba5B/izqtoeeCZwQJLtgbcCp1XVdsBpbR7gxcB27bU/8PF2rE2AdwHPAHYG3jWV/EiSpNXbnIlMkgNb/5hfTnJre90GXAecMNe2VXVtVX2zTd8GXAJsBewBHNFWOwJ4eZveAziyOmcDGyXZAngRcGpV3VRVPwFOBXZbhnOVJEmrmDkTmar6m9Y/5v1VtUF7rV9Vj6iqA/seJMli4CnAN4DNq+ratuhHwOZteivgqpHNrm5ls5VPP8b+SZYkWXLDDTf0DU2SJA1Y3+fIHNiac7YD1hkpP3O+bZOsR3fb9p9U1a1JRvdbSWrWjRegqg4DDgPYaaedlss+JUnSyq1vZ983AmcCpwB/1d7f3WO7h9IlMZ+pqn9rxde1JiPa+9TQB9cAi0Y237qVzVYuSZJWc307+74JeDpwZVU9j66Z6Oa5NkhX9fIp4JKqOmRk0YnA1J1H+3J/X5sTgde1u5eeCdzSmqBOAV6YZONWK/TCViZJklZzfZ/s+9Oq+mkSkqxdVd9N8vh5tnk28FrggiTfamVvAw4Gjk2yH3Al8Kq27CTgJcBS4A7gDQBVdVOS9wLntvXeU1U39YxbkiStwvomMlcn2Qj4d+DUJD+hS0JmVVVn0T0BeCbPn2H9Ag6YZV+HA4f3jFWSJK0m+nb2fUWbfHeS04ENgZPHFpUkSVIP8421tMkMxRe09/UAm3gkSdLEzFcjcx7dYJEzNREV8JjlHpEkSVJP8w0a+egVFYgkSdJC9eojk+Q5M5X3eSCeJEnSuPS9a+nNI9Pr0A3eeB6w63KPSJIkqae+dy39xuh8kkXAh8YRkCRJUl99n+w73dXALy3PQCRJkhaqbx+Zj9LdpQRd8rMj8M0xxSRJktRL3z4yS0am7wGOrqqvjSEeSZKk3vr2kTli3IFIkiQtVK8+Mkl2T3J+kpuS3JrktiS3jjs4SZKkufRtWvoQ8JvABW1wR0mSpInre9fSVcCFJjGSJGll0rdG5i3ASUm+Ctw1VVhVh4wlKkmSpB76JjIHAbfTPdV3rfGFI0mS1F/fRGbLqnriWCORJElaoL59ZE5K8sKxRiJJkrRAfROZ3wdOTnKnt19LkqSVRd8H4q0/7kAkSZIWas5EJskTquq7SZ460/KqcrwlSZI0MfPVyPwpsD/wgRmWFbDrco9IkiSppzkTmarav70/b8WEI0mS1N+cnX2TPD3JI0fmX5fkhCQfSbLJ+MOTJEma3Xx3Lf0j8DOAJM8BDgaOBG4BDhtvaJIkSXObr4/MGlV1U5veCzisqo4Hjk/yrbFGJkmSNI/5amTWSDKV7Dwf+MrIsr5PBZYkSRqL+ZKRo4GvJvkxcCfwXwBJHkvXvCRJkjQx8921dFCS04AtgC9VVbVFDwH+aNzBSZIkzWXe5qGqOnuGsv8ZTziSJEn99R1rSZIkaaVjIiNJkgbLREaSJA2WiYwkSRosExlJkjRYJjKSJGmwTGQkSdJgmchIkqTBGlsik+TwJNcnuXCkbJMkpya5tL1v3MqT5CNJlib5TpKnjmyzb1v/0iT7jiteSZI0POOskfk0sNu0srcCp1XVdsBpbR7gxcB27bU/8HHoEh/gXcAzgJ2Bd00lP5IkSWNLZKrqTOCmacV7AEe06SOAl4+UH1mds4GNkmwBvAg4tapuqqqfAKfywORIkiStplZ0H5nNq+raNv0jYPM2vRVw1ch6V7ey2cofIMn+SZYkWXLDDTcs36glSdJKaWKdfdtI2jXviv33d1hV7VRVO2222WbLa7eSJGkltqITmetakxHt/fpWfg2waGS9rVvZbOWSJEkrPJE5EZi682hf4ISR8te1u5eeCdzSmqBOAV6YZOPWyfeFrUySJIk1x7XjJEcDzwU2TXI13d1HBwPHJtkPuBJ4VVv9JOAlwFLgDuANAFV1U5L3Aue29d5TVdM7EEuSpNXU2BKZqtpnlkXPn2HdAg6YZT+HA4cvx9AkSdIqwif7SpKkwTKRkSRJg2UiI0mSBstERpIkDZaJjCRJGiwTGUmSNFgmMpIkabBMZCRJ0mCZyEiSpMEykZEkSYNlIiNJkgbLREaSJA2WiYwkSRosExlJkjRYJjKSJGmwTGQkSdJgmchIkqTBMpGRJEmDZSIjSZIGy0RGkiQNlomMJEkaLBMZSZI0WCYykiRpsExkJEnSYJnISJKkwTKRkSRJg2UiI0mSBstERpIkDZaJjCRJGiwTGUmSNFgmMpIkabBMZCRJ0mCZyEiSpMEykZEkSYNlIiNJkgbLREaSJA2WiYwkSRosExlJkjRYJjKSJGmwBpPIJNktyfeSLE3y1knHI0mSJm8QiUySNYBDgRcD2wP7JNl+slFJkqRJG0QiA+wMLK2q71fVz4BjgD0mHJMkSZqwVNWkY5hXkj2B3arqjW3+tcAzquoPR9bZH9i/zT4e+N4KD3SyNgV+POkgVlNe+8nx2k+W139yVsdr/6iq2mx64ZqTiGQcquow4LBJxzEpSZZU1U6TjmN15LWfHK/9ZHn9J8drf7+hNC1dAywamd+6lUmSpNXYUBKZc4Htkjw6yVrA3sCJE45JkiRN2CCalqrqniR/CJwCrAEcXlUXTTislc1q26y2EvDaT47XfrK8/pPjtW8G0dlXkiRpJkNpWpIkSXoAExlJkjRYJjIDlWSHJF9pwzZcmuSdSdKWJclH2nAO30ny1EnHuyqZ59o/Icl/J7kryZ9POtZVzTzX/tXt+35Bkq8nefKk412VzHPt92jX/ltJliTZZdLxLoshnmOS1yf5hxV0rI2S/MHI/JZJjlsRx56LicwAJVmX7q6tg6vq8cCTgWcBU1+wFwPbtdf+wMcnEeeqqMe1vwn4Y+DvJxPhqqvHtb8c+LWqehLwXuwMudz0uPanAU+uqh2B3wE+OYk4H4yV/RyTrAw352zE/deDqvphVe05uXA6JjLD9NvA16rqSwBVdQfwh8DUYJp7AEdW52xgoyRbTCbUVc6c176qrq+qc4G7JxfiKmu+a//1qvpJW/dsuudNafmY79rfXvffOfJwYIh3kSz3c0zy7iRHJPmvJFcm+c0kf9dqDU9O8tC23l8mOTfJhUkOG6kFOiPJh5IsAd6U5OmttvHbSc5Jsn471JZtf5cm+bu+J5zk0632/utJvt+eok+S9ZKcluSbLdapIYEOBrZttVLvT7I4yYVtm7OT7DCy7zOS7JTk4UkOb/GeP7Kv5cZEZph2AM4bLaiqy4D1kmwAbAVcNbL46lamB2++a6/xWci13w/44ooKbDUw77VP8ook3wX+k67GYmjGdY7bArsCLwP+BTi91RreCby0rfMPVfX0qnoisC6w+8j2a7Un+H4U+Bzwpqp6MvCCtg+AHYG9gCcBeyVZ1OL9XEs6pr9eN7L/LYBd2jEPbmU/BV5RVU8Fngd8oCVXbwUuq6odq+rN087zc8Cr2nG3ALaoqiXA24GvVNXObV/vT/Lwnteul5WhqkqSlpskz6NLZFaKPgyri6r6PPD5JM+ha9p7wYRDWu6W8Ry/WFV3J7mA7jloJ7fyC4DFbfp5Sd4CPAzYBLgI+EJb9rn2/njg2lbjS1XdCtAqb06rqlva/MXAo4CrqmqvHvH9e1XdC1ycZPNWFuCv23neS/eH8Oaz7aA5FvgS8C66hGaq78wLgZfl/j6D6wDbAJf0iK0Xa2QGIskBU9k0cDPwtGnLHwPc3r7cDumwHC3w2ms5Wui1T/LLdH0X9qiqG1dwuKuUZf3eV9WZwGOSbLqiYl0Wo+eXZEvgYh7kOc6wT4C72jb3AnePNE/dC6yZZB3gY8Cerabmn+j+s5/yvz1O566R6Z/TKil61siMbpv2/mpgM+BprU/QddNieoCquga4sf0G9+L+BCzAb7VanB2rapuqWm5JDJjIDEZVHTr1RQDeD+yS5AVwXye1jwBTbaMnAq9L55nALVV17STiXhUs8NprOVrItU+yDfBvwGur6n8mFPIqY4HX/rEj/TqeCqwNrNSJ5Oj5VdUPgc/wIM9xhn32MZUg/DjJesBsnWe/B2yR5OkthvUzTwfgqtprJJ7R15HzxLQhcH2rSXoeXQ0PwG3A+rNvxueAtwAbVtV3WtkpwB+NXLunzHPsBbNpaYCq6s7WYeqjSQ6lq648Cpi6Be8k4CXAUuAO4A0TCXQVNN+1T/JIYAmwAXBvkj8Btre25sHr8b3/S+ARwMfav5n3ODrw8tHj2v8W3R9Pd9P129hrpOZhECZ1jlV1c5J/Ai4EfkQ3tuBM6/0syV4tvnVbDONqvvsM8IXWHLYE+G6L4cYkX0vXwfeLwKHTtjsO+DBds9uU9wIfAr6T5CF0dxfuznLkEAWSJGmwbFqSJEmDZSIjSZIGy0RGkiQNlomMJEkaLBMZSZI0WCYykiRpsExkJM0qycuTVJIntPnFbf59I+tsmuTuJFPP0nn3yOPISfLnSb7bnih67tRTRdMGlZvj2Osl+ccklyU5r63/jCSLklyeZJO23sZtfnG6QeouSrJWW7ZtusHwNkjy3CS3jDzd9MszxTsthqlz+71p5Zsn+Wzb93lJ/jvJK9qyhyX5TLrB9i5MclZ70JmkMTCRkTSXfYCz2vuUy7l/sDuAV9KNDfMALQH4dWDn9oTY53P/Y9Dn80ngJmC7qnoa3YMdN62qq4CPc/8AdwcDh1XVFW2Quq8CU4nJocDbRx5I+F8jTzft8zCxV9KNpH3f+bcnlP47cGZVPabFtjf3j7b9JuC6qnpSGwRwPxwNXRobn+wraUatFmEXuhFrv0A3GBx0T4u+JMlOLXHYi27AuC1n2M3bgOdOJRLt/Ygex94WeAbw6jZGDVV1OV0SBfBB4Lz25ORdgD+cdszzk9wDrFlVR/c+6QfaB/gz4LNJtq6qq+lGMv5ZVX1iaqWqupJudGLoRhO+cmTZ9x7E8SXNwxoZSbPZAzi5jVt0Y5LRAfWOAfZOsohukLoHjCuTZANg/ar6/jIcewfgW1X185kWVtXdwJvpEpo/afNTy26mq6X5G+CAaZv+6kjT0tvnCqCd2xZVdQ5dojY1kvAOwDfn2PRw4C9ac9P7kmw313EkPTgmMpJmsw9dwkJ7H21eOpmuyWhv7h/ldkV7MXAt8MRZll0HbD+tfLRp6aB59j9V0wQPPP/7JDk0ybeTnAtQVd8CHkM30OImwLlJfqnH+UhaBjYtSXqA1pF2V+BJSYpuAL2iDRLXBrA7j67ZZXvgZdP3UVW3Jrk9yWOWoVbmIuDJSdaYqVYmyY50idQzgbOSHDM1wnuS3elG730R8Pkkp1TVHQs8PnSJyyOTvLrNb9lqVy6iG0AQgKo6IMmmdIPrTZXdTjcS978luZduENdLliEGSfOwRkbSTPYEjqqqR1XV4qpaRNc/ZdHIOh8A/qKqbppjP38DHNqamabuRHrdfAevqsvoEoO/ap1rp+6Yemmb/zhdk9IP6Go+/r6tsy5wCHBAVV0AnADM2YQ0kySPA9arqq3a+S9u57IP8BVgnSS/P7LJw0a2fXaSjdv0WnSJ3pVIGgsTGUkz2Qf4/LSy44EDp2aq6qKqmq/j7seB0+maVy4E/gu4t2cMbwQ2B5a2bT8NXA/8LvCDqjq1rfcx4JeS/BrwTuDzVXVxW/ZuYJ8e/VTekeTqqRezn/8+VVXAy4Ffa7d9n0PXgfkv2nrbAl9NcgFwPl1CdnzPc5a0QOl+k5IkScNjjYwkSRosO/tKmqgk3wDWnlb82tbHRZLmZNOSJEkaLJuWJEnSYJnISJKkwTKRkSRJg2UiI0mSBuv/Aw7BkY1LC4jhAAAAAElFTkSuQmCC\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "plt.bar(\n", + " [\"-O0\", \"-O1\", \"-O2\", \"-O3\", \"-O3 -march=native\"],\n", + " [4357.768, 3276.873, 3140.092, 3069.855, 3039.262],\n", + ")\n", + "plt.title(\"Impact of optimization flags on simulation\")\n", + "plt.xlabel(\"AMICI_CXXFLAGS\")\n", + "plt.ylabel(\"Simulation time (s)\");" + ] + }, + { + "cell_type": "markdown", + "id": "37d0713f", + "metadata": {}, + "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." + ] + }, + { + "cell_type": "markdown", + "id": "47781b8c", + "metadata": {}, + "source": [ + "## Model simulation\n", + "\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", + "\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", + "![](https://journals.plos.org/ploscompbiol/article/figure/image?size=medium&id=10.1371/journal.pcbi.1005331.g002)\n", + "\n", + "*CC BY 4.0 Fröhlich et al., [DOI:10.1371/journal.pcbi.1005331](https://doi.org/10.1371/journal.pcbi.1005331)*\n", + "\n", + "### Sensitivities w.r.t. a subset of parameters\n", + "\n", + "If only sensitivities with respect to a subset of model parameters are of interest to you (see also *Parameters as constants* above), you can speed up the simulation by selecting the relevant parameter indices via [amici.Model.setParameterList](https://amici.readthedocs.io/en/latest/generated/amici.amici.Model.html#amici.amici.Model.setParameterList).\n" + ] + }, + { + "cell_type": "markdown", + "id": "af4bd3d5", + "metadata": {}, + "source": [ + "\n", + "### Parallel simulation of multiple conditions\n", + "\n", + "Whenever there are multiple independent simulations to perform, you can use [amici.runAmiciSimulations(..., num_threads=...)](https://amici.readthedocs.io/en/latest/generated/amici.amici.html#amici.amici.runAmiciSimulations) instead of [amici.runAmiciSimulations(...)](https://amici.readthedocs.io/en/latest/generated/amici.amici.html#amici.amici.runAmiciSimulation) to run them in parallel. Note that all simulation results have to be kept in memory, which may become problematic for very large numbers of simulations.\n", + "Parallelization is based on OpenMP and does not come with the issues associated with Python's multiprocessing or multithreading (spawning extra processes or limitations related to the global interpreter lock).\n", + "\n", + "### Reporting mode\n", + "\n", + "During model simulation, many quantities are calculated, but not all might be of interest for you. For example, for parameter estimation you might only be interested in the likelihood and gradient. In this case, you can save time and memory using \n", + "[amici.Solver.setReturnDataReportingMode(amici.RDataReporting.likelihood)](https://amici.readthedocs.io/en/latest/generated/amici.amici.Solver.html#amici.amici.Solver.setReturnDataReportingMode)." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.6" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": true, + "sideBar": true, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": true, + "toc_window_display": true + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/deps/AMICI/python/examples/example_large_models/results_compile.tsv b/deps/AMICI/python/examples/example_large_models/results_compile.tsv new file mode 100644 index 000000000..6b7a9bfe3 --- /dev/null +++ b/deps/AMICI/python/examples/example_large_models/results_compile.tsv @@ -0,0 +1,21 @@ + model_name task nprocs time +0 hello_pysb compile 1 20.741835117340088 +1 hello_pysb compile 2 11.944610834121704 +2 hello_pysb compile 3 9.319742441177368 +3 hello_pysb compile 4 7.94843316078186 +4 hello_pysb compile 5 7.121765375137329 +5 hello_pysb compile 6 6.689377069473267 +6 hello_pysb compile 7 6.251616954803467 +7 hello_pysb compile 8 6.100285291671753 +8 hello_pysb compile 9 5.753735780715942 +9 hello_pysb compile 10 5.667196750640869 +10 RTKERK__base compile 1 3067.3388171195984 +11 RTKERK__base compile 2 2061.4823524951935 +12 RTKERK__base compile 3 1754.1764636039734 +13 RTKERK__base compile 4 1725.0178718566895 +14 RTKERK__base compile 5 1697.654798746109 +15 RTKERK__base compile 6 1691.5322842597961 +16 RTKERK__base compile 7 1720.0398466587067 +17 RTKERK__base compile 8 1714.9537448883057 +18 RTKERK__base compile 9 1721.5932655334473 +19 RTKERK__base compile 10 1726.7770998477936 diff --git a/deps/AMICI/python/examples/example_large_models/results_import.tsv b/deps/AMICI/python/examples/example_large_models/results_import.tsv new file mode 100644 index 000000000..6770fc5c9 --- /dev/null +++ b/deps/AMICI/python/examples/example_large_models/results_import.tsv @@ -0,0 +1,21 @@ + Unnamed: 0 model_name task nprocs time size +0 0 hello_pysb import 1 1.787031888961792 12054809 +1 1 hello_pysb import 2 15.872491359710692 12067022 +2 2 hello_pysb import 3 16.68442177772522 12054052 +3 3 hello_pysb import 4 16.014235973358154 12067022 +4 4 hello_pysb import 5 15.72178030014038 12054052 +5 5 hello_pysb import 6 16.097049713134766 12067022 +6 6 hello_pysb import 7 15.62270975112915 12054052 +7 7 hello_pysb import 8 16.196629762649536 12067022 +8 8 hello_pysb import 9 16.08016848564148 12054052 +9 9 hello_pysb import 10 16.345548152923584 12067022 +10 10 RTKERK__base import 1 1376.1798136234283 43516541 +11 11 RTKERK__base import 2 1041.8015067577362 43349758 +12 12 RTKERK__base import 3 927.154144525528 43336620 +13 13 RTKERK__base import 4 878.066201210022 43349758 +14 14 RTKERK__base import 5 837.5367295742035 43336620 +15 15 RTKERK__base import 6 823.1699328422546 43349758 +16 16 RTKERK__base import 7 811.7395029067993 43336620 +17 17 RTKERK__base import 8 789.2430837154388 43349758 +18 18 RTKERK__base import 9 772.3326945304871 43336620 +19 19 RTKERK__base import 10 784.638739824295 43349758 diff --git a/deps/AMICI/python/examples/example_petab/petab.ipynb b/deps/AMICI/python/examples/example_petab/petab.ipynb index e1861e5f1..689d793f5 100644 --- a/deps/AMICI/python/examples/example_petab/petab.ipynb +++ b/deps/AMICI/python/examples/example_petab/petab.ipynb @@ -39,10 +39,10 @@ "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", + "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" ] @@ -335,10 +335,15 @@ ], "source": [ "parameters = {\n", - " x_id: x_val for x_id, x_val in\n", - " zip(petab_problem.x_ids, petab_problem.x_nominal_scaled)\n", + " x_id: x_val\n", + " for x_id, x_val in zip(petab_problem.x_ids, petab_problem.x_nominal_scaled)\n", "}\n", - "simulate_petab(petab_problem, amici_model, problem_parameters=parameters, scaled_parameters=True)" + "simulate_petab(\n", + " petab_problem,\n", + " amici_model,\n", + " problem_parameters=parameters,\n", + " scaled_parameters=True,\n", + ")" ] }, { @@ -370,4 +375,4 @@ }, "nbformat": 4, "nbformat_minor": 2 -} \ No newline at end of file +} diff --git a/deps/AMICI/python/examples/example_presimulation/ExampleExperimentalConditions.ipynb b/deps/AMICI/python/examples/example_presimulation/ExampleExperimentalConditions.ipynb index 07da70c02..63fbc7a4f 100644 --- a/deps/AMICI/python/examples/example_presimulation/ExampleExperimentalConditions.ipynb +++ b/deps/AMICI/python/examples/example_presimulation/ExampleExperimentalConditions.ipynb @@ -15,9 +15,9 @@ "outputs": [], "source": [ "# SBML model we want to import\n", - "sbml_file = 'model_presimulation.xml'\n", + "sbml_file = \"model_presimulation.xml\"\n", "# Name of the model that will also be the name of the python module\n", - "model_name = 'model_presimulation'\n", + "model_name = \"model_presimulation\"\n", "# Directory to which the generated model code is written\n", "model_output_dir = model_name\n", "\n", @@ -86,22 +86,45 @@ "sbml_doc = sbml_reader.readSBML(sbml_file)\n", "sbml_model = sbml_doc.getModel()\n", "\n", - "print('Species:')\n", - "pprint([(s.getId(),s.getName()) for s in sbml_model.getListOfSpecies()])\n", + "print(\"Species:\")\n", + "pprint([(s.getId(), s.getName()) for s in sbml_model.getListOfSpecies()])\n", "\n", - "print('\\nReactions:')\n", + "print(\"\\nReactions:\")\n", "for reaction in sbml_model.getListOfReactions():\n", - " reactants = ' + '.join(['%s %s'%(int(r.getStoichiometry()) if r.getStoichiometry() > 1 else '', r.getSpecies()) for r in reaction.getListOfReactants()])\n", - " products = ' + '.join(['%s %s'%(int(r.getStoichiometry()) if r.getStoichiometry() > 1 else '', r.getSpecies()) for r in reaction.getListOfProducts()])\n", - " reversible = '<' if reaction.getReversible() else ''\n", - " print('%3s: %10s %1s->%10s\\t\\t[%s]' % (reaction.getName(), \n", - " reactants,\n", - " reversible,\n", - " products,\n", - " libsbml.formulaToL3String(reaction.getKineticLaw().getMath())))\n", - " \n", - "print('Parameters:')\n", - "pprint([(p.getId(),p.getName()) for p in sbml_model.getListOfParameters()])" + " reactants = \" + \".join(\n", + " [\n", + " \"%s %s\"\n", + " % (\n", + " int(r.getStoichiometry()) if r.getStoichiometry() > 1 else \"\",\n", + " r.getSpecies(),\n", + " )\n", + " for r in reaction.getListOfReactants()\n", + " ]\n", + " )\n", + " products = \" + \".join(\n", + " [\n", + " \"%s %s\"\n", + " % (\n", + " int(r.getStoichiometry()) if r.getStoichiometry() > 1 else \"\",\n", + " r.getSpecies(),\n", + " )\n", + " for r in reaction.getListOfProducts()\n", + " ]\n", + " )\n", + " reversible = \"<\" if reaction.getReversible() else \"\"\n", + " print(\n", + " \"%3s: %10s %1s->%10s\\t\\t[%s]\"\n", + " % (\n", + " reaction.getName(),\n", + " reactants,\n", + " reversible,\n", + " products,\n", + " libsbml.formulaToL3String(reaction.getKineticLaw().getMath()),\n", + " )\n", + " )\n", + "\n", + "print(\"Parameters:\")\n", + "pprint([(p.getId(), p.getName()) for p in sbml_model.getListOfParameters()])" ] }, { @@ -152,7 +175,12 @@ ], "source": [ "from IPython.display import IFrame\n", - "IFrame('https://amici.readthedocs.io/en/latest/glossary.html#term-fixed-parameters', width=600, height=175)" + "\n", + "IFrame(\n", + " \"https://amici.readthedocs.io/en/latest/glossary.html#term-fixed-parameters\",\n", + " width=600,\n", + " height=175,\n", + ")" ] }, { @@ -161,7 +189,7 @@ "metadata": {}, "outputs": [], "source": [ - "fixedParameters = ['DRUG_0','KIN_0']" + "fixedParameters = [\"DRUG_0\", \"KIN_0\"]" ] }, { @@ -190,10 +218,10 @@ "source": [ "# Retrieve model output names and formulae from AssignmentRules and remove the respective rules\n", "observables = amici.assignmentRules2observables(\n", - " sbml_importer.sbml, # the libsbml model object\n", - " filter_function=lambda variable: variable.getName() == 'pPROT' \n", - " )\n", - "print('Observables:')\n", + " sbml_importer.sbml, # the libsbml model object\n", + " filter_function=lambda variable: variable.getName() == \"pPROT\",\n", + ")\n", + "print(\"Observables:\")\n", "pprint(observables)" ] }, @@ -210,11 +238,13 @@ "metadata": {}, "outputs": [], "source": [ - "sbml_importer.sbml2amici(model_name, \n", - " model_output_dir, \n", - " verbose=False,\n", - " observables=observables,\n", - " constant_parameters=fixedParameters)\n", + "sbml_importer.sbml2amici(\n", + " model_name,\n", + " model_output_dir,\n", + " verbose=False,\n", + " observables=observables,\n", + " constant_parameters=fixedParameters,\n", + ")\n", "# load the generated module\n", "model_module = amici.import_model_module(model_name, model_output_dir)" ] @@ -266,7 +296,7 @@ ], "source": [ "# Run simulation using default model parameters and solver options\n", - "model.setTimepoints(np.linspace(0, 60, 60)) \n", + "model.setTimepoints(np.linspace(0, 60, 60))\n", "rdata = amici.runAmiciSimulation(model, solver)\n", "amici.plotting.plotObservableTrajectories(rdata)" ] @@ -298,7 +328,7 @@ ], "source": [ "edata = amici.ExpData(rdata, 0.1, 0.0)\n", - "edata.fixedParameters = [0,2]\n", + "edata.fixedParameters = [0, 2]\n", "rdata = amici.runAmiciSimulation(model, solver, edata)\n", "amici.plotting.plotObservableTrajectories(rdata)" ] @@ -330,7 +360,7 @@ } ], "source": [ - "edata.fixedParametersPreequilibration = [3,0]\n", + "edata.fixedParametersPreequilibration = [3, 0]\n", "rdata = amici.runAmiciSimulation(model, solver, edata)\n", "amici.plotting.plotObservableTrajectories(rdata)" ] diff --git a/deps/AMICI/python/examples/example_presimulation/createModelPresimulation.py b/deps/AMICI/python/examples/example_presimulation/createModelPresimulation.py index c2d373156..1db454a6b 100644 --- a/deps/AMICI/python/examples/example_presimulation/createModelPresimulation.py +++ b/deps/AMICI/python/examples/example_presimulation/createModelPresimulation.py @@ -1,60 +1,68 @@ -from pysb.core import ( - Rule, Parameter, Model, Monomer, Expression, Initial, Observable -) +import os import pysb.export -import os +from pysb.core import ( + Expression, + Initial, + Model, + Monomer, + Observable, + Parameter, + Rule, +) model = Model() -prot = Monomer('PROT', ['kin', 'drug', 'phospho'], {'phospho': ['u', 'p']}) -prot_0 = Parameter('PROT_0', 10) -Initial(prot(phospho='u', drug=None, kin=None), - Expression('initProt', prot_0)) - -drug = Monomer('DRUG', ['bound']) -drug_0 = Parameter('DRUG_0', 9) -Initial(drug(bound=None), - Expression('initDrug', drug_0)) - -kin = Monomer('KIN', ['bound']) -kin_0 = Parameter('KIN_0', 1) -Initial(kin(bound=None), - Expression('initKin', kin_0)) - -Rule('PROT_DRUG_bind', - drug(bound=None) + prot(phospho='u', drug=None, kin=None) | - drug(bound=1) % prot(phospho='u', drug=1, kin=None), - Parameter('kon_prot_drug', 0.1), - Parameter('koff_prot_drug', 0.1) - ) - -Rule('PROT_KIN_bind', - kin(bound=None) + prot(phospho='u', drug=None, kin=None) >> - kin(bound=1) % prot(phospho='u', drug=None, kin=1), - Parameter('kon_prot_kin', 0.1), - ) - -Rule('PROT_KIN_phospho', - kin(bound=1) % prot(phospho='u', drug=None, kin=1) >> - kin(bound=None) + prot(phospho='p', drug=None, kin=None), - Parameter('kphospho_prot_kin', 0.1) - ) - -Rule('PROT_dephospho', - prot(phospho='p', drug=None, kin=None) >> - prot(phospho='u', drug=None, kin=None), - Parameter('kdephospho_prot', 0.1) - ) - -pProt = Observable('pPROT', prot(phospho='p')) -tProt = Observable('tPROT', prot()) - -Expression('pPROT_obs', pProt/tProt) - -sbml_output = pysb.export.export(model, format='sbml') - -outfile = os.path.join(os.path.dirname(os.path.realpath(__file__)), - 'model_presimulation.xml') -with open(outfile, 'w') as f: +prot = Monomer("PROT", ["kin", "drug", "phospho"], {"phospho": ["u", "p"]}) +prot_0 = Parameter("PROT_0", 10) +Initial(prot(phospho="u", drug=None, kin=None), Expression("initProt", prot_0)) + +drug = Monomer("DRUG", ["bound"]) +drug_0 = Parameter("DRUG_0", 9) +Initial(drug(bound=None), Expression("initDrug", drug_0)) + +kin = Monomer("KIN", ["bound"]) +kin_0 = Parameter("KIN_0", 1) +Initial(kin(bound=None), Expression("initKin", kin_0)) + +Rule( + "PROT_DRUG_bind", + drug(bound=None) + prot(phospho="u", drug=None, kin=None) + | drug(bound=1) % prot(phospho="u", drug=1, kin=None), + Parameter("kon_prot_drug", 0.1), + Parameter("koff_prot_drug", 0.1), +) + +Rule( + "PROT_KIN_bind", + kin(bound=None) + prot(phospho="u", drug=None, kin=None) + >> kin(bound=1) % prot(phospho="u", drug=None, kin=1), + Parameter("kon_prot_kin", 0.1), +) + +Rule( + "PROT_KIN_phospho", + kin(bound=1) % prot(phospho="u", drug=None, kin=1) + >> kin(bound=None) + prot(phospho="p", drug=None, kin=None), + Parameter("kphospho_prot_kin", 0.1), +) + +Rule( + "PROT_dephospho", + prot(phospho="p", drug=None, kin=None) + >> prot(phospho="u", drug=None, kin=None), + Parameter("kdephospho_prot", 0.1), +) + +pProt = Observable("pPROT", prot(phospho="p")) +tProt = Observable("tPROT", prot()) + +Expression("pPROT_obs", pProt / tProt) + +sbml_output = pysb.export.export(model, format="sbml") + +outfile = os.path.join( + os.path.dirname(os.path.realpath(__file__)), "model_presimulation.xml" +) +with open(outfile, "w") as f: f.write(sbml_output) diff --git a/deps/AMICI/python/examples/example_splines/.gitignore b/deps/AMICI/python/examples/example_splines/.gitignore new file mode 100644 index 000000000..796b96d1c --- /dev/null +++ b/deps/AMICI/python/examples/example_splines/.gitignore @@ -0,0 +1 @@ +/build diff --git a/deps/AMICI/python/examples/example_splines/ExampleSplines.ipynb b/deps/AMICI/python/examples/example_splines/ExampleSplines.ipynb new file mode 100644 index 000000000..d376ba91e --- /dev/null +++ b/deps/AMICI/python/examples/example_splines/ExampleSplines.ipynb @@ -0,0 +1,1345 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# AMICI Python example \"splines\"\n", + "\n", + "This is an example showing how to add spline assignment rules to a pre-existing SBML model." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Utility functions" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "tags": [] + }, + "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", + "from importlib import import_module\n", + "from uuid import uuid1\n", + "from tempfile import TemporaryDirectory\n", + "import matplotlib as mpl\n", + "from matplotlib import pyplot as plt\n", + "\n", + "# Choose build directory\n", + "BUILD_PATH = None # temporary folder\n", + "# BUILD_PATH = 'build' # specified folder for debugging\n", + "if BUILD_PATH is not None:\n", + " # Remove previous models\n", + " rmtree(BUILD_PATH, ignore_errors=True)\n", + " os.mkdir(BUILD_PATH)\n", + "\n", + "\n", + "def simulate(sbml_model, parameters=None, *, model_name=None, **kwargs):\n", + " if model_name is None:\n", + " model_name = \"model_\" + uuid1().hex\n", + " if BUILD_PATH is None:\n", + " with TemporaryDirectory() as build_dir:\n", + " return _simulate(\n", + " sbml_model,\n", + " parameters,\n", + " build_dir=build_dir,\n", + " model_name=model_name,\n", + " **kwargs\n", + " )\n", + " else:\n", + " build_dir = os.path.join(BUILD_PATH, model_name)\n", + " rmtree(build_dir, ignore_errors=True)\n", + " return _simulate(\n", + " sbml_model,\n", + " parameters,\n", + " build_dir=build_dir,\n", + " model_name=model_name,\n", + " **kwargs\n", + " )\n", + "\n", + "\n", + "def _simulate(\n", + " sbml_model,\n", + " parameters,\n", + " *,\n", + " build_dir,\n", + " model_name,\n", + " T=1,\n", + " discard_annotations=False,\n", + " plot=True\n", + "):\n", + " if parameters is None:\n", + " parameters = {}\n", + " # Build the model module from the SBML file\n", + " sbml_importer = amici.SbmlImporter(\n", + " sbml_model, discard_annotations=discard_annotations\n", + " )\n", + " sbml_importer.sbml2amici(model_name, build_dir)\n", + " # Import the model module\n", + " sys.path.insert(0, os.path.abspath(build_dir))\n", + " model_module = import_module(model_name)\n", + " # Setup simulation timepoints and parameters\n", + " model = model_module.getModel()\n", + " for name, value in parameters.items():\n", + " model.setParameterByName(name, value)\n", + " if isinstance(T, (int, float)):\n", + " T = np.linspace(0, T, 100)\n", + " model.setTimepoints([float(t) for t in T])\n", + " solver = model.getSolver()\n", + " solver.setSensitivityOrder(amici.SensitivityOrder.first)\n", + " solver.setSensitivityMethod(amici.SensitivityMethod.forward)\n", + " # Simulate\n", + " rdata = amici.runAmiciSimulation(model, solver)\n", + " # Plot results\n", + " if plot:\n", + " fig, ax = plt.subplots()\n", + " ax.plot(rdata[\"t\"], rdata[\"x\"])\n", + " ax.set_xlabel(\"time\")\n", + " ax.set_ylabel(\"concentration\")\n", + " return model, rdata" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "### A simple SBML model" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let us consider the following SBML model:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```xml\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " f \n", + " \n", + " \n", + " \n", + " \n", + "\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This model corresponds to the simple ODE $\\dot{x} = f$ for a species $x$ and a parameter $f$.\n", + "\n", + "We can easily import and simulate this model in AMICI." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "image/png": "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pUvJz89n+PDhDBs2jEaNGrF27VpSU1O58847ufrqqxk5ciQA48ePZ+/evSxdupQWLVqwevVqbr75Znbt2kWHDh1q9X2KSM1QuRGROsff3x8PDw98fHzKp6L2799/0XqlpaW8+eabtGvXDoC77rqLf//732RkZNCwYUO6du3KgAED2LBhAyNHjiQtLY3333+ftLQ0WrRoAcBjjz3GunXreP/995kxY0btvUkRqTEqNyLisHx8fMqLDUBQUBBhYWE0bNiwwrLMzEwAdu3ahdVqpWPHjhWep7i4mKZNm9ZOaBGpcSo3IuKw3N3dK3xtsVguucxmswFw/vx5XF1d2b59O66urhXW+99CJCKOTeVGROokDw+PCgcCV4eoqCisViuZmZn079+/Wp9bROoOnS0lInVSWFgYP/30E8eOHSM7O7t89OVKdOzYkXvuuYcxY8awatUqjh49ytatW5k5cyZr1qyphtQiUheo3IhInfTYY4/h6upK165dadasGWlpadXyvO+//z5jxozh0UcfpVOnTgwdOpT//Oc/tGrVqlqeX0TMpysUi4iIiFPRyI2IiIg4FZUbERERcSoqNyIiIuJUVG5ERETEqajciIiIiFNRuRERERGnonIjIiIiTkXlRkRERJyKyo2IiIg4FZUbERERcSoqNyIiIuJU/n+Eyy8Cx5B7JQAAAABJRU5ErkJggg==\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "simulate(\"example_splines.xml\", dict(f=1));" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "### Adding a simple spline" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Instead of using a constant parameter $f$, we want to use a smooth time-dependent function $f(t)$ whose value is known only at a finite number of time instants. The value of $f(t)$ outside such grid points needs to be smoothly interpolated. Several methods have been developed for this problem over the years; AMICI at the moment supports only [cubic Hermite splines](https://en.wikipedia.org/wiki/Cubic_Hermite_spline)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can add a spline function to an existing SBML model with the following code. The resulting time-dependent parameter $f(t)$ will assume values $(1, -0.5, 2)$ at the equally spaced points $(0, 0.5, 1)$ and smoothly vary elsewhere." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "AMICI encodes the spline as a SBML assignment rule for the parameter $f$. Such a rule consists of a piecewise-polynomial formula which can be interpreted in any SBML-compliant software. However, such very complex formulas are computationally inefficient; e.g., in AMICI they lead to very long model creation times. To solve such problem the code below adds AMICI-specific SBML annotations to the assignment rule which can be used by AMICI to recreate the correct interpolant without reading the inefficient piecewise formula." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Create a spline object\n", + "spline = amici.splines.CubicHermiteSpline(\n", + " sbml_id=\"f\",\n", + " evaluate_at=amici.sbml_utils.amici_time_symbol, # the spline function is evaluated at the current time point\n", + " nodes=amici.splines.UniformGrid(0, 1, number_of_nodes=3),\n", + " values_at_nodes=[1, -1, 2],\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "spline value at 0.3 = -0.560000000000000\n", + "spline derivative at 0.3 = -4.60000000000000\n", + "spline integral between 0 and 1 = 0.0416666666666672\n" + ] + } + ], + "source": [ + "# This spline object can be evaluated at any point\n", + "# and so can its derivative/integral\n", + "print(f\"spline value at 0.3 = {spline.evaluate(0.3)}\")\n", + "print(f\"spline derivative at 0.3 = {spline.derivative(0.3)}\")\n", + "print(f\"spline integral between 0 and 1 = {spline.integrate(0.0, 1.0)}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "image/png": "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i8VibjgRqbHczQ4gImIPnp6eBAcHmx1DRGo4fTQmIg7v9ttv56effuKNN97AYrFgsViYM2dOmY/G5syZQ0BAAIsWLaJNmzb4+Phw4403kpOTw9y5c2nevDl169blgQceoLi4uPSx8/Pzefjhh2ncuDG1a9eme/furFmzxpwXKiJVTmeERMThvfHGG+zfv58OHTrw3HPPAbB79+6/7ZeTk8Obb77J/PnzyczMZNiwYVx//fUEBASwZMkSDh06xA033EDv3r0ZPnw4AOPGjSMuLo758+cTEhLCwoULGThwIDt37iQ8PLxaX6eIVD0VIRFxeP7+/nh6euLj41P6cdjevXv/tl9hYSHvvPMOLVu2BODGG2/k448/JjU1lTp16tCuXTuuuOIKVq9ezfDhw0lISOCjjz4iISGBkJAQAB5++GGWLl3KRx99xEsvvVR9L1JE7EJFSERcho+PT2kJAggKCqJ58+bUqVOnzLZjx44BsHPnToqLi2ndunWZx8nPz6d+/frVE1pE7EpFSERchoeHR5mvLRbLWbfZbDYAsrKycHNzY+vWrbi5uZXZ78/lSUQcl4qQiDgFT0/PMhc5V4Xo6GiKi4s5duwYffv2rdLHFpGaQaPGRMQpNG/enE2bNvH7779z4sSJ0rM6F6N169bceuutjBo1igULFnD48GE2b97M1KlTWbx4cRWkFhGzqQiJiFN4+OGHcXNzo127dgQGBpKQkFAlj/vRRx8xatQoJk6cSJs2bRg6dCi//vorTZs2rZLHFxFzaWZpERERcVk6IyQiIiIuS0VIREREXJaKkIiIiLgsFSERERFxWSpCIiIi4rJUhERERMRlqQiJiIiIy1IREhEREZelIiQiIiIuS0VIREREXJaKkIiIiLis/wf0qBxzqVz86wAAAABJRU5ErkJggg==\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Plot the spline\n", + "spline.plot(xlabel=\"time\");" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Load SBML model using libsbml\n", + "import libsbml\n", + "\n", + "sbml_doc = libsbml.SBMLReader().readSBML(\"example_splines.xml\")\n", + "sbml_model = sbml_doc.getModel()\n", + "# We can add the spline assignment rule to the SBML model\n", + "spline.add_to_sbml_model(sbml_model)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Finally, we can simulate it in AMICI\n", + "model, rdata = simulate(sbml_model);" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Final value should be equal to the integral computed above\n", + "assert np.allclose(rdata[\"x\"][-1], float(spline.integrate(0.0, 1.0)))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The following is the SBML code for the above model\n", + "```xml\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " f \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " time \n", + " \n", + " \n", + " \n", + " \n", + " 0\n", + " \n", + " \n", + " 1\n", + " \n", + " \n", + " \n", + " \n", + " 1\n", + " 2\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " 1\n", + " \n", + " \n", + " -1\n", + " \n", + " \n", + " 2\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " ... piecewise representation of the spline ...\n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The spline annotation on its own can be accessed by" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\t\n", + "\t\t\n", + "\t\t\t time \n", + "\t\t\n", + "\t\n", + "\t\n", + "\t\t\n", + "\t\t\t0\n", + "\t\t\n", + "\t\t\n", + "\t\t\t1\n", + "\t\t\n", + "\t\t\n", + "\t\t\t\n", + "\t\t\t\t\n", + "\t\t\t\t1\n", + "\t\t\t\t2\n", + "\t\t\t\n", + "\t\t\n", + "\t\n", + "\t\n", + "\t\t\n", + "\t\t\t1\n", + "\t\t\n", + "\t\t\n", + "\t\t\t-1\n", + "\t\t\n", + "\t\t\n", + "\t\t\t2\n", + "\t\t\n", + "\t\n", + "\n", + "\n" + ] + } + ], + "source": [ + "print(spline.amici_annotation)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "### Splines can be parametrized" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Instead of constant values, SBML parameters can be used as spline values. These can also be automatically added to the model when adding the assignment rule." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "spline = amici.splines.CubicHermiteSpline(\n", + " sbml_id=\"f\",\n", + " evaluate_at=amici.sbml_utils.amici_time_symbol,\n", + " nodes=amici.splines.UniformGrid(0, 1, number_of_nodes=3),\n", + " values_at_nodes=sp.symbols(\"f0:3\"),\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "sbml_doc = libsbml.SBMLReader().readSBML(\"example_splines.xml\")\n", + "sbml_model = sbml_doc.getModel()\n", + "spline.add_to_sbml_model(\n", + " sbml_model,\n", + " auto_add=True,\n", + " y_nominal=[1, -0.5, 2],\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "parameters = dict(f0=-2, f1=1, f2=-2)" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "spline.plot(parameters, xlabel=\"time\");" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "model, rdata = simulate(sbml_model, parameters)" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjcAAAGwCAYAAABVdURTAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8o6BhiAAAACXBIWXMAAA9hAAAPYQGoP6dpAABrR0lEQVR4nO3dd3xT9f7H8VeSNuluKdABlC1bhiwBtyhucSIoIK6r4qJXr+IA1xW9+lMUEdzgQFDcgqCiqCiKLGUje7allO6VJuf3xymBympL25Om7+d95JGT05Pkk3Np8/Z7vsNmGIaBiIiISICwW12AiIiISFVSuBEREZGAonAjIiIiAUXhRkRERAKKwo2IiIgEFIUbERERCSgKNyIiIhJQgqwuoKZ5vV527dpFZGQkNpvN6nJERESkHAzDICcnh0aNGmG3H71tps6Fm127dpGUlGR1GSIiIlIJ27dvp0mTJkc9ps6Fm8jISMA8OVFRURZXIyIiIuWRnZ1NUlKS73v8aOpcuNl/KSoqKkrhRkREpJYpT5cSdSgWERGRgGJ5uJk4cSLNmzcnJCSE3r17s2jRoqMen5mZyciRI0lMTMTlctGmTRtmz55dQ9WKiIiIv7P0stSMGTNITk5m8uTJ9O7dm/HjxzNgwADWrVtHXFzcIccXFxdzzjnnEBcXx8yZM2ncuDFbt24lJiam5osXERERv2QzDMOw6s179+5Nz549efnllwFzmHZSUhJ33nknDzzwwCHHT548mWeffZa1a9cSHBxcqffMzs4mOjqarKyso/a58Xg8uN3uSr1HbRQcHIzD4bC6DBERkcMq7/c3WNhyU1xczJIlSxg9erRvn91up3///ixcuPCwz/niiy/o06cPI0eO5PPPP6dhw4YMGTKE+++//4hfzEVFRRQVFfkeZ2dnH7UuwzBISUkhMzOz4h+qlouJiSEhIUHz/4iISK1mWbhJT0/H4/EQHx9fZn98fDxr16497HM2bdrE999/z7XXXsvs2bPZsGEDt99+O263m7Fjxx72OePGjeOxxx4rd137g01cXBxhYWF14oveMAzy8/NJS0sDIDEx0eKKREREKq9WDQX3er3ExcXx2muv4XA46N69Ozt37uTZZ589YrgZPXo0ycnJvsf7x8kfjsfj8QWb+vXrV8tn8FehoaEApKWlERcXp0tUIiJSa1kWbho0aIDD4SA1NbXM/tTUVBISEg77nMTExEP6hrRv356UlBSKi4txOp2HPMflcuFyucpV0/4+NmFhYeX9GAFl/+d2u90KNyIiUmtZNhTc6XTSvXt35s2b59vn9XqZN28effr0Oexz+vXrx4YNG/B6vb5969evJzEx8bDBprLqwqWow6mrn1tERAKLpfPcJCcn8/rrrzN16lTWrFnDbbfdRl5eHiNGjABg2LBhZToc33bbbWRkZHD33Xezfv16Zs2axVNPPcXIkSOt+ggiIiLiZyztczNo0CD27NnDmDFjSElJoWvXrsyZM8fXyXjbtm1lVv5MSkpi7ty5jBo1is6dO9O4cWPuvvtu7r//fqs+goiIiPgZS+e5scLRxskXFhayefNmWrRoQUhIiEUVVo5hGPzrX/9i5syZ7Nu3j2XLltG1a9cKvUZt/vwiIhLYKjLPjeXLL0jVmDNnDlOmTOGrr75i9+7ddOrUqcJLW4iIiJSb1wvF+ZCfAXnpkJMKWTshcxtk77a0tFo1FFyObOPGjSQmJtK3b1+g4ktbiIhIHVaYDVk7IDcVctMgL83cztsLhVlQmAkFmea2Ow/cBVBSeOTXS+oNN35TU9UfQuHmGAzDoMDtseS9Q4Md5RrBdP311zN16lTAHPHUrFkz4uPjufnmm32dsydPnsysWbN46623Dru0hYiIBDDDgJzdkP437N0AezfCvi2Qtc1saSnMOv73sNnB5gC7AxxVN4K5MhRujqHA7aHDmLmWvPfqxwcQ5jz2/0UvvvgirVq14rXXXuOPP/7AZrPRuHHjCi1tISIiAaIgE1JXld5Wmre0tWaLy9GExEBkIkTEld7iIaw+hMaYPwuJNu9dERAcCsFhEBQCQS6wB4EfTSeicBMAoqOjiYyMxOFwkJCQwK5duyq8tIWIiNRCJcWQsgJ2Lim9LTZbZg7H5oB6zaF+a2hwgrkd0wxikiA6yQwtAULh5hhCgx2sfnyAZe8tIiLiU5wPO/6Arb/C1l9gx2IoKTj0uKgmkNAJ4jtBfEfzVq8FBFl7uaimKNwcg81mK9elIX9SmaUtRETED3m9kPIXbPzevG37DbzusseExkKTHtC4u3lrdBKE1631Ef+pdn1rS7kcvLTFwIEDgQNLW9xxxx3WFiciIkdXmA0b58G6r2HDd5C/t+zPoxpDs76lt37QoI1f9XfxBwo3ASo5OZnhw4fTo0cPevXqxfjx48ssbSEiIn4kJwXWfGkGms0/lW2dcUZAi9Og1VnmLbalwswxKNwEqGMtbSEiIhbL3QNrPodVn8GWBcBBCwbEtoJ2F0Cb88w5YxzBVlVZK2n5hYPU9eUH6vrnFxGpdsV5sHYWLJ8Gm38Ew3vgZ417QPuLod2F5mgmKaMiyy+o5UZERKQ6eb2w7VdY/gGs/gyKcw/8rFE36Hg5dBwIMU2tqjDgKNyIiIhUh7y9sPx9WDIFMjYe2B/TDLoMhi6DzP4zUuUUbkRERKqKYcC2hfDHm7DmC/AUm/udEdDxMug6BJr2UYfgaqZwIyIicrxKimDlJ/DbK+a8NPsldoUeI6DTlQE1A7C/U7gRERGprLx0s5XmjzfMlbTBXG+p89XQ4wazT43UOIUbERGRitq3FRa+DEvfPbD8QWQj6HUTdB8BYbHW1lfHKdyIiIiUV9oaWPACrJgJhsfcl9gV+t4JHS7VfDR+QuFGRETkWFJXw49Pw+rPD+xreQacMgpanK4Own7GbnUBUjUMw+CWW24hNjYWm83G8uXLrS5JRKT2S1sDHw6HSX0OBJv2l8DNP8Cwz82Ao2DjdxRuAsScOXOYMmUKX331Fbt37yY7O5uLL76YRo0aYbPZ+Oyzz6wuUUSk9sjYBB/fBK/0MSfeA/Oy020LYdC70PgkS8uTo9NlqQCxceNGEhMT6du3LwDLli2jS5cu3HDDDVx++eUWVyciUkvkpsFPz8Lit8BbYu5rfwmcfj8kdLK2Nik3hZsAcP311zN16lQAbDYbzZo1Y8uWLZx//vkWVyYiUksU5cKvE8ybO8/c1+osOHssNOpqaWlScQo3x2IY4M635r2Dw8p1LffFF1+kVatWvPbaa/zxxx84HI4aKE5EJAB4vfDnBzDvcchNMfc1Ogn6PwotT7e0NKk8hZtjcefDU42see8Hd4Ez/JiHRUdHExkZicPhICEhoQYKExEJAFt/hTmjYfdy83G95mao6TBQnYRrOYUbERGpW7J2wjcPwapPzceuKDjtXuh9KwS5rK1NqoTCzbEEh5ktKFa9t4iIVI2SYvh9Esx/xuxXY7PDScPhzIcgoqHV1UkVUrg5FputXJeGRETEj23+CWbdC+nrzMdJveGC5yCxs7V1SbVQuAlQubm5bNiwwfd48+bNLF++nNjYWJo2bWphZSIiNShvL8x9EP6abj4OawDnPgGdrwG7pnoLVAo3AWrx4sWceeaZvsfJyckADB8+nClTplhUlYhIDTEM+GuG2WG4IAOwQc+b4KyHITTG6uqkmtkMwzCsLqImZWdnEx0dTVZWFlFRUWV+VlhYyObNm2nRogUhISEWVWiduv75RSRAZGyGr0bBph/Mx3Ed4ZIJ0KS7tXXJcTna9/c/qeVGREQCg9cLf7wO3z1qTuMRFGLOLNz3Tq3WXcco3IiISO2XsQk+vwO2/mI+bn4qXPwi1G9lbV1iCYUbERGpvf7ZWhMcDuc8Bj1uVIfhOkzhRkREaqesnfDZreYwbzBbay592ZxpWOo0hRsREal9VsyEWclQmGVOeHrO42qtER+FGxERqT0KMmH2vbDiI/Nx4+5w2WvQoLWlZYl/UbgREZHaYdtv8PFNkLUdbA447T5zTSiNhJJ/ULgRERH/5vXAz8/D/HFgeKBeC7jiDWjSw+rKxE8p3IiIiP/K3gWf3AJbfjYfd74GLnwOXJHW1iV+TT2vAoRhGNxyyy3ExsZis9lYvny51SWJiByfv7+FSf3MYBMcDgMnw+WvKtjIMSncBIg5c+YwZcoUvvrqK3bv3s2XX35Jz549iYyMJC4ujoEDB7Ju3TqryxQROTZPCcx7HN6/0lwXKqEz/Osn6DrY6sqkllC4CRAbN24kMTGRvn37kpCQwC+//MLIkSP57bff+Pbbb3G73Zx77rnk5eVZXaqIyJHlpMK7A+Hn/zMf97wZbvpOo6GkQtTnJgBcf/31TJ06FQCbzUazZs3YsmVLmWOmTJlCXFwcS5Ys4bTTTrOgShGRY9jyC8wcAbmp4IyAS16CTldYXZXUQgo3x2AYBgUlBZa8d2hQKDab7ZjHvfjii7Rq1YrXXnuNP/74A4fDccgxWVlZAMTGxlZ5nSIix8UwYOFE+HaMORqqYXu4+h1o2MbqyqSW8otwM3HiRJ599llSUlLo0qULEyZMoFevXoc9dsqUKYwYMaLMPpfLRWFhYbXUVlBSQO9pvavltY/l9yG/ExYcdszjoqOjiYyMxOFwkJCQcMjPvV4v99xzD/369aNTp07VUaqISOUU58MXd8LKmebjE6+Gi8eDM9zSsqR2szzczJgxg+TkZCZPnkzv3r0ZP348AwYMYN26dcTFxR32OVFRUWU6x5andaMuGzlyJCtXrmTBggVWlyIickDGZphxHaSuBHsQDHgKet0C+psux8nycPP8889z8803+1pjJk+ezKxZs3jrrbd44IEHDvscm8122BaKwykqKqKoqMj3ODs7u0L1hQaF8vuQ3yv0nKoSGhR63K9xxx138NVXX/HTTz/RpEmTKqhKRKQKbJgHM2+AwkwIbwhXTYXm/ayuSgKEpeGmuLiYJUuWMHr0aN8+u91O//79Wbhw4RGfl5ubS7NmzfB6vZx00kk89dRTdOzY8bDHjhs3jscee6zSNdpstnJdGvI3hmFw55138umnnzJ//nxatGhhdUkiImb/mt8mwTcPgeGFxj1g0LsQ1cjqyiSAWDoUPD09HY/HQ3x8fJn98fHxpKSkHPY5bdu25a233uLzzz/nvffew+v10rdvX3bs2HHY40ePHk1WVpbvtn379ir/HP5o5MiRvPfee0ybNo3IyEhSUlJISUmhoMCaztEiIpQUwRd3wNzRZrDpeh2MmK1gI1XO8stSFdWnTx/69Onje9y3b1/at2/Pq6++yhNPPHHI8S6XC5fLVZMl+oVJkyYBcMYZZ5TZ//bbb3P99dfXfEEiUrflpsGMobD9N7DZ4dwn4eTb1b9GqoWl4aZBgwY4HA5SU1PL7E9NTS13n5rg4GC6devGhg0bqqPEWuOee+7hnnvu8T02DMO6YkREDpayEj64xlzN2xUNV70FrftbXZUEMEsvSzmdTrp37868efN8+7xeL/PmzSvTOnM0Ho+HFStWkJiYWF1liohIZf39Lbw1wAw2sa3g5nkKNlLtLL8slZyczPDhw+nRowe9evVi/Pjx5OXl+UZPDRs2jMaNGzNu3DgAHn/8cU4++WRat25NZmYmzz77LFu3buWmm26y8mOIiMg/LXodvv6P2b+m+almx+HQelZXJXWA5eFm0KBB7NmzhzFjxpCSkkLXrl2ZM2eOr5Pxtm3bsNsPNDDt27ePm2++mZSUFOrVq0f37t359ddf6dChg1UfQUREDub1wNyH4Hez7x9dr4WLxkOQ09KypO6wGXWsc0Z2djbR0dFkZWURFRVV5meFhYVs3ryZFi1aEBISYlGF1qnrn19EqkBxPnx8I6ybbT4+ewyckqyOw3Lcjvb9/U+Wt9z4ozqW93zq6ucWkSqSlw7TroadS8DhgssmaeFLsYTCzUGCg4MByM/PJzT0+GcHrm3y8/OBA+dBRKTc9m6E96+EjE1mv5rB06HpyVZXJXWUws1BHA4HMTExpKWlARAWFlYn1q0yDIP8/HzS0tKIiYk57KriIiJHtGOx2WKTvxdimsK1H2tFb7GUws0/7J9fZ3/AqUtiYmLKPb+QiAgA6+fCh8OhpAASu8CQjyAy/tjPE6lGCjf/YLPZSExMJC4uDrfbbXU5NSY4OFgtNiJSMcs/gM9HguEx5665aiq4IqyuSkTh5kgcDoe+7EVEjuTXCfDNw+Z252vg0pfBof564h8UbkREpPwMA74dA7++ZD7ucwec8wTYLZ3wXqQMhRsRESkfTwl8eTcsf898fM7j0O9ua2sSOQyFGxERObaSInNyvjVfgs0Bl7wE3a6zuiqRw1K4ERGRoyvOgxnXwcbvweGEK9+G9hdZXZXIESnciIjIkRVkmnPYbP8dgsNh8DRoeYbVVYkclcKNiIgcXu4eeO8ySFkBIdFw7UxI6mV1VSLHpHAjIiKHyt4N71wC6eshvCEM/RQSTrS6KpFyUbgREZGyMrfD1Ith32aIagzDvoAGra2uSqTcFG5EROSAjE0w9VLI2gYxzWD4F1CvudVViVSIwo2IiJjS/zZbbHJ2Q2wrGP4lRDe2uiqRClO4ERERSFtrBpu8NGjYDoZ9DpFaSFdqJ4UbEZG6Lm1NabDZA/EnwrDPILyB1VWJVJrCjYhIXZa62gw2+emQ0NlssQmLtboqkeOilc5EROqq1FUHgk1iFwUbCRhquRERqYt8wWYvJHY1L0WF1rO6KpEqoZYbEZG6xncpai806qZgIwFHLTciInXJnnXmzMP7W2yGfgahMRYXJVK11HIjIlJX7J/HJm+P2Xl46KcKNhKQFG5EROqCvRvNYJObCvGd1HlYAprCjYhIoNu35cDMww3bK9hIwFO4EREJZFk7YeolkL0TGrQx14rSBH0S4BRuREQCVW6a2Xk4cyvUa2GuFRURZ3VVItVO4UZEJBDlZ8A7l8LeDRCdZLbYaK0oqSMUbkREAk1BJrw7ENJWQ0SC2ccmpqnVVYnUGIUbEZFAUpwH066G3X9CWAOzxaZ+K6urEqlRCjciIoGipAhmXAfbf4eQaHMem4Ztra5KpMYp3IiIBAJPCXx8I2z8HoLD4dqPIbGz1VWJWELhRkSktvN64cu7YM2X4HDCNe9DUk+rqxKxjMKNiEhtZhgwdzQsfx9sDrhqCrQ60+qqRCylcCMiUpv9+Az8PtncHjgJ2l1obT0ifkDhRkSktvr9NZg/zty+4DnoMsjaekT8hMKNiEht9NdH8PV95vYZD0Kvm62tR8SPKNyIiNQ2f38Ln91qbve6BU7/j7X1iPgZhRsRkdpk2+8wYyh4S6DTlXDeM2CzWV2ViF9RuBERqS3S1pizD5cUQOtzzA7Edv0ZF/kn/VaIiNQGWTvgvSugMBOa9ISrp0KQ0+qqRPySwo2IiL/Lz4B3L4fsndCgLQz5EJzhVlcl4rcUbkRE/FlxPkwbBOnrILIRXPcxhMVaXZWIX/OLcDNx4kSaN29OSEgIvXv3ZtGiReV63vTp07HZbAwcOLB6CxQRsYKnBGaOgB2LzIUwr/sYYpKsrkrE71kebmbMmEFycjJjx45l6dKldOnShQEDBpCWlnbU523ZsoV7772XU089tYYqFRGpQYYBs0bB+jkQFAKDZ0B8B6urEqkVLA83zz//PDfffDMjRoygQ4cOTJ48mbCwMN56660jPsfj8XDttdfy2GOP0bJlyxqsVkSkhvz4DCx9B2x2uOJNaNbH6opEag1Lw01xcTFLliyhf//+vn12u53+/fuzcOHCIz7v8ccfJy4ujhtvvPGY71FUVER2dnaZm4iIX1v6TtllFdpfZG09IrWMpeEmPT0dj8dDfHx8mf3x8fGkpKQc9jkLFizgzTff5PXXXy/Xe4wbN47o6GjfLSlJ16tFxI+t/wa+vMfcPvXf0PPY/xEnImVZflmqInJychg6dCivv/46DRo0KNdzRo8eTVZWlu+2ffv2aq5SRKSSdi6Bj4aD4YEug+GsR6yuSKRWCrLyzRs0aIDD4SA1NbXM/tTUVBISEg45fuPGjWzZsoWLL77Yt8/r9QIQFBTEunXraNWqVZnnuFwuXC5XNVQvIlKFMjbD+1eDOx9anQ2XTNCyCiKVZGnLjdPppHv37sybN8+3z+v1Mm/ePPr0ObTzXLt27VixYgXLly/33S655BLOPPNMli9frktOIlI75WfA+1dCfjokdDZnH3YEW12VSK1lacsNQHJyMsOHD6dHjx706tWL8ePHk5eXx4gRIwAYNmwYjRs3Zty4cYSEhNCpU6cyz4+JiQE4ZL+ISK3gLoQPBsPeDRCdZM4+7Iq0uiqRWs3ycDNo0CD27NnDmDFjSElJoWvXrsyZM8fXyXjbtm3YtTCciAQirxc+/Rds/w1c0XDtRxCVaHVVIrWezTAMw+oialJ2djbR0dFkZWURFRVldTkiUpd98zD8OgHswTD0U2ihSUlFjqQi399qEhERscKi181gAzBwkoKNSBVSuBERqWnr5sDX/zG3z3oEOl9lbT0iAUbhRkSkJu3+E2beAIYXug01J+oTkSqlcCMiUlOydsK0QeDOg5ZnwkUvaC4bkWqgcCMiUhMKs2Ha1ZCzGxq211w2ItVI4UZEpLp5SmDmCEhdCRHxcO2HEBJtdVUiAUvhRkSkOhmG2Xl4w3cQHAaDp0NMU6urEgloCjciItXp98mw+E3ABpe/Do1PsroikYCncCMiUl3WfQ1zRpvb5z4B7S+yth6ROkLhRkSkOuz+C2beCBjQ/Xroc4fVFYnUGQo3IiJVLXv3QUO+z4ALntOQb5EapHAjIlKVivPhg2sgZxc0aANXaci3SE1TuBERqSr7V/nevRzC6sOQDyE0xuqqROochRsRkaryw5Ow5gtwOGHQ+xDbwuqKROokhRsRkarw53T4+f/M7YtfgmZ9rK1HpA5TuBEROV5bF8IXd5rbp/4bug62th6ROk7hRkTkeOzbAjOuBU8xtL8EznzY6opE6jyFGxGRyirMhmnXQP5eSOwCl00Gu/6silhNv4UiIpXh9cDHN8GeNRCRYK4Z5Qy3uioRQeFGRKRyvh0Df8+FoBAYPA2iGlldkYiUUrgREamope/CwpfN7YGToHF3a+sRkTIUbkREKmLrr/DVKHP79Aeg0+XW1iMih1C4EREpr31bYMZ14HVDx8vg9PutrkhEDkPhRkSkPIpy4IPBpSOjusKlr2hklIif0m+miMixeL3wyS2Qthoi4mHwB+AMs7oqETkChRsRkWP5/glYNxscLrhGI6NE/F2lwk1eXl5V1yEi4p/++hAWPG9uX/oyNOlhbT0ickyVCjfx8fHccMMNLFiwoKrrERHxHzuWwOd3mNunjILOV1tbj4iUS6XCzXvvvUdGRgZnnXUWbdq04emnn2bXrl1VXZuIiHWyd8H0IeApgjbnw1ljrK5IRMqpUuFm4MCBfPbZZ+zcuZNbb72VadOm0axZMy666CI++eQTSkpKqrpOEZGa4y6A6ddCbgo0bA9XvK6RUSK1yHH9tjZs2JDk5GT++usvnn/+eb777juuvPJKGjVqxJgxY8jPz6+qOkVEaoZhwBd3wa6lEFrPHBnlirS6KhGpgKDjeXJqaipTp05lypQpbN26lSuvvJIbb7yRHTt28Mwzz/Dbb7/xzTffVFWtIiLV75cXYcWHYHPA1e9AbAurKxKRCqpUuPnkk094++23mTt3Lh06dOD222/nuuuuIyYmxndM3759ad++fVXVKSJS/dbPhe8eNbfPfwZanGZpOSJSOZUKNyNGjOCaa67hl19+oWfPnoc9plGjRjz00EPHVZyISI3Zsw5m3ggY0H0E9LzJ6opEpJJshmEYFX1Sfn4+YWG1c3bO7OxsoqOjycrKIioqyupyRMQfFOyD18+CjE3QrB8M/QyCnFZXJSIHqcj3d6U6FEdGRpKWlnbI/r179+JwOCrzkiIi1vCUwMwbzGAT3dTsZ6NgI1KrVSrcHKmxp6ioCKdTfxREpBb5bixs/B6Cw2DwNAhvYHVFInKcKtTn5qWXXgLAZrPxxhtvEBER4fuZx+Php59+ol27dlVboYhIdVk+DRa+bG4PnAQJJ1pbj4hUiQqFmxdeeAEwW24mT55c5hKU0+mkefPmTJ48uWorFBGpDjsWw5d3m9un/Qc6DrS0HBGpOhUKN5s3bwbgzDPP5JNPPqFevXrVUpSISLXK3m3OQOwphrYXwhmjra5IRKpQpYaC//DDD1Vdh4hIzXAXwoyDlla4/FUtrSASYModbpKTk3niiScIDw8nOTn5qMc+//zzx12YiEiVMwz4ahTsXAIhMWYHYi2tIBJwyh1uli1bhtvt9m0fic1mO/6qRESqw2+T4M9pYLPDVVMgtqXVFYlINSh3uDn4UpQuS4lIrbPxB/imdNb0c/8Lrc60th4RqTaVutD83nvvacVvEak9MjbBR9eD4YWu18LJt1ldkYhUo0qFm1GjRhEXF8eQIUOYPXs2Ho/nuIqYOHEizZs3JyQkhN69e7No0aIjHvvJJ5/Qo0cPYmJiCA8Pp2vXrrz77rvH9f4iEsCKcuCDIVCYCY17wIXPgy6fiwS0SoWb3bt3M336dGw2G1dffTWJiYmMHDmSX3/9tcKvNWPGDJKTkxk7dixLly6lS5cuDBgw4LDLOwDExsby0EMPsXDhQv766y9GjBjBiBEjmDt3bmU+iogEMq8XPr0V9qyBiAQY9B4Eh1hdlYhUs0otnHmw/Px8Pv30U6ZNm8Z3331HkyZN2LhxY7mf37t3b3r27MnLL5uzhHq9XpKSkrjzzjt54IEHyvUaJ510EhdeeCFPPPHEIT8rKiqiqKjI9zg7O5ukpCQtnClSF8x/GuaPA4cTRnwNTXpYXZGIVFK1L5x5sLCwMAYMGMD555/PCSecwJYtW8r93OLiYpYsWUL//v0PFGS3079/fxYuXHjM5xuGwbx581i3bh2nnXbaYY8ZN24c0dHRvltSUlK56xORWmzNl2awAbjoBQUbkTqk0uEmPz+f999/nwsuuIDGjRszfvx4LrvsMlatWlXu10hPT8fj8RAfH19mf3x8PCkpKUd8XlZWFhERETidTi688EImTJjAOeecc9hjR48eTVZWlu+2ffv2ctcnIrVU6mr45F/mdu/boNt11tYjIjWqUjMUX3PNNXz11VeEhYVx9dVX88gjj9CnT5+qru2IIiMjWb58Obm5ucybN4/k5GRatmzJGWecccixLpcLl8tVY7WJiMXyM2D6YHDnQYvT4Nwnra5IRGpYpcKNw+Hgww8/ZMCAAWUWz6yoBg0a4HA4SE1NLbM/NTWVhISEIz7PbrfTunVrALp27cqaNWsYN27cYcONiNQhnhKYOQL2bYGYZnDVVHBU6s+ciNRilbostf9y1PEEGzBXEu/evTvz5s3z7fN6vcybN69CLUFer7dMp2ERqaO+HQOb5kNwOAz+AMJira5IRCxQ7v+keemll7jlllsICQnhpZdeOuqxd911V7kLSE5OZvjw4fTo0YNevXoxfvx48vLyGDFiBADDhg2jcePGjBtndgwcN24cPXr0oFWrVhQVFTF79mzeffddJk2aVO73FJEAtPwD+G2iuX3ZJIjvaG09ImKZcoebF154gWuvvZaQkBBeeOGFIx5ns9kqFG4GDRrEnj17GDNmDCkpKXTt2pU5c+b4Ohlv27YN+0Er9ubl5XH77bezY8cOQkNDadeuHe+99x6DBg0q93uKSIDZsQS+vNvcPu0/0OFSa+sREUsd9zw3tU1FxsmLSC2QkwKvnQk5u6DtBTDofbAf9ywXIuJnqn2em8cff/ywa0sVFBTw+OOPV+YlRUQqrqQIZgw1g02DtnDZqwo2IlK5lhuHw8Hu3buJi4srs3/v3r3ExcUd91pT1UktNyIBwjDgizth2bsQEg03/wD1W1ldlYhUk2pvuTEMA9thFp77888/iY3V6AQRqQF/vGEGG5sdrnxLwUZEfCo0AUS9evWw2WzYbDbatGlTJuB4PB5yc3O59dZbq7xIEZEyNv8MX99vbvd/DFr3P/rxIlKnVCjcjB8/HsMwuOGGG3jssceIjo72/czpdNK8efManalYROqgfVvhw2FgeODEq6HvnVZXJCJ+pkLhZvjw4QC0aNGCvn37EhwcXC1FiYgcVnEeTB8CBRmQ2BUueQkOc4lcROq2coeb7OxsXweebt26UVBQQEFBwWGPVUddEalyhgGf3QapKyE8Dq55H4JDra5KRPxQucNNvXr1fCOkYmJiDtuheH9HY38eLSUitdRPz8Hqz8EeDIPehegmVlckIn6q3OHm+++/942E+uGHH6qtIBGRQ6ydBT+Uru594XPQ9GRr6xERv1bucHP66acfdltEpFqlrYFPbjG3e94M3a+3tBwR8X+Vmudmzpw5LFiwwPd44sSJdO3alSFDhrBv374qK05E6rj8DPhgMBTnQrNT4LxxVlckIrVApcLNfffdR3Z2NgArVqwgOTmZCy64gM2bN5OcnFylBYpIHeUpgZkjYN9miG4KV08Fh0ZoisixVWgo+H6bN2+mQ4cOAHz88cdcfPHFPPXUUyxdupQLLrigSgsUkTrq20dg03wIDoPB0yC8gdUViUgtUamWG6fT6Vs487vvvuPcc88FIDY21teiIyJSacveh99eMbcvmwwJJ1pbj4jUKpVquTnllFNITk6mX79+LFq0iBkzZgCwfv16mjTR8EwROQ7bF8FX95jbp98PHS61tBwRqX0q1XLz8ssvExQUxMyZM5k0aRKNGzcG4Ouvv+a8886r0gJFpA7J3gUzrgNPMbS7CE5/wOqKRKQWshmGYVhdRE2qyJLpIlKD3AXw9vmwaxnEdYQbvwFXhNVViYifqMj3d6UuSwF4vV42bNhAWloaXq+3zM9OO+20yr6siNRFhgFf3GkGm9BYswOxgo2IVFKlws1vv/3GkCFD2Lp1K/9s+NHyCyJSYQtegBUfgT0Irn4H6jW3uiIRqcUqFW5uvfVWevTowaxZs0hMTDzsOlMiIuWy7muY97i5ff7/oMWp1tYjIrVepcLN33//zcyZM2ndunVV1yMidUnaGvj4JsCAHjdCzxutrkhEAkClRkv17t2bDRs2VHUtIlKX5GfAB9eYSys0PxXOf8bqikQkQFSq5ebOO+/k3//+NykpKZx44okEB5edEr1z585VUpyIBCiPGz4cBvu2QExTuEpLK4hI1anUUHC7/dAGH5vNhmEYft+hWEPBRfzAV8mw+E1wRphDvuM7Wl2RiPi5ah8Kvnnz5koVJiLCotfNYIMNLn9dwUZEqlylwk2zZs2qug4RqQs2/Qhf329unz0G2mmhXRGpepXqUAzw7rvv0q9fPxo1asTWrVsBGD9+PJ9//nmVFSciAWTvRrOfjeGBE6+GU0ZZXZGIBKhKhZtJkyaRnJzMBRdcQGZmpq+PTUxMDOPHj6/K+kQkEBRmwQeDoTATGneHSyaA5scSkWpSqXAzYcIEXn/9dR566CEcDodvf48ePVixYkWVFSciAcBTAh+NgPR1ENkIrpkGwSFWVyUiAaxS4Wbz5s1069btkP0ul4u8vLzjLkpEAsg3D8PGeRAUCoM/gMgEqysSkQBXqQ7FLVq0YPny5Yd0LJ4zZw7t27evksJEJAAsfht+n2RuX/4qNOpa6Zfyeg2KPV6KSry4PV48XqPM7XBzWthtYLfZcNjNW5DdRnCQnWC7nWCHuU/Lx4gEnkqFm+TkZEaOHElhYSGGYbBo0SI++OADxo0bxxtvvFHVNYpIbbT5J5h9LwAlpz9IWqNz2bsji8yCYvblu8nML2ZfnpvsQje5hSXkFLnJKSwhp7CEgmIP+e4SCoq9FBSXUFTipcRb4Sm5jslmA1eQnZBgh+8+JMhBiNNBWLCDMKeDUKeDcGcQ4a4gIlwOwl3mdmTI/luw7z46NJhwp0OBScRilQo3N910E6GhoTz88MPk5+czZMgQGjduzIsvvsg111xT1TWKiB/LKXSzPaOAXZkF7M4uJCWrAHfaBu7cdCuRRglf04/b5naEud9X6fs67DYcNht2O+b9PwKFAXgNs1XnwH3Z1zAMKHR7KXR7q7SuqJAgokKDiQkNJjrMSUxoMDFhwaX3TuqFB1MvzEm9MCex4U7qhTsVikSqUKXCTUFBAZdddhnXXnst+fn5rFy5kl9++YUmTZpUdX0i4gcy84vZlJ7H5j15bErPZUt6Ptsy8tm+L5/MfHeZY6PI5VPnWCLtOSz3tuSe4psB85JQbLj5ZR4dWvrlHh5MVGgwka4DLSARriDCnEGEOktbToIdhAQ7cAbZcQaZl5OcDnulgoDXa+D2einxGLg9XopLzMtcRSUeCt3mfUGxlwK3h/zi0hakYnM7t2j/fQm5heZ9ju/eTXZBCcWll8v25bvZl+9mawVqcwbZiS0NO/UjSu/DXdSPcFI/3En9CHO7Qem+MIUhkSOqVLi59NJLufzyy7n11lspLi7mkksuITg4mPT0dJ5//nluu+22qq5TRGpATqGbdSk5rEvNYX1KDutTc1mfmsPevOKjPi8mLJgm9UJpEhXMfenP0ypnN/khCRRf8D5fxifRMMJFdGgwdru1X8Z2uw2X3YGrUn/5js4wDIpKvGQVuMkucJNZ4CYr37zPzC8mM999yCW5ffnFZOQVU1RiBq2U7EJSsgvL9X4hwXYaRLioH+GiQbiTBhEuGkSW3pfeGkY6aRgRQlRokIKQ1CmV+hVfunQpL7zwAgAzZ84kPj6eZcuW8fHHHzNmzBiFG5FaIDO/mD93ZLFyZxardmWxelc2W/bmH/H4hKgQWjQIp0XDcFo2CCcpNoykemEkxYYSGRJsXuOZlQybF0NwOGHDP6JXYoca/ETWstlsZp+dYAfxUeUf6m4YBgVuDxl5ZuDZm1dERl4xe3OL2ZtXzN5c83F66XZ6bpHvUtqOfQXs2FdwzPdwOuzUj3DSMHJ/8HGWhh/XIfdRIQpCUvtVKtzk5+cTGRkJwDfffMPll1+O3W7n5JNP9s1WLCL+o8TjZW1KDou3ZPDnjiyWb89kc/rhp21IiAqhXWIkbeL33yJo1TCC8GM1d/z+Kix+C7DBFW9AYueq/yAByGazEeY0L8U1qVe+5+QVlbA3t5g9uUWlgaeY9NLgs39/ek4Re3KLyCk0L5ftzipkd9axW4WcDrsZfv4RhMyWIfNxw9LH/tAaJ3I4lQo3rVu35rPPPuOyyy5j7ty5jBplTqOelpamlbZF/ECh28PSbfv4Y/M+Fm/NYOnWfeQVew45rnn9ME5sEkPHRlGlt2hiw50Vf8O/v4W5o83tcx7XmlHVbP+Irab1w455bKHbw968YvbkmIFnfwhKzzX3HS4I7coqZFc5gtD+flQN9vcHKg1D9SNc1C/dv78PUYMIFyHBjmO+pkhVqFS4GTNmDEOGDGHUqFGcffbZ9OnTBzBbcQ43uZ+IVK8Sj5cVO7P4deNeft2YzuIt+ygqKTsCKDIkiO7N6tEtqR5dkqLp0iSGepUJMv+UusqcgdjwQreh0PfO439NqTIhwQ4ax4TSOCb0mMcWuj1lgk967sGBqLRFqLR1KKvATYnXIC2niLSconLVEuZ0lHaUdpZ2LjdDkTlqLLjM6LF6YWbHc4dahqQSbIZhVGryiJSUFHbv3k2XLl2w282JjhctWkRUVBTt2rWr0iKrUnZ2NtHR0WRlZamVSWq11OxCfly/hx/X7eHnv/eQXVhS5udxkS56t6xPr+b16NE8ljbxkVX/RZGTCm+cDVnbofmpcN0nEFQFgUn8XnGJl715RaTnmJfE9uaV3h90aSwjr9jXf6jYU/Hh9jYbvpF1+4fS1wtzEh0WTExo6b4wc8Rd9D9uwY5Krwstfqoi39+VDje1lcKN1FaGYbByZzbfrk7huzVprN6dXebnUSFB9GlVn36tG9C3VX1aNYyo3o6hxfkw9SLYuQTqt4Ybv4Ww2Op7P6m1DMMgt6jEDDqlYScjzwxE+/KKySgdObb/8b78YnL+EdYrKszpICokmKhQc5qBqIMmXIwICSLqoKkHfLeQ/ZM1mvdhwQ71KfIjFfn+roYBkSJSVdweLws37uWb1Sl8tzqtzDBhmw06N4nh9DYNOb1NQ7omxdRcE77XC5/dagab0How5EMFGzkim81WGiyCaVY/vFzPcXu8ZOaboefAvTmUPqvATWa+m6yC0iH2pfuyC92+UJRfOkdRSvYx3ugYwpyO0g7f5rxL4S5zO6R0BmtzLqYgQoLtvjmZQoLtuPZvB5Vul947HXZcwXZcpfM2OR3752+yE6TlQKqMwo2In3F7vPy6cS+z/9rN3NUpZSbJC3M6OL1NQ/q3j+eMtg2pH+Gypsjvn4DVn4M92Fzlu34ra+qQgBXssNMw0hyeXhEer0FOoRl2cgpLyC4NPdkFJb7wY06+6PZNwnjwxIy5RSXkFZX4ZrPeH5JqitNhTlQZHGQnqHQNtCCHjWC73VwfrTQE7V8rzdxnO7CGms2G/aDZu+2ls3fvX2cNG77HNmzYbJTebNgwt+HAz8zt0vujBK/9P/IYxSzMfZ6+Da7gsXMHVtdpOiaFGxE/4PUaLNm2j0+X7WT2it1lAk39cCfndozn3A4J9GlV3/oRJ8vegwXPm9uXvgzN+lpbj8hBHHYbMWFOYsIq3/fLMAwK3V7yis2gs3+W6vxiD3lFJaUzWHt8M1gXuD0U+m5eCoo9FJZ4KHJ7y9z7ZsR2e3yLwP6zY0ixx0uxB6jBQFV1DEIafUhw9HI+37WR+93nEhZ87BF91cEvws3EiRN59tlnSUlJoUuXLkyYMIFevXod9tjXX3+dd955h5UrVwLQvXt3nnrqqSMeL+LPNqTl8umyHXy2bBc7Mw9MxtYgwsmAjglceGIivVrEEuQvnSM3/gBf3m1un3YfdNFachJ4bDYboaWLpjaoxtZRo3TNs+LSpUCKS7y4vQYlHi9ujxd36TIhJV6DEo9BSenSIR6vQYnXvDe3vb5tcx01c101r2Hg9Rp4DPM/oAwMDAO8hvlzwzAfG1B6f+Dx/tT1z065h+ula5QetSrvC5bkLMOGnauaPmBZsAE/CDczZswgOTmZyZMn07t3b8aPH8+AAQNYt24dcXFxhxw/f/58Bg8eTN++fQkJCeGZZ57h3HPPZdWqVTRu3NiCTyBSMblFJXz15y4+XLydpdsyffsjXEGc1ymBgV0b06dVff8bApu2Bj4cBt4S6HQlnPmQ1RWJ1Go2m3lJKchh5zgamvzCTzt+4t157wHwQK/7GdL+EkvrsXy0VO/evenZsycvv/wyAF6vl6SkJO68804eeOCBYz7f4/FQr149Xn75ZYYNG3bM4zVaSqxgGAZLt2XywaJtzPprNwVus8nZYbdxRpuGXHZSY/q3j7f+ktORHDzku2kfGPY5BFnU30dE/MqmrE1cO+tact25XHHCFYztM7ZaOkbXmtFSxcXFLFmyhNGjR/v22e12+vfvz8KFC8v1Gvn5+bjdbmJjDz9So6ioiKKiAxNMZWcfZ9d5kQrILSrhs2U7ef/3baw5aOh2y4bhDOqRxGUnNSYusvzrEFmiOA8+GGQGm9hWZgdiBRsRAbKKsrj7+7vJdedyUtxJPNT7Ib8Y8WVpuElPT8fj8RAfH19mf3x8PGvXri3Xa9x///00atSI/v37H/bn48aN47HHHjvuWkUqYkNaLlN/3cKny3aSW2QOTXUF2bm4SyMG90ripKb1/OIPwDF5PfDJLbBrGYTGwrUfaci3iABQ4i3h/p/uZ0v2FhLCE3j+jOcJdgRbXRbgB31ujsfTTz/N9OnTmT9/PiEhh/+v39GjR5OcnOx7nJ2dTVJSUk2VKHWIYRj89Hc6by3YzI/r9/j2t2wYzrW9m3HlSU2IDvOPX/xyMQyYMxrWfgUOJwz+QEO+RcTnucXP8cuuXwgNCmXCWROoH1rf6pJ8LA03DRo0wOFwkJqaWmZ/amoqCQkJR33uc889x9NPP813331H585HXn3Y5XLhcqkJXapPUYmHT5fu5I0Fm9mQlguYcz70bx/P9X2b07dV/drRSvNPv70Ci141ty97FZqebG09IuI3Plz3Ie+veR+Ap055inax/rXskqXhxul00r17d+bNm8fAgQMBs0PxvHnzuOOOO474vP/973/897//Ze7cufTo0aOGqhUpK7vQzfu/beOtXzazp3ThwHCng6t7JnF93+blnonVL636DOaWjoY65wnodLml5YiI//h99+889ftTANzV7S76Nzt8txArWX5ZKjk5meHDh9OjRw969erF+PHjycvLY8SIEQAMGzaMxo0bM27cOACeeeYZxowZw7Rp02jevDkpKSkAREREEBERYdnnkLpjT04RbyzYxPu/bfP1p0mICuHGU1owqFcSUSG16NLT4Wz73exngwE9b9Yq3yLisyVrC6Pmj8JjeLio5UXcdOJNVpd0WJaHm0GDBrFnzx7GjBlDSkoKXbt2Zc6cOb5Oxtu2bfOtOg4wadIkiouLufLKK8u8ztixY3n00UdrsnSpY1KzC3n1x01MW7SVQre5wvEJcRH86/RWXNKlEc4gP5lo73ikb4APrgFPEbQ5H85/5sC86iJSp2UVZXHn93eSU5xDl4ZdeLTvo357yd3yeW5qmua5kYranVXApPkbmf7HdopLzFDTNSmGO85szVnt4gJn1eDcNHijP2RuhUYnwfVfgbMWX1oTkSrj9ri59btbWZSyiMTwRKZdOI0GoQ1qtIZaM8+NiD/bk1PEK/M38P7v23yhpkezetx19gmcekIDv/0vlkopyoX3rzKDTb3mMGSGgo2IAOZI0Md/e5xFKYsIDw5nwlkTajzYVJTCjcg/ZOW7efWnjbz9yxbfTMK9msdyT/8T6FNbRz4djccNHw2H3cshrD5c9wlEHLr0iYjUTW+ufJPPNnyG3Wbn2dOepW1sW6tLOiaFG5FShW4Pb/+yhVfmbyCn0Owo3KVJNP8+t23gtdTsZxjw5T2w4TsICoUhH2ouGxHxmbNlDi8ufRGA0b1Gc2qTUy2uqHwUbqTO83gNPl22k//7Zh27swoBaBsfyb/PbcM5HeIDM9Ts98NTsPw9sNnhqinQRFMriIjpzz1/8tDP5pQQ17W/jmvaXWNxReWncCN12k/r9/DU7DWsTckBoFF0CP8+ty0DuzX2v1W5q9ofb8JP/zO3L3oB2p5nbT0i4je2Z2/nru/vothbzBlJZ3Bvj3utLqlCFG6kTtq0J5cnZ63h+7VpAESGBHHHma0Z3re5/67MXZVWfw6z/m1un34/dL/e0nJExH9kFmZy27zbyCjMoH1se5459Rkc9tr1d1HhRuqUrAI3E+b9zZRft1DiNQiy2xjWpzl3ntWaeuFOq8urGVsWwMc3AYYZas4YbXVFIuInijxF3PXDXWzN3kpieCITz55IWHCY1WVVmMKN1Aler8FHS7bzzJx1ZOQVA3Bm24Y8fFEHWjWsQzNbp6yADwaDpxjaXQQXPq9J+kQEAK/h5cGfH2RZ2jIigyOZ1H8SDcMaWl1WpSjcSMBbuTOLRz5fybJtmQC0ahjOIxd14Iy2dWy4876t8N6VUJQNTfvCFW9CLWtqFpHq88KSF/hm6zcE2YN48awXaRVTe0dOKtxIwMrKd/N/367jvd+24jXMRS3v6d+G6/s1J9gRAEslVETuHnj3MshNgbiOMPgDCA6xuioR8RPvr3mfKaumAPBkvyfpmdDT2oKOk8KNBBzDMPjyr908/uUq0nPNS1AXd2nEQxe0JyG6Dn6hF2bDe5dDxkaIbgrXzYTQGKurEhE/MXfLXJ5Z9AwAd590Nxe2vNDiio6fwo0ElO0Z+Tzy+Urmr9sDQMuG4TxxaSf6tfbvqcKrjbvQ7GOT8heENYBhn0FUI6urEhE/8UfKH4z+eTQGBoPaDuLGTjdaXVKVULiRgFDi8TLl1y383zfrKXB7cDrsjDyzNbee0RJXUB3tV+IpgZk3wNYF4IyEoZ9o9mER8Vm/bz13f383bq+b/k37M7rX6ICZtFThRmq9dSk5/Gfmn/y5IwuAXi1ieeqyE2kdV4dGQf2TYcCXd8O6WeBwmX1sErtYXZWI+Indubu57dvbyHHncFLcSYw7dVytm8vmaBRupNZye7xMnr+Rl77/G7fHIDIkiIcuaM/VPZKwB/rswkdjGDD3oYOWVXgbWtSO9WBEpPplFmZy63e3klaQRqvoVrx01kuEBAVWf0SFG6mVVu/K5r6Zf7JqVzYA/dvH8d/LTiQ+KrB+QSvlx//BbxPN7Utehna1v3OgiFSNfHc+I+eNZFPWJuLC4ph8zmSiXdFWl1XlFG6kVinxeHn1p02M/249bo9BTFgwj17ckUu7NgqYa8XH5bdJMP8pc/u8p6HbtdbWIyJ+w+1xM2r+KP5K/4soZxSv9n+VhPAEq8uqFgo3UmtsSc8j+cPlLC2djO/cDvE8eVkn4iLVWgPA0ndhzgPm9hkPwsm3WVuPiPgNr+HloQUP8euuXwkNCuWV/q/Qul5rq8uqNgo34vcMw+D937fx31lrKHB7iHQF8eglHbn8pMZqrdlv1Wfw5V3mdp874PT/WFqOiPgPwzAY9/s4vt7yNUG2IJ4/43m6NAzsAQYKN+LX0nOLuO+jP/mhdN6aPi3r89zVXWgcE2pxZX5k/Vz4+EYwvNBtKJz7pNaLEhGfSX9OYvq66diw8d9T/sspjU+xuqRqp3AjfuvH9Xv494d/kp5bhDPIzv3ntWNE3+Z1eyTUP238AWYMBW8JdLwcLn5RwUZEfN5Z9Q6T/pwEwAO9HuCClhdYXFHNULgRv1NU4uHZOet4Y8FmANrGR/LS4G60TYi0uDI/s3UhTB8CniJoeyFc/poWwhQRn0/+/oRnFz8LwMiuIxnSfojFFdUchRvxK5v25HLHtGWs3m0O8R7epxmjL2hPSLC+tMvYuQTevwrc+dDqbHMuG0ew1VWJiJ+Ys2UOj/76KADDOwznX53/ZW1BNUzhRvzG58t38uAnK8gr9hAb7uTZKztzdvt4q8vyP7v/gncvh+IcaH4qDHoPglxWVyUifuLnHT/71ou64oQr+HePf9e5wRcKN2K5QreHR79YxfQ/tgNwcstYXrymmybkO5zUVfDOpVCYCU16mssqOMOsrkpE/MQfKX8wav4oSrwlnNf8PB45+ZE6F2xA4UYstiEtlzumLWVtSg42G9x51gncffYJONRp+FBpa2DqJVCQAY1Ogus+Bpf6IYmIaVnaMkbOG0mRp4jTmpzGU6c+FVDrRVWEwo1YZvaK3dz30Z/kFXtoEOFk/KBunHJCA6vL8k971pvBJj/dXABz6CcQEnhTpotI5axMX8nt391OQUkBJyeezPNnPE+wve72w1O4kRrn9nh55uu1vtFQvVvEMmFwN+J0Gerw0jfA1IshLw0SToShn0FoPaurEhE/sS5jHf/69l/kunPpHt+dl856CZejbvfDU7iRGpWWXcgd05axaEsGAP86vSX3nduWIIfd4sr81N6NMPUiyE2BuI4w9HMIi7W6KhHxExszN3LzNzeTXZxNl4ZdmHj2REKDNMmpwo3UmMVbMrjt/aXsySkiwhXEc1d14bxOgbloW5VI/xumlAabhu1h2OcQXt/qqkTET2zK3MSNc29kX9E+OtTvwKT+kwgPDre6LL+gcCM1Ytrv2xj7xUrcHoM28RFMvq47LRtGWF2W/9qz3rwUlZsCcR1g2BcQ0dDqqkTET2zK2sQNc29gb+Fe2tZry6v9XyXSqQEG+yncSLUqLvHy6JermPb7NgAuPDGRZ6/qTJhT//SOaM/60ktRqealqOFfQLg6WouIaXPWZm6ceyN7C/fSpl4bXj/3dWJCYqwuy6/oG0aqTVpOIbe/t5TFW/dhs8G957bl9jNa1ck5F8otbe2BzsPxncwWG12KEpFSW7K2cOPcG0kvSOeEeifwxrlvUC9EAwz+SeFGqsXKnVnc/M5idmcVEhkSxEvXdOPMdnFWl+XfUlaYE/Tl74X4E80WG3UeFpFS+4PNnoI9tI5prWBzFAo3UuXmrNzNqBl/UuD20LJhOG8M66H+Nceyc4m5pEJhJiR2haGfKtiIiM+mzE3c+I3ZYrM/2MSG6G/EkSjcSJUxDIOJP2zguW/WA3DqCQ14echJRIfW3YmkymXbb+YimEXZ0KQXXDdTE/SJiM/f+/7mpm9uIqMww3cpSsHm6BRupEoUuj3c//FffL58FwDX923Owxe21/w1x7L5J5h2DbjzoNkpMGS6llQQEZ91Geu46ZubyCzKpH1se1475zV1Hi4HhRs5bhl5xdz8zmKWbN2Hw27jsUs6ct3Jzawuy/+tnwsfDoOSQmh1Fgx6X4tgiojPqr2ruOWbW8guzqZT/U5MPmcy0S616paHwo0cl83peYx4exFb9uYTGRLE5Ou606+1hi0f04qZ8Om/wFsCbc6Hq6ZAsJafEBHT8rTl3PbdbeS6c+ncsDOT+0/WPDYVoHAjlbZocwa3vLuYzHw3TeqFMmVET1rH6ZfvmBa/DV+NAgw48SoYOAkc6pckIqaFuxZy9w93U1BSwElxJzHx7IlEODUooyIUbqRSPl++k/s++otij5cuSTG8MawHDSPr9kJt5fLLS/DtI+Z2jxvggv8Du/oliYjph20/8O8f/43b66Zfo368cOYLWiuqEhRupEIMw+C1nzYx7uu1AAzoGM/4Qd0IdTosrszPGQZ8/wT8/H/m4373QP9HQRMaikiprzd/zeifR+MxPJzd9Gz+d9r/cDqcVpdVKyncSLl5vQZPzFrN279sAeCGfi146ML2OOz6gj4qTwnMGgVL3zEfnz0GTv23tTWJiF/5aP1HPLHwCQwMLmp5EU/0e4Igu76iK0tnTsql0O3h3x/+yawVuwF46IL23HxaS4urqgXchfDxjbD2K7DZ4aIXoPv1VlclIn7CMAzeWPEGLy17CYCr21zNQyc/hN2my9XHQ+FGjimrwM0t7yzm980ZBDtsPHdVFy7t2tjqsvxfYRZ8MAS2LgCHC658E9pfbHVVIuInvIaX5xY/x7ur3wXg5hNv5s5ud2r9vSpgeTScOHEizZs3JyQkhN69e7No0aIjHrtq1SquuOIKmjdvjs1mY/z48TVXaB2VllPIoFcX8vvmDCJcQUwd0UvBpjyyd8PbF5rBxhUF132sYCMiPm6vm4cXPOwLNvf1uI+7TrpLwaaKWBpuZsyYQXJyMmPHjmXp0qV06dKFAQMGkJaWdtjj8/PzadmyJU8//TQJCQk1XG3ds21vPldOWsjalBwaRLiY8a+T6as5bI4tbS28eQ6kroDwOLh+FrQ41eqqRMRPFJQUMOqHUXy56UscNgf/PeW/DOs4zOqyAorNMAzDqjfv3bs3PXv25OWXXwbA6/WSlJTEnXfeyQMPPHDU5zZv3px77rmHe+65p0LvmZ2dTXR0NFlZWURFRVW29IC3Znc2w95axJ6cIpJiQ3nvxt40qx9udVn+b8svMH2weUmqfmu4dibEtrC6KhHxE/sK93HH93fw156/cDlcPHf6c5yRdIbVZdUKFfn+tqzPTXFxMUuWLGH06NG+fXa7nf79+7Nw4cIqe5+ioiKKiop8j7Ozs6vstQPVH1syuGHKH+QUltAuIZJ3buhFXJRmzz2mlZ+Ysw57iiGpNwyerpW9RcRnR84ObvvuNrZkbyHKGcXLZ79Mt7huVpcVkCy7LJWeno7H4yE+Pr7M/vj4eFJSUqrsfcaNG0d0dLTvlpSUVGWvHYh+XL+HoW/+Tk5hCT2a1WPGLX0UbI7FMMzJ+WaOMINNu4tg2OcKNiLis2bvGoZ+PZQt2VtIDE/k3fPfVbCpRpZ3KK5uo0ePJisry3fbvn271SX5rTkrU7hp6h8Uur2c0bYh797Ym+gwLQtwVB63uZTC/lmHe90CV78DwZpRVERMv+76lRFzR5BekE6bem149/x3aRmjqTSqk2WXpRo0aIDD4SA1NbXM/tTU1CrtLOxyuXC5tCzAsXy6bAf3fvQXHq/BhScm8sKgrjiDAj77Hp/CLPhwOGz6AbDBgKfg5Ns067CI+Hy8/mOe+O0JPIaHXgm9GH/meC2AWQMs+/ZyOp10796defPm+fZ5vV7mzZtHnz59rCqrTnr/960kf/gnHq/Bld2b8OI1CjbHtG8rvDnADDbBYXDNNOhzu4KNiADmHDbjl4zn0YWP4jE8XNTyIib1n6RgU0MsncQvOTmZ4cOH06NHD3r16sX48ePJy8tjxIgRAAwbNozGjRszbtw4wOyEvHr1at/2zp07Wb58OREREbRu3dqyz1GbvfHzJp6ctQaA4X2aMfbijti1nMLRbV8E04dA3h6ITDQ7DjfqanVVIuInijxFPLzgYeZsmQPArV1u5fYut2sOmxpkabgZNGgQe/bsYcyYMaSkpNC1a1fmzJnj62S8bds27AetmLxr1y66dTvQAeu5557jueee4/TTT2f+/Pk1XX6t98r8DfxvzjoAbjujFf8Z0Fa/fMeyfBp8ebfZcTj+RBgyA6I1qaGImPYW7OWeH+5h+Z7lBNmDeLTPo1za+lKry6pzLJ3nxgqa58b00ry/ef7b9QCM6t+Gu/ufYHFFfs7rgW/HwEJzTibaXQSXvQquCGvrEhG/sX7feu6cdye78nYR6Yxk/Bnj6ZXYy+qyAkatmOdGrGEYBs9/u54J328A4L4BbRl5pi7pHVVhFsy8ETZ8az4+7T9wxmiwq1+SiJh+3P4j//npP+SX5NM0sikTzp5Ay2iNiLKKwk0dYhgGz8xZx+QfNwJa2btc9qyD6dfC3r8hKBQGvgKdLre6KhHxE4ZhMHXVVJ5f8jwGBr0SevH8Gc8T7Yq2urQ6TeGmjjAMg6fnrOXVHzcBMPbiDozop2UBjmrNl/DprVCcC1GNzRFR6jgsIqWKPEU8vvBxvtj4BQBXtbmK0b1HE2zX/GBWU7ipA/a32OwPNk9c2pGhfZpbW5Q/83rgh//Cz/9nPm5+Klz5NkQ0tLYuEfEbKXkpjPphFCv3rsRus/Ofnv9hSLshGpThJxRuApxhGDw798ClqMcVbI4uPwM+uRk2fGc+PnkknPM4OPSrIiKmpalLGTV/FBmFGUS7onnu9Oc4OfFkq8uSg+gvdgAzDIPnvlnHK/PNYPPYJR0ZpmBzZDuWwEfXQ9Y2s3/NJROg81VWVyUifsIwDD5a/xHjFo2jxFtCm3ptePHMF2kS2cTq0uQfFG4C2AvfrmfiD2awGXtxB4b3bW5tQf7KMGDR6zD3QfC6IbaluT5UwolWVyYifqKwpJAnf3uSzzd+DsCA5gN4vO/jhAWHWVyZHI7CTYCa+MMGXiod7v3IReo8fERFOfDFXbDqE/Nx+0vg0pchRCMdRMS0PWc7yfOTWZuxFrvNzl3d7uKGTjeof40fU7gJQG/8vIln55ozD48+vx03nqJgc1i7/4SZN8DeDWAPgnOe0MKXIlLGj9t/ZPSC0eQU5xAbEsv/TvsfvRN7W12WHIPCTYB5d+EW31pRo/q34V+nt7K4Ij9kGPD7ZHPGYU+xOcz7yrehqf5giYipxFvCK8tf4fUVrwPQuWFn/u/0/yMhPMHiyqQ8FG4CyId/bOeRz1cBcPsZrbjrbM08fIi8vfD57bDeXNCOtheal6HCYq2tS0T8RmpeKv/56T8sTVsKwOB2g7mvx30EOzR/TW2hcBMgvvhzF/d/8hcAN/RrwX1aBPNQm+abk/Ll7AaHCwb8F3repMtQIuKzYOcCHvz5QfYV7SM8OJxH+zzKeS3Os7osqSCFmwAwb00qyTOWYxgwpHdTHrmovYLNwdyFMO9x+G2i+bj+CXDV2xoNJSI+bq+bicsm8ubKNwFoH9ue505/jqZRTS2uTCpD4aaW+3VjOre9v5QSr8HAro148tJOCjYHS1kBn9wCaavNx91HmC02znBr6xIRv7E9ezv3/3w/K9JXAHBN22u4t+e9uBwuiyuTylK4qcWWbdvHzVMXU1zi5ZwO8Tx7VRfsdgUbwFxCYeHL8P2TZqfh8IZwycvQVs3LInLAlxu/5MnfniS/JJ9IZySP9nmUc5ufa3VZcpwUbmqptSnZXP/2H+QVe+jXuj4TBncj2GG3uiz/kP43fHY77FhkPm57AVz8ktaGEhGfnOIc/vv7f5m1aRYAJ8WdxNOnPk1iRKLFlUlVULiphbbtzWfom4vIKnDTrWkMrw3tQUiww+qyrOf1wG+vmK01JYXgijIvQXUbqk7DIuLzR8ofPLzgYXbl7cJhc3Bbl9u46cSbcNj1dzRQKNzUMmk5hVz35u/sySmiXUIkU67vRbhL/zeyZz18cQds/9183Oosc22oaK35IiKmIk8RE5ZO4J3V72Bg0DiiMU+f+jRd47paXZpUMX0r1iJZBW6Gv/UH2zLySYoN5Z0behEdVsfnXSgpggUvwM//Z/atcUaarTUnDVNrjYj4rNm7hgcXPMiGTHNZmitOuIL7et5HeLAGFwQihZtaotDt4eapi1mzO5sGES7evaE3cVEhVpdlra0L4cu7Id1caoLW58BFL0BMkrV1iYjfcHvcvLHiDV5b8Rol3hLqh9Tnsb6PcXrS6VaXJtVI4aYWKPF4uWPaMhZtySDSFcTUG3rSvEEd/q+N/Axz3polb5uPwxvC+c9Ax8vVWiMiPqv3ruaRXx5h/b71APRv2p9H+jxCbIhmJA90Cjd+zjAMHvp0Jd+tScUZZOf14T3o2KiOrljt9cLy9+G7sZC/19zXbSic87iWTxARn2JPMZP/nMxbK9/CY3iIccXwYO8HOa/5eZoHrI5QuPFzL3y7nhmLt2O3wcuDu3Fyy/pWl2SN3X/CrHsPDO9u2A4ueA5anGptXSLiV5alLeOxXx9jY9ZGAM5tdi4P9n6Q+qF19G9nHaVw48fe/30rL31vdn57cuCJnNuxDq5Gm5cOPzxlXoIyvOCMgDMegN63ghaxE5FS2cXZvLjkRT5c/yEAsSGxPHzyw5zT7ByLKxMrKNz4qbmrUnjks5UA3H32CQzpXcfWNykphkWvwY//g6Isc1+nK+DcJyGqkbW1iYjfMAyD77Z9x7jfx7GnYA8Al59wOcndk4l21dFL+KJw448Wb8ngrg+W4TVgcK8k7ul/gtUl1RzDgLWz4NtHIGOTuS+hM5w3DpqfYm1tIuJXtmdvZ9yicfy882cAmkc1Z0yfMfRM6GlxZWI1hRs/s3FPLjdOXUxRiZf+7eN4oi4thLl1IXz3KGz/zXwcEQ9nj4Eug0Ezh4pIqcKSQt5a+RZvrniTYm8xQfYgbuh0A7d0vkWLXQqgcONX9uQUcf3b5rIKXZNimDD4JILqwnpRqath3mOwfo75OCgU+twOp4wCV6S1tYmI3zAMg592/MS4RePYmbsTgD6JfRjdezQtoltYXJ34E4UbP5FfXMJNU/9ge0YBzeqH8ebwHoQ6A7y1In0D/PQ/+OtDwACbA04aCqc/AFFavE5EDtiYuZFn/3iWX3b9AkBcWBz397yfc5qdU3dat6XcFG78gMdrcNcHy/hzRxb1woKZMqIX9SMCuGl170b46Vn4a4Y5Agqgw6Vw1iPQoA71LxKRY8oqyuKV5a8wY90MPIaHYHsw13W4jls730pYcJjV5YmfUrixmGEYPPrFKr5bk4YryM4bw3vSIlBnH96z3lwH6q8ZYHjMfW3OM4d2N+pmbW0i4lfcHjcfrv+QSX9OIqt0xORZSWdxb497SYrSEitydAo3Fnvj5828+9tWbDYYP6gr3ZvVs7qkqrdziRlq1nwFGOa+EwbAGfdD4+6WliYi/sUwDOZuncuLS15kR+4OAFrHtOb+XvdzcuLJFlcntYXCjYXmrEzhqa/XAPDQBe05/8QA6mdiGLBxHvzyImz+6cD+thfCqf+GJgo1IlLWHyl/8Pzi51m515zjq0FoA27vejuXtb6MILu+rqT89K/FIn/tyOSeGcswDBh6cjNuPCVAevoX58Gf0+H3Vw+s1m0PghOvhn53Q1w7a+sTEb+zMn0lE5ZN4NddvwIQFhTGiE4jGNZhmPrVSKUo3FhgZ2YBN05dTKHbyxltGzL24g61v7f/vi2w+G1YMgUKM819zkjodh30GQkxukYuImWt37eeicsm8v327wEIsgVxRZsruLXLrTQIbWBxdVKbKdzUsJxCNzdO+YM9OUW0S4hkwuButXcuG0+JOTfN4rdg4/f4+tPUa26u/dT1WgiJsrJCEfFDf+/7m9f+eo25W+ZiYGC32bmo5UXc1uU2mkQ2sbo8CQAKNzWoxOPlzg+WsTYlh4aRLt68vieRIbVw8ce9G+HPD2DZe5Cz+8D+lmdAr1vMEVCaUVhE/mFtxlpe++s1vt36rW/fuc3OZWTXkbSMaWlhZRJoFG5q0JOz1jB/3R5Cgu28ObwHjWNCrS6p/AoyYdUnsPwD2LHowP6wBualp+7DIVZ/nETkUMvTlvPmyjeZv30+ADZs9G/Wn391/hdtY9taWpsEJoWbGvLeb1uZ8usWAF64uiudm8RYWk+5FOWal51WfQp/fwueInO/zQ6tzoauQ6DdRRDktLZOEfE7XsPLzzt+5q2Vb7E0bSlghprzWpzHLSfeQut6rS2uUAKZwk0N+HVDOmO/WAXAvee28e8h34XZsOE7WP0ZrP8GSgoO/Cyug7mIZeerITLBshJFxH8VeYr4evPXTF01lQ2ZGwAIsgdxccuLGdFphNaAkhqhcFPNNu3J5bb3l+LxGgzs2oiRZ/rhf61kbjdbaNbNhs0/g9d94Gf1WkCny6HjZRDfCWr7qC4RqRbpBenMWDeDD9d9SEZhBgDhweFc3eZqrutwHXFhcRZXKHWJwk01ysp3c9PUxWQVuOnWNIanr+jsH0O+i/Nh26+w8QdzlFPa6rI/r98a2l0IHS+HxC4KNCJyWIZhsCJ9BdPXTufrLV9T4i0BICE8gcHtBnNVm6uIdEZaXKXURQo31aTE42XktKVsSs+jUXQIrw3tQUiwRSOI3AXmEghbf4UtC2Dbbwf6z4DZhyapN7Q9H9peoMUrReSo8t35zN48mw/XfciajDW+/V0bduW6DtdxdtOzNaOwWEr/+qrJf2evYcGGdMKcDt4Y3pOGkTW0yrdhQNYOM8zsXALbF8GupeApLntcVBNodSa0Osscwh0WWzP1iUitZBgGazLW8Onfn/LVpq/IdecC4LQ7Oa/FeVzT9hpObHiixVWKmBRuqsGHf2zn7V+2APD81V3o0KiaJrLzesw5Z1JXQuoq837nUshLO/TYiARo3g+a9YXmp0KDNrrcJCLHlFmYyazNs/j0709Zt2+db3/TyKZc3fZqLm11KTEhMdYVKHIYfhFuJk6cyLPPPktKSgpdunRhwoQJ9OrV64jHf/TRRzzyyCNs2bKFE044gWeeeYYLLrigBis+ssVbMnjosxUA3NP/BM7rdJwjowwD8vdC5lYzyKT/DXv/hr0bzO2SwkOfYw+C+I7mituNu5uBpl4LhRkRKZciTxE/7/iZrzZ9xU87fsJdOsgg2B5M/6b9GXjCQE5OPBm7rZbOri4Bz/JwM2PGDJKTk5k8eTK9e/dm/PjxDBgwgHXr1hEXd2jv+l9//ZXBgwczbtw4LrroIqZNm8bAgQNZunQpnTp1suATHLArs4Bb31uC22NwfqcE7jrrCH1XDMPsB1OYaU6OV7DPbG3J3X9Lheyd5iimrB1lh2P/U3CYOUQ7vqN5a9QNEk6E4Fo0QaCIWM7j9bAkdQmzN8/mmy3fkOPO8f2sfWx7BrYeyIUtLyTaFW1hlSLlYzMMw7CygN69e9OzZ09efvllALxeL0lJSdx555088MADhxw/aNAg8vLy+Oqrr3z7Tj75ZLp27crkyZMPOb6oqIiiogOdZ7Ozs0lKSiIrK4uoqKq7XLT27295ct4DlHgNguwQHRKEDcMMMoYHvF7z3vBg85SA4S3zfNtB9wffAGwG2IJDsDsjsLkisbkisbuisIVE43BFY7c7sNlsOGwObNgIsgdht9lx2Bzmvd1BkC0Ih92Bw+YgyB5EkD2ozHawPdh3H2wPJthh3jvtzgPbDicuhwun3enbdmiZBZFaq8RbwpLUJXyz5Ru+2/adbwg3QHxYPBe0vIALW1yoWYTFL2RnZxMdHV2u729LW26Ki4tZsmQJo0eP9u2z2+3079+fhQsXHvY5CxcuJDk5ucy+AQMG8Nlnnx32+HHjxvHYY49VWc1H8vPadfwZenCn3dK5YvYnFMf+B0FU/rTnm8O4i1Mh59hH14QgexAuh6vMLTQoFJfDRUhQCCFBIYQ6QgkNDiU06MAtLCjMvA8OIywozLwPDiM8KJzwYPMWGhTqH0PnRQJIYUkhv+3+jR+2/8D87fPLBJpoVzRnNz2bi1peRPf47rrsJLWWpeEmPT0dj8dDfHx8mf3x8fGsXbv2sM9JSUk57PEpKSmHPX706NFlwtD+lpuqdlmfc9g3dyXR4S7iokLN4dU2G4bNDg5n2VtQCASHYgQ5ARtG6WraBgb7G9IMw8D3P+PAvdfw+rY9hgcDc9/BN4/hwWN48HrN7f37SrwllHhLfNsew4Pb6/btd3vd5s3j9u0v9hRT7C2m2FOM2+umyFOE96BWp/3PzXPnVfk5tdvsZthxhhMRHEF4cDgRzggigyPNe2ckkcGR5n3pLcoZZd5c5r3ToaUhRNLy0/hl5y/8sP0HFu5aSKHnQF+9/YHm3Gbn0iuxF8H2WriYr8g/WN7nprq5XC5cruofht2gQVv+c+2hl8UCkS/0eIop9BT67otKisx7TxGFJYUUegopLCmkoKSAgpICCksKyS/JNx+7C3zbee488kvyzXt3Pvkl+b6gluPOKXPtv6JCg0KJdEYS7Yom2hlt3u+/OaOJccUQ44oh2hVNvZB6vm3N0SG1mdvjZvme5SzYuYBfdv5SZpQTmJPsnZl0JmcmnUmPhB4KNBJwLP0L3qBBAxwOB6mpqWX2p6amkpBw+LWLEhISKnS8VL39/XTCgsOq5fUNw/CFnlx3LvnufHLcOeQV55HjziG3OPfAfXGO75ZdnO275RbnYmD4glVa/mGGxx9FpDOS2JBYYlwx1HPVo16IeYsNiTW3Xeb2/schQSHVci5EysPj9bB231p+3/07i3YvYmnaUgoOGohgw0bH+h05tcmpnJl0Ju1i2+mSrwQ0S8ON0+mke/fuzJs3j4EDBwJmh+J58+Zxxx13HPY5ffr0Yd68edxzzz2+fd9++y19+vSpgYqlJthsNl8fnIY0rNRreA2vL/RkFWeRVZRFdlE2WUVZZBVnkVmUaW4XZbGvaJ95X7iP7OJsAN9zt7K1XO8XFhRmhp1QM/DUD6lv3ofWp35IfeqH1vftj3JFqS+DHJdiTzGr9q5iSeoSlqUtY1naMnKKy7ZwxobE0rdRX/o17kffRn2JDdFEnVJ3WN72npyczPDhw+nRowe9evVi/Pjx5OXlMWLECACGDRtG48aNGTduHAB33303p59+Ov/3f//HhRdeyPTp01m8eDGvvfaalR9D/IzdZvddfmpCk3I/r8RbQnZxNpmFmewr2se+wn0H7gv3kVGY4duXUZhBRmEGJd4S8kvyyc/NZ0fujmO+R5AtyBd8YkNjfeHn4PsGoQ2oH1qfGFeMglAdZxgGO3J3sGLPClakr2Bl+kpW711NsbfsrOMRwRH0iO9B78Te9ErsReuY1vq3I3WW5eFm0KBB7NmzhzFjxpCSkkLXrl2ZM2eOr9Pwtm3bsNsP/IL27duXadOm8fDDD/Pggw9ywgkn8Nlnn1k+x40EhiB7kO9yU3kYhkGuO5eMwgz2FuxlX+E+9hbu9T3OKMxgb+Fe33Z2cTYlRglpBWmkFRz7UpnD5iA2JJYGoQ3KBKEGIWb4OXg72hWtL7NazuP1sDVnK2v3rmXtvrWs3buWNRlryCzKPOTY2JBYusd3p1tcN06KO4m2sW3VV0yklOXz3NS0ioyTF6lqbo/bDDulgWdvwaHb6QXp7C3cS1ZRVoVeO8gWRL2Qeoe0Au2/JOZrLSrtJ6ROpNYp8ZawK3cXW7K3sCFzAxv2bWBD5gY2ZW2i6OBFbUsF24NpF9uOTg06cWKDE+ncsDNNI5uq34zUKbVmnhuRuibYEUxCeAIJ4cfuAO/2uMkozCC9MP2IQWhvwV7SC9PJKsqixChhT8Ee9hTsKVctUc6oMp2iy9y76hETcqAzdYwrRp2mK6iwpJBdubvYkbuDnbk72Zmzk605W9mavZXtOdsp8ZYc9nkhjhDaxLahXb12tKvfjvax7WlTr42mNRCpAIUbET8V7AgmPjye+PD4Yx67Pwj5ws8/7g/+WWZRJl7D6xtZtiV7S7nqCXGEEBNyYOh8jCuGaGc0Ua4o332UM8o339D+uYfCg8MD6nJJQUkBmYWZB1rZSs9zWn4aqXmppOSnkJqXyr6ifUd9HZfDRdOoprSObk2rmFa0rtea1jGtaRLRRDN/ixynwPmLI1KHVSQIeQ0vWUVZvg7R+28Hd5jOLDrQoTqzMJMSo4RCTyEpeSmk5B1+wsyjCQ0KJTI4knBnuG8W6rDgMN9M1AffQoJCCHGE+Jb4CAkKOWRZkP3Lhxy8zIjNZsPGgcs0BoZvIsv9t/1zNO2frLLYW+ybi2n/vEv57nxy3bnkufPIKc4h151LVtGBEXaHu2x0JBHBETSOaGzeIhuTFJlE86jmNI9qTnx4vPpIiVQThRuROsZus/vm7WlFq2Mev7/T9P4v98yiTN92dlG2b6h9VlHWIfMO7Z8Jd/98QxxlDdjaJMgeVGZUW/2Q+sSFxZkBM8y8JYQnEOWMUr8YEQso3IjIUdlsNt8lpqTIii1d4va4yXXn+iZezHPn+Waizis5MCP1/lmr94egYk8xRZ4i3+2fS4O4vW7fsiJew4vH6/EtY3Iwu81OkC0Iu91s4QmyBRHsMBeB3b8A7P6WooNbjyKd5hIfEcHmzXcZrvQ+PDhcoUXEjynciEi1CXYEU89hthKJiNQUXfAVERGRgKJwIyIiIgFF4UZEREQCisKNiIiIBBSFGxEREQkoCjciIiISUBRuREREJKAo3IiIiEhAUbgRERGRgKJwIyIiIgFF4UZEREQCisKNiIiIBBSFGxEREQkoCjciIiISUIKsLqCmGYYBQHZ2tsWViIiISHnt/97e/z1+NHUu3OTk5ACQlJRkcSUiIiJSUTk5OURHRx/1GJtRnggUQLxeL7t27SIyMhKbzValr52dnU1SUhLbt28nKiqqSl9bDtB5rhk6zzVD57nm6FzXjOo6z4ZhkJOTQ6NGjbDbj96rps613Njtdpo0aVKt7xEVFaVfnBqg81wzdJ5rhs5zzdG5rhnVcZ6P1WKznzoUi4iISEBRuBEREZGAonBThVwuF2PHjsXlclldSkDTea4ZOs81Q+e55uhc1wx/OM91rkOxiIiIBDa13IiIiEhAUbgRERGRgKJwIyIiIgFF4UZEREQCisJNBU2cOJHmzZsTEhJC7969WbRo0VGP/+ijj2jXrh0hISGceOKJzJ49u4Yqrd0qcp5ff/11Tj31VOrVq0e9evXo37//Mf9/EVNF/z3vN336dGw2GwMHDqzeAgNERc9zZmYmI0eOJDExEZfLRZs2bfS3oxwqep7Hjx9P27ZtCQ0NJSkpiVGjRlFYWFhD1dZOP/30ExdffDGNGjXCZrPx2WefHfM58+fP56STTsLlctG6dWumTJlS7XViSLlNnz7dcDqdxltvvWWsWrXKuPnmm42YmBgjNTX1sMf/8ssvhsPhMP73v/8Zq1evNh5++GEjODjYWLFiRQ1XXrtU9DwPGTLEmDhxorFs2TJjzZo1xvXXX29ER0cbO3bsqOHKa5eKnuf9Nm/ebDRu3Ng49dRTjUsvvbRmiq3FKnqei4qKjB49ehgXXHCBsWDBAmPz5s3G/PnzjeXLl9dw5bVLRc/z+++/b7hcLuP99983Nm/ebMydO9dITEw0Ro0aVcOV1y6zZ882HnroIeOTTz4xAOPTTz896vGbNm0ywsLCjOTkZGP16tXGhAkTDIfDYcyZM6da61S4qYBevXoZI0eO9D32eDxGo0aNjHHjxh32+Kuvvtq48MILy+zr3bu38a9//ata66ztKnqe/6mkpMSIjIw0pk6dWl0lBoTKnOeSkhKjb9++xhtvvGEMHz5c4aYcKnqeJ02aZLRs2dIoLi6uqRIDQkXP88iRI42zzjqrzL7k5GSjX79+1VpnIClPuPnPf/5jdOzYscy+QYMGGQMGDKjGygxDl6XKqbi4mCVLltC/f3/fPrvdTv/+/Vm4cOFhn7Nw4cIyxwMMGDDgiMdL5c7zP+Xn5+N2u4mNja2uMmu9yp7nxx9/nLi4OG688caaKLPWq8x5/uKLL+jTpw8jR44kPj6eTp068dRTT+HxeGqq7FqnMue5b9++LFmyxHfpatOmTcyePZsLLrigRmquK6z6HqxzC2dWVnp6Oh6Ph/j4+DL74+PjWbt27WGfk5KSctjjU1JSqq3O2q4y5/mf7r//fho1anTIL5QcUJnzvGDBAt58802WL19eAxUGhsqc502bNvH9999z7bXXMnv2bDZs2MDtt9+O2+1m7NixNVF2rVOZ8zxkyBDS09M55ZRTMAyDkpISbr31Vh588MGaKLnOONL3YHZ2NgUFBYSGhlbL+6rlRgLK008/zfTp0/n0008JCQmxupyAkZOTw9ChQ3n99ddp0KCB1eUENK/XS1xcHK+99hrdu3dn0KBBPPTQQ0yePNnq0gLK/Pnzeeqpp3jllVdYunQpn3zyCbNmzeKJJ56wujSpAmq5KacGDRrgcDhITU0tsz81NZWEhITDPichIaFCx0vlzvN+zz33HE8//TTfffcdnTt3rs4ya72KnueNGzeyZcsWLr74Yt8+r9cLQFBQEOvWraNVq1bVW3QtVJl/z4mJiQQHB+NwOHz72rdvT0pKCsXFxTidzmqtuTaqzHl+5JFHGDp0KDfddBMAJ554Inl5edxyyy089NBD2O36b/+qcKTvwaioqGprtQG13JSb0+mke/fuzJs3z7fP6/Uyb948+vTpc9jn9OnTp8zxAN9+++0Rj5fKnWeA//3vfzzxxBPMmTOHHj161ESptVpFz3O7du1YsWIFy5cv990uueQSzjzzTJYvX05SUlJNll9rVObfc79+/diwYYMvPAKsX7+exMREBZsjqMx5zs/PPyTA7A+UhpZcrDKWfQ9Wa3flADN9+nTD5XIZU6ZMMVavXm3ccsstRkxMjJGSkmIYhmEMHTrUeOCBB3zH//LLL0ZQUJDx3HPPGWvWrDHGjh2roeDlUNHz/PTTTxtOp9OYOXOmsXv3bt8tJyfHqo9QK1T0PP+TRkuVT0XP87Zt24zIyEjjjjvuMNatW2d89dVXRlxcnPHkk09a9RFqhYqe57FjxxqRkZHGBx98YGzatMn45ptvjFatWhlXX321VR+hVsjJyTGWLVtmLFu2zACM559/3li2bJmxdetWwzAM44EHHjCGDh3qO37/UPD77rvPWLNmjTFx4kQNBfdHEyZMMJo2bWo4nU6jV69exm+//eb72emnn24MHz68zPEffvih0aZNG8PpdBodO3Y0Zs2aVcMV104VOc/NmjUzgENuY8eOrfnCa5mK/ns+mMJN+VX0PP/6669G7969DZfLZbRs2dL473//a5SUlNRw1bVPRc6z2+02Hn30UaNVq1ZGSEiIkZSUZNx+++3Gvn37ar7wWuSHH3447N/b/ed2+PDhxumnn37Ic7p27Wo4nU6jZcuWxttvv13tddoMQ+1vIiIiEjjU50ZEREQCisKNiIiIBBSFGxEREQkoCjciIiISUBRuREREJKAo3IiIiEhAUbgRERGRgKJwIyIiIgFF4UZEaoX58+djs9nIzMy0uhQR8XOaoVhE/NIZZ5xB165dGT9+PADFxcVkZGQQHx+PzWaztjgR8WtBVhcgIlIeTqeThIQEq8sQkVpAl6VExO9cf/31/Pjjj7z44ovYbDZsNhtTpkwpc1lqypQpxMTE8NVXX9G2bVvCwsK48soryc/PZ+rUqTRv3px69epx11134fF4fK9dVFTEvffeS+PGjQkPD6d3797Mnz/fmg8qItVCLTci4ndefPFF1q9fT6dOnXj88ccBWLVq1SHH5efn89JLLzF9+nRycnK4/PLLueyyy4iJiWH27Nls2rSJK664gn79+jFo0CAA7rjjDlavXs306dNp1KgRn376Keeddx4rVqzghBNOqNHPKSLVQ+FGRPxOdHQ0TqeTsLAw36WotWvXHnKc2+1m0qRJtGrVCoArr7ySd999l9TUVCIiIujQoQNnnnkmP/zwA4MGDWLbtm28/fbbbNu2jUaNGgFw7733MmfOHN5++22eeuqpmvuQIlJtFG5EpNYKCwvzBRuA+Ph4mjdvTkRERJl9aWlpAKxYsQKPx0ObNm3KvE5RURH169evmaJFpNop3IhIrRUcHFzmsc1mO+w+r9cLQG5uLg6HgyVLluBwOMocd3AgEpHaTeFGRPyS0+ks0xG4KnTr1g2Px0NaWhqnnnpqlb62iPgPjZYSEb/UvHlzfv/9d7Zs2UJ6erqv9eV4tGnThmuvvZZhw4bxySefsHnzZhYtWsS4ceOYNWtWFVQtIv5A4UZE/NK9996Lw+GgQ4cONGzYkG3btlXJ67799tsMGzaMf//737Rt25aBAwfyxx9/0LRp0yp5fRGxnmYoFhERkYCilhsREREJKAo3IiIiElAUbkRERCSgKNyIiIhIQFG4ERERkYCicCMiIiIBReFGREREAorCjYiIiAQUhRsREREJKAo3IiIiElAUbkRERCSg/D80S+ZmCmJruAAAAABJRU5ErkJggg==\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Sensitivities with respect to the spline values can be computed\n", + "fig, ax = plt.subplots()\n", + "ax.plot(rdata[\"t\"], rdata.sx[:, 0], label=model.getParameterNames()[0])\n", + "ax.plot(rdata[\"t\"], rdata.sx[:, 1], label=model.getParameterNames()[1])\n", + "ax.plot(rdata[\"t\"], rdata.sx[:, 2], label=model.getParameterNames()[2])\n", + "ax.set_xlabel(\"time\")\n", + "ax.set_ylabel(\"sensitivity\")\n", + "ax.legend();" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "### Specifying derivatives, boundary conditions and extrapolation methods" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "When derivatives are not specified in the `CubicHermiteSpline` constructor, they are computed automatically using finite differences and according to the boundary conditions. If their form is known a priori (e.g., they are known constants or functions of parameters), they can be passed explicitly to the spline constructor." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# A simple spline for which finite differencing would give a different result\n", + "spline = amici.splines.CubicHermiteSpline(\n", + " sbml_id=\"f\",\n", + " evaluate_at=amici.sbml_utils.amici_time_symbol,\n", + " nodes=amici.splines.UniformGrid(0, 1, number_of_nodes=3),\n", + " values_at_nodes=[1.0, -1.0, 1.0],\n", + " derivatives_at_nodes=[10.0, -10.0, -10.0],\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAkIAAAGwCAYAAABFFQqPAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8o6BhiAAAACXBIWXMAAA9hAAAPYQGoP6dpAABaM0lEQVR4nO3dd3xT5eIG8OckadKdAt2DFkqhlBZaQKBsBAVEpgMRBVHxuq+CCxduvPzcezMURVBAlsjem7ZQtqWli066V9ok5/dH2miV0ZQkJ+P5fj753Nv0pH16QPL0Pe95X0EURRFERERETkgmdQAiIiIiqbAIERERkdNiESIiIiKnxSJERERETotFiIiIiJwWixARERE5LRYhIiIicloKqQPYOr1ejwsXLsDLywuCIEgdh4iIiFpAFEVUVlYiODgYMtnlx31YhK7iwoULCAsLkzoGERERtUJ2djZCQ0Mv+3kWoavw8vICYDiR3t7eEqchIiKilqioqEBYWJjxffxyWISuoulymLe3N4sQERGRnbnatBZOliYiIiKnxSJERERETotFiIiIiJwWixARERE5LRYhIiIiclosQkREROS0WISIiIjIabEIERERkdNiESIiIiKnxSJERERETotFiIiIiJwWixARERE5LW66Shan1elxsboeLnIZPFUKKBXs30REZBtYhMhs6rV6JGWVYk9aMU7nV6Kgog755XUortJAL/51nFIhg5dKgXaeSsSGqBEf5oPuoT7oGuQFlUIu3Q9AREROh0WIrklJdT1Wp+Ri55/F2J9+ETX1ukseJxNgLEP1Wj0uautxsboeZwuqsCIpFwDgIhfQr2M7TIgPwcjYQHiq+NeTiIgsSxBFUbz6Yc6roqICarUa5eXl8Pb2ljqOzfizoBLf7TmPFUk50Gj1xufbeSgxoJMveke0QZDaDYHerghQq+DroYJeFFFdr0OVRouqOi1yy2pwNLscx3LKcDSnHCXV9cav4+oiw40xgZjYMwRDovwgkwlS/JhERGSnWvr+zSJ0FSxCze09V4wvdqRj59ki43OxId64uXswBnbyRUyQd6tKiyiKyCiuxtpjeViVnIv04mrj57oGeeOJEVG4MSYAgsBCREREV8ciZCYsQgYFFXV4fe1JrD2WBwAQBODGmADcN7AjrotoY9aCIooijuWUY2VyLn45koMqjRYA0C3Y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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "spline.plot(xlabel=\"time\");" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "image/png": "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wEABwBVvCxYsMH1wC9PR7+e/Yrlpv45e5z179gixsbGCWq0WgoODhbfeektoamoycWrL05Hr3NjYKLz22mtCSEiIYGtrKwQEBAhz584VLl++bPrgFmTbtm1X/Xnbcm1nzpwpjBgx4or3iYyMFFQqlRAcHCysWLGi03PKBIHjb0RERCQdnHNDREREksJyQ0RERJLCckNERESSwnJDREREksJyQ0RERJLCckNERESSwnJDREREksJyQ0RERJLCckNEFiElJQUymQzl5eViRyEiM8cdionILI0cORKRkZFYvHgxAECj0aCsrAxeXl6QyWTihiMis6YUOwARUXuoVCp4e3uLHYOILABvSxGR2bn//vuxfft2fPjhh5DJZJDJZFi5cmWb21IrV66Eq6srfvvtN/Tq1Qv29va4++67UVtbi1WrViEoKAhdunTBk08+Ca1W2/qxGxoa8Nxzz8HPzw8ODg6IjY1FSkqKOF8oEXUKjtwQkdn58MMPcebMGfTt2xdvvPEGAODkyZNXPK+2thYfffQRVq9ejaqqKkyePBmTJk2Cq6srNmzYgMzMTNx1110YMmQIEhISAACPP/44Tp06hdWrV8PX1xc///wzxo0bh+PHj6Nnz54m/TqJqHOw3BCR2XFxcYFKpYK9vX3rrajTp09f8bzGxkZ89tlnCAkJAQDcfffd+Oabb1BYWAhHR0eEh4dj1KhR2LZtGxISEpCbm4sVK1YgNzcXvr6+AIDnnnsOGzduxIoVK/D222+b7oskok7DckNEFsve3r612ACAl5cXgoKC4Ojo2OaxoqIiAMDx48eh1WoRGhra5uM0NDSga9eupglNRJ2O5YaILJaNjU2bP8tksqs+ptPpAADV1dVQKBRIS0uDQqFo87y/FiIismwsN0RkllQqVZuJwMYwYMAAaLVaFBUVYdiwYUb92ERkPrhaiojMUlBQEPbt24fs7GyUlJS0jr7ciNDQUEybNg0zZszA+vXrkZWVhf379yMpKQm///67EVITkTlguSEis/Tcc89BoVAgPDwcHh4eyM3NNcrHXbFiBWbMmIFnn30WvXr1wsSJE3HgwAF069bNKB+fiMTHHYqJiIhIUjhyQ0RERJLCckNERESSwnJDREREksJyQ0RERJLCckNERESSwnJDREREksJyQ0RERJLCckNERESSwnJDREREksJyQ0RERJLCckNERESS8v/klLgONLudJQAAAABJRU5ErkJggg==\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Simulation\n", + "sbml_doc = libsbml.SBMLReader().readSBML(\"example_splines.xml\")\n", + "sbml_model = sbml_doc.getModel()\n", + "spline.add_to_sbml_model(sbml_model)\n", + "simulate(sbml_model, T=1);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The spline annotation in this case is\n", + "```xml\n", + "\n", + "\t ... \n", + "\t ... \n", + "\t ... \n", + "\t\n", + "\t\t\n", + "\t\t\t10\n", + "\t\t\n", + "\t\t\n", + "\t\t\t-10\n", + "\t\t\n", + "\t\t\n", + "\t\t\t-10\n", + "\t\t\n", + "\t\n", + "\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---\n", + "The default boundary conditions depend on the extrapolation method (which defaults to no extrapolation). For example, below we have a spline with constant extrapolation." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "spline = amici.splines.CubicHermiteSpline(\n", + " sbml_id=\"f\",\n", + " evaluate_at=amici.sbml_utils.amici_time_symbol,\n", + " nodes=amici.splines.UniformGrid(0, 1, number_of_nodes=3),\n", + " values_at_nodes=[-2, 1, -1],\n", + " extrapolate=(\n", + " None,\n", + " \"constant\",\n", + " ), # no extrapolation required on the left side\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "spline.plot(xlabel=\"time\", xlim=(0, 1.5));" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "image/png": "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leo6fPLnzSc4biksQkRERNRiFHIZ5o/uCAD46cA5ZBVWSJqHRYiIiIhaVL/2Hhje0RtanYi34tIkXYeMRYiIiIha3OsjI6BUyLAv+zK2pl2ULAeLEBEREbU4f3d7zBgQBAelHGVXpRs0bTYTKhIREZFleXpwMB7p0xZezraSZWARIiIiIknYKxWwV0pbRXhrjIiIiKwWixARERFZLRYhIiIislosQkRERGS1WISIiIjIarEIERERkdViESIiIiKrxSJEREREVotFiIiIiKwWixARERFZLRYhIiIislosQkRERGS1WISIiIjIanH1+dsQRREAUF5eLnESIiIiaqrrv7ev/x6/GRah26ioqAAA+Pv7S5yEiIiImquiogIuLi43/bkg3q4qWTmdTofz58/DyckJgiAY7H3Ly8vh7++PvLw8ODs7G+x9TZm1nTPP17LxfC0bz9f8iaKIiooK+Pn5QSa7+UggXhG6DZlMhjZt2hjt/Z2dnS3m/3RNZW3nzPO1bDxfy8bzNW+3uhJ0HQdLExERkdViESIiIiKrxSIkEZVKhfnz50OlUkkdpcVY2znzfC0bz9ey8XytBwdLExERkdXiFSEiIiKyWixCREREZLVYhIiIiMhqsQgRERGR1WIRMqKlS5ciMDAQtra2iIyMRFJS0i33X7NmDcLCwmBra4vOnTtj8+bNLZTUcJpzzt988w0GDBgANzc3uLm5ITo6+rb/jkxNc/+Mr1u1ahUEQcC4ceOMG9DAmnu+paWleOaZZ+Dr6wuVSoWQkBCz+v91c8/3k08+QWhoKOzs7ODv749Zs2ahpqamhdLemd27d2P06NHw8/ODIAj4/fffb3vMrl27cNddd0GlUqF9+/ZYsWKF0XMaSnPPd926dRg6dCg8PT3h7OyMPn36YOvWrS0T1gD0+fO9bt++fVAoFOjWrZvR8kmJRchIVq9ejRdffBHz58/HkSNH0LVrVwwfPhxFRUU33H///v148MEHMW3aNKSkpGDcuHEYN24cTpw40cLJ9dfcc961axcefPBB7Ny5E4mJifD398ewYcNQUFDQwsn109zzve7s2bOYM2cOBgwY0EJJDaO551tbW4uhQ4fi7NmzWLt2LbKysvDNN9+gdevWLZxcP809319++QVz587F/PnzkZGRge+++w6rV6/Gq6++2sLJ9VNVVYWuXbti6dKlTdr/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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "sbml_doc = libsbml.SBMLReader().readSBML(\"example_splines.xml\")\n", + "sbml_model = sbml_doc.getModel()\n", + "spline.add_to_sbml_model(sbml_model)\n", + "simulate(sbml_model, T=1.5);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The spline annotation in this case is\n", + "```xml\n", + "\n", + "\t ... \n", + "\t ... \n", + "\t ... \n", + "\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And here we have a periodic spline." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "spline = amici.splines.CubicHermiteSpline(\n", + " sbml_id=\"f\",\n", + " evaluate_at=amici.sbml_utils.amici_time_symbol,\n", + " nodes=amici.splines.UniformGrid(0, 1, number_of_nodes=3),\n", + " values_at_nodes=[-2, 1, -2], # first and last node must coincide\n", + " extrapolate=\"periodic\",\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAkIAAAGwCAYAAABFFQqPAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8o6BhiAAAACXBIWXMAAA9hAAAPYQGoP6dpAABvtklEQVR4nO3deXzcVb0//tdn9kz2fWnTpEmXtHSh1EtpZZVqERAqyy1crgVEUH5WhSJI70UQXKp+hSpeBMULBZWLooAICNRCi0Kh0IXuW5ImafZ1JjNJZv38/ph8ZiZtks4kM/PZXs/HIw9lOpM5mZx8Pu/zPu9zjiCKoggiIiIiHTLI3QAiIiIiuTAQIiIiIt1iIERERES6xUCIiIiIdIuBEBEREekWAyEiIiLSLQZCREREpFsmuRugdMFgEC0tLcjMzIQgCHI3h4iIiGIgiiL6+/tRVlYGg2HsvA8DodNoaWlBeXm53M0gIiKiCWhqasLUqVPH/HcGQqeRmZkJIPRBZmVlydwaIiIiioXT6UR5eXn4Pj4WBkKnIU2HZWVlMRAiIiJSmdOVtbBYmoiIiHSLgRARERHpFgMhIiIi0i0GQkRERKRbDISIiIhItxgIERERkW4xECIiIiLdYiBEREREusVAiIiIiHSLO0uT8gUDQMP7gKsdyCgGKpYBBqPcrSIiIg1QVUbo3XffxRe+8AWUlZVBEAS8/PLLp33Nli1bcNZZZ8FqtWLGjBnYuHFj0ttJCXTgFeDn84BnLgf+ckvof38+L/Q4ERHRJKkqEHK73Vi4cCEee+yxmJ5fX1+Pyy67DBdddBF2796NO+64A1/5ylfw5ptvJrmllBAHXgH+tBpwtox83NkaepzBEBERTZIgiqIodyMmQhAEvPTSS1i5cuWYz/nOd76D1157Dfv27Qs/dt1116Gvrw9vvPFGTO/jdDqRnZ0Nh8PBQ1dTKRgIZX5ODoLCBCCrDLhjL6fJiIjoFLHevzVdI7Rt2zYsX758xGMrVqzAHXfcMeZrPB4PPB5P+L+dTmeymkfjaXh/nCAIAETA2Rx63vTzUtYs0oZjHf1weQIjHivMtGJKTppMLSIt6OgfQkvf0IjHrCYDakoyT3sCOslH04FQW1sbiouLRzxWXFwMp9OJwcFBpKWdetFbv349HnzwwVQ1kcYg9rchpsuGqz3ZTSGN+b/tjVj34t5THjcbBTx/21IsrsiVoVWkdvVdblz+6D/h9gZO+bdvXTwTd352lgytolioqkYoFdatWweHwxH+ampqkrtJuvRua4zTXRnFp38O0bATvQP4wasHAAAlWTZMzU3D1Nw05KVb4AuIuOfPn2DId+qNjGg8waCI7/x5D9zeALLTzOF+JWUY/+edY9jX7JC5lTQWTWeESkpK0N4+MmPQ3t6OrKysUbNBAGC1WmG1WlPRPBpDh3MI39qWhtfFPJQKPaNmhoIi4EsvhbViWcrbR+okiiLWvbgXbm8An6rIxZ++uhQGQ6h39Q148dkN76K2041fbD6K71xSI3NrSU1+90EDth/vgd1ixKvfOBflefbwv/1/f9iB1/e24Z4/78Ff13waZiPzD0qj6d/I0qVLsXnz5hGPbdq0CUuXLpWpRXQ6oijiv1/eh76hIDZmfg2AMPwV9Zzh//1R8EYMcfBOMXrh4xP459EuWE0G/PSaBeEgCABy7Bb8YOU8AMBv3q3DnhN9MrWS1KapZwA/eeMQAGDd52tGBEEA8OAV85BrN+NAqxNPbKmVo4l0GqoKhFwuF3bv3o3du3cDCC2P3717NxobGwGEprVWr14dfv7XvvY11NXV4Z577sGhQ4fwq1/9Cn/6059w5513ytF8isHf9rRi04F2mI0CrvrP2yH8+7NAVumI54iZU7DOfDee6VuADZuOyNRSUpM2xxC+/1poSmztZ2ehqjDjlOesOKMEly8oRSAo4p4/74HXH0x1M0llRFHEvS/uwYA3gCXT83DDkopTnlOYacX3rjgDAPDo20dxuK0/1c2k01BVIPTxxx9j0aJFWLRoEQBg7dq1WLRoEe6//34AQGtrazgoAoDp06fjtddew6ZNm7Bw4UI8/PDD+O1vf4sVK1bI0n4aX7fLg++9sh8AsOaimagpyQLmXgHcsQ+48VXg6v8FbnwVhjv34nNX3woAePKfddjd1Cdjq0npRFHEf7+0F/1Dfiwsz8FXzqsa87kPXnEG8tItONTWj8feOZbCVpIaPf9RE9471g2b2YCfXD0yyxjtioVlWD6nOFyH5g8wyFYS1e4jlCrcRyh1vv7cTry2pxU1JZl4Zc25sJjGj9PveH4XXt7dgplFGXj1m+fCauJ+QnSql3adwJ1//AQWowGvfvNczCrOHPf5f/ukBd/4v10wGQS8suZczC3j3z2dqqVvEJ/b8C5cHj/uu2zOuAE2ALQ7h/DZR7bCOeTHvZ+vwdcuqE5RS/Ur1vu3qjJCpF3b63vw2p5WGA0CfnbtwtMGQQDwwBfOQEGGBUc7XPjDB42nfT7pj9cfxA9ePQgA+ObFM04bBAHA5QtKseKMYviDIn4wPJ1GdLKfvXkYLo8fZ03Lwc2fnn7a5xdn2fDdy+cCADZsOoJetzfZTaQYMRAiRXhp1wkAwNVnTcG8KdkxvSY33YJvXjxz+PXNSWsbqdc/j3ai2+1FYaYVX41xBC4IAu67LHTD2lbXjZa+wWQ2kVRowOvH3/e1AQDuu3wujGNMiZ3smsVTMac0Cx5/EK/va01mEykODIRIdh5/AK/vDV1UVi6aEtdrL19QBpNBwN5mB2o7XcloHqnYX3eHdif/woKyuJYtl+fZcXZlHkQReHXPeDuckx5tOtCOQV8AFfl2LCrPifl1giDgi4vKAET6JsmPgRDJ7t0jXXAM+lCcZcWS6flxvTYv3YLzZhYAAF7hhYWiuD1+bDoQ2kfsyjPL4n79FWfyhkWjk641Vy4si/vojC8sLIMghMoBmG1UBgZCJLu/7g5Na31hQVnMKeZoV54ZyiK98kkLWPtPkn8cDI3aK/PtWDA1tunWaJfOL4XJIGB/ixPHOrjkmUJ63V5sPdIJIBIsx6M0Ow1nV+YBCBXmk/wYCJGs3B4//nFQGrXHNy0m+ezcYtjMBtR3ubGX29jTMCmTc8WZUyZ04GVeugXnzyoEwGwjRby+rxX+oIgzyrIwo+j0xfejka51zDYqAwMhktWmA+0Y8gUxvSAd86ZMbJlyutWE5XNCZ47xhkVAaNT+rjRqXxj/qF0ivZbZRpJI15jJ9KvPzyuBySDgQCuzjUrAQIhkJU2LXTGBufZo0gjrb3taEAjyhqV3I0ftp+4iHSsp23i8ewB7TjDbqHctfYPYfrwHQKjWZ6Jy0y24gNlGxWAgRLLpdnnw7tEuABOba492waxCZKeZ0e704MP67kQ0j1RMmnKYSJF0tHSrCZ+dWzLie5J+vbqnBaIInD09D2U5ox/cHatwMT6zjbJjIESyeX1fGwJBEfOnZKN6lLOf4mExGXDp/NANiyMsfWvpG8T2+h4IwuRG7ZIrh78Hs42UqAAbCGUb08xGNHQP4BNmG2XFQIhk88rwtFgiLioAcMXC0PTY63tb4fHzWHq9klbinF2Zh9LsyY3aAeD84WxjZ78HH9Qx26hXxzr6sb/FCZNBwKXzSk//gtOwW0z43Bmh2kapRIDkwUCIZHGidwAfHe+FIIQ2RUyEs6fnoSTLBueQH1sPdybke5L6REbtE1uFeLJQtrF0+HvzhqVXUqb5glmFyE23JOR7SoPAv33SymyjjBgIkSz+9kloe/lzpuejJNuWkO9pNAj4wsLhGxb359ClYx39ONDqhNko4PPzShL2faUb1t/3tTHbqEOiKIavKZOtZ4x23sxC5NrN6HJ5sK2W2Ua5MBAiWby+NxQIJfKiAkSmxzYfbMeQjzcsvXltT+iolvNnJm7UDoSm2UqybOgf8uOfR7oS9n1JHfa3ONHQPYA0sxGfnVucsO9rNhrw+eFs42t7OXiTCwMhSrm+AS/2tYSKAy+eU5TQ7z1vShaKs6wY8gWxs7E3od+blO+92lCQcvGcxN2sAMBgEPCZ4b76PkfuuvP+cL9aVp0Pu8WU0O/92eG+yn4lHwZClHLb63sgikB1YTqKMhMzLSYRBAHnVIXOK/ugrieh35uUbcgXwO7GPgDA0ur4zqyLRaRf8YalN9K1JBn96lOVuTAaBDR0D/DsMZkwEKKUky4q0o0l0XjD0qedjb3wBoIoybKhMt+e8O9/TlXofKiDbU70DXgT/v1JmfyBID6qT941K9NmxrwpobPwuAeaPBgIUcptGw5QkjG6AoClwxer3Y19rBPSkQ+GpxbOqcqb1C7lYynKtKG6MB2iGMpqkj4caHWi3+NHls2EOaUTOwbodKQgmwXT8mAgRCnVN+DFoTYnAGDJ9OQEQhX5dpRk2eANBLGzgXVCepHsTGP0997GbKNuSMHJ2dPzYTQkPsAGwOl8mTEQopT6cLg+aEZRBgozrUl5j1CdUGiExekxfRj0BrC7qQ9A8jKN0d+bNyz9kK4h0jUlGf6tMg9Gg4DGngE0s04o5RgIUUptq03+RSX0/Tly1xOpPqg024ZpeYmvD5JIWcxDrBPSBX8giI+Oh7LKycw0ZlhNmD9cJ/QBp8dSjoEQpZQ0ulpaVZDU95FG7rub+jDoZZ2Q1kVG7flJqQ+SFGZaMaMoA6IYym6Stu1vccLl8SM7zYy5SaoPknCRh3wYCFHK9Lq9ONTWDwBYkuSM0LQ8O0qzbfAFRO4npAOpmL6QsLBVP6SM8tnT82BIUn2QJDydz5VjKcdAiFJGWho6sygDBRnJqQ+SRO8nxBuWto2oD0pypjH6PThy177oTGOySXVCTT2DONE7kPT3owgGQpQyqVjVE40F0/qwo6EXvoCIsmwbyvMmf9r86UjZzENt/eh1s05Iq0buH5T8TGO61YQFU4frhFiMn1IMhChlPkjy/kEnk0bun5zow4DXn5L3pNRLVX2QpCDDiplFGQC4AZ6W7W12wO0NIDvNjDklya0PkrBOSB4MhCglul2ecH3Q2dOTP7oCgPK8NJQN1wnt4H5CmiXVcZyTogAb4DJ6PZB+t0tSUB8kWcrpfFkwEKKUkHbinVWc/Pogychzx3hh0aIBrx+fhOuDUhcIsV9pXyrrgySLK3JhMgho7htEUw/rhFKFgRClhBwXlej348hdm3Y09MIfFDElJw1Tc5NfHyRZMj1SJ9TDOiHN8QWC+Ph4amsagZPrhBhkpwoDIUqJ8OnNKQ6EpCmMT5pYJ6RFqa4PkuRnWDG7OBMA8CFvWJoj1Qfl2M2oKclM6Xtz2jX1GAhR0nW7PDjcntr6IMnU3DRMyUmDPyji4+OsE9KaVO1UPhquStQu6XeayvogSfS0qyiKKX1vvWIgREkn7cA7uzgT+SmqD5IIghBe7swblrYMeP3Yc8IBIPVTrtHvyZG79qR6q49o0XVCJ3p57lgqMBCipJOKWRdX5sry/p+qCAVCn5zok+X9KTn2tzjhD4oozrKiPInni41F6s9HOvrh9nDaVStEUQxfs6RrRyrZLSacURZari9tFErJxUCIkk4atS8cLgJMNan4cM8JB1PNGiL1qwVTc2R5/6JMG0qzbRDFUFBG2tDUMwjHoA8WowGzU1wfJJH69N5mhyzvrzcMhCipgkER+4b/mOdPyZGlDbOKM2ExGdA/5EdDN5ekasXe4QzfginyBNgAwieG72G2UTP2NPcBAOaUhq4bcpg/lf0qlRgIUVId73aj3+OH1WTAzOIMWdpgMRkwZ/jk6D0cYWmG9LucL1OmEYhkGzly1469J5TTr/Y1OxEMMoudbAyEKKmkG8TcsiyYjfJ1NylrsJcjLE3oH/KhrtMNIJKVkcN8aQrjBAMhrQhPucqUwQaAGYUZsJkNcHn8qO92y9YOvWAgREkVuajId7MColPNvGFpwb7mUE3OlJy0lK9EjCYFYXVdbjiHfLK1gxJjxFS+jBkhk9GAM8qkwRuvWcnGQIiSKpJmzpG1HZFUs4OpZg3YO1zHsUDGmxUA5KVbwjta7+P0mOqNmMovkmcqXxKpP2O/SjYGQpQ0gaCIfS3Syh55b1hSqtntDaCui6lmtdujgDoOSbhOiDcs1ZOm8s8oy4JJxql8ILr+rE/WdugBAyFKmrpOFwa8AaSZjagulHd0NSLVzAuL6kk3LDnrOCTSakgW4quf3FsyRIsumA4wi51UDIQoaaSLyrwpWTCmeJv60TDVrA2OAV94GwQ5C6UlzAhpR3gqXwH9anpBBtItRgz6AqjtdMndHE1jIERJs1fm/YNOxhuWNkj9qiLfjmy7WebWAPOGM42NPQPoG+BJ9GqlpKl8ADAaBJzBwVtKMBCipJE2A1PCRQWItGN/ixP+QFDm1tBESRveKWHUDgDZdjMq80NHfHA/IfWSpvLtFiOqZJ7Kl3Dbj9RgIERJ4Q8Ew8cOKKGgFTg51cyCabXae0I5o3aJtCqSI3f1Ck/ll2UrYiofiNr2gwF2UqkuEHrsscdQWVkJm82GJUuWYPv27WM+d+PGjRAEYcSXzWZLYWv162iHCx5/EBlWE6bnp8vdHAAnp5r75G0MTVh4xZhCplyB6JE7b1hqtVcB+wedTCraPtDihI9Z7KRRVSD0xz/+EWvXrsUDDzyAnTt3YuHChVixYgU6OjrGfE1WVhZaW1vDXw0NDSlssX7tjSqUNihkdAVE3bA4wlKlbpcHzX2DAEJ9Synm86gN1VPaVD4AVOTZkWkzweMP4mg7C6aTRVWB0COPPIJbb70VN998M+bOnYsnnngCdrsdTz311JivEQQBJSUl4a/i4uIUtli/9oQ3vMuRtR0n4w7T6iZNEVQVpiPTJn+htOSMsiwIAtDcN4gul0fu5lCcfNFT+QqpPQMAg0Hgwb4poJpAyOv1YseOHVi+fHn4MYPBgOXLl2Pbtm1jvs7lcqGiogLl5eW48sorsX///nHfx+PxwOl0jvii+ClpGWq0cKq5lalmNdqrkCNbTpZpM6OqIDQFzOkx9TnaHprKz7SaUKmQqXwJ64SSTzWBUFdXFwKBwCkZneLiYrS1tY36mtmzZ+Opp57CX//6V/z+979HMBjEsmXLcOLEiTHfZ/369cjOzg5/lZeXJ/Tn0AOvP4iDrf0AlJVmBiKpZq8/iCPt/XI3h+K0RyFHtoxmAQumVUvaZHXelGxFTeUDkU1DGWAnj2oCoYlYunQpVq9ejTPPPBMXXHABXnzxRRQWFuLXv/71mK9Zt24dHA5H+KupqSmFLdaGI+398AaCyLKZMC3PLndzRohONfPCoj5KOWNsNOF+xZ3LVWePAlciSqQ2HWpzwuMPyNwabVJNIFRQUACj0Yj29vYRj7e3t6OkpCSm72E2m7Fo0SIcO3ZszOdYrVZkZWWN+KL4RG9TLwjKGl0BTDWrVbtzCO1ODwwCMLdUeX+XC1h/plpKXDEmmZqbhhy7Gb6AiMNtzGIng2oCIYvFgsWLF2Pz5s3hx4LBIDZv3oylS5fG9D0CgQD27t2L0tLSZDWTEBkRK/GiAjDVrFbS72tGUQbSrSaZW3OquWVZMAhAR78H7c4huZtDMfL4AzjYGqoFVcLZdScTBIHHAyWZagIhAFi7di2efPJJPPPMMzh48CBuv/12uN1u3HzzzQCA1atXY926deHnP/TQQ3jrrbdQV1eHnTt34j//8z/R0NCAr3zlK3L9CLqwR6EFrRKmmtVpj8KObDmZ3WLCzKJMALxhqcmRNhd8ARE5djPK89Lkbs6oeDxQcilvWDWOVatWobOzE/fffz/a2tpw5pln4o033ggXUDc2NsJgiMR2vb29uPXWW9HW1obc3FwsXrwY77//PubOnSvXj6B50UXI8xQaCEmp5r4BH462uxTbThrpQIsUCClvWkwyb0o2Drf3Y1+zA5+dy6061GB/S2RHaSVO5QOR+jPpLDRKLFUFQgCwZs0arFmzZtR/27Jly4j/3rBhAzZs2JCCVpGkris0usq0mjA1V5mjK0EQMLs4Ex/W9+BQWz8DIZWQViLWKLA+SDKnNJQRYi2Hehwa/l3VlGTK3JKx1ZSE+vzRDhf8gSBMRlVN5igeP01KKOkGMKskU7GjKyBy0Tvcxn2i1KB/yBfeUVrJN6zZUr/i1gyqcWj4GjBbwf1qWp4daWYjvP4gjncPyN0czWEgRAmlhtEVEMkqHOLIXRWk6daSLBty7BaZWzM2aeR+vNuNQS/rz5ROFCMrseYoONNoMAiYVcJsY7IwEKKEOqySQGg2LyqqIgWsSh61A0BhphX56RaIInC0g31L6Tr7Pegd8MEghFYjKllNMbPYycJAiBLqUKuUZlbu6AoAZg1fVDr6Pehxe2VuDZ3OoVZ1BNhAJFiT2kzKdXA4wK4sSIfNbJS5NeOT+tVBDt4SjoEQJYxj0IcWR2j/lNnFyr5hZVgju14f4ghL8Q6rJCMERAVCvGEpnpRdUUOAXcMsdtIwEKKEkeo4yrJtyLYr52TwsXB6TB1EUQwHqzUKzzQCwJzhNh5uZ4CtdJGaRuX3K+l61dgzALfHL3NrtIWBECWMWuo4JBxhqUObcwjOIT+MBgHVRco6GXw0DLDVQ02ZxvwMKwozrQDAA6MTjIEQJYxa6oMk0iiQc+7KJtXaVBWkw2pSdh0HEKo/EwSgy+VFZ79H7ubQGPyBII52uACoY2oMiLST066JxUCIEkYtK8Yk0ijwaHs/gkFR5tbQWNSWaUyzGFExXH/GrJByHe92w+sPwm4xojzXLndzYjK7mNnGZGAgRAkhimJ4E7maUnXcsCrz7bCYDBjwBtDUy03KlEoqaFXyPi8nk7KNLMRXLinAnlWcCYNBuZu/Rovsf8Z+lUgMhCghWhxD6B/yw2QQUFWg7P04JCajATOH9w5hqlm5whkhha9EjMY6IeVTWwYbGFnXKIrMYicKAyFKCKk+qLowAxaTerpVeOTOPV8UyRcIorYzVMehlqkxgLUcaiCdXaemfjWjKAMGAegd8KGD9WcJo547Fima2uo4JOERFpc6K1Jdpxu+gIgMBR/iOxrp7+BIez8CrD9TJOlvXk3XLJvZiMqC0MpJBtmJw0CIEiKcZlZJfZCEm98pW/SBmEo+xPdkFfnpsJkN8PiDaOh2y90cOonL40dTj3SIr3pqz4CofapYJ5QwDIQoIdQ43w5EArfjXW4M+XhIptKoaZ+XaEaDED7GhXVCyiPtw1OUaUVeunIP8R0NB2+Jx0CIJs3rj67jUNfoqjAjdCEMisDRdpfczaGTHFJpgA1Eiru5T5XyHFJhfZCEZ9klHgMhmrTaThf8QRGZNhPKsm1yNycugiCEb1hckqo8h1W4YkwSWTnGfqU0ajpj7GRSm491uuAPBGVujTYwEKJJi54WU1Mdh4RLnZXJOeRDc5866ziAyL5H7FfKo6Yzxk5WnmuH3WKE1x/EcdafJQQDIZo0ta4Yk8wplVaO8YalJEeG+1WpSg7xPZn099DQM4ABLw/JVIrozV/VeM0yRNWfsU4oMRgI0aRFVvaob3QFRNp9kHPuinJQ5QF2QYYVBRkWiCJwhPVnitHu9KBvwAejQcCMInVs/nqyGtYJJRQDIZo0ta4Yk8wqzhg+JNODbhc3KVOKw23q2+flZKwTUh5p4FaZb4fNrPxDfEfDlWOJxUCIJsUx4EOrYwiAem9YdouJh2QqkPS7mKPSTCMQfeYY+5VSRPY8U3+/4kawicFAiCZFmmufkpOGLJv66jgkHGEpiyiKqq89A1iIr0ThQEiFKxElUva9qWcQLg/rzyaLgRBNyiENTF8AkTohLqFXhuhDfKsL1VnHAfDMMSVSe+0ZAOSmW1CUaQXAIDsRGAjRpEibEM5S8egKiOxTc7SDRa1KcHQ40zi9IF1Vh/iebGZRJgQB6HF7WX+mAIGgqMpDfEcjtf9YBwOhyVLvFYYU4ejwH+FMla6+kMwsDrX/WLsLoshDMuV2bDgglX4vapVmMYYPi2WQLb+mngF4/UFYTQZMzbXL3ZxJkVa8cUf8yWMgRJNyrCO0oZdal6FKKvPTYTQI6Pf40dHPkbvcpEBohoqnxSTSz3CMgZDspN9BVWEGjAb1bf4aTbrmHutkv5osBkI0YX0DXnQNp/urVR4IWUyG8Mox3rDkJ/0O1N6vgKgbFvuV7KSgQe0DN4ABdiIxEKIJk/4Ay7JtyLCaZG7N5FWHU82cc5eTKIrhaSQt3LBmFoVqOWo5cpedNI2k9ql8IPK30dw3yJ3LJ4mBEE2YlkbtQOTiyFSzvLpcXjgGfRAEqHrFmKSatRyKoaWMUH6GFXnpoZ3L6zp55thkMBCiCTumoVE7wCkMpZA+//Jc9e78G03qV23OIfQP+WRujX6JooharV2zOD2WEAyEaMK0NLoCogMhjq7kpLV+lZ1mRuHwni+1HLnLpt3pgcvjh9EgoDI/Xe7mJEQ1B28JwUCIJiwy367u/Tgk0jRMl8uDvgGvzK3RL62N2oGoaVfesGQjbfVRkW9X9d5U0ZjFTgxt9AZKuQGvH819gwC0c8NKt5owJSe05wsvLPKRblha6VdA1J4v3PxONlrakkEyk/0qIRgI0YRIxXl56RbkpVtkbk3iMNUsP63VngGRn6WW/Uo2Wu5XDd0D8AWCMrdGvRgI0YRocdQOREaL3AVYHs4hH9qdob2ptNS3OIUhv6Ma2a08Wmm2DekWI/xBEQ3drD+bKAZCNCFaHF0BUUdt8IYlC+lzL86yIstmlrk1iSP9nTT2DGDIF5C5NfoUrj0r1EZNIwAIgsCjNhKAgRBNiBbn2wGO3OWm1QC7MMOKLJsJQRGo7+LIPdV63F50u0MLIKqLtLFiTMLp/MljIEQTosU0MxAJ7Jr7BuH2cLfWVKvVaIAtCAJmFkunhfOGlWrSZz4lJw12i/p3wY/GM8cmj4EQxc3rD6KhewCA9kbuuekW5A8Xf3O31tTTakYI4OZ3cmK/ovEwEKK4NXS7EQiKyLCaUJJlk7s5CRcZYXFJaqpFzhjTTh2HhNOu8tFyICRlGms7XQgGRZlbo04MhChu0s2quigDgiDI3JrEY/GhPIZ8ATT1ajPTCAAzWIgvG2mVqxYOWz1ZeW4aLEYDhnzB8N5uFB8GQhQ3rRZKSzhyl0ddpxuiGDqSoiBDO3tTSaS/l/ouN/zc8yWltLhbucRkNGB6QagAnNesiWEgRHHTcpoZiBwZwuLD1IoetWsx0zglJw1pZiO8gSAaewbkbo5uuDx+tDiGAGj3miVlG7nD9MQwEKK4hVeMafWiErVbq9fPkXuqaHnUDgAGgxBeus2Re+pI/aogw4ocu/YyjQALpidLdYHQY489hsrKSthsNixZsgTbt28f9/kvvPACampqYLPZMH/+fLz++uspaqk2BYIi6jR2OvjJirOsyLCaEAiKOM7dWlNGa6fOjyZ8w2K2MWUiGWxt7R8UjdP5k6OqQOiPf/wj1q5diwceeAA7d+7EwoULsWLFCnR0dIz6/Pfffx/XX389brnlFuzatQsrV67EypUrsW/fvhS3XDuaewfh8QdhMRlQnmeXuzlJIQgCNymTwbGoInyt4g0r9XQRYEf1K1HkyrF4qSoQeuSRR3Drrbfi5ptvxty5c/HEE0/AbrfjqaeeGvX5v/jFL3DJJZfg7rvvxpw5c/D9738fZ511Fv7nf/4nxS3XDmlJeVVBOowG7dVxSGbyhpVS/kAwvOOyVqdcAQZCctD64g4AmF6QDoMAOIf86Oz3yN0c1VFNIOT1erFjxw4sX748/JjBYMDy5cuxbdu2UV+zbdu2Ec8HgBUrVoz5fADweDxwOp0jvihCWlKu5dEVELWEnjeslGjoGYAvICLNbERZdprczUkaaX+kWo7cU0YKhKT9drTIZjZi2nCGnkF2/FQTCHV1dSEQCKC4uHjE48XFxWhraxv1NW1tbXE9HwDWr1+P7Ozs8Fd5efnkG68hWl8xJmHxYWpFpsXSYdBwprEi3w6TQYDbG0Dr8EomSh6PPxA+lV3z1ywetTFhqgmEUmXdunVwOBzhr6amJrmbpCh6mG8HIj9fXacLAe7WmnR6mL4AALPRgEru+ZIyx7sGEBSBTKsJRZlWuZuTVKxrnDjVBEIFBQUwGo1ob28f8Xh7eztKSkpGfU1JSUlczwcAq9WKrKysEV8UIooijrVLS+e1m2YGgPI8OywmAzz+IE70cs+XZNPD9IVkJqddU0baV2dGsTb3poomXZO5I378VBMIWSwWLF68GJs3bw4/FgwGsXnzZixdunTU1yxdunTE8wFg06ZNYz6fxtfZ70G/xw+DAFQWaHPFmMRoEFA1PHKvZao56aTPuLpQu0ucJVK2kf0q+Wo7QtNi1RrPNALsV5OhmkAIANauXYsnn3wSzzzzDA4ePIjbb78dbrcbN998MwBg9erVWLduXfj53/rWt/DGG2/g4YcfxqFDh/C9730PH3/8MdasWSPXj6BqtcOnsZfn2WE1GWVuTfJJF0+eQp9coiiGP2M93LCqhoO9Ot6wkq6uSwqw9dOvOvo96B/yydwadTHJ3YB4rFq1Cp2dnbj//vvR1taGM888E2+88Ua4ILqxsREGQyS2W7ZsGZ577jncd999+K//+i/MnDkTL7/8MubNmyfXj6Bq0kVFypRonXRhqWUglFSd/R64hjON0/K1nWkEgKoCaeTOfpVsUoBdpYNMY5bNjIIMK7pcHtR3ubFgao7cTVINVQVCALBmzZoxMzpbtmw55bFrr70W1157bZJbpQ9SmrlKB6MrIDoQ4sg9maQCfL1kGqV+1Tk8cs+0mWVukTaFMo36mXIFQn2ry+VBbaeLgVAcVDU1RvIKZ4T0clEp4NRYKoRH7TrJNGbazCgcXsHEvpU87U4P3N4AjAYB0/L00beqw9Ou7FfxYCBEMYvcsPSVEepyeeDknHvSRKYv9NGvgEjQJw0uKPGkbFB5bhosJn3c6jh4mxh99A6aNI8/EF5GXq3hwwujZdrM4b1HeGFJHj0VtEqqWIifdLVd+inAl0jXZk7nx4eBEMWkoTuyMVlhhrY3JovGFT7Jp6eCVgmnMJJP+pvVU7+SMkLHu90IciPYmDEQophEX1S0vjFZNI7ckys606inG5aUpeDIPXn0OOU6NTcNZqOAIV8QLY5BuZujGgyEKCa1OryoAKzlSDa9ZxrruzhyTxa9bfcBACajARX5zDbGi4EQxURvK3sk3FQxufSaaZyaa4fFGDrCpbmPI/dEG/IFcKI39LnqdvDGbGPMGAhRTCJL53V2UYkaufPw1cTTa6bRaBBQMbx5ZF0Xg+xEa+gegCgCmTYTCjIscjcnpcLT+exXMWMgRKcliiJqO/RXeAiMHLm3cOSecFKNjN4yjQAL8ZMp3K8KtX/Y6sm4EWz8JhQI1dbW4r777sP111+Pjo4OAMDf//537N+/P6GNI2XodnvhHPJDEIDpOrthRY/ceWFJPD0WtEpYiJ884R2ldXa9ArgicSLiDoS2bt2K+fPn48MPP8SLL74IlyvU4T755BM88MADCW8gyU/6g5qSkwabWftHIJyMdULJMeIIBJ3sTRWNhfjJEz7Et0iHAfbwEvpWxxAGvH6ZW6MOcQdC9957L37wgx9g06ZNsFgic6+f+cxn8MEHHyS0caQMdZ36rA+ShKcweMNKqOhMY2W+DgMhBthJI22mqMcp19x0C/LSQ/dm9q3YxB0I7d27F1/84hdPebyoqAhdXV0JaRQpS52OLyoAb1jJwkxj6O+p1TEEt4cj90SJzjTqdvAWzjbymhWLuAOhnJwctLa2nvL4rl27MGXKlIQ0ipRFbyc4n6yKc+5JofebVY49MnKv5w0rYbpcXvQPZxql+j69YSF+fOIOhK677jp85zvfQVtbGwRBQDAYxHvvvYdvf/vbWL16dTLaSDLTc0ErAFQPz7m3OTlyTyS9ZxqByM/OQvzEkW7+U3P1mWkEmMWOV9yB0I9+9CPU1NSgvLwcLpcLc+fOxfnnn49ly5bhvvvuS0YbSUa+QBCNPfo7AiFatt2MfI7cE07vmUaAhfjJEAmw9TlwA1iIHy9TvC+wWCx48skn8d3vfhf79u2Dy+XCokWLMHPmzGS0j2TW2DMAf1CE3WJESZZN7ubIpqowHd1uL2o7XZg3JVvu5miC3jONQHQhPgOhRNHjYasni84IiaKou72U4hV3ICSZNm0apk2blsi2kAJJGylOL9DXEQgnqyrIwEfHe8M7IdPkeP1BNOg80whE37A4ck8Uve5WHm1anh1Gg4ABbwBtziGUZqfJ3SRFizsQ+vKXvzzuvz/11FMTbgwpjzRSrdbxRQWI7HPDG1ZiNPYMIMBM44hC/GBQhMGg38FGonDKFbCYDKjIs6Ouy426TjcDodOIOxDq7e0d8d8+nw/79u1DX18fPvOZzySsYaQMTDOHSPUGrOVIDL0etnqyaXl2mAwCBn2hkXtZDm9Yk+H1B9E0fNiq3gdvVYXpw4GQC5+eUSB3cxQt7kDopZdeOuWxYDCI22+/HdXV1QlpFCkH6zhCog9f5ch98ljQGmI2GjAtauTOQGhyGntChyOnW4woyrTK3RxZVRVmAAc7OJ0fg4QcumowGLB27Vps2LAhEd+OFIRLnEPKTxq50+Qw0xjBncsTJ7o+SM+ZRoCbKsYjYafP19bWwu/nHita0jfgRY/bC4A3LLPRgGnDm7NxemzymGmM4J4viRPpV/q+XgEsxI9H3FNja9euHfHfoiiitbUVr732Gm688caENYzkJ42uSrNtsFsmvMBQM6oKMlDX6UZdlwvnzuSc+2Qw0xjBTRUTJ5xp1PmUKxAJBpv7BjHkC+h2c8lYxH1327Vr14j/NhgMKCwsxMMPP3zaFWWkLpy+GKm6MB3/OMiR+2Qx0zgSM0KJEw6w2a+Qn25Bls0E55Afx7vdqCnJkrtJihV3IPTOO+8kox2kQCxoHUm6uHLkPjnMNI4k9asWxyAGvQGkWThynygO3iIEQUBVYQZ2N/WhtoOB0HgSViNE2sOLykg8DiEx2K9Gyk+3IDvNDFEEjnezb01Ur9uL3gEfgNAGsBS5ZtWzEH9cMQ3HFi1aFHMF/s6dOyfVIFKO+i4WtEaTLq4tDs65T0Y9M40jCIKA6QXp2N3Uh/ouN+aUcuQ+EVIGu4yZxjAe4RKbmHrLypUrk9wMUppAUMTx7uEjEDi6AgDkRc25N3QPYHZJptxNUiUpEOKoPaIqKhCiiQn3K2Yaw6S/Mfar8cUUCD3wwAPJbgcpTEvfILz+ICwmAzd5GyYIAqYXZuCTpj7UdboYCE2QNLXIG1bEdK4cmzRpypUBdoT0WfDw1fGxRohGJaVSK/NDh/dRSDU3KZuUYFBE/XAdTDWnxsKqwrUc7FcTxSnXU0mBkGPQF66folPFHQgFAgH87Gc/w9lnn42SkhLk5eWN+CJtqOfoalRMNU9OiyOUaTQbBUzJZaZRwn41eZwaO5XNbMSU4Yw+C6bHFncg9OCDD+KRRx7BqlWr4HA4sHbtWlx11VUwGAz43ve+l4QmkhwidRwcXUWbXsgb1mRIn1tFfjozjVEqC0K7lvcN+NA7vMcSxS4YFKMyQgyEokVPj9Ho4g6E/vCHP+DJJ5/EXXfdBZPJhOuvvx6//e1vcf/99+ODDz5IRhtJBtz5d3SRiwpHVxMRrg9ivxrBbjGhNNsGgGeOTUSLYxAeKdPImsYRpnM6/7TiDoTa2towf/58AEBGRgYcDgcA4PLLL8drr72W2NaRbHhmz+iki0ovR+4TUs+df8cUXurMkXvcpH41Lc8Ok5Glr9GkflXPfjWmuHvM1KlT0draCgCorq7GW2+9BQD46KOPYLVaE9s6ksWQL4AWxyAAjtxPFj1yr+fmd3FjpnFsrBOaOO55Njb2q9OLOxD64he/iM2bNwMAvvGNb+C73/0uZs6cidWrV/OsMY1o6B6AKAJZNhPy0i1yN0dxwhcWjrDiJhVssvbsVNJnwhtW/MIZbAbYp5BW0dV3uxEMijK3Rpni3n7zxz/+cfj/r1q1ChUVFXj//fcxc+ZMfOELX0ho40ge4f04CjO478Qophek4/3abtZyxGnIF8CJXmYax1LFotYJq+MmnWOakpsGi9EArz+I5r5BlOfZ5W6S4sQdCA0NDcFms4X/+5xzzsE555yT0EaRvKSLSjUvKqPini8T09gTyjRm2kwoyGCm8WThWo7hkbuBq+piFsk08pp1MqNBQEW+HUc7XKjvcjMQGkXcU2NFRUW48cYbsWnTJgSDwWS0iWTGIxDGx5H7xERPXzDTeKopOWkwGwV4/cFwjR6dnscfyTSyRmh0rBMaX9yB0DPPPIOBgQFceeWVmDJlCu644w58/PHHyWgbyYQbk41Puqgc55x7XBhgj89kNGDa8GidN6zYNQ7XNGZamWkcC/c/G9+EiqVfeOEFtLe340c/+hEOHDiAc845B7NmzcJDDz2UjDZSivHMnvFNzQ2N3Id8QbQ6h+RujmpE+hVH7WORPhtmG2NXG3V2HTONo5Oy2DzLbnQT3nAhMzMTN998M9566y3s2bMH6enpePDBBxPZNpJBr9sbPpOGgdDoRozcecOKGfcQOr1qjtzjxkzj6bGucXwTDoSGhobwpz/9CStXrsRZZ52Fnp4e3H333YlsG8lA2hunNNsGuyXuWnrdiCx15ggrVrxhnR53AY4fC6VPT/psmvsGMeQLyNwa5Yn7Tvfmm2/iueeew8svvwyTyYRrrrkGb731Fs4///xktI9SrJ5HIMSkqjAdOMgbVqwcAz50D+/Ezb41tkhRKwPsWHEzxdPLT7cg02ZC/5AfjT0DmFWcKXeTFGVCNUKDg4N49tln0dbWhl//+tcpCYJ6enpwww03ICsrCzk5Objlllvgco1/sbjwwgshCMKIr6997WtJb6uaSXvjcPpifFw5Fh+pX5Vk2ZBuZaZxLNLN/EQvR+6x4maKpycIQrhv8ZzEU8V9RWpvb0dmZuqjyRtuuAGtra3YtGkTfD4fbr75Ztx222147rnnxn3drbfeOqKI227nHgrj4anzseFy1PhwWiw2BRkWZFpN6Pdw5B6L6ExjJfvWuKoK0vFJUx+z2KOIOxCSIwg6ePAg3njjDXz00Uf41Kc+BQD45S9/iUsvvRQ/+9nPUFZWNuZr7XY7SkpKUtVU1ePoKjbSctQTvQPw+AOwmowyt0jZuCVDbARBwPTCdOw54UBdp5uB0GlINY1FmVZkMNM4Lh4NNDZVHNO7bds25OTkhIMgAFi+fDkMBgM+/PDDcV/7hz/8AQUFBZg3bx7WrVuHgYGBcZ/v8XjgdDpHfOlFMCjieDdH7rEozAhdeIMi0NQzfp8iHrYaD2YbY1fPqfyYsV+NTRUhdFtbG4qKikY8ZjKZkJeXh7a2tjFf9x//8R+oqKhAWVkZ9uzZg+985zs4fPgwXnzxxTFfs379et1uA9DqHMKQLwizUcDU3DS5m6NooTn30Mi9ttONGUUcuY8nnGnkDeu0qgpYyxGruk5O5cdK+tvj1NipZM0I3XvvvacUM5/8dejQoQl//9tuuw0rVqzA/PnzccMNN+DZZ5/FSy+9hNra2jFfs27dOjgcjvBXU1PThN9fbaSU6bQ8O0xGVSQLZcURVmyCQRHHWXsWM+4CHDtmGmNXmR/6jHrcXvQNeGVujbJMOCN07Ngx1NbW4vzzz0daWhpEUYx7V8+77roLN91007jPqaqqQklJCTo6OkY87vf70dPTE1f9z5IlS8Jtr66uHvU5VqsVVqs15u+pJZH9OHizigXn3GPT3j+EQV8AJgMzjbGoYoAdM273Ebt0qwklWTa0OYdQ3+XGomk8jkQSdyDU3d2NVatW4e2334YgCDh69Ciqqqpwyy23IDc3Fw8//HDM36uwsBCFhYWnfd7SpUvR19eHHTt2YPHixQCAt99+G8FgMBzcxGL37t0AgNLS0phfoyd13Pk3LswIxSY602hmpvG0pNVP3W4vHAM+ZNvNMrdImURRZBF+nKYXpEcFQrlyN0cx4r4q3XnnnTCZTGhsbByxFH3VqlV44403Eto4yZw5c3DJJZfg1ltvxfbt2/Hee+9hzZo1uO6668Irxpqbm1FTU4Pt27cDAGpra/H9738fO3bswPHjx/HKK69g9erVOP/887FgwYKktFPtuGIsPtXSvhzc/G5ctQyw45JhNaE4K5SVZt8aW5szlGk0GoTwkTc0vnCdELPYI8QdCL311lv4yU9+gqlTp454fObMmWhoaEhYw072hz/8ATU1Nbj44otx6aWX4txzz8VvfvOb8L/7fD4cPnw4vCrMYrHgH//4Bz73uc+hpqYGd911F66++mr87W9/S1ob1Y57vcRHGrl3ubxwDPpkbo1ycfoifsw2nh4zjfFjvxpd3FNjbrd71E0Je3p6klpbk5eXN+7miZWVlRBFMfzf5eXl2Lp1a9LaozUefwAnekNBJNPMscmwmlCUaUVHvwfHu9xYWJ4jd5MUibVn8ZtekIEP6np4wxpHHQducePKsdHFHUafd955ePbZZ8P/LQgCgsEgfvrTn+Kiiy5KaOModRq7BxAUQzf3wgx9FotPROSQTE5hjIU3rPjxCJfTq2OmMW7Rh0UHg+Jpnq0fcWeEfvrTn+Liiy/Gxx9/DK/Xi3vuuQf79+9HT08P3nvvvWS0kVIgulA63tV/elZVmIEP63u4cmwMXn8wvOFkNTONMePI/fS4mWL8ynPTYDIIGPIF0eYcQlkOV3ECE8gIzZs3D0eOHMG5556LK6+8Em63G1dddRV27do15pJ0Uj6OriZGGrnX8oY1qsYeN4IikG4xojCTmcZYRZ9Cz5H76JhpjJ/JaMC0/FBpC7ONERPaRyg7Oxv//d//nei2kIzCoyvWccRFGo0yIzS6yI7SGcw0xqE8z86R+zhGZhp5zYpHVUEG6jrdqO9y4dyZBXI3RxEmFAj19fVh+/bt6OjoQDAYHPFvq1evTkjDKLXCGSGmmeMSvQojGBRhMPBmH42j9okxGw2YlmdHXZcbdZ1uBkInic40FjHTGJeqwnTgIFDLwVtY3IHQ3/72N9xwww1wuVzIysoaMcoTBIGBkErVc6v6CZFG7oO+ANr7h1CazRtWtHqeMTZhVYXpqOviyH000QM3Zhrjw53LTxV3jdBdd92FL3/5y3C5XOjr60Nvb2/4q6enJxltpCRzDPjQ7Q6dPcORe3ykkTvAOffR1IWXzrNfxUv6zDhyP1Udz66bMK50PVXcgVBzczO++c1vjrqXEKmT9AdRkmVDunXCx8/pFlf4jE0adbKOI35VhdJSZ/ark9VzF/wJk/rVid5BePwBmVujDHEHQitWrMDHH3+cjLaQTLhibHLCI6xOjrCiOQZ96HKFMo2V7Ftx48h9bHVcOj9hBRkWZFpNEEWgoXtA7uYoQtzD/8suuwx33303Dhw4gPnz58NsHnkg4BVXXJGwxlFq1PMsqEnhyH100udRnGVFBjONcZP+HqWRu9VklLlFyhGpaWSmMV6CIKCqMB2fnHCgrtONWcWZcjdJdnFfnW699VYAwEMPPXTKvwmCgECAqTa1YR3H5EznLsCjkjJk7FcTU5gRCiBdHj8augd4wxoWnWnkKteJmV4wHAgx2whgAlNjwWBwzC8GQeok3cBZxzExkZH7AOfco0TvIUTxk0buAKddo0mfRVEmM40TJf1NcvAWwiN7dS4YFDk1NkmFGVZkWk0IiqEz2yiEWzJMXvjMMU67hvF6NXnhjWDZrwDEODX26KOP4rbbboPNZsOjjz467nO/+c1vJqRhlBotjkF4/EGYjQKmcNO2CREEAdML07HnhAO1nW7M5BQGAKC2kwWtkyUtD+fIPSKyuIOZxoniAo+RYgqENmzYgBtuuAE2mw0bNmwY83mCIDAQUhlpRFCRnw6TkQnCiaoqCAVCHGGFBIMijnezoHWyOHI/VWRLBgbYEyUFQr0DPvS6vchNt8jcInnFFAjV19eP+v9J/bh0PjEiI3eOsACg1TmEIV8o0zg1l5nGieLI/VTMNE6e3WJCabYNrY4h1HW5sVjngRBTADrH+fbE4Mh9JGnDu2l5dmYaJ+HkkbveRWcaOTU2ObxmRcSUEVq7dm3M3/CRRx6ZcGMo9cKjK2aEJmU6i1pHiGzJwJvVZKRbTSjJsqHNyZE7EMk0mgwCyplpnJTpBel471g3s42IMRDatWtXTN+Mh9+pTyQjxBvWZEiBUI/bi74BL3Ls+r5hRbZkYIA9WVWF6WhzDqG+y43FFblyN0dW4UxjPjONkyXV7jEjFGMg9M477yS7HSSDIV8AzX2DAFgjNFknj9zPmqbzQKiLtWeJMr0gHe/XcuQORB2twUzjpE0v5EawkkmF1E1NTWhqakpUWyjFGroHIIpAls2EfJ2n3BMhPOfOCwvqw2dB8YY1WTzCJSKySScD7MmqljJC3W4Eg6LMrZFX3IGQ3+/Hd7/7XWRnZ6OyshKVlZXIzs7GfffdB5/Pl4w2UpKEj0AozOC0ZgLwkMyQIV8AJ3qZaUyUKh7hElbHTToTZkpuGixGA7z+YHhmQK/i3p/8G9/4Bl588UX89Kc/xdKlSwEA27Ztw/e+9z10d3fj8ccfT3gjKTmki0o1LyoJwW3rQ6RMY6bNhIIMZhonK5xp7HYjEBRhNOh30FLXyUxjohgNAiry7Tja4UJdlxvleXa5mySbuAOh5557Ds8//zw+//nPhx9bsGABysvLcf311zMQUhHuIZRY0ihV71MY4WmxgnRmGhNgSk4azEYBXn8QLX2Dur1hsaYx8aYXpONohwv1nS5cMKtQ7ubIJu6pMavVisrKylMenz59OiwWjv7UpI51HAkVvS+Hnufca3nYakKZjAZU5HN7BmYaEy+cxdZxvwImEAitWbMG3//+9+HxeMKPeTwe/PCHP8SaNWsS2jhKrnqu7EkoaeTu8QfR4tDvnDv7VeJJn2W9jleOMdOYeMxih8Q9NbZr1y5s3rwZU6dOxcKFCwEAn3zyCbxeLy6++GJcddVV4ee++OKLiWspJVRov5tQcTtvWIkhjdyPdbhQ1+nG1Fx9TmHU8QiEhJM+Sz2P3JlpTLwqLqEHMIFAKCcnB1dfffWIx8rLyxPWIEoNaXQ1JScNaRajzK3RjqqCUCBU3+XG+Tqdcw9v0sm9XhKmmpvfMdOYBFJQ2dw3iCFfADazPu8FcQdCTz/9dDLaQSlWy0LppIhsUqbPKYxetxe9w5nGygJ9ZsSSgZvfMdOYDLl2M7LTzHAM+lDf5cac0iy5mySLuGuEBgcHMTAwEP7vhoYG/PznP8dbb72V0IZRcvGw1eSQRu56ncKQfu6ybBvslrjHWTQGqZZDGrnrETONiScIAg9fxQQCoSuvvBLPPvssAKCvrw9nn302Hn74YVx55ZVcOq8i4c0UmRFKKL2P3CObdLJfJVJeugVZtlBgqccbFjONyRPeCFanWWxgAoHQzp07cd555wEA/vznP6OkpAQNDQ149tln8eijjya8gZQckUMxObpKJOnzbHEMYtCrv5F7eJNO9quEEgQB1UX63bCzLqqmkZnGxKrmRrDxB0IDAwPIzMwEALz11lu46qqrYDAYcM4556ChoSHhDaTE8weCON49fMMq4g0rkfLSLci1myGK+hy513aEblgMhBJP+kxrdThyr+3gVH6y6LlfSeIOhGbMmIGXX34ZTU1NePPNN/G5z30OANDR0YGsLH0WWqlNY88AfAERaWYjSrNscjdHc6QLyzEdXlikn5mBUOLp+YZVy36VNDOKQsFlbacboqjPjWDjDoTuv/9+fPvb30ZlZSWWLFkSPm/srbfewqJFixLeQEq82qgTnA06PrcoWcI3rA593bB8gSAau0MLKaqLOHJPtOpC6Yalr34FRAVCzGAn3LS8dBgNAlweP9qdntO/QIPinmy95pprcO6556K1tTW8oSIAXHzxxfjiF7+Y0MZRcnB0lVzVRfq8YTV0D8AfFGG3GFHCTGPCSUFAbUfoCBc9DWJqwzWNDLATzWIyoCLPjrouN2o7XSjJ1t/fbtwZIQAoKSnBokWLYDBEXn722WejpqYmYQ2j5GEdR3JFpjD0VSMUHWDzCITEm5Znh8kgYNAXQJtzSO7mpIzHH0BjTyjTOIPXrKSo0vG0KzDBQIjULZJm5ugqGSKrMFy6Onw1EgixXyWD2WhARX5o6biebliN3QMIBEVkWk0ozLTK3RxNCmexdTadL2EgpDOiKEalmTm6SoapuWmwGA3w+INo7tPP4avSyh72q+TRY/2ZFPRVFTHTmCx6zWJLGAjpTLfbC8egD4LAzRSTxWQ0hDd909PInQWtyReuE9LRDYv1Qcmn5xWJAAMh3ZFGklNz03R7wF4q6G2EFco0svYs2fR4w2JNY/JJQWarYwguj1/m1qQeAyGd4bRYaujthtXp8qB/yA+DgHAdCyWeHpfQM8BOvhy7BQUZFgBAvU4Gb9EYCOkMLyqpobfiQ6k+qDzPzkxjEkmre9qdHvQP+WRuTfJF1zTO4OKOpNLzyjEGQjrDQCg19DY1xn6VGtlp5vDKKT2cDdXR74HL44fRIGBaHgOhZNJbFjsaAyGd4RLn1JBGV10uDxwD2h+5s1+ljp6mx6SMakWeHRYTb1fJpKd+dTLV9Kwf/vCHWLZsGex2O3JycmJ6jSiKuP/++1FaWoq0tDQsX74cR48eTW5DFWzIF8CJ3tBybq7sSa4Mqym8u3Jtl/YvLKw9Sx09jdzDS+fZr5IueudyvVFNIOT1enHttdfi9ttvj/k1P/3pT/Hoo4/iiSeewIcffoj09HSsWLECQ0P62ZU1Wn2XG6IYSq/np1vkbo7m6alOKLyyhwF20kX2EtL+DSscYLM+KOmkXbvru9wI6GgjWEBFgdCDDz6IO++8E/Pnz4/p+aIo4uc//znuu+8+XHnllViwYAGeffZZtLS04OWXX05uYxUqevqCG5Mln17qhAa9gfDGkcwIJV9kLyEdBNisPUuZspw0WE0GeANBnOgdkLs5KaWaQChe9fX1aGtrw/Lly8OPZWdnY8mSJdi2bduYr/N4PHA6nSO+tII7/6aWXqYw6oan/nLtZuQx05h0Ui3H8W43/IGgzK1JLu4hlDpGgxDeZFfr16yTaTYQamtrAwAUFxePeLy4uDj8b6NZv349srOzw1/l5eVJbWcqceff1NJLIBRZ3sx+lQpl2WlIMxvhC4ho6tXuES5ujx8tjlAZA4vwU0OvdUKyBkL33nsvBEEY9+vQoUMpbdO6devgcDjCX01NTSl9/2Rimjm1pLqGxu4B+DQ8cueoPbUMBgFVhdqvP6vvCt2MCzIsyLEz05gKehm8ncwk55vfdddduOmmm8Z9TlVV1YS+d0lJCQCgvb0dpaWl4cfb29tx5plnjvk6q9UKq1V7JxwHg2J43xGOrlKjJMsGu8WIAW8ADd0Dms2YMMBOverCDOxvcaK204XlKD79C1SIK8ZST69L6GUNhAoLC1FYWJiU7z19+nSUlJRg8+bN4cDH6XTiww8/jGvlmVa0Oocw6AvAbBRQnscjEFJBEARUF2Zgb7MDtZ0uDQdCXNmTanoYuTPTmHp6WeBxMtXUCDU2NmL37t1obGxEIBDA7t27sXv3brhckQtBTU0NXnrpJQChm9Add9yBH/zgB3jllVewd+9erF69GmVlZVi5cqVMP4V8whuT5afDbFTNr131tD7CCmUaecNKtfDWDBq+YfHU+dSTplx73F70uL0ytyZ1ZM0IxeP+++/HM888E/7vRYsWAQDeeecdXHjhhQCAw4cPw+FwhJ9zzz33wO1247bbbkNfXx/OPfdcvPHGG7DZbCltuxJw5195aH3Pl+a+QXj8QViMBkzNZaYxVaR+dazDBVEUNbkdBhd3pJ7dYsKUnDQ09w2irtOFvPQ8uZuUEqoJhDZu3IiNGzeO+xxRHLkJlCAIeOihh/DQQw8lsWXqwDoOeWh9zxfp55pekA6jQXs3Y6WaXpAOQQAcgz70uL3Iz9BWXWMgKKJuuFh6Bq9ZKVVVmI7mvkHUdrrwqUp9BEKcI9EJ7iEkj+hajpMDdS1gfZA8bGYjpuamAdDm9Fhz7yC8/iCsJgPKctLkbo6u6LFOiIGQTjDNLI+KfDsMAtA/5EenyyN3cxKOmUb5aLlgmplG+UT2EtJevxoLAyEdcA750NEfuglXsUYopWxmY3iVnhbrhLiyRz6R+jPt3bA4cJOP1hd4jIaBkA5IF8qiTCuybGaZW6M/4cJWDV5YmBGSj5b71TEG2LKRarIaewYw5AvI3JrUYCCkA0eHLypa3cdG6aTP/Vh7v8wtSaxetxddrtASW2YaU0/qV0fbtRcI8Zoln8JMKzJtJgTFyO7eWsdASAeODt+AZxVnytwSfZo5fDE/orEb1pHhfjU1Nw3pVtUsQNWMWcWhftXcNwi3xy9zaxJHFMVw35J+RkodQRDC94ojGhu8jYWBkA5IN+CZvKjIQrqoHO3Q1kXlyPConQG2PHLsFhRmhpbNH9VQnVC704P+If+I09AptaQAVIvZxtEwENIBZoTkJaX3u1za2q1V6lcMsOUj3bC0NHKXfpbKfDusJqPMrdGnmUXMCJGG9A/50OIYAgDMKmIgJId0qym854uWLizh6Qv2K9lIN6yjWuxXHLjJJpLFZkaINOBo1IqxbDtXjMklfGHR0A1LSpvzhiWfSC2Hdm5YR8NT+exXcpEyjQ3dbl2sHGMgpHGcFlOGmcXaKpjudnnQ7fZCELiyR06RWg7tBNhHOlgoLbfCTCuy08wIivrYT4iBkMaxUFoZZmlszl3qV1Nz05BmYR2HXKSpsRbHEPqHfDK3ZvJEUcQxZhplF1o5pp+CaQZCGsf5dmXQ2py7tAKO9UHyyrabUaShlWOtjiH0e/wwGQRU5nPFmJxm6mgJPQMhjYvUcTAjJKcZRRkQBKDH7UWXBs4cOxJeMcZASG5aqj+T+tX0gnRYTLw9yWmWRvc/Gw17moY5Bn1oc4ZWjM3gyF1WaRYjynNDZ45pYYR1hAG2Ymip/owF+Mqh1f3PRsNASMOODXfgkiwbstO4YkxuWplzF0WRRfgKoqVdgI9wbyrFkLK9jT0DGPRqe+UYAyENY6G0smhlzr3L5UXvgA+CwEMxlUArATbA3cqVpCDDgly7GaIOVo4xENIwFkori1ZuWFI2aFqenSvGFECa9m5zDsExqN6VY6EVY1w6rxSCIGhm8HY6DIQ0jIXSyhLetr6jH6IoytyaiQtPX7DuTBGy08woybIBiEyHq1Fz3yDc3gDMRgEVXDGmCLM0VH82HgZCGsaVPcoyoygDBgHoG/ChU8UrxyLTFwywlUILBdPSwK2qIANmI29NSqClFYnjYW/TKMeADx39oZvtTO78qwg2sxHT8kIrx9Q8PcZCaeXRQsE0C6WVJzqLrWUMhDRK6rhl2TZk2rhiTCnUPucuiiKL8BVIC/VnR7h0XnGkftXUM4gBr1/m1iQPAyGN4rSYMql9zr2z3wPHoA8GrhhTFLUH2EDUbuUMsBUjP8OK/HQLAOCYBnYuHwsDIY1iobQyqX3OXQrgKvLTYTNzxZhSSNPfHf0e9A14ZW5N/IJBkafOK5QW6s9Oh4GQRjEjpEwzow5fVePKsciKMQbYSpJpM6MsO7RyTI03rBO9gxj0BWAxGlAxXEdHyqD2wVssGAhpFOfblamqMB0GAXAO+cPF7GoSmb5gv1IaNU+PSW2uKkyHiSvGFEXN/SpW7HEa1Bt1sCdH7spiMxvDp2qr8cLCQmnlihRMq7BfMcBWLD0cvspASIOkG+yUnDSkW00yt4ZOptY599CKMd6wlCoycldXvwJY06hk0t96c98g3B5trhxjIKRB3PBO2dQ6597u9KB/yA+jQUBVIXf+VRo1nxbOmkblyk23oCDDCgA4qtGVYwyENIgb3imbWufcpfZW5NthNXHFmNJI0+BdLi963OpZORYIiuGl2bxmKVNk2w91XbNixUBIgzi6Urboze/UtHIsPC3GM8YUKd1qwpScNADqumE19QzA4w/CajKEd14nZVFrFjtWDIQ0RhRFHGoLddbZDIQUKXSWkoB+jx/NfYNyNydmUr+aVcJ+pVQ1w7+bw23quWFJ/WpGUQaMBkHm1tBoZg/3q0Mq6lfxYCCkMa2OIfQN+GAyCFzZo1AWkwEzhrMqB1qcMrcmdlJbzyjLkrklNJa5w7+b/S0OmVsSuwPDbWW/Uq65paHfzYEWp6qy2LFiIKQx0s1qRlEGd/5VsPCFpVUdgZDXHwwX4UptJ+VRW78CIm1lv1Ku2SWZMAhAt9uryv3PToeBkMbwoqIO0shdLRmhYx0u+AIismwmTM1Nk7s5NAapXx1pc8EXCMrcmthIfwNzy7JlbgmNxWY2hs8WVMs1Kx4MhDQmclFhIKRk0jSAWkbu4QC7LAuCwDoOpSrPtSPTaoI3EERtp/KXOve6vWhxDAEA5pSy9kzJ1HbNigcDIY3Z3xqab2cgpGxzhjN2J3oH4Rjwydya05NqTuaWctSuZAaDEO5bahi5Hxy+qVbk25FpM8vcGhqPGuvPYsVASEMcgz409YRWIXFqTNmy08zhKSY1jLCYaVQPNU27cipfPaRBkBr6VbwYCGnIoeGLypScNOTYLTK3hk5HLYWtoijyhqUi0u9ovwpuWFIb2a+UT5q6PN49AJfGjtpgIKQh0s1qDi8qqqCWkfuJ3kH0D/lhNgqYwUN8FW9uVC2H0pc6M9OoHvkZVpRk2QBEBt1awUBIQ3hRURe1ZISk9s0syoTFxEuG0s0szoDJIMAx6AsXIivRkC+AY8MF3bxmqcNcjRZM86qmIfu54Z2qnDElNOd+tL0fHn9A5taMjf1KXawmYzhzp+Rs49F2FwJBEXnplnCmgZRNugbsb1Zuv5oIBkIawQ3v1Kcs24bsNDP8QRFH25W71JmZRvVRw7TrAWmFaym3ZFALtWSx48VASCOkDe8yueGdagiCoIoLy0EWSqtOpF8pd6kzA2z1kX5Xh9v7VbNhZywYCGlE9Koejq7UQ+kj974Bb/hg2Dm8YanGGcO7NCt55RhXjKlPea4dGVYTvP4g6jrdcjcnYVQTCP3whz/EsmXLYLfbkZOTE9NrbrrpJgiCMOLrkksuSW5DZcLRlTopPSMktas8Lw1Z3PBONeZGb9g5qLwNO4NBMZJp5DVLNUIbdg4fGK3gbGO8VBMIeb1eXHvttbj99tvjet0ll1yC1tbW8Nf//d//JamF8tofPsGZO/+qyRlTQjeBgy1OBIPKW+ocPnGeO0qrSrbdjCk5oSnygwoMsht7BuD2BmA1GVBVkC53cygO4WyjhgqmTXI3IFYPPvggAGDjxo1xvc5qtaKkpCQJLVIObninXtWFGbAYDej3+HGidxDT8u1yN2kEZhrVa25ZFpr7BnGgxYlzqvLlbs4I0vWqpiQTJqNqxuME5WexJ0LzPXDLli0oKirC7Nmzcfvtt6O7u3vc53s8HjidzhFfSscN79TLbDRgVsnwUmcFppoZYKuXkg/JZICtXmrasDNWmg6ELrnkEjz77LPYvHkzfvKTn2Dr1q34/Oc/j0Bg7D1b1q9fj+zs7PBXeXl5Cls8MdzwTt3mKvSQzCFfAMc6uOGdWim1XwEMsNVsRlFow86+AR9aFbxhZzxkvWvee++9pxQzn/x16NChCX//6667DldccQXmz5+PlStX4tVXX8VHH32ELVu2jPmadevWweFwhL+ampom/P6pwtGVuik11XyswwV/UESO3YzSbG54pzbS9eBoRz+8fmUtdZZqGnnNUh+bWR0bdsZD1hqhu+66CzfddNO4z6mqqkrY+1VVVaGgoADHjh3DxRdfPOpzrFYrrFZrwt4zFbgMVd3mKnSpc/hmxS0ZVGlKThqybCY4h/w42tGvmIUUXS4P2p0eCAJQU8JrlhrNLc3CobZ+7G9xYvncYrmbM2myBkKFhYUoLCxM2fudOHEC3d3dKC0tTdl7poK0KoRHIKiTtBy11TGEHrcXeekWmVsUcoBHa6iaIAiYW5aFD+p6cKDFqZhASLpeTc9PR7pVNet1KMrcsiy8uKtZkXWNE6GagpLGxkbs3r0bjY2NCAQC2L17N3bv3g2XK3I0QU1NDV566SUAgMvlwt13340PPvgAx48fx+bNm3HllVdixowZWLFihVw/RsJxwzv1y7SZUTG8WkxJS50PcJ8X1ZOCHyVNu0oBNq9X6qW1w1dVEwjdf//9WLRoER544AG4XC4sWrQIixYtwscffxx+zuHDh+FwhCJUo9GIPXv24IorrsCsWbNwyy23YPHixfjnP/+puqmv8XDDO21QWmFraMM76ew6ZWQSKH5K61cAC6W1QPrdNfUoc8POeKkmL7lx48bT7iEUvZQvLS0Nb775ZpJbJb89J4Y3UuTNStXOKMvC3/e14ZMTfXI3BQBQ1+WGy+MPbXhXyA3v1ErasHNfswOBoAijQf5ar/A1ixkh1cqxWzAlJw3NfYPY1+zAp2cUyN2kSVFNRohGt7OhFwCwaFqOvA2hSVk0LRcAsKuxT96GDNvZGOpXC6Zmw8wN71RrZlEmMqwmuL0BHGnvl7s56HF7Ud8VOqNqUXmuzK2hyZDuOdI9SM14hVMxURSxc/jGubiCFxU1W1ieA4MANPcNot0p/94cu4YDobPYr1TNaBBwZnkOgEhwKyepX80oykC2nVP5aibdc5TQryaLgZCKnegdRJfLA7NRwLwpnBpTswyrCbOHlxIrYYS1s6EPAHDWNAZCandWeOTeJ2s7AGDHcN8+ixls1ZOuDbua+hR5TmI8GAipmHRRmVuWDZvZKHNraLKkm8MOmQMh55APRzpC0ygMhNRvkYJG7lIb2K/Ub05pFqwmA/oGfKgbnu5UKwZCKha5qOTI2xBKCOnmIPcNa3djH0QxtBKxMFM7Kyz16qzhWpz6Ljd63F7Z2uEPBPFJU6hQmlOu6mcxGbBgamgmQu5r1mQxEFIxjq60Rbo57Gt2wuMf+zy8ZGO/0pZsuxnVwyv/dsl4wzrU1o9BXwCZNhNmFPJwaC0IT48xECI5DHj94X1eWCitDZX5duSlW+ANBGU9boMF+NqjhMJW6b0XTcuFQQHL+GnypMGbEurPJoOBkErtORHaF6Qky4aynDS5m0MJIAhCVGGrPDesYFCMrBhjRkgzwtOuMt6wdrJQWnOkfnWkox/OIfVurMhASKXCqy8qcuRtCCXUIpnrhI51utA/5Eea2YiakkxZ2kCJJ43cdzf1wR+Q5yR6KdPIAFs7CjOtKM9LgyiGagvVioGQSnHUrk1yj9ylUfuCqdkwcSNFzZhRmIFMmwmDvgAOtaV+Y8XOfg8aewYgCMCZzAhpilIWeUwGr3QqFL2R4iIGQpqysDwbRoOANucQWoYP002lndxIUZMMURsrylHYKvWrmUUZPBNRYyKBUJ+8DZkEBkIq1NA9gB63FxajAfOm8LweLbFbTJhTGpqSkmOEFS6UZoCtOZGC6b6Uv7fUl1mArz3S73RXY69qN1ZkIKRC0kVl3pQsWE3cSFFr5Joecwz4cKzDBYBn12mRnFMYu4b7MjPY2lNTkok0sxH9Q37Udrrkbs6EMBBSocg29byoaJH0e92R4hvWzqbQ+1Xm25GfwY0UtebMaTkQhFBGucvlSdn7+gJBfHKiDwCvWVpkMkY2VpR7V/yJYiCkQuHVF0wza5J0szjQ4sCQL3UbK+5igK1pWTYzZhaFNjJM5fYMB1qc8PiDyE4zo6ogPWXvS6lzlgL2qZoMBkIq4/L4cbgttNkeb1jaVJ6XhoIMC3wBEfuaHSl733ABPgNszZKjsDWykWION1LUKLUXTDMQUpk9TX0IisCUnDSUZNvkbg4lQWhjxdSOsAJBEbub+gCwUFrL5Bi5swBf+6RNMo91uOAYUN/GigyEVCZ6dEXaleqt64929MPl8SPdYsRsbqSoWVKAvedEH3wp2lgxvKM0M42alZ9hRWW+HQCwq0l902MMhFSGhdL6EF0wLYrJX5Iq9auF5TkwcvpCs6oK0pGdZsaQL4iDrck/z67dOYTmvkEYhFDfIu2KrHZlIERJFAiKLJTWiQVTs2EyCOjs96CheyDp7/fxcQbYemAwCOFs8vb6nqS/30fHQ+8xqzgTGVZT0t+P5CPVFm4/nvx+lWgMhFRkd1MvHIM+ZNlMmFfGjRS1zGY2hjcq23qkM6nvFQyK4ff49IyCpL4Xye/c4d9xsvsVAGw53DniPUm7pN/xjoZe9KvsAFYGQiryzqHQReX8WYU8B0oHLqopAgC8c7gjqe+zp9mBHrcXmVYTPlXJjJDWXTg71K8+rOuB2+NP2vsEg2I4EJL6MmnX9IJ0VObb4QuIeO9Yt9zNiQvvpiry9qHQDfEzvKjogvR73lbbjUFv8vYTkvrVebMKYGaArXnVhemYlmeHNxDE+7XJu2Hta3Ggy+VBusWIf6vMS9r7kHKEB2+Hkjt4SzRe9VSizTGEA61OCAJwwaxCuZtDKTCzKANTctLg8Qexra4rae+zZTjjJGUKSNsEQcBFs0PXkLeTeMOSMtjnziyAxcRbjR5cNDuSxU7FIo9EYe9UCelmtXBqDo8/0AlBEHBRTXJvWB39Q9hzIrRp44WzGWDrhTRy35LEG9bbh5nB1pslVXlIMxvR0e/B/pbkr0pMFAZCKiHViVzEUbuuhEdYhzqTcsPaOlzDMX9KNooyuUGnXpxTlQ+b2YBWxxAOt/cn/Pt3uTzYM3y+GDON+mE1GcMLLrYkubYxkRgIqYDXH8S/joamRqQMAenD0up8WEwGNPcNhk+GTyQWs+qTzWzEsurQDSsZ2cZ3j3RCFIG5pVkozmKArSfSPeqdw8lflZgoDIRU4KPjPXB7AyjIsGJeWbbczaEUsltMWFqVDyDxNyxfIIh3h5dQX8RpMd0JT48dSvwNiws79EvKYu9q7EWv2ytza2LDQEgFpAr8C2cX8tBCHZKClEQvo9/R0It+jx/56RYsnJqT0O9Nynfh8KKLHY29CT0fyh8dYDODrTtlOWmoKclEUATePaqOrBADIRV4m/VBuiaN3D8+3gtnAjcqkwLsC2YxwNaj8jw7ZhZlIBAUE3rD2tnYB+eQHzl2M84s575UeiTVhSVzVWIiMRBSuIZuN+o63TAaBJw3i7uz6lFFfjqqCtPhD4rhWrFEkDJMF3L6Qrc+k4R9X6R+dcGsQp5bp1NSv9p6pBOBoPKX0TMQUjipmPVTFbnIspllbg3JJbJ6LDE3rBO9AzjS7oJBAC6YyekLvZJG7luOdCKYoBuW1EeZwdavs6blIMtmQt+AD7ub+uRuzmkxEFI4Fh0SEDVyP5yYG5a0omNxRS6y7Qyw9epTlbnItJrQ4/bik+Hl7pPR0jeIQ2393PhV50xGA84f/v2rYZdpBkIKNugNYFtdaAt8Lm/Wt09V5iLdYkSXKzEblW2RRu3sV7pmNhrCU+6JWO4sZbAXlecgN90y6e9H6hW9y7TSMRBSsPdru+D1BzElJw0zizLkbg7JKHqjsskWIA75AnivdnhfKk5f6N6FCZx2fZvTYjTsgtmFEARgf4sT7c4huZszLgZCCvbCxycAABfPKYIgsOhQ75bPKQYA/GXniUkVIL66pxVDvlCAXVOSmajmkUpdNLsIBgHY2+zAwdaJZxs7nEPYeiQUCF083FdJvwoyrFhUngMA+POOE/I25jQYCCnU8S433jzQBgD4z3MqZG4NKcHlC0uRYzejsWcAb+1vm9D3EEURT75bBwD40tIKBtiEwkwrPj+/FADw5D/rJvx9Nr5/HL6AiE9V5GJuWVaimkcqtnppJQDg6feOw+MPyNuYcTAQUqj//Vc9RDG0md6sYo7aKbTL9JeGg+LfTPCG9e7RLhxu70e6xYjrz56WyOaRit12XhUA4JXdLWhzxD+N4fb48fsPGgAAt55fldC2kXpdtqAUpdk2dLk8+OuuFrmbMyYGQgrU6/bihR1NAIBbz+NFhSK+tLQCFqMBuxr7sKOhJ+7XS9mgVf82DdlpXC1GIQvLc3B2ZR78QREb3z8e9+tf+LgJziE/KvPt4SlcIrPRgJs/XQkglG1MxsHRicBASIF+/0EDhnxBnFGWhaXV+XI3hxSkKNOGLy6aAgD4zbvxZYX2tzjwr2NdMBqE8MWJSCJlcv7wYQNcHn/Mr/MHgvjf9+oBALecV8VNFGmE686ehgyrCUc7XNhyRJlHbjAQUpghXwDPbDsOALjt/CrWcNApvnLedADAWwfaUd/ljvl1v/1n6GZ16fxSlOfZk9I2Uq+La4pQVZCO/iE//vhRU8yve3N/O5p6BpFrN+Oas6YmsYWkRlk2M64/uxxAJCOtNAyEFOblXc3ocnlRlm3DpcMFjETRZhZn4jM1RRBF4H//FduFpaVvEH/7JDRHf+twIEUUzWAQ8JXhqfin/lUPfyB42teIoojfvFsLAPjS0kqkWYxJbSOp082fng6TQcD7td3Y1+yQuzmnYCCkIMGgGF618eVzp8Ns5K+HRifVjr3w8Ql0uzynff7G94/DHxRxTlUeFvCkeRrDVWdNQX66Bc19g3h93+lXJn50vBefnHDAYjJg9VKubqXRleWk4fIFk1+ZmCy80yrIO4c7UNvpRqbVhFX/Vi53c0jBzqnKw/wp2fD4g/j9B43jPtc55MNzH4aecxtX9NA4bGZjeMnzb96tPW1xq1SndvVZU1GQYU1280jFpGzjq3ta0dw3KHNrRmIgJIdgAKj/J7D3z6H/DQbgCwTx+JZQivk/lkxDJg9YpXEIghAubn1223H0DXhD/zBK33rmveNwefyYUZSBC2dxx18a35eWVsBqMmBfszN8ZMZo9rc48I+D7QAidWtEY5k3JRufnpGPQFDEr945pqgVZKoIhI4fP45bbrkF06dPR1paGqqrq/HAAw/A6/WO+7qhoSF8/etfR35+PjIyMnD11Vejvb09Ra0ew4FXgJ/PA565HPjLLcAzlyPwyBn4+aMP4+OGXliMBtzEFT0Ug0vnlWBqbhq63V584X/+hcZ/PX9K33Kur8G+zb8HENorxsAVPXQaeekW/PunQhnpr/5uB57ffmrG8Y19bVj16w8AAJ+dW4zqQh4BRKd32/nVAIA/fNiIu174BINeZWyyqIpA6NChQwgGg/j1r3+N/fv3Y8OGDXjiiSfwX//1X+O+7s4778Tf/vY3vPDCC9i6dStaWlpw1VVXpajVozjwCvCn1YBz5MZSgqsVd/X9ACutO/CrG85CaXaaTA0kNTEZDXhy9adQnpeGuX1bMXXTVyGe1LcyvB143PxzPDyvAdcs5ooeis13Pl+Dz84thjcQxL0v7sU9f/4EQ74A/IEgfvz3Q/ja73fA5fHj7Mo8/OiL8+VuLqnEBbMK8V+X1sAgAC/ubMZVj7+Phu7YV74miyAqKT8Vh//3//4fHn/8cdTVjV545XA4UFhYiOeeew7XXHMNgFBANWfOHGzbtg3nnHNOTO/jdDqRnZ0Nh8OBrKxJbBsfDIRG687Rd9cMAghmlMG0dh9g4MoLip3DNYTAhjOQ4+/CaAkfEQKErDLgjr3sWxSzYFDE41tr8fBbhxEUgXlTspBlM+P92m4AwC3nTse9n6/hog6K2/u1Xfjm/+1Cl8uLLJsJG1admZTz6WK9f6u2BzscDuTl5Y357zt27IDP58Py5cvDj9XU1GDatGnYtm3bmK/zeDxwOp0jvhKi4f0xgyAg9IswuVpCzyOKQ3bnR8gLjB4EAYAAEXA2s29RXAwGAV+/aAae/fIS5KVbsK/Zifdru2G3GPE//7EI3718LoMgmpBl1QV49Rvn4axpOXAO+XHLMx/LuseQKnvxsWPH8Mtf/hJf/epXx3xOW1sbLBYLcnJyRjxeXFyMtraxl4WuX78e2dnZ4a/y8gSt3nLFWJsU6/OIJOxblETnzizAq984F8uq87Fwajb++vVP4/IFZXI3i1SuJNuG529bihuXVsBoEDB/arZsbZE1ELr33nshCMK4X4cOHRrxmubmZlxyySW49tprceuttya8TevWrYPD4Qh/NTXFvsPquDJiTPvF+jwiCfsWJVlZThqeu/Uc/HXNuZjJQ6ApQSwmAx68ch7euvN8nFMl33FSJtneGcBdd92Fm266adznVFVF9j1paWnBRRddhGXLluE3v/nNuK8rKSmB1+tFX1/fiKxQe3s7SkpKxnyd1WqF1ZqE/TAqlgFZZYCzFcBoZVlC6N8rliX+vUnb2LeISMXkXnUoayBUWFiIwsLCmJ7b3NyMiy66CIsXL8bTTz8Ng2H8ZNbixYthNpuxefNmXH311QCAw4cPo7GxEUuXLp102+NmMAKX/CS0agwCRt6whos7Lvkxi1kpfuxbREQTpooaoebmZlx44YWYNm0afvazn6GzsxNtbW0jan2am5tRU1OD7du3AwCys7Nxyy23YO3atXjnnXewY8cO3HzzzVi6dGnMK8YSbu4VwL8/C2SddIZYVlno8blXyNMuUj/2LSKiCZE1IxSrTZs24dixYzh27BimTh25F4q0+t/n8+Hw4cMYGBgI/9uGDRtgMBhw9dVXw+PxYMWKFfjVr36V0rafYu4VQM1loRU8rvZQ3UbFMo7WafLYt4iI4qbafYRSJWH7CBEREVHKaH4fISIiIqLJYiBEREREusVAiIiIiHSLgRARERHpFgMhIiIi0i0GQkRERKRbDISIiIhItxgIERERkW4xECIiIiLdUsURG3KSNt52Op0yt4SIiIhiJd23T3eABgOh0+jv7wcAlJeXy9wSIiIiild/fz+ys7PH/HeeNXYawWAQLS0tyMzMhCAICfu+TqcT5eXlaGpq4hlmMeDnFTt+VrHjZxU7flax42cVu2R+VqIoor+/H2VlZTAYxq4EYkboNAwGwykn3idSVlYW/1DiwM8rdvysYsfPKnb8rGLHzyp2yfqsxssESVgsTURERLrFQIiIiIh0i4GQTKxWKx544AFYrVa5m6IK/Lxix88qdvysYsfPKnb8rGKnhM+KxdJERESkW8wIERERkW4xECIiIiLdYiBEREREusVAiIiIiHSLgVASPfbYY6isrITNZsOSJUuwffv2cZ//wgsvoKamBjabDfPnz8frr7+eopYqQzyf18aNGyEIwogvm82WwtbK491338UXvvAFlJWVQRAEvPzyy6d9zZYtW3DWWWfBarVixowZ2LhxY9LbqQTxflZbtmw5pU8JgoC2trbUNFhG69evx7/9278hMzMTRUVFWLlyJQ4fPnza1+nxmjWRz0qv16vHH38cCxYsCG+WuHTpUvz9738f9zVy9CkGQknyxz/+EWvXrsUDDzyAnTt3YuHChVixYgU6OjpGff7777+P66+/Hrfccgt27dqFlStXYuXKldi3b1+KWy6PeD8vILQTaWtra/iroaEhhS2Wh9vtxsKFC/HYY4/F9Pz6+npcdtlluOiii7B7927ccccd+MpXvoI333wzyS2VX7yfleTw4cMj+lVRUVGSWqgcW7duxde//nV88MEH2LRpE3w+Hz73uc/B7XaP+Rq9XrMm8lkB+rxeTZ06FT/+8Y+xY8cOfPzxx/jMZz6DK6+8Evv37x/1+bL1KZGS4uyzzxa//vWvh/87EAiIZWVl4vr160d9/r//+7+Ll1122YjHlixZIn71q19NajuVIt7P6+mnnxazs7NT1DplAiC+9NJL4z7nnnvuEc8444wRj61atUpcsWJFElumPLF8Vu+8844IQOzt7U1Jm5Sso6NDBCBu3bp1zOfo/ZolieWz4vUqIjc3V/ztb3876r/J1aeYEUoCr9eLHTt2YPny5eHHDAYDli9fjm3bto36mm3bto14PgCsWLFizOdryUQ+LwBwuVyoqKhAeXn5uKMMPdNzv5qoM888E6WlpfjsZz+L9957T+7myMLhcAAA8vLyxnwO+1ZILJ8VwOtVIBDA888/D7fbjaVLl476HLn6FAOhJOjq6kIgEEBxcfGIx4uLi8esN2hra4vr+Voykc9r9uzZeOqpp/DXv/4Vv//97xEMBrFs2TKcOHEiFU1WjbH6ldPpxODgoEytUqbS0lI88cQT+Mtf/oK//OUvKC8vx4UXXoidO3fK3bSUCgaDuOOOO/DpT38a8+bNG/N5er5mSWL9rPR8vdq7dy8yMjJgtVrxta99DS+99BLmzp076nPl6lM8fZ5UaenSpSNGFcuWLcOcOXPw61//Gt///vdlbBmp1ezZszF79uzwfy9btgy1tbXYsGEDfve738nYstT6+te/jn379uFf//qX3E1RvFg/Kz1fr2bPno3du3fD4XDgz3/+M2688UZs3bp1zGBIDswIJUFBQQGMRiPa29tHPN7e3o6SkpJRX1NSUhLX87VkIp/XycxmMxYtWoRjx44lo4mqNVa/ysrKQlpamkytUo+zzz5bV31qzZo1ePXVV/HOO+9g6tSp4z5Xz9csIL7P6mR6ul5ZLBbMmDEDixcvxvr167Fw4UL84he/GPW5cvUpBkJJYLFYsHjxYmzevDn8WDAYxObNm8ecG126dOmI5wPApk2bxny+lkzk8zpZIBDA3r17UVpamqxmqpKe+1Ui7N69Wxd9ShRFrFmzBi+99BLefvttTJ8+/bSv0WvfmshndTI9X6+CwSA8Hs+o/yZbn0pqKbaOPf/886LVahU3btwoHjhwQLztttvEnJwcsa2tTRRFUfzSl74k3nvvveHnv/fee6LJZBJ/9rOfiQcPHhQfeOAB0Ww2i3v37pXrR0ipeD+vBx98UHzzzTfF2tpacceOHeJ1110n2mw2cf/+/XL9CCnR398v7tq1S9y1a5cIQHzkkUfEXbt2iQ0NDaIoiuK9994rfulLXwo/v66uTrTb7eLdd98tHjx4UHzsscdEo9EovvHGG3L9CCkT72e1YcMG8eWXXxaPHj0q7t27V/zWt74lGgwG8R//+IdcP0LK3H777WJ2dra4ZcsWsbW1Nfw1MDAQfg6vWSET+az0er269957xa1bt4r19fXinj17xHvvvVcUBEF86623RFFUTp9iIJREv/zlL8Vp06aJFotFPPvss8UPPvgg/G8XXHCBeOONN454/p/+9Cdx1qxZosViEc844wzxtddeS3GL5RXP53XHHXeEn1tcXCxeeuml4s6dO2VodWpJS7xP/pI+mxtvvFG84IILTnnNmWeeKVosFrGqqkp8+umnU95uOcT7Wf3kJz8Rq6urRZvNJubl5YkXXnih+Pbbb8vT+BQb7XMCMKKv8JoVMpHPSq/Xqy9/+ctiRUWFaLFYxMLCQvHiiy8OB0GiqJw+JYiiKCY350RERESkTKwRIiIiIt1iIERERES6xUCIiIiIdIuBEBEREekWAyEiIiLSLQZCREREpFsMhIiIiEi3GAgRERGRbjEQIiJN2rJlCwRBQF9fn9xNISIF487SRKQJF154Ic4880z8/Oc/BwB4vV709PSguLgYgiDI2zgiUiyT3A0gIkoGi8WCkpISuZtBRArHqTEiUr2bbroJW7duxS9+8QsIggBBELBx48YRU2MbN25ETk4OXn31VcyePRt2ux3XXHMNBgYG8Mwzz6CyshK5ubn45je/iUAgEP7eHo8H3/72tzFlyhSkp6djyZIl2LJlizw/KBElHDNCRKR6v/jFL3DkyBHMmzcPDz30EABg//79pzxvYGAAjz76KJ5//nn09/fjqquuwhe/+EXk5OTg9ddfR11dHa6++mp8+tOfxqpVqwAAa9aswYEDB/D888+jrKwML730Ei655BLs3bsXM2fOTOnPSUSJx0CIiFQvOzsbFosFdrs9PB126NChU57n8/nw+OOPo7q6GgBwzTXX4He/+x3a29uRkZGBuXPn4qKLLsI777yDVatWobGxEU8//TQaGxtRVlYGAPj2t7+NN954A08//TR+9KMfpe6HJKKkYCBERLpht9vDQRAAFBcXo7KyEhkZGSMe6+joAADs3bsXgUAAs2bNGvF9PB4P8vPzU9NoIkoqBkJEpBtms3nEfwuCMOpjwWAQAOByuWA0GrFjxw4YjcYRz4sOnohIvRgIEZEmWCyWEUXOibBo0SIEAgF0dHTgvPPOS+j3JiJl4KoxItKEyspKfPjhhzh+/Di6urrCWZ3JmDVrFm644QasXr0aL774Iurr67F9+3asX78er732WgJaTURyYyBERJrw7W9/G0ajEXPnzkVhYSEaGxsT8n2ffvpprF69GnfddRdmz56NlStX4qOPPsK0adMS8v2JSF7cWZqIiIh0ixkhIiIi0i0GQkRERKRbDISIiIhItxgIERERkW4xECIiIiLdYiBEREREusVAiIiIiHSLgRARERHpFgMhIiIi0i0GQkRERKRbDISIiIhIt/5/J0KTt3NqxlMAAAAASUVORK5CYII=\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "spline.plot(xlabel=\"time\", xlim=(0, 3));" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "image/png": "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AWrqpeseYyFEREREJhcT2A7+KgdU1DZg79kiyXKwECIiIiKTu9Y95qJUoFBTI1kOQRRFUbK7WwCNRgOVSgW1Wg03Nzep4xAREVmNsqo6ONjJ4WAnN/hzt/T7W2HwOxMRERG1gLuTvdQR2DVGREREtouFEBEREdksFkJERERks1gIERERkc1iIUREREQ2i4UQERER2SwWQkRERGSzWAgRERGRzWIhRERERDaLhRARERHZLBZCREREZLNYCBEREZHNYiFERERENou7z9+GKIoAAI1GI3ESIiIiaqlr39vXvsdvhoXQbZSXlwMAAgMDJU5CRERErVVeXg6VSnXTnwvi7UolG6fT6ZCbmwtXV1cIgmCw59VoNAgMDER2djbc3NwM9rzWiu9Xy/G9ajm+Vy3H96rl+F61nDHfK1EUUV5eDn9/f8hkNx8JxBah25DJZOjQoYPRnt/NzY3/obQC36+W43vVcnyvWo7vVcvxvWo5Y71Xt2oJuoaDpYmIiMhmsRAiIiIim8VCSCJKpRJvvfUWlEql1FEsAt+vluN71XJ8r1qO71XL8b1qOXN4rzhYmoiIiGwWW4SIiIjIZrEQIiIiIpvFQoiIiIhsFgshIiIislkshIxo8eLFCA4OhoODA2JjYxEfH3/L81evXo3w8HA4ODggMjISW7ZsMVFS89Ca92v58uUQBKHZ4eDgYMK00ti3bx/uv/9++Pv7QxAErF+//rbX7NmzB7169YJSqUTnzp2xfPlyo+c0B619r/bs2XPdZ0oQBOTn55smsITmz5+PO+64A66urvD29sbYsWORnp5+2+ts8XeWPu+Vrf6++uqrr9CzZ8+mxRL79++PrVu33vIaKT5TLISM5JdffsErr7yCt956C4mJiYiKisKwYcNQWFh4w/MPHTqERx55BNOnT8eJEycwduxYjB07FmlpaSZOLo3Wvl9A40qkeXl5TcelS5dMmFgalZWViIqKwuLFi1t0fmZmJkaNGoW7774bSUlJmDlzJp566ils377dyEml19r36pr09PRmnytvb28jJTQfe/fuxQsvvIAjR45gx44dqK+vx3333YfKysqbXmOrv7P0ea8A2/x91aFDB3z44YdISEjA8ePHcc899+CBBx7AyZMnb3i+ZJ8pkYyib9++4gsvvND0b61WK/r7+4vz58+/4fkTJ04UR40a1eyx2NhY8dln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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "sbml_doc = libsbml.SBMLReader().readSBML(\"example_splines.xml\")\n", + "sbml_model = sbml_doc.getModel()\n", + "spline.add_to_sbml_model(sbml_model)\n", + "simulate(sbml_model, T=3);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The spline annotation in this case is\n", + "```xml\n", + "\n", + "\t ... \n", + "\t ... \n", + "\t ... \n", + "\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can modify the spline's boundary conditions, for example requiring that the derivatives is zero." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "spline = amici.splines.CubicHermiteSpline(\n", + " sbml_id=\"f\",\n", + " evaluate_at=amici.sbml_utils.amici_time_symbol,\n", + " nodes=amici.splines.UniformGrid(0, 1, number_of_nodes=4),\n", + " values_at_nodes=[-1, 2, 4, 2],\n", + " bc=\"zeroderivative\",\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "spline.plot(xlabel=\"time\");" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```xml\n", + "\n", + "\t ... \n", + "\t ... \n", + "\t ... \n", + "\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Or we can impose natural boundary conditions." + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "spline = amici.splines.CubicHermiteSpline(\n", + " sbml_id=\"f\",\n", + " evaluate_at=amici.sbml_utils.amici_time_symbol,\n", + " nodes=amici.splines.UniformGrid(0, 1, number_of_nodes=4),\n", + " values_at_nodes=[-1, 2, 4, 2],\n", + " bc=\"natural\",\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "spline.plot(xlabel=\"time\");" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```xml\n", + "\n", + "\t ... \n", + "\t ... \n", + "\t ... \n", + "\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Even if all node values are positive, due to under-shooting a cubic Hermite spline can assume negative values. In certain settings (e.g., when the spline represents a chemical reaction rate) this should be avoided. A possible solution is to carry out the interpolation in log-space (the resulting function is no longer a spline, but it is still a smooth interpolant)." + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "spline = amici.splines.CubicHermiteSpline(\n", + " sbml_id=\"f\",\n", + " evaluate_at=amici.sbml_utils.amici_time_symbol,\n", + " nodes=amici.splines.UniformGrid(0, 1, number_of_nodes=5),\n", + " values_at_nodes=[2, 0.05, 0.1, 2, 1],\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# This spline assumes negative values!\n", + "spline.plot(xlabel=\"time\");" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "spline = amici.splines.CubicHermiteSpline(\n", + " sbml_id=\"f\",\n", + " evaluate_at=amici.sbml_utils.amici_time_symbol,\n", + " nodes=amici.splines.UniformGrid(0, 1, number_of_nodes=5),\n", + " values_at_nodes=[2, 0.05, 0.1, 2, 1],\n", + " logarithmic_parametrization=True,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "image/png": "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iLh7927L90aPHi3OnDmz1fnfffed2KNHD1GlUom9e/cW165d6+CKXZM117lz584igAtu8+fPd3zhLsba/8/nY7hpP2uv886dO8WhQ4eKarVa7Nq1q/jPf/5TbG5udnDVrsea69zU1CS+/PLLYrdu3UQvLy8xNjZW/Nvf/iaWl5c7vnAXsnXr1jbfb83XdubMmeLo0aMvuE9iYqKoUqnErl27ip9//rnd6xREke1vREREJB8cc0NERESywnBDREREssJwQ0RERLLCcENERESywnBDREREssJwQ0RERLLCcENERESywnBDREREssJwQ0QuITU1FYIgoKKiQupSiMjJcYViInJKY8aMQWJiIhYuXAgAaGxsRFlZGcLDwyEIgrTFEZFT85C6ACKi9lCpVIiIiJC6DCJyAeyWIiKnc88992Dbtm147733IAgCBEHA0qVLW3VLLV26FAEBAVizZg0SEhLg4+OD2267DbW1tfjiiy8QFxeHwMBAPP744zAYDJbHbmhowNNPP43o6Gj4+vpi6NChSE1NleaFEpFdsOWGiJzOe++9h5MnT6JPnz549dVXAQBHjhy54Lza2lq8//77WL58OaqqqjBt2jTccsstCAgIwLp165CdnY1bb70VI0aMwPTp0wEAs2fPxtGjR7F8+XJERUVh1apVuPHGG5GRkYH4+HiHvk4isg+GGyJyOlqtFiqVCj4+PpauqOPHj19wXlNTEz766CN069YNAHDbbbfhv//9L4qKiuDn54devXph7Nix2Lp1K6ZPn468vDx8/vnnyMvLQ1RUFADg6aefxvr16/H555/j9ddfd9yLJCK7YbghIpfl4+NjCTYAEB4ejri4OPj5+bU6VlxcDADIyMiAwWBAjx49Wj1OQ0MDgoODHVM0Edkdww0RuSxPT89WXwuC0OYxo9EIAKiuroZSqcSBAwegVCpbnXd+ICIi18ZwQ0ROSaVStRoIbAsDBgyAwWBAcXExRo4cadPHJiLnwdlSROSU4uLisGfPHpw6dQqlpaWW1pcr0aNHD9x1111ITk7GypUrkZOTg7179yIlJQVr1661QdVE5AwYbojIKT399NNQKpXo1asXQkNDkZeXZ5PH/fzzz5GcnIynnnoKCQkJmDp1Kvbt24dOnTrZ5PGJSHpcoZiIiIhkhS03REREJCsMN0RERCQrDDdEREQkKww3REREJCsMN0RERCQrDDdEREQkKww3REREJCsMN0RERCQrDDdEREQkKww3REREJCsMN0RERCQr/w/9GTPZftCcqwAAAABJRU5ErkJggg==\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Instead of under-shooting we now have over-shooting,\n", + "# but at least the \"spline\" is always positive\n", + "spline.plot(xlabel=\"time\");" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The spline annotation in this case is\n", + "```xml\n", + "\n", + "\t ... \n", + "\t ... \n", + "\t ... \n", + "\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Comparing model import time for the SBML-native piecewise implementation and the AMICI spline implementation" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "import pandas as pd\n", + "import seaborn as sns\n", + "import tempfile\n", + "import time" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "nruns = 6 # number of replicates\n", + "num_nodes = [\n", + " 5,\n", + " 10,\n", + " 15,\n", + " 20,\n", + " 25,\n", + " 30,\n", + " 40,\n", + "] # benchmark model import for these node numbers\n", + "amici_only_nodes = [\n", + " 50,\n", + " 75,\n", + " 100,\n", + " 125,\n", + " 150,\n", + " 175,\n", + " 200,\n", + " 225,\n", + " 250,\n", + "] # for these node numbers, only benchmark the annotation-based implementation" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "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 os.getenv(\"GITHUB_ACTIONS\") is not None:\n", + " nruns = 1\n", + " num_nodes = [4]\n", + " amici_only_nodes = [5]" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "df = None\n", + "for n in num_nodes + amici_only_nodes:\n", + " # Create model\n", + " spline = amici.splines.CubicHermiteSpline(\n", + " sbml_id=\"f\",\n", + " evaluate_at=amici.sbml_utils.amici_time_symbol,\n", + " nodes=amici.splines.UniformGrid(0, 1, number_of_nodes=n),\n", + " values_at_nodes=np.random.rand(n),\n", + " )\n", + " sbml_doc = libsbml.SBMLReader().readSBML(\"example_splines.xml\")\n", + " sbml_model = sbml_doc.getModel()\n", + " spline.add_to_sbml_model(sbml_model)\n", + " # Benchmark model creation\n", + " timings_amici = []\n", + " timings_piecewise = []\n", + " for _ in range(nruns):\n", + " with tempfile.TemporaryDirectory() as tmpdir:\n", + " t0 = time.perf_counter_ns()\n", + " amici.SbmlImporter(sbml_model).sbml2amici(\"benchmark\", tmpdir)\n", + " dt = time.perf_counter_ns() - t0\n", + " timings_amici.append(dt / 1e9)\n", + " if n in num_nodes:\n", + " with tempfile.TemporaryDirectory() as tmpdir:\n", + " t0 = time.perf_counter_ns()\n", + " amici.SbmlImporter(\n", + " sbml_model, discard_annotations=True\n", + " ).sbml2amici(\"benchmark\", tmpdir)\n", + " dt = time.perf_counter_ns() - t0\n", + " timings_piecewise.append(dt / 1e9)\n", + " # Append benchmark data to dataframe\n", + " df_amici = pd.DataFrame(\n", + " dict(num_nodes=n, time=timings_amici, use_annotations=True)\n", + " )\n", + " df_piecewise = pd.DataFrame(\n", + " dict(num_nodes=n, time=timings_piecewise, use_annotations=False)\n", + " )\n", + " if df is None:\n", + " df = pd.concat(\n", + " [df_amici, df_piecewise], ignore_index=True, verify_integrity=True\n", + " )\n", + " else:\n", + " df = pd.concat(\n", + " [df, df_amici, df_piecewise],\n", + " ignore_index=True,\n", + " verify_integrity=True,\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": 88, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "kwargs = dict(markersize=7.5)\n", + "df_avg = df.groupby([\"use_annotations\", \"num_nodes\"]).mean().reset_index()\n", + "fig, ax = plt.subplots(1, 1, figsize=(6.5, 3.5))\n", + "ax.plot(\n", + " df_avg[np.logical_not(df_avg[\"use_annotations\"])][\"num_nodes\"],\n", + " df_avg[np.logical_not(df_avg[\"use_annotations\"])][\"time\"],\n", + " \".\",\n", + " label=\"MathML piecewise\",\n", + " **kwargs,\n", + ")\n", + "ax.plot(\n", + " df_avg[df_avg[\"use_annotations\"]][\"num_nodes\"],\n", + " df_avg[df_avg[\"use_annotations\"]][\"time\"],\n", + " \".\",\n", + " label=\"AMICI annotations\",\n", + " **kwargs,\n", + ")\n", + "ax.set_ylabel(\"model import time (s)\")\n", + "ax.set_xlabel(\"number of spline nodes\")\n", + "ax.set_yscale(\"log\")\n", + "ax.yaxis.set_major_formatter(\n", + " mpl.ticker.FuncFormatter(lambda x, pos: f\"{x:.0f}\")\n", + ")\n", + "ax.xaxis.set_ticks(\n", + " [\n", + " 10,\n", + " 20,\n", + " 30,\n", + " 40,\n", + " 60,\n", + " 70,\n", + " 80,\n", + " 90,\n", + " 110,\n", + " 120,\n", + " 130,\n", + " 140,\n", + " 160,\n", + " 170,\n", + " 180,\n", + " 190,\n", + " 210,\n", + " 220,\n", + " 230,\n", + " 240,\n", + " 260,\n", + " ],\n", + " minor=True,\n", + ")\n", + "ax.yaxis.set_ticks(\n", + " [20, 30, 40, 50, 60, 70, 80, 90, 200, 300, 400],\n", + " [\"20\", \"30\", \"40\", \"50\", None, None, None, None, \"200\", \"300\", \"400\"],\n", + " minor=True,\n", + ")\n", + "ax.legend()\n", + "ax.figure.tight_layout()\n", + "# ax.figure.savefig('benchmark_import.pdf')" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.6" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/deps/AMICI/python/examples/example_splines/example_splines.xml b/deps/AMICI/python/examples/example_splines/example_splines.xml new file mode 100644 index 000000000..2ff6a8d73 --- /dev/null +++ b/deps/AMICI/python/examples/example_splines/example_splines.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + f + + + + + diff --git a/deps/AMICI/python/examples/example_splines_swameye/ExampleSplinesSwameye2003.ipynb b/deps/AMICI/python/examples/example_splines_swameye/ExampleSplinesSwameye2003.ipynb new file mode 100644 index 000000000..8e3ee6db1 --- /dev/null +++ b/deps/AMICI/python/examples/example_splines_swameye/ExampleSplinesSwameye2003.ipynb @@ -0,0 +1,2401 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "c10af447-e4f1-46e1-bf60-910dc67f5d77", + "metadata": { + "tags": [] + }, + "source": [ + "# Spline implementation of JAK2-STAT5 signaling pathway\n", + "In this notebook a practical example of the usage of AMICI spline functionalities is shown.\n", + "The model under consideration is the JAK2-STAT5 signaling pathway ([Swameye et al., 2003](https://doi.org/10.1073/pnas.0237333100)),\n", + "in which the dynamics of the system depend on a measured input function (the quantity `pEpoR` in the model).\n", + "\n", + "Following the approach of ([Schelker et al., 2012](https://doi.org/10.1093/bioinformatics/bts393)), a continuous approximation of this input function is estimated together with the other parameters.\n", + "As in the original paper, we will use a spline with logarithmic parameterization in order to enforce the positivity constraint.\n", + "\n", + "The model of the signaling pathway will be implemented in SBML using AMICI's spline annotations, experimental data integrated using the PEtab format and parameter estimation will be carried out using the [pyPESTO](https://pypesto.readthedocs.io/) library." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7323a4fb", + "metadata": {}, + "outputs": [], + "source": [ + "%pip install pypesto" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "78dbeefe", + "metadata": {}, + "outputs": [], + "source": [ + "%pip install fides" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "5f94310d-d95c-4fb5-bd1e-a29bb1c92a30", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "import os\n", + "import math\n", + "import logging\n", + "import contextlib\n", + "import multiprocessing\n", + "import copy\n", + "\n", + "import numpy as np\n", + "import sympy as sp\n", + "import pandas as pd\n", + "from matplotlib import pyplot as plt\n", + "\n", + "import libsbml\n", + "import amici\n", + "import petab\n", + "import pypesto\n", + "import pypesto.petab" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "02162090-1008-4eb7-b954-c10370aae9c5", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Number of multi-starts for MAP estimation\n", + "n_starts = 150\n", + "# n_starts = 0 # when loading results" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c2a366ac", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Set default pypesto engine/optimizer\n", + "pypesto_optimizer = pypesto.optimize.FidesOptimizer(verbose=logging.WARNING)\n", + "pypesto_engine = pypesto.engine.MultiProcessEngine()" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "21eca425", + "metadata": { + "tags": [] + }, + "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 os.getenv(\"GITHUB_ACTIONS\") is not None:\n", + " n_starts = 15\n", + " pypesto_optimizer = pypesto.optimize.FidesOptimizer(\n", + " verbose=logging.WARNING, options=dict(maxiter=10)\n", + " )\n", + " pypesto_engine = pypesto.engine.SingleCoreEngine()" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "ff552b79-96a1-42b4-a5cb-a9f2143f10bb", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# A dictionary to store different approaches for a final comparison\n", + "all_results = {}" + ] + }, + { + "cell_type": "markdown", + "id": "3ab1206d-f0dd-4b77-a668-be3076c28fa3", + "metadata": { + "tags": [] + }, + "source": [ + "## Spline approximation with few nodes, using finite differences for the derivatives\n", + "As a first attempt, we fix a small amount of nodes, create new parameters for the values of the splines at the nodes and let AMICI compute the derivative at the nodes by using finite differences." + ] + }, + { + "cell_type": "markdown", + "id": "5684adb0-4c66-48d3-9076-270839a77a5f", + "metadata": {}, + "source": [ + "### Creating the PEtab model" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "c796cf8b-d7e9-473a-a368-b8919ef93efe", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Problem name\n", + "name = \"Swameye_PNAS2003_5nodes_FD\"" + ] + }, + { + "cell_type": "markdown", + "id": "c684a2e4-e4e8-4f4a-aa06-c1c9378a3bce", + "metadata": {}, + "source": [ + "First, we create a spline to represent the input function `pEpoR`, parametrized by its values at the nodes.\n", + "Since the value of the input function reaches its steady state by the end of the experiment, we extrapolate constantly after that (useful if we need to simulate the model after the last spline node)." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "68562fc1-7213-4dac-9177-61fba454f9d4", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Create spline for pEpoR\n", + "nodes = [0, 5, 10, 20, 60]\n", + "values_at_nodes = [\n", + " sp.Symbol(f\"pEpoR_t{str(t).replace('.', '_dot_')}\") for t in nodes\n", + "] # new parameter symbols for spline values\n", + "spline = amici.splines.CubicHermiteSpline(\n", + " sbml_id=\"pEpoR\", # matches name of species in SBML model\n", + " evaluate_at=amici.sbml_utils.amici_time_symbol, # the spline is evaluated at the current time\n", + " nodes=nodes,\n", + " values_at_nodes=values_at_nodes, # values at the nodes (in linear scale)\n", + " extrapolate=(None, \"constant\"), # because steady state is reached\n", + " bc=\"auto\", # automatically determined from extrapolate (bc at right end will be 'zero_derivative')\n", + " logarithmic_parametrization=True,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "4a3de7bd-3157-4950-a264-cf8ff8f2250e", + "metadata": {}, + "source": [ + "We can then add the spline to a skeleton SBML model based on the d2d implementation by (Schelker et al., 2012).\n", + "The skeleton SBML model defines a species `pEpoR` which interacts with the other species,\n", + "but has no reactions or rate rules of its own.\n", + "The code below creates an assignment rule for `pEpoR` using the spline formula, completing the model.\n", + "The parameters `pEpoR_t*` are automatically added to the SBML model too (using nominal values of `0.1` and declaring them to be constant)." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "118feb66-b3d3-46d9-9b4b-5a3f43586a64", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Add spline formula to SBML model\n", + "sbml_doc = libsbml.SBMLReader().readSBML(\n", + " os.path.join(\"Swameye_PNAS2003\", \"swameye2003_model.xml\")\n", + ")\n", + "sbml_model = sbml_doc.getModel()\n", + "spline.add_to_sbml_model(\n", + " sbml_model, auto_add=True, y_nominal=0.1, y_constant=True\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "dedc2418-dc8b-4f54-84f3-22dc26f2846b", + "metadata": {}, + "source": [ + "A skeleton PEtab problem is provided, containing parameter bounds, observable definitions and experimental data.\n", + "Of particular relevance is the noise model used for the measurements of `pEpoR`, normal additive noise with standard deviation equal to `0.0274 + 0.1 * pEpoR`;\n", + "this is the same choice used in (Schelker et al., 2012), where it was estimated from experimental replicates.\n", + "\n", + "However, the parameters associated to the spline are to be added too.\n", + "The code below defines parameter bounds for them according to the PEtab format and then creates a full PEtab problem integrating them together with the edited SBML file.\n", + "The condition, measurement and observable PEtab tables do not require additional modification and can be used as they are." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "5c2f1c0b-72fe-4df4-b25c-525e36f66514", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Extra parameters associated to the spline\n", + "spline_parameters_df = pd.DataFrame(\n", + " dict(\n", + " parameterScale=\"log\",\n", + " lowerBound=0.001,\n", + " upperBound=10,\n", + " nominalValue=0.1,\n", + " estimate=1,\n", + " ),\n", + " index=pd.Series(list(map(str, values_at_nodes)), name=\"parameterId\"),\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "f832bfe0-ddda-48fc-9a73-6e62db19b445", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Create PEtab problem\n", + "petab_problem = petab.Problem(\n", + " sbml_model,\n", + " condition_df=petab.conditions.get_condition_df(\n", + " os.path.join(\"Swameye_PNAS2003\", \"swameye2003_conditions.tsv\")\n", + " ),\n", + " measurement_df=petab.measurements.get_measurement_df(\n", + " os.path.join(\"Swameye_PNAS2003\", \"swameye2003_measurements.tsv\")\n", + " ),\n", + " parameter_df=petab.core.concat_tables(\n", + " [\n", + " os.path.join(\"Swameye_PNAS2003\", \"swameye2003_parameters.tsv\"),\n", + " spline_parameters_df,\n", + " ],\n", + " petab.parameters.get_parameter_df,\n", + " ),\n", + " observable_df=petab.observables.get_observable_df(\n", + " os.path.join(\"Swameye_PNAS2003\", \"swameye2003_observables.tsv\")\n", + " ),\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "c69bc77d-3efc-4ac5-bde4-c0e43219f560", + "metadata": {}, + "source": [ + "The resulting PEtab problem can be checked for errors and exported to disk if needed." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "3c10e6ad-c891-43ff-b1d7-199943bdfa4b", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Check whether PEtab model is valid\n", + "assert not petab.lint_problem(petab_problem)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "0f2ec7eb-b4af-46ac-a96e-6e484f774883", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Save PEtab problem to disk\n", + "# import shutil\n", + "# shutil.rmtree(name, ignore_errors=True)\n", + "# os.mkdir(name)\n", + "# petab_problem.to_files_generic(prefix_path=name)" + ] + }, + { + "cell_type": "markdown", + "id": "7ebcbcc9-82d9-44cf-a0ad-2a80711cb382", + "metadata": {}, + "source": [ + "### Creating the pyPESTO problem\n", + "We can now create a pyPESTO problem directly from the PEtab problem.\n", + "Due to technical limitations in AMICI, currently the PEtab problem has to be \"flattened\" before it can be simulated from, but such operation is merely syntactical and thus does not change the essence of the model." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "5a46a020-c670-4f52-9bc7-a1dd74faedd0", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Problem must be \"flattened\" to be used with AMICI\n", + "petab.core.flatten_timepoint_specific_output_overrides(petab_problem)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "fab8bd39-eb26-4462-b158-0fae8a8ba027", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Check whether simulation from the PEtab problem works\n", + "# import amici.petab_simulate\n", + "# simulator = amici.petab_simulate.PetabSimulator(petab_problem)\n", + "# simulator.simulate(noise=False)" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "1beb3a3e-6966-4d3e-acc5-546c1932df9f", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Import PEtab problem into pyPESTO\n", + "pypesto_problem = pypesto.petab.PetabImporter(\n", + " petab_problem, model_name=name\n", + ").create_problem()\n", + "\n", + "# Increase maximum number of steps for AMICI\n", + "pypesto_problem.objective.amici_solver.setMaxSteps(10**5)" + ] + }, + { + "cell_type": "markdown", + "id": "715a8a22-878c-4c37-98e8-7f3898a2a2dc", + "metadata": {}, + "source": [ + "### Maximum Likelihood estimation\n", + "Using pyPESTO we can optimize for the parameter vector that maximizes the probability of observing the experimental data (maximum likelihood estimation).\n", + "\n", + "A multistart method with local gradient-based optimization is used and the results of each multistart can be visualized in a waterfall plot." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "bf1e3a52-8f72-466e-b63c-2f15fbe59137", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Load existing results if available\n", + "if os.path.exists(f\"{name}.h5\"):\n", + " pypesto_result = pypesto.store.read_result(\n", + " f\"{name}.h5\", problem=pypesto_problem\n", + " )\n", + "else:\n", + " pypesto_result = None\n", + "# Overwrite\n", + "# pypesto_result = None" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d6e20c36-d6fe-4264-b366-c23e707246ff", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Parallel multistart optimization with pyPESTO and FIDES\n", + "if n_starts > 0:\n", + " if pypesto_result is None:\n", + " new_ids = [str(i) for i in range(n_starts)]\n", + " else:\n", + " last_id = max(int(i) for i in pypesto_result.optimize_result.id)\n", + " new_ids = [str(i) for i in range(last_id + 1, last_id + n_starts + 1)]\n", + " pypesto_result = pypesto.optimize.minimize(\n", + " pypesto_problem,\n", + " n_starts=n_starts,\n", + " ids=new_ids,\n", + " optimizer=pypesto_optimizer,\n", + " engine=pypesto_engine,\n", + " result=pypesto_result,\n", + " )\n", + " pypesto_result.optimize_result.sort()\n", + " if pypesto_result.optimize_result.x[0] is None:\n", + " raise Exception(\n", + " \"All multistarts failed (n_starts is probably too small)! If this error occurred during CI, just run the workflow again.\"\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "0592af7c-227a-4e52-b383-d43061a4c52f", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Save results to disk\n", + "# pypesto.store.write_result(pypesto_result, f'{name}.h5', overwrite=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "7762a31f-55e8-4e12-8107-3f3e6f0af5e5", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Print result table\n", + "# pypesto_result.optimize_result.as_dataframe()" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "2cfbcf68-6d13-49eb-b18f-26d9d32e79ae", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "image/png": "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vq5du+K7775DQEAAbGwM83Xs7u6OjIwM9XZKSgru3r1rkGsRNUQcNUbUgK1duxYlJSWIiIjA0aNHkZaWhn379mHQoEFo2rSpRrNUZaZMmYJ9+/ZhxYoVSElJwdq1azWaxQBg3rx5+PLLL7Fw4UIkJibiwoUL2LZtG955551qz13T+8LDw9GmTRtERkbizz//xG+//aYxyq0ydnZ2mDx5MmbOnIl9+/YhKSkJkyZNwt27d/Hvf/9b49hFixYhJiYG58+fx/jx4+Hm5oaRI0cCeDA6rLCwEDExMcjJyTFYYpCZmYlRo0bh2WefRUREBDIzM5GZmYm//voLwINRf7du3cJzzz2HuLg4XLlyBfv378eECRP0lqgNGDAAa9euxZkzZ3Dq1Cm88sorGrU/RJaOiRBRA9a6dWucOnUKLVq0wNNPP42WLVvipZdeQv/+/REbGwsXF5dq39+rVy9s2LABH330EYKDg3HgwIEKCU5ERAR+/PFHHDhwAN27d0evXr2watUqdY1MVWp6n5WVFXbt2oV79+6hR48eePHFF2tM3ADggw8+wJNPPomxY8eia9euuHz5Mvbv3w9nZ+cKx02dOhUhISHIzMzEnj171P1nwsLC8Morr+CZZ56Bu7s7li1bVuN1a+PixYvIysrCF198AW9vb/Wre/fuAAAfHx8cO3YMSqUSgwcPRseOHTFt2jQ0adIEVlb6+Xr+8MMP4evri759++L555/HjBkz6jxXEpE5kQhdeloSEZm4w4cPo3///rh9+7ZRlwohooaBNUJERERksZgIERERkcVi0xgRERFZLNYIERERkcViIkREREQWi4kQERERWSwmQkRERGSxmAgRERGRxWIiRERERBaLiRARERFZLCZCREREZLGYCBEREZHF+n/XWu5nHsrgTAAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Visualize the results of the multistarts\n", + "pypesto.visualize.waterfall(pypesto_result, size=[6.5, 3.5]);" + ] + }, + { + "cell_type": "markdown", + "id": "5b58b02a-d09c-4974-862a-0fbd29ce624b", + "metadata": {}, + "source": [ + "Below the maximum likelihood estimates for `pEpoR` and the other observables are plotted, together with the experimental measurements.\n", + "\n", + "To assess whether the noise model used in the observable is reasonable, we have also plotted 2-sigma error bands for `pEpoR`." + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "6464be7c-1365-4bf6-8625-cc7cf5a9c988", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Functions for simulating observables given a parameter vector\n", + "def _simulate(x=None, *, problem=None, result=None, N=500, **kwargs):\n", + " if result is None:\n", + " result = pypesto_result\n", + " if problem is None:\n", + " problem = pypesto_problem\n", + " if x is None:\n", + " x = result.optimize_result.x[0]\n", + " if N is None:\n", + " objective = problem.objective\n", + " else:\n", + " objective = problem.objective.set_custom_timepoints(\n", + " timepoints_global=np.linspace(0, 60, N)\n", + " )\n", + " if len(x) != len(problem.x_free_indices):\n", + " x = x[problem.x_free_indices]\n", + " simresult = objective(x, return_dict=True, **kwargs)\n", + " return problem, simresult[\"rdatas\"][0]\n", + "\n", + "\n", + "def simulate_pEpoR(x=None, **kwargs):\n", + " problem, rdata = _simulate(x, **kwargs)\n", + " assert problem.objective.amici_model.getObservableIds()[0].startswith(\n", + " \"pEpoR\"\n", + " )\n", + " return rdata[\"t\"], rdata[\"y\"][:, 0]\n", + "\n", + "\n", + "def simulate_pSTAT5(x=None, **kwargs):\n", + " problem, rdata = _simulate(x, **kwargs)\n", + " assert problem.objective.amici_model.getObservableIds()[1].startswith(\n", + " \"pSTAT5\"\n", + " )\n", + " return rdata[\"t\"], rdata[\"y\"][:, 1]\n", + "\n", + "\n", + "def simulate_tSTAT5(x=None, **kwargs):\n", + " problem, rdata = _simulate(x, **kwargs)\n", + " assert problem.objective.amici_model.getObservableIds()[-1].startswith(\n", + " \"tSTAT5\"\n", + " )\n", + " return rdata[\"t\"], rdata[\"y\"][:, -1]\n", + "\n", + "\n", + "# Experimental data\n", + "df_measurements = petab.measurements.get_measurement_df(\n", + " os.path.join(\"Swameye_PNAS2003\", \"swameye2003_measurements.tsv\")\n", + ")\n", + "df_pEpoR = df_measurements[\n", + " df_measurements[\"observableId\"].str.startswith(\"pEpoR\")\n", + "]\n", + "df_pSTAT5 = df_measurements[\n", + " df_measurements[\"observableId\"].str.startswith(\"pSTAT5\")\n", + "]\n", + "df_tSTAT5 = df_measurements[\n", + " df_measurements[\"observableId\"].str.startswith(\"tSTAT5\")\n", + "]" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "1fca8d58-1d37-48cb-8880-e9bacf256117", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "image/png": "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DL169YJWq0VkZCRee+21OgVYkydPhlQqxfTp01FQUIC2bdti7969aNmyJQDA3d0d+/btw0cffYROnTqhbdu2+OabbzBw4MBa160294/UDodpZv1FMpkMEokEUqm02eb/pKWloWfPnkhPT0e3bt0q7D9w4AD69u2LHTt2IC4urtqyHj16hNjYWJhMJpw+fRrR0dGOqjaAx8Pcx40bh4SEBCQlJSEmJgY3b97EypUrcfToUaxdu7bCMPu1a9di/vz5CAkJwenTp61epLW8kpIShIWFwcvLq46vhJC60Wg0yMzMRHR0tEUSbWM2atQolJWVYc+ePc6uCmmgqvu7t+bzvVGN9iL2YZ6Yq127dhX2mUwmhIWFAUCVM22Xt3PnTphMJsTGxjo88AGAPn36YO3atbh58yb69++P1q1bo3///sjIyKg08AEed58GBwfj0aNHbGK2rQQCAUpLS2m9L0IIacQo+GmGgoODATyeSfRJGo2GXRXdnDtTFYZhsH37dgCPuzDrS58+ffD7779jx44dWLVqFXbs2IHTp09XObGiUChkF0Jcvnx5ndbqEolEUKlUtN4XIYQ0YhT8NEM9evRAVFQUvv766wotGEqlEuvWrauQO1OZixcv4v79+xCLxejbt68jq1wBj8dDXFwcEhMTERcXV2Ou0ZAhQxAVFYWioiKsX7/e5utyuVxwuVwa9k6IA2zcuJG6vEi9oOCnGeLxeFiyZAn279+PxMREdrTXmTNnMHToUJw8eZKdJLA6u3btAvC4JcbWIaL1xdXVlV309IcffqjTqC3zel807J0QQhonCn6aqQEDBmDnzp24evUq4uLi4Onpifj4eGRkZGD16tU1rs2l1+vZuXMGDBhQH1Wus/79+6NFixYoKyvDhg0bbC7H1dUVRqORhr2TBoFaIElzYq+/dwp+mrEBAwbgzp07OHHiBLZs2YJ9+/Zh//796NevX43nnjp1CqWlpfDz86swpXtDxePx2NyfH374oU6LlZqHvev1ejvVjhDrmFtmq1vxnJCmxpxvWdfpRpw6zw9xPh6Ph5deegkAkJOTU+uAwNwv/+abb9Z6CYyG4M0338TSpUtx7949bNy4EUlJSTaVIxQKUVJSAoVCAW9vbzvXkpCaubi4QCwWo7CwEK6urnabBJCQhohhGKhUKhQUFMDLy6vOc8rRPD8EwONurPv378PF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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Plot ML fit for pEpoR\n", + "fig, ax = plt.subplots(figsize=(6.5, 3.5))\n", + "t, pEpoR = simulate_pEpoR()\n", + "sigma_pEpoR = 0.0274 + 0.1 * pEpoR\n", + "ax.fill_between(\n", + " t,\n", + " pEpoR - 2 * sigma_pEpoR,\n", + " pEpoR + 2 * sigma_pEpoR,\n", + " color=\"black\",\n", + " alpha=0.10,\n", + " interpolate=True,\n", + " label=\"2-sigma error bands\",\n", + ")\n", + "ax.plot(t, pEpoR, color=\"black\", label=\"MLE\")\n", + "ax.plot(\n", + " df_pEpoR[\"time\"],\n", + " df_pEpoR[\"measurement\"],\n", + " \"o\",\n", + " color=\"black\",\n", + " markerfacecolor=\"none\",\n", + " label=\"experimental data\",\n", + ")\n", + "ylim1 = ax.get_ylim()[0]\n", + "ax.plot(\n", + " nodes,\n", + " len(nodes) * [ylim1],\n", + " \"x\",\n", + " color=\"black\",\n", + " label=\"spline nodes\",\n", + " zorder=10,\n", + " clip_on=False,\n", + ")\n", + "ax.set_ylim(ylim1, ax.get_ylim()[1])\n", + "ax.set_xlabel(\"time\")\n", + "ax.set_ylabel(\"pEpoR\")\n", + "ax.legend();" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "75c999c0-af7b-4ee2-9e14-52c1c7d2e5bc", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Plot ML fit for pSTAT5\n", + "fig, ax = plt.subplots(figsize=(6.5, 3.5))\n", + "t, pSTAT5 = simulate_pSTAT5()\n", + "ax.plot(t, pSTAT5, color=\"black\", label=\"MLE\")\n", + "ax.plot(\n", + " df_pSTAT5[\"time\"],\n", + " df_pSTAT5[\"measurement\"],\n", + " \"o\",\n", + " color=\"black\",\n", + " markerfacecolor=\"none\",\n", + " label=\"experimental data\",\n", + ")\n", + "ylim1 = ax.get_ylim()[0]\n", + "ax.plot(\n", + " nodes,\n", + " len(nodes) * [ylim1],\n", + " \"x\",\n", + " color=\"black\",\n", + " label=\"spline nodes\",\n", + " zorder=10,\n", + " clip_on=False,\n", + ")\n", + "ax.set_ylim(ylim1, ax.get_ylim()[1])\n", + "ax.set_xlabel(\"time\")\n", + "ax.set_ylabel(\"pSTAT5\")\n", + "ax.legend();" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "226588b1-32b4-4aba-beac-fd70f0e79697", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Plot ML fit for tSTAT5\n", + "fig, ax = plt.subplots(figsize=(6.5, 3.5))\n", + "t, tSTAT5 = simulate_tSTAT5()\n", + "ax.plot(t, tSTAT5, color=\"black\", label=\"MLE\")\n", + "ax.plot(\n", + " df_tSTAT5[\"time\"],\n", + " df_tSTAT5[\"measurement\"],\n", + " \"o\",\n", + " color=\"black\",\n", + " markerfacecolor=\"none\",\n", + " label=\"experimental data\",\n", + ")\n", + "ylim1 = ax.get_ylim()[0]\n", + "ax.plot(\n", + " nodes,\n", + " len(nodes) * [ylim1],\n", + " \"x\",\n", + " color=\"black\",\n", + " label=\"spline nodes\",\n", + " zorder=10,\n", + " clip_on=False,\n", + ")\n", + "ax.set_ylim(ylim1, ax.get_ylim()[1])\n", + "ax.set_xlabel(\"time\")\n", + "ax.set_ylabel(\"tSTAT5\")\n", + "ax.legend();" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "65bcf2fe-0189-4f92-bee1-d4229717c535", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Store results for later\n", + "all_results[\"5 nodes, FD\"] = (pypesto_problem, pypesto_result)" + ] + }, + { + "cell_type": "markdown", + "id": "ec09ff5d-d370-4fac-ab90-6806a2d5945a", + "metadata": { + "tags": [] + }, + "source": [ + "## Spline approximation with many nodes, using finite differences for the derivatives\n", + "Five nodes is arguably not enough to represent all plausible input choices. Increasing the number of nodes would give the spline more freedom and it can be done with minimal changes to the example above. However, more degrees of freedom mean more chance of overfitting. Thus, following (Schelker et al., 2012), we will add a regularization term consisting in the squared L2 norm of the spline's curvature, which promotes smoother and less oscillating functions. The value for the regularization strength $\\lambda$ is chosen by comparing the sum of squared normalized residuals with its expected value, which can be computing by assuming it is roughly $\\chi^2$-distributed." + ] + }, + { + "cell_type": "markdown", + "id": "f0e8d4ef-33f4-4e4a-8754-539251aa4d11", + "metadata": {}, + "source": [ + "### Creating the PEtab model" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "9ea0c348-88d1-4d0e-845f-dd1dc1a43edc", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Problem name\n", + "name = \"Swameye_PNAS2003_15nodes_FD\"" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "cefd1af3-dac6-4e0d-92a0-33016dce168d", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Create spline for pEpoR\n", + "nodes = [0, 2.5, 5.0, 7.5, 10.0, 12.5, 15.0, 17.5, 20, 25, 30, 35, 40, 50, 60]\n", + "values_at_nodes = [\n", + " sp.Symbol(f\"pEpoR_t{str(t).replace('.', '_dot_')}\") for t in nodes\n", + "]\n", + "spline = amici.splines.CubicHermiteSpline(\n", + " sbml_id=\"pEpoR\",\n", + " evaluate_at=amici.sbml_utils.amici_time_symbol,\n", + " nodes=nodes,\n", + " values_at_nodes=values_at_nodes,\n", + " extrapolate=(None, \"constant\"),\n", + " bc=\"auto\",\n", + " logarithmic_parametrization=True,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "0851dd99-83e8-4e7c-8678-7f0914d2bf01", + "metadata": {}, + "source": [ + "The regularization term can be easily computed by symbolic manipulation of the spline expression using AMICI and SymPy. Since it is very commonly used, we already provide a function for it in AMICI. Note: we regularize the curvature of the spline, which for positivity-enforcing spline is the logarithm of the function.\n", + "\n", + "In order add the regularization term to the PEtab likelihood, a dummy observable has to be created." + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "525ed7b4-999d-40ad-9383-8f5c1157088f", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Compute L2 norm of the curvature of pEpoR\n", + "regularization = spline.squared_L2_norm_of_curvature()" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "46182f2e-a8a4-4a7f-8072-e8034ed09c1a", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Add a parameter for regularization strength\n", + "reg_parameters_df = pd.DataFrame(\n", + " dict(\n", + " parameterScale=\"log10\",\n", + " lowerBound=1e-6,\n", + " upperBound=1e6,\n", + " nominalValue=1.0,\n", + " estimate=0,\n", + " ),\n", + " index=pd.Series([\"regularization_strength\"], name=\"parameterId\"),\n", + ")\n", + "# Encode regularization term as an additional observable\n", + "reg_observables_df = pd.DataFrame(\n", + " dict(\n", + " observableFormula=f\"sqrt({regularization})\".replace(\"**\", \"^\"),\n", + " observableTransformation=\"lin\",\n", + " noiseFormula=\"1/sqrt(regularization_strength)\",\n", + " noiseDistribution=\"normal\",\n", + " ),\n", + " index=pd.Series([\"regularization\"], name=\"observableId\"),\n", + ")\n", + "# and correspoding measurement\n", + "reg_measurements_df = pd.DataFrame(\n", + " dict(\n", + " observableId=\"regularization\",\n", + " simulationConditionId=\"condition1\",\n", + " measurement=0,\n", + " time=0,\n", + " observableTransformation=\"lin\",\n", + " ),\n", + " index=pd.Series([0]),\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "c8214d30-1cb8-4575-9fef-2e485cabf319", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Add spline formula to SBML model\n", + "sbml_doc = libsbml.SBMLReader().readSBML(\n", + " os.path.join(\"Swameye_PNAS2003\", \"swameye2003_model.xml\")\n", + ")\n", + "sbml_model = sbml_doc.getModel()\n", + "spline.add_to_sbml_model(\n", + " sbml_model, auto_add=True, y_nominal=0.1, y_constant=True\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "a75cf7db-918e-4048-a77b-082cab06dcb8", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Extra parameters associated to the spline\n", + "spline_parameters_df = pd.DataFrame(\n", + " dict(\n", + " parameterScale=\"log\",\n", + " lowerBound=0.001,\n", + " upperBound=10,\n", + " nominalValue=0.1,\n", + " estimate=1,\n", + " ),\n", + " index=pd.Series(list(map(str, values_at_nodes)), name=\"parameterId\"),\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "4fdd82f8-adc5-471f-aa80-6856834e6913", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Create PEtab problem\n", + "petab_problem = petab.Problem(\n", + " sbml_model,\n", + " condition_df=petab.conditions.get_condition_df(\n", + " os.path.join(\"Swameye_PNAS2003\", \"swameye2003_conditions.tsv\")\n", + " ),\n", + " measurement_df=petab.core.concat_tables(\n", + " [\n", + " os.path.join(\"Swameye_PNAS2003\", \"swameye2003_measurements.tsv\"),\n", + " reg_measurements_df,\n", + " ],\n", + " petab.measurements.get_measurement_df,\n", + " ).reset_index(drop=True),\n", + " parameter_df=petab.core.concat_tables(\n", + " [\n", + " os.path.join(\"Swameye_PNAS2003\", \"swameye2003_parameters.tsv\"),\n", + " spline_parameters_df,\n", + " reg_parameters_df,\n", + " ],\n", + " petab.parameters.get_parameter_df,\n", + " ),\n", + " observable_df=petab.core.concat_tables(\n", + " [\n", + " os.path.join(\"Swameye_PNAS2003\", \"swameye2003_observables.tsv\"),\n", + " reg_observables_df,\n", + " ],\n", + " petab.observables.get_observable_df,\n", + " ),\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "id": "43e47476-8f75-4d63-a57d-09b9b55f086b", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Check whether PEtab model is valid\n", + "assert not petab.lint_problem(petab_problem)" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "e4f9f7a8-dcab-431e-8f80-e10605bdb69c", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Save PEtab problem to disk\n", + "# import shutil\n", + "# shutil.rmtree(name, ignore_errors=True)\n", + "# os.mkdir(name)\n", + "# petab_problem.to_files_generic(prefix_path=name)" + ] + }, + { + "cell_type": "markdown", + "id": "784aebee-b501-4fa8-808e-ff6277c9432c", + "metadata": {}, + "source": [ + "### Creating the pyPESTO problem" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "id": "19654099-93d0-469e-bb03-82f1d9319419", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Problem must be \"flattened\" to be used with AMICI\n", + "petab.core.flatten_timepoint_specific_output_overrides(petab_problem)" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "id": "fd8e3502-f9de-457e-9652-95bea94c0972", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Check whether simulation from the PEtab problem works\n", + "# import amici.petab_simulate\n", + "# simulator = amici.petab_simulate.PetabSimulator(petab_problem)\n", + "# simulator.simulate(noise=False)" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "id": "a7104b34-d33c-46d8-afa5-1278070d6af1", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Import PEtab problem into pyPESTO\n", + "pypesto_problem = pypesto.petab.PetabImporter(\n", + " petab_problem, model_name=name\n", + ").create_problem()" + ] + }, + { + "cell_type": "markdown", + "id": "0c890937-c9e8-4617-992f-65d39479ed55", + "metadata": {}, + "source": [ + "### Maximum Likelihood estimation\n", + "We will optimize the problem for different values of the regularization strength $\\lambda$, then compute the sum of squared normalized residuals for each of the resulting parameter vectors. The one for which such a value is nearest to its expected value of $15$ (the number of observations from the input function) will be chosen as the final estimate." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "116f6d10-9aba-4db7-88fb-f503b1b5d408", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Try different regularization strengths\n", + "regstrengths = np.asarray([1, 10, 40, 75, 150, 500])\n", + "if os.getenv(\"GITHUB_ACTIONS\") is not None:\n", + " regstrengths = np.asarray([75])\n", + "regproblems = {}\n", + "regresults = {}\n", + "\n", + "for regstrength in regstrengths:\n", + " # Fix parameter in pypesto problem\n", + " name = f\"Swameye_PNAS2003_15nodes_FD_reg{regstrength}\"\n", + " pypesto_problem.fix_parameters(\n", + " pypesto_problem.x_names.index(\"regularization_strength\"),\n", + " np.log10(\n", + " regstrength\n", + " ), # parameter is specified as log10 scale in PEtab\n", + " )\n", + " regproblem = copy.deepcopy(pypesto_problem)\n", + "\n", + " # Load existing results if available\n", + " if os.path.exists(f\"{name}.h5\"):\n", + " regresult = pypesto.store.read_result(f\"{name}.h5\", problem=regproblem)\n", + " else:\n", + " regresult = None\n", + " # Overwrite\n", + " # regresult = None\n", + "\n", + " # Parallel multistart optimization with pyPESTO and FIDES\n", + " if n_starts > 0:\n", + " if regresult is None:\n", + " new_ids = [str(i) for i in range(n_starts)]\n", + " else:\n", + " last_id = max(int(i) for i in regresult.optimize_result.id)\n", + " new_ids = [\n", + " str(i) for i in range(last_id + 1, last_id + n_starts + 1)\n", + " ]\n", + " regresult = pypesto.optimize.minimize(\n", + " regproblem,\n", + " n_starts=n_starts,\n", + " ids=new_ids,\n", + " optimizer=pypesto_optimizer,\n", + " engine=pypesto_engine,\n", + " result=regresult,\n", + " )\n", + " regresult.optimize_result.sort()\n", + " if regresult.optimize_result.x[0] is None:\n", + " raise Exception(\n", + " \"All multistarts failed (n_starts is probably too small)! If this error occurred during CI, just run the workflow again.\"\n", + " )\n", + "\n", + " # Save results to disk\n", + " # pypesto.store.write_result(regresult, f'{name}.h5', overwrite=True)\n", + "\n", + " # Store result\n", + " regproblems[regstrength] = regproblem\n", + " regresults[regstrength] = regresult" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "id": "5d3ef681-81f0-423d-9b78-18f3b3939adb", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Target value is 15\n", + "Regularization strength: 1. Statistic is 6.794369874307712\n", + "Regularization strength: 10. Statistic is 8.435094498146606\n", + "Regularization strength: 40. Statistic is 11.83872830962955\n", + "Regularization strength: 75. Statistic is 15.030926511510327\n", + "Regularization strength: 150. Statistic is 19.971139477161476\n", + "Regularization strength: 500. Statistic is 32.44623424533765\n" + ] + } + ], + "source": [ + "# Compute sum of squared normalized residuals\n", + "print(f\"Target value is {len(df_pEpoR['time'])}\")\n", + "regstrengths = sorted(regproblems.keys())\n", + "stats = []\n", + "for regstrength in regstrengths:\n", + " t, pEpoR = simulate_pEpoR(\n", + " N=None,\n", + " problem=regproblems[regstrength],\n", + " result=regresults[regstrength],\n", + " )\n", + " assert np.array_equal(df_pEpoR[\"time\"], t[:-1])\n", + " pEpoR = pEpoR[:-1]\n", + " sigma_pEpoR = 0.0274 + 0.1 * pEpoR\n", + " stat = np.sum(((pEpoR - df_pEpoR[\"measurement\"]) / sigma_pEpoR) ** 2)\n", + " print(f\"Regularization strength: {regstrength}. Statistic is {stat}\")\n", + " stats.append(stat)\n", + "# Select best regularization strength\n", + "chosen_regstrength = regstrengths[\n", + " np.abs(np.asarray(stats) - len(df_pEpoR[\"time\"])).argmin()\n", + "]" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "id": "6e362c17-4222-48ce-8db1-9f9956078e2b", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAkIAAAFjCAYAAADRv2QOAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAABcZElEQVR4nO3deXhM5/s/8PckMQlZZZU9sRSxBLFG1L7EVqqotgQtLVFVpaW0hNZaW0m1qkWVlio+al9qqX1P7URiS5OQkF0imXl+f/jNfI1sc2LGLHm/risX85wzz7nPzEnmnmc7MiGEABEREVE5ZGHoAIiIiIgMhYkQERERlVtMhIiIiKjcYiJERERE5RYTISIiIiq3mAgRERFRucVEiIiIiMotJkJERERUbjERIiIionKLiRCZveTkZLzxxhtwcXGBTCbDwoULtX7urVu3IJPJsHLlSnXZ1KlTIZPJdBZfUcfQt65du2LYsGEv7XhlpevXWiUgIACDBw/Web3GetzyRiaTYdSoUYYOQy9SU1Nha2uL7du3GzoUs8FEiIq0fv16yGQybNq0qdC24OBgyGQy7N+/v9A2Pz8/hIaGSjrWd999p9ck4OOPP8auXbswceJErF69Gl26dNHbsV62o0ePYurUqUhLS9P6OUeOHMHu3bvx2Wef6S+wcqws74kxWLt2raQvCYZmCq/zgQMHIJPJiv35+uuv1fuuXLmy2P2SkpLU+7m4uOC9997DF198YYhTMktWhg6AjFNYWBgA4PDhw+jdu7e6PCMjAxcvXoSVlRWOHDmCtm3bqrfdvXsXd+/exZtvvinpWN999x1cXV319k3577//xmuvvYZx48bppX5DOnr0KKKiojB48GA4OTlp9Zy5c+eiffv2qF69un6DM2LXrl2DhYV+vgeW9J7o87gvau3atbh48SLGjBlj6FC0UpZr/2WrXbs2Vq9eXah89erV2L17Nzp16lRo27Rp0xAYGKhR9vz5ffDBB/j222/x999/o127djqNuTxiIkRF8vLyQmBgIA4fPqxRfuzYMQgh0Ldv30LbVI9VSZQhFRQUQKlUQi6X4/79+0b7h/Jlu3//PrZt24bvv/++1H2zs7Nha2v7EqJ6OYQQyM3NRcWKFWFtbW2QGAx1XF3Lzc2FXC432qTOWHh4eOCdd94pVB4VFYUaNWqgSZMmhbaFh4ejcePGJdZbu3Zt1K1bFytXrmQipAO8iqlYYWFhOHfuHB4/fqwuO3LkCOrUqYPw8HAcP34cSqVSY5tMJkPLli0BACtWrEC7du3g7u4Oa2trBAUFYenSpRrHCAgIwKVLl3Dw4EF1M3CbNm3U29PS0jBmzBj4+vrC2toa1atXx+zZszWOqxpj880332DhwoWoVq0arK2t8d1330Emk0EIgejoaHX9APDw4UOMGzcO9erVg52dHRwcHBAeHo6YmBidvX5t2rRB3bp1cebMGYSGhqJixYoIDAzUKgkBnrZktWrVCra2tnBycsJrr72GK1euqLdPnToV48ePBwAEBgaqz+/WrVvF1rlt2zYUFBSgQ4cOGuWqZvmDBw9i5MiRcHd3h4+Pj3r7jh071LHY29ujW7duuHTpUqH6//jjDwQFBcHGxgZ169bFpk2bMHjwYAQEBKj3UXUXHDhwQOO52o6V0ua6Ap5eW927d8euXbvQuHFjVKxYET/88IN627MtkCV1X6hez3///ReDBw9G1apVYWNjgypVqmDo0KFITU1V11Pae1LUGKG4uDj07dsXzs7OqFSpEpo3b45t27Zp7KN6zdavX4+vv/4aPj4+sLGxQfv27REbG1vi6wUAmZmZGDNmDAICAmBtbQ13d3d07NgRZ8+eBfD0Wt22bRtu376tjln1nqmO/fvvv2Py5Mnw9vZGpUqVkJGRAQA4ceIEunTpAkdHR1SqVAmtW7fGkSNHNI6vGusVGxurbsFxdHTEkCFDkJOTo7Hv48ePMXr0aLi6usLe3h49e/ZEQkICZDIZpk6dqtXrrLJ582bUrVsX1tbWqFOnDnbu3Fnqa6VvJ0+eRGxsLN5+++1i98nMzIRCoSixno4dO+Kvv/6CEELXIZY7bBGiYoWFhWH16tU4ceKEOjk5cuQIQkNDERoaivT0dFy8eBH169dXb6tVqxZcXFwAAEuXLkWdOnXQs2dPWFlZ4a+//sLIkSOhVCoRGRkJAFi4cCE+/PBD2NnZYdKkSQCefosCgJycHLRu3RoJCQl4//334efnh6NHj2LixIlITEwsNJ5hxYoVyM3NxfDhw2FtbY1GjRph9erVGDhwIDp27IhBgwap942Li8PmzZvRt29fBAYGIjk5GT/88ANat26Ny5cvw8vLSyev4aNHj9C1a1f069cPAwYMwPr16zFixAjI5XIMHTq02Oft3bsX4eHhqFq1KqZOnYrHjx9j8eLFaNmyJc6ePYuAgAC8/vrruH79On777TcsWLAArq6uAAA3N7di6z169ChcXFzg7+9f5PaRI0fCzc0NX375JbKzswE8bcaPiIhA586dMXv2bOTk5GDp0qXqRFn1gblt2zb0798f9erVw8yZM/Ho0SO8++678Pb2LuOrVzRtriuVa9euYcCAAXj//fcxbNgw1KxZs8g6i+q+mDx5Mu7fvw87OzsAwJ49exAXF4chQ4agSpUquHTpEpYtW4ZLly7h+PHjkMlkkt+T5ORkhIaGIicnB6NHj4aLiwtWrVqFnj17YsOGDRrd0gAwa9YsWFhYYNy4cUhPT8ecOXPw9ttv48SJEyW+Zh988AE2bNiAUaNGISgoCKmpqTh8+DCuXLmCRo0aYdKkSUhPT8e9e/ewYMECAFCft8r06dMhl8sxbtw45OXlQS6X4++//0Z4eDhCQkIwZcoUWFhYqBPVf/75B02bNtWoo1+/fggMDMTMmTNx9uxZLF++HO7u7pg9e7Z6n8GDB2P9+vUYOHAgmjdvjoMHD6Jbt24a9WjzOh8+fBgbN27EyJEjYW9vj2+//RZ9+vTBnTt31H+jipOSklLidhV7e3vJrXxr1qwBgGITobZt2yIrKwtyuRydO3fGvHnzUKNGjUL7hYSEYMGCBbh06RLq1q0rKQZ6jiAqxqVLlwQAMX36dCGEEPn5+cLW1lasWrVKCCGEh4eHiI6OFkIIkZGRISwtLcWwYcPUz8/JySlUZ+fOnUXVqlU1yurUqSNat25daN/p06cLW1tbcf36dY3yCRMmCEtLS3Hnzh0hhBDx8fECgHBwcBD3798vVA8AERkZqVGWm5srFAqFRll8fLywtrYW06ZN0ygDIFasWKEumzJlitDmV6d169YCgJg3b566LC8vTzRo0EC4u7uLJ0+eFHsM1T6pqanqspiYGGFhYSEGDRqkLps7d64AIOLj40uNRwghwsLCREhISKHyFStWCAAiLCxMFBQUqMszMzOFk5OTxvsqhBBJSUnC0dFRo7xevXrCx8dHZGZmqssOHDggAAh/f3912f79+wUAsX//fo06tX2ttb2u/P39BQCxc+fOQvv7+/uLiIiIQuUqc+bMEQDEL7/8UuJxf/vtNwFAHDp0SF1W0nvy/HHHjBkjAIh//vlHXZaZmSkCAwNFQECA+hpVvWa1a9cWeXl56n0XLVokAIgLFy4Uey5CCOHo6Fjod+B53bp103ifVFTHrlq1qsZroFQqRY0aNUTnzp2FUqlUl+fk5IjAwEDRsWNHdZnqfRw6dKhG3b179xYuLi7qx2fOnBEAxJgxYzT2Gzx4sAAgpkyZoi4r6XUGIORyuYiNjVWXxcTECABi8eLFJb4Oqudr8/PstaqNgoIC4eHhIZo2bVpo27p168TgwYPFqlWrxKZNm8TkyZNFpUqVhKurq/pv3bOOHj0qAIh169ZJioEKY9cYFat27dpwcXFRj/2JiYlBdna2elZYaGiougn82LFjUCgUGuODKlasqP5/eno6UlJS0Lp1a8TFxSE9Pb3U4//xxx9o1aoVKleujJSUFPVPhw4doFAocOjQIY39+/TpU2JryLOsra3V4xsUCgVSU1NhZ2eHmjVrqrsLdMHKygrvv/+++rFcLsf777+P+/fv48yZM0U+JzExEefPn8fgwYPh7OysLq9fvz46duz4QtNmU1NTUbly5WK3Dxs2DJaWlurHe/bsQVpaGgYMGKDxHlhaWqJZs2bqmYP//fcfLly4gEGDBmm0JLRu3Rr16tUrc7xFkXJdBQYGonPnzpLq379/PyZOnIgPP/wQAwcOLPK4ubm5SElJQfPmzQGgzNfM9u3b0bRpU43fGzs7OwwfPhy3bt3C5cuXNfYfMmQI5HK5+nGrVq0APG3hLImTkxNOnDiB//77r0xxAkBERITGa3D+/HncuHEDb731FlJTU9XXRnZ2Ntq3b49Dhw5pdGEDT1umntWqVSukpqaqu9lUXVcjR47U2O/DDz+UHG+HDh1QrVo19eP69evDwcGh1NcKeHrda/Mj9drat28fkpOTi2wN6tevH1asWIFBgwahV69emD59Onbt2oXU1FSN2WUqqt9jbVuvqHjsGqNiyWQyhIaGqv+gHTlyBO7u7urZRqGhoViyZAkAqBOiZ/+gHzlyBFOmTMGxY8cKjQNIT0+Ho6Njice/ceMG/v3332KTm/v372s8fn6mRUmUSiUWLVqE7777DvHx8Rr98aU1m0vh5eVVaMDxK6+8AuDpmBjVB+mzbt++DQBFduPUrl0bu3bteqGBzKKEMQXPv4Y3btwAgGIHZDo4OGjEXNRMtOrVq+s0uZRyXUm5JgDg3r176N+/P1q2bIn58+drbHv48CGioqLw+++/F7r2tEnsi3L79m00a9asUHnt2rXV25/t9vDz89PYT/Vh+OjRoxKPM2fOHERERMDX1xchISHo2rUrBg0ahKpVq2oda3HXRkRERLHPSU9P10i8S4rfwcEBt2/fhoWFRaFjlWWG4/PHUh2vtNcKQKExdLqyZs0aWFpaon///lrtHxYWhmbNmmHv3r2Ftql+j/WxzlZ5w0SIShQWFoa//voLFy5cUI8PUgkNDcX48eORkJCAw4cPw8vLS/2H9ebNm2jfvj1q1aqF+fPnw9fXF3K5HNu3b8eCBQsKfVMsilKpRMeOHfHpp58WuV2VUKg8+221NDNmzMAXX3yBoUOHYvr06XB2doaFhQXGjBmjVWymysXFpcQPgudfQ9VrsXr1alSpUqXQ/lZW0v+EFPeHu7TBoYD060rKNfHkyRO88cYbsLa2xvr16wudW79+/XD06FGMHz8eDRo0gJ2dHZRKJbp06fLSrplnW+ueVVJyCzyNvVWrVti0aRN2796NuXPnYvbs2di4cSPCw8O1OnZx18bcuXPRoEGDIp/z/DijssZfFi9yrGfX7SmJo6Oj1tfY48ePsWnTJnTo0EE9DlIbvr6+uHbtWqFy1e+xanwUlR0TISrRs+sJHTlyRGONkZCQEFhbW+PAgQM4ceIEunbtqt72119/IS8vD1u2bNH4ZlbUIozFfTBWq1YNWVlZevl2tmHDBrRt2xY//fSTRnlaWppO/7D8999/hVpvrl+/DgAaM6mepRrIXNQfv6tXr8LV1VVdn9Rvg7Vq1cKff/6p9f6qrgV3d/cS3wdVzEXNYHq+TNUK8PxCeKpWpZJIua6kGj16NM6fP49Dhw4V+qB69OgR9u3bh6ioKHz55ZfqclWryLOkvCf+/v7Fvs+q7bri6emJkSNHYuTIkbh//z4aNWqEr7/+Wp0ISb2WVNeGg4ODzn5H/f39oVQqER8frzFAuKjrSp8tIZ6enlrtt2LFCq3XP9uyZQsyMzNLnC1WlLi4uCJbxePj4wH8X+shlR3HCFGJGjduDBsbG6xZswYJCQkaLUKqmVnR0dHIzs7W6BZTfRt79ttXeno6VqxYUegYtra2Ra4O269fPxw7dgy7du0qtC0tLQ0FBQVlPi9LS8tC3wz/+OMPJCQklLnOohQUFKinbANPWx1++OEHuLm5ISQkpMjneHp6okGDBli1apXG63Lx4kXs3r1bI+FUJUTarq7bokULPHr0SKtxEgDQuXNnODg4YMaMGcjPzy+0/cGDBwCedgHWrVsXv/zyC7KystTbDx48iAsXLmg8x9/fH5aWloXGeH333XelxiPlupJixYoV+OGHHxAdHV1oplNxxwVQ5ErMUt6Trl274uTJkzh27Ji6LDs7G8uWLUNAQACCgoIknEXRFApFoa47d3d3eHl5IS8vTyNuKV18ISEhqFatGr755huN91xFdW1IoRpz8/y1sHjx4kL7Sr32pdDHGKG1a9eiUqVKhWYCqhT1em3fvh1nzpwpcjX8M2fOwNHREXXq1NH+xKhIbBGiEsnlcjRp0gT//PMPrK2tC314h4aGYt68eQA0xwd16tQJcrkcPXr0wPvvv4+srCz8+OOPcHd3R2JiokYdISEhWLp0Kb766itUr14d7u7uaNeuHcaPH48tW7age/fuGDx4MEJCQpCdnY0LFy5gw4YNuHXrVplbb7p3745p06ZhyJAhCA0NxYULF7BmzRpJYya04eXlhdmzZ+PWrVt45ZVXsG7dOpw/fx7Lli1DhQoVin3e3LlzER4ejhYtWuDdd99VT593dHRUr6UCQP1+TJo0CW+++SYqVKiAHj16FDt+qFu3brCyssLevXsxfPjwUuN3cHDA0qVLMXDgQDRq1Ahvvvkm3NzccOfOHWzbtg0tW7ZUjxObMWMGXnvtNbRs2RJDhgzBo0ePsGTJEtStW1fjg9LR0RF9+/bF4sWLIZPJUK1aNWzdurXQuJuiSLmutJWSkoKRI0ciKCgI1tbW+PXXXzW29+7dGw4ODnj11VcxZ84c5Ofnw9vbG7t371Z/K3+WlPdkwoQJ+O233xAeHo7Ro0fD2dkZq1atQnx8PP7880+dLFiYmZkJHx8fvPHGGwgODoadnR327t2LU6dOqX93VXGvW7cOY8eORZMmTWBnZ4cePXoUW6+FhQWWL1+O8PBw1KlTB0OGDIG3tzcSEhKwf/9+ODg44K+//pIUa0hICPr06YOFCxciNTVVPX1e1Yr6bCuQ1GtfCl23Qj98+BA7duxAnz59CnUXqoSGhqJhw4Zo3LgxHB0dcfbsWfz888/w9fXF559/Xmj/PXv2oEePHhwjpAsGm69GJmPixIkCgAgNDS20bePGjQKAsLe315h2LYQQW7ZsEfXr1xc2NjYiICBAzJ49W/z888+FprwmJSWJbt26CXt7ewFAYyp9ZmammDhxoqhevbqQy+XC1dVVhIaGim+++abQ9PO5c+cWGT+KmT7/ySefCE9PT1GxYkXRsmVLcezYMdG6dWuN47/o9Pk6deqI06dPixYtWggbGxvh7+8vlixZorFfUccQQoi9e/eKli1biooVKwoHBwfRo0cPcfny5ULHmT59uvD29hYWFhZaTaXv2bOnaN++vUaZavr8qVOninzO/v37RefOnYWjo6OwsbER1apVE4MHDxanT5/W2O/3338XtWrVEtbW1qJu3bpiy5Ytok+fPqJWrVoa+z148ED06dNHVKpUSVSuXFm8//774uLFi1q91tpeV/7+/qJbt25Fns+z09hVr39xP6o67927J3r37i2cnJyEo6Oj6Nu3r/jvv/8KTesWovj3pKhp+zdv3hRvvPGGcHJyEjY2NqJp06Zi69athV5/AOKPP/7QKC/u2nlWXl6eGD9+vAgODhb29vbC1tZWBAcHi++++05jv6ysLPHWW28JJycnjSUPiju2yrlz58Trr78uXFxchLW1tfD39xf9+vUT+/btU++jeh8fPHig8VzVdffs+5adnS0iIyOFs7OzsLOzE7169RLXrl0TAMSsWbM0nl/c61zU77wQpS+boC/ff/+9ACC2bNlS7D6TJk0SDRo0EI6OjqJChQrCz89PjBgxQiQlJRXa98qVKwKA2Lt3rz7DLjdkQnBZSiJ9aNOmDVJSUnDx4kVDh6Lhn3/+QZs2bXD16tUiF2rTtQYNGsDNzQ179uzR+7HIPJ0/fx4NGzbEr7/+KnmMjTkaM2YMDh06hDNnzrBFSAc4RoionGnVqhU6deqEOXPm6LTe/Pz8QuO2Dhw4gJiYGI3bphCV5Nlb+qgsXLgQFhYWePXVVw0QkXFJTU3F8uXL8dVXXzEJ0hGTHyN09+5dDBw4EPfv34eVlRW++OIL9O3b19BhERm1HTt26LzOhIQEdOjQAe+88w68vLxw9epVfP/996hSpUqhhfSIijNnzhycOXMGbdu2hZWVFXbs2IEdO3Zg+PDh8PX1NXR4Bufi4lLk4HQqO5PvGktMTERycjIaNGiApKQkhISE4Pr162Z112wyTcbaNaYv6enpGD58OI4cOYIHDx7A1tYW7du3x6xZszRW+CUqyZ49exAVFYXLly8jKysLfn5+GDhwICZNmlSmdauISmPyidDzgoODsXXrVn5zICIiolIZfIzQoUOH0KNHD3h5eUEmk2Hz5s2F9omOjkZAQABsbGzQrFkznDx5ssi6zpw5A4VCwSSIiIiItGLwRCg7OxvBwcGIjo4ucrtqXYspU6bg7NmzCA4ORufOnQutOfLw4UMMGjQIy5YtexlhExERkRkwqq4xmUyGTZs2oVevXuqyZs2aoUmTJupF25RKJXx9ffHhhx9iwoQJAIC8vDx07NgRw4YN07hbdFHy8vI0VlNVKpV4+PAhXFxcOAKfiIjIDAghkJmZCS8vr1IXJjXqkWdPnjzBmTNnMHHiRHWZhYUFOnTooF6SXgiBwYMHo127dqUmQQAwc+ZMREVF6S1mIiIiMg53796Fj49PifsYdSKUkpIChUJR6AaIHh4e6psSHjlyBOvWrUP9+vXV44tWr16NevXqFVnnxIkTMXbsWPXj9PR0+Pn54e7du3BwcNDPiRARkdYUQok3/1mIlLzMYvdxs3HAb2EfwVJW9Ld9Y6mDDCMjIwO+vr6wt7cvdV+jToS0ERYWBqVSqfX+1tbWsLa2LlTu4ODARIiIyEhMaPwGPju/ttjtnzXog8qOTiZRBxmONkNejDqFdXV1haWlJZKTkzXKk5OTUaVKFQNFRURE+tbGow5Gub6KyhYVNco9bBwxu8FbaFulbql1tK1SF7MbvAV3a80vuS+7DjJuRt0iJJfLERISgn379qkHUCuVSuzbtw+jRo0ybHBERKQ32dnZaCD3QkOvXsitUhEPn2TD1doeDZwDJHVDta1SF696BOH8w1tIycs0WB1kvAyeCGVlZSE2Nlb9OD4+HufPn4ezszP8/PwwduxYREREoHHjxmjatCkWLlyI7OxsDBky5IWOGx0djejoaCgUihc9BSIi0rH09HQAgJOjE3xdX2xtOEuZBUJcqhq8DjJOBp8+f+DAAbRt27ZQeUREBFauXAkAWLJkCebOnYukpCQ0aNAA3377LZo1a6aT42dkZMDR0RHp6ekcI0REZASEELhy5QoKCgoQGBio1YBXomdJ+Ww3eCJkaEyEiIiMS2ZmJuLj42FpaYmgoCCu8UaSSflsZwcnEREZFVW3mKOjI5Mg0jsmQkREZDSEEP83PsjJybDBULlQpsHSd+7cwe3bt5GTkwM3NzfUqVOnyLV5iIiIpMjMzIRCoYCVlRVsbW0NHQ6VA1onQrdu3cLSpUvx+++/4969e3h2aJFcLkerVq0wfPhw9OnTp9T7ehgDzhojIjI+7Bajl02rjGX06NEIDg5GfHw8vvrqK1y+fBnp6el48uQJkpKSsH37doSFheHLL79E/fr1cerUKX3H/cIiIyNx+fJlk4iViKg8UCqVyMjIAPA0ESJ6GbRqEbK1tUVcXBxcXFwKbXN3d0e7du3Qrl07TJkyBTt37sTdu3fRpEkTnQdLRETmKysrCwqFAhUqVGC3GL00nD7P6fNEREbhzp07SEtLg6urK7y8vAwdDpkwTp8nIiKTwm4xMhSdJUJXrlxB1apcfpyIiKTLzMyEUqmEXC5HpUqVDB0OlSM6S4SePHmC27dv66o6IiIqR9LS0gBwthi9fFpPnx87dmyJ2x88ePDCwbxMnD5PRGQcFAoFu8XIYLQeLG1paYkGDRoUO+goKysLZ8+eNbnEgoOliYgMKy0tDXfu3IFcLkfNmjXZIkQvTMpnu9YtQtWrV8fHH3+Md955p8jt58+fR0hIiLRIiYio3FN1izk5OTEJopdO6zFCjRs3xpkzZ4rdLpPJUM5n4hMRkUQKhQKZmZkA2C1GhqF1i9C8efOQl5dX7Pbg4GAolUqdBEVEROVDRkYGhBCwtraGjY2NocOhckjrRKhKlSr6jIOIiMohdouRoUm++3xBQQEuXbqEpKQkAE8TpKCgIFSoUEHnwRERkflSKBTIysoCwG4xMhytEyGlUokvv/wS0dHR6rsDqzg6OmLUqFGIiooyiTvPExGR4aWnp0MIARsbG3aLkcFonbVMmDABy5Ytw6xZsxAXF4fs7GxkZ2cjLi4Os2fPxrJlyzBx4kR9xqpT0dHRCAoK4s1hiYgM5NlFFIkMRet1hKpUqYJVq1ahc+fORW7ftWsXBg0ahOTkZJ0GqG9cR4iI6OUrKCjAlStXIIRAzZo1YW1tbeiQyIzo5aarmZmZJd4N2NPTE9nZ2dpHSURE5ZaqW6xixYpMgsigtE6E2rRpg3HjxiElJaXQtpSUFHz22Wdo06aNLmMjIiIz9exsMSJD0nqw9Pfff4+uXbvC09MT9erVg4eHBwAgOTkZFy5cQFBQELZu3aq3QImIyDzk5+erexA4PogMTetEyNfXFzExMdi1axeOHz+unj7ftGlTzJgxA506deKMMSIiKpVq5nGlSpUgl8sNHA2Vd5LWEbKwsEB4eDjCw8P1FQ8REZk5douRMWETDhERvTRPnjxBTk4OAHaLkXF4oUTIwcEBcXFxuoqFiIjMnKpbzNbWlnckIKPwQomQKd9tngsqEhG9fKpEiK1BZCzKbddYZGQkLl++jFOnThk6FCKicoHdYmSMJA2WPnTokMZjhUKBkydP4t69e+qyV199VTeRERGRWVENkrazs2O3GBkNSYlQRESExuO8vDyMHz8eVlZPq5HJZBwzRERERWK3GBkjSYlQfHy8xmN7e3scPHgQVatW1WlQRERkXvLy8vD48WPIZDImQmRUyu0YISIienme7RZT9SIQGQMmQkREpHfsFiNj9UKJ0DvvvFPq7e2JiKh8y83NRW5uLrvFyCi9UPvk0qVLdRUHERGZKVVrkL29PSwtLQ0cDZGmMrcIPXnyBNeuXUNBQYEu4yEiIjMhhEBKSgru3r2L3Nxc9iCQUZKcCOXk5ODdd99FpUqVUKdOHdy5cwcA8OGHH2LWrFk6D5CIiExPYmIi9u3bh+PHjyMpKQn379/H2bNnkZiYaOjQiDRIToQmTpyImJgYHDhwADY2NuryDh06YN26dToNTp94iw0iIv1ITEzEmTNnkJubq1Gem5uLM2fOMBkioyI5Edq8eTOWLFmCsLAwyGQydXmdOnVw8+ZNnQanT7zFBhGR7gkhcOnSpRL3uXTpkknfq5LMi+RE6MGDB3B3dy9Unp2drZEYERFR+ZOamlqoJeh5ubm5SE1NfUkREZVMciLUuHFjbNu2Tf1YlfwsX74cLVq00F1kRERkcvLy8nS6H5G+SZ4+P2PGDISHh+Py5csoKCjAokWLcPnyZRw9ehQHDx7UR4xERGQirK2tdbofkb5JbhEKCwvD+fPnUVBQgHr16mH37t1wd3fHsWPHEBISoo8YiYjIRLi4uGhMpCmKjY0NXFxcXlJERCWTiXI+Yi0jIwOOjo5IT0/nGhdERDqgmjVWnJCQEHh6er7EiKi8kfLZLrlrLDs7Wz390cLCAtWqVUPDhg05UJqIiAAAnp6eCAkJQUxMjMaiuzY2NqhTpw6TIDIqWidCSqUSEyZMQHR0tHpGgKoxyc/PD4sXL0aPHj30EyUREZmUKlWq4MGDB8jNzYW7uzvs7Ozg4uLCL81kdLQeI/T5559j69atWLduHXbt2oWwsDDMmjULly9fxqBBg9C3b1/s3r1bn7ESEZGJUCgUAJ62Avn5+cHV1ZVJEBklrccIeXl5Yd26dWjVqhUAICEhAbVq1UJKSgqsra0xffp07NixA0ePHtVrwLrGMUJERLqXk5OD2NhYWFlZISgoyNDhUDkj5bNd6xahrKwseHt7qx97enoiNzcXjx49AgD06dMHMTExZQyZiIjMSX5+PgBALpcbOBKikmmdCNWrVw+//fab+vH69ethZ2eHKlWqAHg6hojrQhAREfB/iVCFChUMHAlRybQeLD1t2jR069YNW7ZsgY2NDY4ePYq5c+eqt+/cuRMNGzbUS5BERGRanjx5AoCJEBk/SesIxcTEYP369cjLy0Pnzp3RsWNHfcamV9HR0YiOjoZCocD169c5RoiISIdu376N9PR0eHl5wdXV1dDhUDkjZYwQF1TkYGkiIp2LjY1FTk4O/P394ejoaOhwqJzR64KKcXFxOHz4sHpBxapVq6Jjx45MIoiISI1dY2QqtE6EsrOzMXjwYPz5558Ant513t3dHQ8ePEDFihUxa9YsREZG6i1QIiIyDUqlUr2iNGeNkbHTetbY2LFjkZiYiH///RfXr1/H66+/jkGDBiEjIwOLFi3Cp59+irVr1+ozViIiMgGqGWMymQyWlpYGjoaoZFqPEXJzc8POnTvVd5h/9OgRvLy8kJqaikqVKiE6OhrLly/HuXPn9BqwrnGMEBGRbmVlZSEuLg7W1taoWbOmocOhckgvCyoWFBRoVGZnZ4eCggJkZ2cDADp16oSrV6+WMWQiIjIXXEOITInWiVCTJk2waNEi9eNFixbBzc0Nbm5uAJ5+A7Czs9N9hEREZFJUA6U5PohMgdaDpWfNmoWOHTvizz//hFwuR1JSElatWqXefvToUXTt2lUvQRIRkelgixCZEknrCCUmJmLr1q3Iy8tDu3btzOJGehwjRESkW3FxccjKyoKPjw+cnZ0NHQ6VQ3pbR8jT0xPDhg17oeCIiMi88YarZEokJUIxMTE4c+YM2rZti8DAQFy6dAnR0dFQKpXo3bs3OnfurK84iYjIBAgh2DVGJkXrwdIbN25ESEgIPv30U9SvXx979+5FWFgYbty4gVu3bqFbt25cR4iIqJxTKBRQKpUAmAiRadA6Efr6668RFRWFlJQU/Pjjj+jbty/Gjh2LPXv2YOfOnZg9e7bG3eiJiKj8UbUGWVlZwcJC648YIoPR+iq9du0a3n77bQBA//79kZ2djV69eqm39+7dG7GxsToPkIiITAe7xcjUaJ0I2dvbIzU1FQCQlpaGgoIC9WMASE1N5TpCRETlHNcQIlOjdSLUoUMHREZGYs2aNYiIiECnTp0wceJEXL16FdeuXcP48eMRFhamz1iJiMjIsUWITI3WidA333wDBwcHfPDBB3jy5AnWrVuHxo0bIygoCEFBQfjvv/8wa9YsfcZKRERGTtUixESITIWkBRWLEhcXh5ycHNSqVQtWVpJm4xsFLqhIRKQ7sbGxyMnJgb+/PxwdHQ0dDpVTeltQsShVq1Z90SqIiMhMsGuMTI3O5jaePn0ahw4d0lV1ehcdHY2goCA0adLE0KEQEZkFpVLJRIhMzgt3janUrl0b169fh0Kh0EV1Lw27xoiIdOPJkye4evUqZDIZ6tatC5lMZuiQqJx6qV1jKvv27VN/EyAiovLn2anzTILIVOgsEfLy8tJVVUREZILYLUamqMyJUEFBAfbv3487d+7A398fbdu2haWlpS5jIyIiE8Kp82SKtE6EPvzwQ3Tu3Bndu3fHvXv30LFjR9y4cQOurq5ISUlBUFAQduzYAW9vb33GS0RERkrVIsRVpcmUaD1r7I8//kBAQAAA4JNPPoGPjw+SkpKQlJSE+/fvw9/fH2PGjNFTmEREZOzYNUamSOsWofT0dNja2gIAjh49ij///BOurq4AAGdnZ8ycORNt27bVT5RERGT02DVGpkjrFqFXXnkFJ0+eBPD0BqwZGRka2zMzM6FUKnUbHRERmQQhBLvGyCRp3SL08ccfY9y4cfDw8MDEiRMxevRoLF68GLVr18a1a9fw0Ucf4fXXX9dnrEREZKSUSqX6yzBbhMiUaJ0IDR48GA8fPkS3bt0ghIBCoUCnTp3U23v27IkFCxboJUgiIjJuqm4xKysrWFjo7KYFRHonafr82LFjMXToUOzZswdxcXFQKpXw9PREy5YtUaNGDX3FSERERo4DpclUSV5HyMnJCX379tVHLEREZKI4UJpMFdsviYjohXGgNJmqMiVCDg4OiIuLK/R/IiIqn9g1RqaqTInQszes19HN64mIyISxa4xMFbvGiIjohbFrjEwVEyEiInohzy6myBYhMjVMhIiI6IWokiCZTAYrK8mTkYkMiokQERG9kGfHB8lkMgNHQyQNEyEiInoh7BYjU8ZEiIiIXggHSpMpK1Mi9M4778DBwaHQ/4mIqPzh1HkyZTJRzhcCysjIgKOjI9LT05nQERGVQXx8PDIzM+Ht7Q0XFxdDh0Mk6bNdcovQtGnTkJOTU6j88ePHmDZtmtTqiIjIxLFrjEyZ5EQoKioKWVlZhcpzcnIQFRWlk6CIiMh0sGuMTJnkREgIUeT0yJiYGDg7O+skKCIiMg0KhQJKpRIAEyEyTVqvfFW5cmXIZDLIZDK88sorGsmQQqFAVlYWPvjgA70ESURExknVGmRpaQlLS0sDR0MkndaJ0MKFCyGEwNChQxEVFQVHR0f1NrlcjoCAALRo0UIvQRIRkXHi+CAydVonQhEREQCAwMBAtGzZ0qiWUe/duzcOHDiA9u3bY8OGDYYOh4io3OBiimTqJI8Rat26NW7fvo3JkydjwIABuH//PgBgx44duHTpks4D1MZHH32EX375xSDHJiIqzzhQmkyd5ETo4MGDqFevHk6cOIGNGzeqZ5DFxMRgypQpOg9QG23atIG9vb1Bjk1EVJ6xa4xMneREaMKECfjqq6+wZ88ejQu/Xbt2OH78uOQADh06hB49esDLywsymQybN28utE90dDQCAgJgY2ODZs2a4eTJk5KPQ0REuseuMTJ1khOhCxcuoHfv3oXK3d3dkZKSIjmA7OxsBAcHIzo6usjt69atw9ixYzFlyhScPXsWwcHB6Ny5s7pLjoiIDIddY2TqJCdCTk5OSExMLFR+7tw5eHt7Sw4gPDwcX331VZHJFQDMnz8fw4YNw5AhQxAUFITvv/8elSpVws8//yz5WACQl5eHjIwMjR8iIpJOCIGCggIA7Boj0yU5EXrzzTfx2WefISkpCTKZDEqlEkeOHMG4ceMwaNAgnQb35MkTnDlzBh06dPi/gC0s0KFDBxw7dqxMdc6cOROOjo7qH19fX12FS0RUruTn56sX2TWmmcREUkhOhGbMmIFatWrB19cXWVlZCAoKwquvvorQ0FBMnjxZp8GlpKRAoVDAw8NDo9zDwwNJSUnqxx06dEDfvn2xfft2+Pj4lJgkTZw4Eenp6eqfu3fv6jRmIqLy4tnxQUXdcYDIFEhO4eVyOX788Ud8+eWXuHDhArKystCwYUPUqFFDH/FpZe/evVrva21tDWtraz1GQ0RUPnCgNJmDMrdl+vr6wtfXFwqFAhcuXMCjR49QuXJlXcYGV1dXWFpaIjk5WaM8OTkZVapU0emxiIhIGtVAaY4PIlMmuWtszJgx+OmnnwA8vcdY69at0ahRI/j6+uLAgQM6DU4ulyMkJAT79u1TlymVSuzbt4+38yAiMjC2CJE5kJwIbdiwAcHBwQCAv/76C3Fxcbh69So+/vhjTJo0SXIAWVlZOH/+PM6fPw8AiI+Px/nz53Hnzh0AwNixY/Hjjz9i1apVuHLlCkaMGIHs7GwMGTJE8rGeFR0djaCgIDRp0uSF6iEiKq+YCJE5kAkhhJQn2NjYIDY2Fj4+Phg+fDgqVaqEhQsXIj4+HsHBwZKnox84cABt27YtVB4REYGVK1cCAJYsWYK5c+ciKSkJDRo0wLfffotmzZpJOk5xMjIy4OjoiPT0dDg4OOikTiKi8uD69evIzc1FYGAgV/cnoyLls13yGCEPDw9cvnwZnp6e2LlzJ5YuXQoAyMnJgaWlpeRg27Rpg9JysVGjRmHUqFGS6yYiIv1hixCZA8mJ0JAhQ9CvXz94enpCJpOp1/g5ceIEatWqpfMAiYjI+CgUCigUCgBMhMi0SU6Epk6dinr16uHOnTvo27eveiq6paUlJkyYoPMAiYjI+KhagywtLcvUG0BkLLRKhJydnXH9+nW4urpi6NChWLRoUaH+4IiICL0ESERExof3GCNzodWssSdPnqgHQa9atQq5ubl6Depl4KwxIqKyU7UIcQ0hMnVazRrr2LEjkpOTERISglWrVqF///6oWLFikfuW9WaohsJZY0RE0iUlJeH+/ftwcXEp0w23ifRJ57PGfv31VyxYsAA3b94EAKSnp5tFqxAREZUNu8bIXGiVCHl4eGDWrFkAgMDAQKxevRouLi56DYyIiIwXu8bIXGg1RsjZ2RkpKSkAgLZt2/LCJyIq57iGEJmLcjtYmoiIykYIwUSIzIZWXWMtWrRAr169EBISAiEERo8ebTaDpYmISJqCggIIISCTyZgIkcmTPFhaJpOZxWDp6OhoREdHq1dGJSIi7Tw7UFomkxk4GqIXI/mmq4GBgTh9+rTZDJbm9HkiImnS0tJw584d2Nraolq1aoYOh6gQvd50NT4+vsyBERGR6eP4IDInWg2Wft7BgwfRo0cPVK9eHdWrV0fPnj3xzz//6Do2IiIyQqquMc4gJnMgORH69ddf0aFDB1SqVAmjR49WD5xu37491q5dq48YiYjIiLBFiMyJ5DFCtWvXxvDhw/Hxxx9rlM+fPx8//vgjrly5otMA9Y1jhIiIpLlx4wYeP36MgIAA/t0koyTls11yi1BcXBx69OhRqLxnz54cP0REVA6wa4zMieREyNfXF/v27StUvnfvXvj6+uokqJeBd58nIpJOoVColx1h1xiZA8mzxj755BOMHj0a58+fR2hoKADgyJEjWLlyJRYtWqTzAPUlMjISkZGR6uYzIiIqnWp8kKWlJSwtLQ0cDdGLk5wIjRgxAlWqVMG8efOwfv16AE/HDa1btw6vvfaazgMkIiLjwYHSZG4kJ0IA0Lt3b/Tu3VvXsRARkZF7dlVpInOg1RghiRPLiIjITKlahDhQmsyFVolQnTp18Pvvv6u/CRTnxo0bGDFiBGbNmqWT4IiIyLiwa4zMjVZdY4sXL8Znn32GkSNHomPHjmjcuDG8vLxgY2ODR48e4fLlyzh8+DAuXbqEUaNGYcSIEfqOm4iIDIBdY2RutEqE2rdvj9OnT+Pw4cNYt24d1qxZg9u3b+Px48dwdXVFw4YNMWjQILz99tuoXLmyvmMmIiIDYdcYmRtJg6XDwsIQFhamr1iIiMiICSHYNUZmp0w3XTUHXFCRiEiagoIC9eQZJkJkLiTfa8zc8F5jRETaycnJQWxsLCpUqIDatWsbOhyiYun1XmNERFQ+caA0mSMmQkREpBUOlCZzxESIiIi0woHSZI7KlAjdvHkTkydPxoABA3D//n0AwI4dO3Dp0iWdBkdERMaDXWNkjiQnQgcPHkS9evVw4sQJbNy4EVlZWQCAmJgYTJkyRecBEhGRcWDXGJkjyYnQhAkT8NVXX2HPnj0avwzt2rXD8ePHdRocEREZD3aNkTmSnAhduHChyDvPu7u7IyUlRSdBERGRcVEqlSgoKADAFiEyL5ITIScnJyQmJhYqP3fuHLy9vXUSFBERGRdVa5CFhQUsLDjPhsyH5Kv5zTffxGeffYakpCTIZDIolUocOXIE48aNw6BBg/QRIxERGZhqoLRcLodMJjNwNES6IzkRmjFjBmrVqgVfX19kZWUhKCgIr776KkJDQzF58mR9xKgXvMUGEZH2OD6IzFWZb7Fx584dXLx4EVlZWWjYsCFq1Kih69heCt5ig4iodMnJyUhOToazszN8fHwMHQ5RiaR8tku6+/yz/Pz84OfnV9anExGRCXm2a4zInEhOhIYOHVri9p9//rnMwRARkXFi1xiZK8mJ0KNHjzQe5+fn4+LFi0hLS0O7du10FhgRERkPJkJkriQnQps2bSpUplQqMWLECFSrVk0nQRERkfEQQrBrjMyWThaDsLCwwNixY7FgwQJdVEdEREakoKAAqnk1bBEic6OzVbFu3rypXnWUiIjMx7PdYlxDiMyN5K6xsWPHajwWQiAxMRHbtm1DRESEzgIjIiLjwPFBZM4kJ0Lnzp3TeGxhYQE3NzfMmzev1BllRERkelTjg5gIkTmSnAjt379fH3EQEZGRUrUIcaA0mSPeOY+IiErErjEyZ1q1CDVs2FDrAXJnz559oYCIiMi4cOo8mTOtEqFevXrpOQwiIjJWbBEic6ZVIjRlyhR9x0FEREZIqVSql0ZhIkTmqNyOEYqOjkZQUBCaNGli6FCIiIyWqjXIwsIClpaWBo6GSPdkQrVcqJYUCgUWLFiA9evX486dO+q+Y5WHDx/qNEB9y8jIgKOjI9LT0+Hg4GDocIiIjEpWVhbi4uJgbW2NmjVrGjocIq1I+WyX3CIUFRWF+fPno3///khPT8fYsWPx+uuvw8LCAlOnTi1rzEREZIQ4UJrMneREaM2aNfjxxx/xySefwMrKCgMGDMDy5cvx5Zdf4vjx4/qIkYiIDIQDpcncSU6EkpKSUK9ePQCAnZ0d0tPTAQDdu3fHtm3bdBsdEREZFBMhMneSEyEfHx8kJiYCAKpVq4bdu3cDAE6dOgVra2vdRkdERAbFrjEyd5ITod69e2Pfvn0AgA8//BBffPEFatSogUGDBvFeY0REZoYtQmTuJN9rbNasWer/9+/fH/7+/jh69Chq1KiBHj166DQ4IiIyHCEEW4TI7ElOhHJzc2FjY6N+3Lx5czRv3lynQRERkeEpFAqoVlixspL8cUFkEiR3jbm7uyMiIgJ79uyBUqnUR0xERGQEVK1BVlZWsLAot+vvkpmTfGWvWrUKOTk5eO211+Dt7Y0xY8bg9OnT+oiNiIgMSDU+iN1iZM7KNFj6jz/+QHJyMmbMmIHLly+jefPmeOWVVzBt2jR9xEhERAbAgdJUHpS5rdPe3h5DhgzB7t278e+//8LW1hZRUVG6jI2IiAxI1TXGRIjMWZkTodzcXKxfvx69evVCo0aN8PDhQ4wfP16XsRERkQGxa4zKA8nTAHbt2oW1a9di8+bNsLKywhtvvIHdu3fj1Vdf1Ud8RGTmFEolzsUmICU9G66OtmhY3RuWEgfmsg791HE+LhHJDzNQs0COMGdnyXUQmQLJiVDv3r3RvXt3/PLLL+jatSubTImozPadu4G56w/gflqWuszdyQ7j+7VB+4Y1WIfR1HEJ7k7/SKqDyFTIhGqRCC1lZmbC3t5eX/G8dBkZGXB0dER6ejocHBwMHQ5RubHv3A18umwrnv8DJPv//84Z3r3UD13WYZx1EBmalM92yS1C5pQEEZFhKJRKzF1/oNCHLQB12Zx1+9HA36XY7hiFUok56/5mHS+xDhmAb/44gDbB1dhNRmaDS4US0Ut3LjZBo+umKA/Ss7H3xAXU9q5c5PYrCY/wID2HdbzEOgSA5EdZOBebgMav+JZ4LCJTwUSIiF66lPRsrfbLU8pgZ2dXzLY01mGgOrR9/4hMQblNhKKjoxEdHQ2FQmHoUIjKHVdHW632C6oRiKpVi255eFhQAcAZ1mGAOrR9/4hMQZk7eWNjY7Fr1y48fvwYACBxzLXBRUZG4vLlyzh16pShQyEqdxpW94arQ8Vit8sAeFS2Q8Pq3iXW4e5kpx7EyzqMow4iUyM5EUpNTUWHDh3wyiuvoGvXrkhMTAQAvPvuu/jkk090HiARmZ/srCy82aJ6kdtUH8Lj+rYpcUCupYUFxvdro/Ec1mH4OohMjeSr+eOPP4aVlRXu3LmDSpUqqcv79++PnTt36jQ4IjI/eXl5uHPnDhpXdcfEN0Lh7qQ5XsW9sp3WU7TbN6yBOcO7w411GFUdRKZE8jpCVapUwa5duxAcHAx7e3vExMSgatWqiIuLQ/369ZGVVfJMEGPDdYSIXh6FQoGbN28iNzcXlSpVQtWqVSEAo1lJmXXotg4iQ9HrOkLZ2dkaLUEqDx8+hLW1tdTqiKicEEIgISEBubm5sLKygr+/Pyz+/wfri07FtrSwYB1GWAeRKZCc3rdq1Qq//PKL+rFMJoNSqcScOXPQtm1bnQZHROYjNTUVaWlpkMlk8PPz4+15iMgoSG4RmjNnDtq3b4/Tp0/jyZMn+PTTT3Hp0iU8fPgQR44c0UeMRGTisrOz1RMrPD09i13HhojoZZPcIlS3bl1cv34dYWFheO2115CdnY3XX38d586dQ7Vq1fQRIxGZsPz8fNy+fRtCCDg5OcHFxcXQIRERqZVpQUVHR0dMmjRJ17EQkZkRQuDOnTsoKCiAjY0NvL29IZMVt0oNEdHLJ7lFqHr16pg6dSpu3Lihj3iIyIwkJiYiOzsbFhYW8Pf3h6WlpaFDIiLSIDkRioyMxLZt21CzZk00adIEixYtQlJSkj5iIyITlpaWhpSUFACAn58fZ5USkVEq04KKp06dwtWrV9G1a1dER0fD19cXnTp10phNRkTl1+PHj3Hv3j0AgLu7O9foIiKjJXlBxaIcP34cI0aMwL///mtyNzHlgopEuqVQKBAbG4u8vDzY2dkhMDCQ44KI6KXS64KKzzp58iTWrl2LdevWISMjA3379n2R6ojIxAkhcPfuXeTl5aFChQrw8/NjEkRERk1yInT9+nWsWbMGv/32G+Lj49GuXTvMnj0br7/+OtcGIXpJFAoFLv5zFamJj+DiWRl1W9WSPBBZH3V41HRBRkYGZDIZ/P39YWX1Qt+1iIj0TvJfqVq1aqFJkyaIjIzEm2++CQ8PD33ERUTF+GfjCXw3ZgVS7qWqy1x9XDBy4RC0er2ZQetw9LBHr087o2tExyJvxUNEZGwkjxG6ceMGatQwn7sPc4wQmZJ/Np7AtL7fAM//1v7/3qcv/xhXaiJjLHUQEemLlM92ybPGzCkJIjIlCoUC341ZUTj5ANRlSz9eUeKEBWOpg4jIWGjVNebs7Izr16/D1dUVlStXLnHw48OHD3UWHBH9n4v/XNXohipEAA/upmLfhoOo1aJ6kbtcPRarVR17NxxArebF1HFcuzou/nMVwW3qFL8fEZER0CoRWrBgAezt7dX/5ywQopcvNfGRVvv9dysJVWq7FrtNG4m3kuFZ263YbdrQNl4iIkPSKhGKiIhQ/3/w4MH6ioWISuDiWVmr/QJr+sPT07OYbZla1VG1VgC8vLyK3JZWK0urOrSNl4jIkCTPGrO0tERiYiLc3d01ylNTU+Hu7s5xAUR6Uq1xAJw8HJB2P6Po8TkywM3HBWE9mhc7DT6shzNcfVyQkpBaYh0tuzcrto6W3StrVUfdVrW0PjciIkORPFi6uElmeXl5kMvlLxwQERWWnp6OW7fi8dqnnZ4WPN87/f8fj1gwpMS1gCwtLTFy4RCD10FEZCy0bhH69ttvAQAymQzLly/XWDxRoVDg0KFDqFWL3wCJdEkIgQcPHqhvbBz6WhP4+vji+09+0Riw7ObjghELtFsDqNXrzfDlH+MKrQH0susgIjIGWq8jFBgYCAC4ffs2fHx8NL7tyeVyBAQEYNq0aWjWzLT+AHIdITJWSqUS9+7dQ1paGgDAxcUFXl5ekMlkRruydFnqICLSNSmf7ZIXVGzbti02btyIypXNYyAkEyEyRvn5+bh16xYeP34MmUwGLy8vuLi4GDosIiKToNebru7fv7/MgRFR6XJycnD79m3k5+fD0tIS/v7+vI8fEZGeSB4s3adPH8yePbtQ+Zw5cwx29/mtW7eiZs2aqFGjBpYvX26QGIh0IS0tDTdv3kR+fj6sra1Ro0YNJkFERHokuWvMzc0Nf//9N+rVq6dRfuHCBXTo0AHJydottqYrBQUFCAoKwv79++Ho6IiQkBAcPXpU624EvXSNKRTAP/8AiYmApyfQqhUgddwE6yhXdQgLCyQnJ+P+/fsAAAcHB/j6+nK8DRFRGUj6bBcS2djYiKtXrxYqv3LlirCxsZFa3Qs7cuSI6NWrl/rxRx99JNauXav189PT0wUAkZ6erpuA/vxTCB8fIYD/+/HxeVrOOlhHEXUofXxE8tKlIiYmRsTExIj//vtPKJVK7eskIiINUj7bJSdCTZo0EVFRUYXKp0yZIho1aiS1OnHw4EHRvXt34enpKQCITZs2FdpnyZIlwt/fX1hbW4umTZuKEydOqLf98ccfIjIyUv14zpw5Yu7cuVofX6eJ0J9/CiGTaX5QAk/LZDLtPjBZR7mrQymTCaVMJm7Nny8ePnxYej1ERFQivSZCW7ZsEVZWVmLQoEFi5cqVYuXKlWLgwIHCysqqyCSmNNu3bxeTJk0SGzduLDIR+v3334VcLhc///yzuHTpkhg2bJhwcnISycnJQggjSoQKCgq3Fjz/genr+3Q/1sE6ikiGFD4+JddBRERakfLZLnnWWI8ePbB582bMmDEDGzZsQMWKFVG/fn3s3bsXrVu3llodwsPDER4eXuz2+fPnY9iwYRgy5OlKtt9//z22bduGn3/+GRMmTICXlxcSEhLU+yckJKBp06bF1peXl4e8vDz144yMDMkxF+mff4B794rfLgRw9y4ebNyIvBYtitzF+tgxuLGOclmHTAjI7t17eh21aVP8sYiISKckJ0IA0K1bN3Tr1k3XsRTy5MkTnDlzBhMnTlSXWVhYoEOHDjh27BgAoGnTprh48SISEhLg6OiIHTt24Isvvii2zpkzZyIqKkr3wSYmarXb47g4pNWsWeQ2p7g41lHO69D2OiIiIt0oUyKUlpaGDRs2IC4uDuPGjYOzszPOnj0LDw8PeHt76yy4lJQUKBQKeHh4aJR7eHjg6tWrAAArKyvMmzcPbdu2hVKpxKefflrijLGJEydi7Nix6scZGRnw9fV98WCLudv38+xfeQU2VaoUua3CK6+wjnJeh7bXERER6YjUfreYmBjh5uYmqlevLqysrMTNmzeFEEJMmjRJDBw4UHpH3jPw3BihhIQEAUAcPXpUY7/x48eLpk2bvtCxVHQ+RqioAbVSx5GwDtZBRERlJuWzXfKCimPHjsXgwYNx48YN2NjYqMu7du2KQ4cO6SxBAwBXV1dYWloWWpsoOTkZVYr55m0wlpbAokVP/y977pbcqscLF5a85gzrYB1cN4iI6OWSmmU5ODiI2NhYIYQQdnZ26hahW7duCWtra6nVaUARs8aaNm0qRo0apX6sUCiEt7e3mDlz5gsdS+WlrCPk6/vi69WwDtZBRERakfLZLnllaXd3d+zatQsNGzaEvb09YmJiULVqVezZswdDhw7F3bt3JSViWVlZiI2NBQA0bNgQ8+fPR9u2beHs7Aw/Pz+sW7cOERER+OGHH9C0aVMsXLgQ69evx9WrVwuNHSoLrizNOkyyDiIiKpZe7z7/3nvvITU1FevXr4ezszP+/fdfWFpaolevXnj11VexcOFCScEeOHAAbdu2LVQeERGBlStXAgCWLFmCuXPnIikpCQ0aNMC3336LZs2aSTrO86KjoxEdHQ2FQoHr16/z7vNERERmQq+JUHp6Ot544w2cPn0amZmZ8PLyQlJSElq0aIHt27fD1tb2hYJ/2fTSIkREREQGI+WzXfL0eUdHR+zZsweHDx/Gv//+i6ysLDRq1AgdOnQoc8BEREREhiC5RcjcsEWIiIjIvOi8Rejbb7/F8OHDYWNjg2+//bbEfe3s7FCnTp0XHsNDREREpG9atQgFBgbi9OnTcHFxQWBgYIn75uXl4f79+/j4448xd+5cnQWqL2wRIiIiMi96HSytjT179uCtt97CgwcPdF21znDWGBERkXkyeCL0+PFjLFu2DB999JGuq9Y5tggRERGZFymf7ZJvsQEA+/btQ/fu3VGtWjVUq1YN3bt3x969e9XbK1asaBJJEBEREZVvkhOh7777Dl26dIG9vT0++ugjfPTRR3BwcEDXrl0RHR2tjxiJiIiI9EJy15iPjw8mTJiAUaNGaZRHR0djxowZSEhI0GmA+sauMSIiIvOi166xtLQ0dOnSpVB5p06dkJ6eLrU6IiIiIoORnAj17NkTmzZtKlT+v//9D927d9dJUC9DdHQ0goKC0KRJE0OHQkRERAaiVdfYs4soZmRk4JtvvkHLli3RokULAMDx48dx5MgRfPLJJ5g8ebL+otUDdo0RERGZF51Pny9tEUV1ZTIZ4uLitIvSSDARIiIiMi86v8VGfHy8TgIjIiIiMiZlWkcIAFJSUpCSkqLLWIiIiIheKkmJUFpaGiIjI+Hq6goPDw94eHjA1dUVo0aNQlpamp5CJCIiItIPrbrGAODhw4do0aIFEhIS8Pbbb6N27doAgMuXL2PlypXYt28fjh49isqVK+stWCIiIiJd0joRmjZtGuRyOW7evAkPD49C2zp16oRp06ZhwYIFOg+SiIiISB+07hrbvHkzvvnmm0JJEABUqVIFc+bMKXJ9IWPFdYSIiIhI61tsWFtb4+bNm/Dx8Sly+71791C9enXk5ubqNEB94/R5IiIi86KXW2y4urri1q1bxW6Pj4+Hs7Oz1kESERERGZrWiVDnzp0xadIkPHnypNC2vLw8fPHFF0Xeg4yIiIjIWGndNXbv3j00btwY1tbWiIyMRK1atSCEwJUrV/Ddd98hLy8Pp0+fhq+vr75j1il2jREREZkXna8sDQA+Pj44duwYRo4ciYkTJ0KVP8lkMnTs2BFLliwxuSSIiIiIyjetEyHg6T3HduzYgUePHuHGjRsAgOrVq3NsEBEREZkkSYmQSuXKldG0aVNdx0JERET0UpX5XmNEREREpq7cJkJcUJGIiIi0njVmrjhrjIiIyLzoZUFFIiIiInPDRIiIiIjKLSZCREREVG6Vafq8OVENkcrIyDBwJERERKQLqs90bYZBl/tEKDMzEwC4KjYREZGZyczMhKOjY4n7lPtZY0qlEv/99x/s7e0hk8l0Vm9GRgZ8fX1x9+5dk5+NxnMxTjwX48RzMU48F+Ojz/MQQiAzMxNeXl6wsCh5FFC5bxGysLCAj4+P3up3cHAw6Qv1WTwX48RzMU48F+PEczE++jqP0lqCVDhYmoiIiMotJkJERERUbjER0hNra2tMmTIF1tbWhg7lhfFcjBPPxTjxXIwTz8X4GMt5lPvB0kRERFR+sUWIiIiIyi0mQkRERFRuMREiIiKicouJEBEREZVbTIT0JDo6GgEBAbCxsUGzZs1w8uRJQ4ck2dKlS1G/fn31YlctWrTAjh07DB1WmSUkJOCdd96Bi4sLKlasiHr16uH06dOGDqtMMjMzMWbMGPj7+6NixYoIDQ3FqVOnDB1WqQ4dOoQePXrAy8sLMpkMmzdvVm/Lz8/HZ599hnr16sHW1hZeXl4YNGgQ/vvvP8MFXIKSzgUABg8eDJlMpvHTpUsXwwRbgtLOIysrC6NGjYKPjw8qVqyIoKAgfP/994YJthQzZ85EkyZNYG9vD3d3d/Tq1QvXrl3T2GfZsmVo06YNHBwcIJPJkJaWZphgS6HNuagIIRAeHl7k+2cMSjuXW7duFfpdUf388ccfeo+PiZAerFu3DmPHjsWUKVNw9uxZBAcHo3Pnzrh//76hQ5PEx8cHs2bNwpkzZ3D69Gm0a9cOr732Gi5dumTo0CR79OgRWrZsiQoVKmDHjh24fPky5s2bh8qVKxs6tDJ57733sGfPHqxevRoXLlxAp06d0KFDByQkJBg6tBJlZ2cjODgY0dHRhbbl5OTg7Nmz+OKLL3D27Fls3LgR165dQ8+ePQ0QaelKOheVLl26IDExUf3z22+/vcQItVPaeYwdOxY7d+7Er7/+iitXrmDMmDEYNWoUtmzZ8pIjLd3BgwcRGRmJ48ePY8+ePcjPz0enTp2QnZ2t3icnJwddunTB559/bsBIS6fNuagsXLhQp7eI0rXSzsXX11fj9yQxMRFRUVGws7NDeHi4/gMUpHNNmzYVkZGR6scKhUJ4eXmJmTNnGjAq3ahcubJYvny5ocOQ7LPPPhNhYWGGDkMncnJyhKWlpdi6datGeaNGjcSkSZMMFJV0AMSmTZtK3OfkyZMCgLh9+/bLCaqMijqXiIgI8dprrxkknrIq6jzq1Kkjpk2bplFmKtfa/fv3BQBx8ODBQtv2798vAIhHjx69/MDKoLhzOXfunPD29haJiYla/U4Zg5LeF5UGDRqIoUOHvpR42CKkY0+ePMGZM2fQoUMHdZmFhQU6dOiAY8eOGTCyF6NQKPD7778jOzsbLVq0MHQ4km3ZsgWNGzdG37594e7ujoYNG+LHH380dFhlUlBQAIVCARsbG43yihUr4vDhwwaKSj/S09Mhk8ng5ORk6FDK5MCBA3B3d0fNmjUxYsQIpKamGjokyUJDQ7FlyxYkJCRACIH9+/fj+vXr6NSpk6FDK1V6ejoAwNnZ2cCRvLiiziUnJwdvvfUWoqOjUaVKFUOFJllp78uZM2dw/vx5vPvuuy8lHiZCOpaSkgKFQgEPDw+Ncg8PDyQlJRkoqrK7cOEC7OzsYG1tjQ8++ACbNm1CUFCQocOSLC4uDkuXLkWNGjWwa9cujBgxAqNHj8aqVasMHZpk9vb2aNGiBaZPn47//vsPCoUCv/76K44dO4bExERDh6czubm5+OyzzzBgwACTvLFkly5d8Msvv2Dfvn2YPXs2Dh48iPDwcCgUCkOHJsnixYsRFBQEHx8fyOVydOnSBdHR0Xj11VcNHVqJlEolxowZg5YtW6Ju3bqGDueFFHcuH3/8MUJDQ/Haa68ZMDpptHlffvrpJ9SuXRuhoaEvJaZyf/d5KlnNmjVx/vx5pKenY8OGDYiIiMDBgwdNLhlSKpVo3LgxZsyYAQBo2LAhLl68iO+//x4REREGjk661atXY+jQofD29oalpSUaNWqEAQMG4MyZM4YOTSfy8/PRr18/CCGwdOlSQ4dTJm+++ab6//Xq1UP9+vVRrVo1HDhwAO3btzdgZNIsXrwYx48fx5YtW+Dv749Dhw4hMjISXl5eGi3fxiYyMhIXL140i1bSos5ly5Yt+Pvvv3Hu3DkDRiZdae/L48ePsXbtWnzxxRcvLSa2COmYq6srLC0tkZycrFGenJxsUk2XKnK5HNWrV0dISAhmzpyJ4OBgLFq0yNBhSebp6Vkoeatduzbu3LljoIheTLVq1XDw4EFkZWXh7t27OHnyJPLz81G1alVDh/bCVEnQ7du3sWfPHpNsDSpK1apV4erqitjYWEOHorXHjx/j888/x/z589GjRw/Ur18fo0aNQv/+/fHNN98YOrxijRo1Clu3bsX+/fvh4+Nj6HBeSHHn8vfff+PmzZtwcnKClZUVrKyetmv06dMHbdq0MVC0JdPmfdmwYQNycnIwaNCglxYXEyEdk8vlCAkJwb59+9RlSqUS+/btM8mxNc9TKpXIy8szdBiStWzZstDU0+vXr8Pf399AEemGra0tPD098ejRI+zatcukmsiLokqCbty4gb1798LFxcXQIenMvXv3kJqaCk9PT0OHorX8/Hzk5+fDwkLzo8LS0hJKpdJAURVPCIFRo0Zh06ZN+PvvvxEYGGjokMqstHOZMGEC/v33X5w/f179AwALFizAihUrDBBx8aS8Lz/99BN69uwJNze3lxYfu8b0YOzYsYiIiEDjxo3RtGlTLFy4ENnZ2RgyZIihQ5Nk4sSJCA8Ph5+fHzIzM7F27VocOHAAu3btMnRokqn60mfMmIF+/frh5MmTWLZsGZYtW2bo0Mpk165dEEKgZs2aiI2Nxfjx41GrVi2jv8aysrI0WkTi4+Nx/vx5ODs7w9PTE2+88QbOnj2LrVu3QqFQqMfVOTs7Qy6XGyrsIpV0Ls7OzoiKikKfPn1QpUoV3Lx5E59++imqV6+Ozp07GzDqwko6Dz8/P7Ru3Rrjx49HxYoV4e/vj4MHD+KXX37B/PnzDRh10SIjI7F27Vr873//g729vfr6cXR0RMWKFQEASUlJSEpKUp/zhQsXYG9vDz8/P6MaVF3auVSpUqXIXgY/Pz+jSwC1eV8AIDY2FocOHcL27dtfboAvZW5aObR48WLh5+cn5HK5aNq0qTh+/LihQ5Js6NChwt/fX8jlcuHm5ibat28vdu/ebeiwyuyvv/4SdevWFdbW1qJWrVpi2bJlhg6pzNatWyeqVq0q5HK5qFKlioiMjBRpaWmGDqtUqinLz/9ERESI+Pj4IrcBEPv37zd06IWUdC45OTmiU6dOws3NTVSoUEH4+/uLYcOGiaSkJEOHXUhJ5yGEEImJiWLw4MHCy8tL2NjYiJo1a4p58+YJpVJp2MCLUNz1s2LFCvU+U6ZMKXUfY6DNuRT1HGOcPq/tuUycOFH4+voKhULxUuOT/f8giYiIiModjhEiIiKicouJEBEREZVbTISIiIio3GIiREREROUWEyEiIiIqt5gIERERUbnFRIiIiIjKLSZCRKRh5cqVcHJyMtjxZTIZNm/ebJBjBwQEYOHChS9Ux9SpU9GgQQOdxENE+sdEiMjE3b17F0OHDoWXlxfkcjn8/f3x0UcfITU11dChGa3ikr1Tp05h+PDhL1T3uHHjNO41SETGjYkQkQmLi4tD48aNcePGDfz222+IjY3F999/r77J78OHD4t97pMnT/QWV35+vt7q1ic3NzdUqlTpheqws7PT681itX3f9Pn+EpkTJkJEJiwyMhJyuRy7d+9G69at4efnh/DwcOzduxcJCQmYNGmSet+AgABMnz4dgwYNgoODg7rlY+XKlfDz80OlSpXQu3fvIluS/ve//6FRo0awsbFB1apVERUVhYKCAvV2mUyGpUuXomfPnrC1tcXXX3+t1fNu3LiBV199FTY2NggKCsKePXtKPee8vDyMHj0a7u7usLGxQVhYGE6dOqXefuDAAchkMmzbtg3169eHjY0NmjdvjosXL6q3DxkyBOnp6ZDJZJDJZJg6dar6NXq2a0wmk+GHH35A9+7dUalSJdSuXRvHjh1DbGws2rRpA1tbW4SGhuLmzZvq5zzfNaY6xrM/AQEB6u0XL15EeHg47Ozs4OHhgYEDByIlJUW9vU2bNhg1ahTGjBkDV1fXYm/aOnjwYPTq1Qtff/01vLy8ULNmTfXxn+9qdHJywsqVKwEAt27dgkwmw8aNG9G2bVtUqlQJwcHBOHbsWKnvBZFZeKl3NiMinUlNTRUymUzMmDGjyO3Dhg0TlStXVt8c09/fXzg4OIhvvvlGxMbGitjYWHH8+HFhYWEhZs+eLa5duyYWLVoknJychKOjo7qeQ4cOCQcHB7Fy5Upx8+ZNsXv3bhEQECCmTp2q3geAcHd3Fz///LO4efOmuH37dqnPUygUom7duqJ9+/bi/Pnz4uDBg6Jhw4al3jhy9OjRwsvLS2zfvl1cunRJREREiMqVK4vU1FQhxP/dRLR27dpi9+7d4t9//xXdu3cXAQEB4smTJyIvL08sXLhQODg4iMTERJGYmCgyMzPVr9GCBQs0zsvb21usW7dOXLt2TfTq1UsEBASIdu3aiZ07d4rLly+L5s2biy5duqifM2XKFBEcHKx+rDpGYmKiiI2NFdWrVxcDBw4UQgjx6NEj4ebmJiZOnCiuXLkizp49Kzp27Cjatm2rfn7r1q2FnZ2dGD9+vLh69aq4evVqka9LRESEsLOzEwMHDhQXL14UFy9eVJ/D86+no6Oj+oaXqpvd1qpVS2zdulVcu3ZNvPHGG8Lf31/k5+cX+z4QmQsmQkQm6vjx4yUmDfPnzxcARHJyshDi6Yd8r169NPYZMGCA6Nq1q0ZZ//79NRKh9u3bF0q2Vq9eLTw9PdWPAYgxY8Zo7FPa83bt2iWsrKxEQkKCevuOHTtKPKesrCxRoUIFsWbNGnXZkydPhJeXl5gzZ44Q4v8Sod9//129T2pqqqhYsaJYt26dEEKIFStWaJyjSlGJ0OTJk9WPjx07JgCIn376SV3222+/CRsbG/Xj5xMhFaVSKXr37i1CQkJETk6OEEKI6dOni06dOmnsd/fuXQFAXLt2TQjxNBFq2LBhka/HsyIiIoSHh4fIy8vTKNc2EVq+fLl6+6VLlwQAceXKlVKPS2TqrF56ExQR6ZQQQut9GzdurPH4ypUr6N27t0ZZixYtsHPnTvXjmJgYHDlyRN3dBQAKhQK5ubnIyclRj6l5vu7SnnflyhX4+vrCy8tL49gluXnzJvLz89GyZUt1WYUKFdC0aVNcuXKl0HmoODs7o2bNmoX20Ub9+vXV//fw8AAA1KtXT6MsNzcXGRkZcHBwKLaezz//HMeOHcPp06dRsWJFAE9fo/3798POzq7Q/jdv3sQrr7wCAAgJCdEq1nr16kEul2u17/OePU9PT08AwP3791GrVq0y1UdkKpgIEZmo6tWrQyaTFZnMAE+TnMqVK8PNzU1dZmtrK/k4WVlZiIqKwuuvv15om42NTbF1a/s8Y1ehQgX1/2UyWbFlSqWy2Dp+/fVXLFiwAAcOHIC3t7e6PCsrCz169MDs2bMLPUeVjADav29F7SeTyQoly0UNZpd6TkTmgokQkYlycXFBx44d8d133+Hjjz9WtzIAQFJSEtasWYNBgwapP9SKUrt2bZw4cUKj7Pjx4xqPGzVqhGvXrqF69eqS4ivtebVr18bdu3eRmJio/tB//tjPq1atGuRyOY4cOQJ/f38ATz/UT506hTFjxhQ6Dz8/PwDAo0ePcP36ddSuXRsAIJfLoVAoJJ1PWR07dgzvvfcefvjhBzRv3lxjW6NGjfDnn38iICAAVlb6+XPs5uaGxMRE9eMbN24gJydHL8ciMkWcNUZkwpYsWYK8vDx07twZhw4dwt27d7Fz50507NgR3t7eGt1SRRk9ejR27tyJb775Bjdu3MCSJUs0usUA4Msvv8Qvv/yCqKgoXLp0CVeuXMHvv/+OyZMnl1h3ac/r0KEDXnnlFURERCAmJgb//POPxiy3otja2mLEiBEYP348du7cicuXL2PYsGHIycnBu+++q7HvtGnTsG/fPly8eBGDBw+Gq6srevXqBeDp7LCsrCzs27cPKSkpeksMkpKS0Lt3b7z55pvo3LkzkpKSkJSUhAcPHgB4Ouvv4cOHGDBgAE6dOoWbN29i165dGDJkiM4StXbt2mHJkiU4d+4cTp8+jQ8++ECj9YeovGMiRGTCatSogdOnT6Nq1aro168fqlWrhuHDh6Nt27Y4duwYnJ2dS3x+8+bN8eOPP2LRokUIDg7G7t27CyU4nTt3xtatW7F79240adIEzZs3x4IFC9QtMsUp7XkWFhbYtGkTHj9+jKZNm+K9994rNXEDgFmzZqFPnz4YOHAgGjVqhNjYWOzatQuVK1cutN9HH32EkJAQJCUl4a+//lKPnwkNDcUHH3yA/v37w83NDXPmzCn1uGVx9epVJCcnY9WqVfD09FT/NGnSBADg5eWFI0eOQKFQoFOnTqhXrx7GjBkDJycnWFjo5s/zvHnz4Ovri1atWuGtt97CuHHjXnitJCJzIhNSRloSERm5AwcOoG3btnj06JFBbxVCRKaBLUJERERUbjERIiIionKLXWNERERUbrFFiIiIiMotJkJERERUbjERIiIionKLiRARERGVW0yEiIiIqNxiIkRERETlFhMhIiIiKreYCBEREVG5xUSIiIiIyq3/B2m/swSad2lXAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Visualize the results of the multistarts for a chosen regularization strength\n", + "ax = pypesto.visualize.waterfall(\n", + " regresults[chosen_regstrength], size=[6.5, 3.5]\n", + ")\n", + "ax.set_title(\n", + " f\"Waterfall plot (regularization strength = {chosen_regstrength})\"\n", + ")\n", + "ax.set_ylim(ax.get_ylim()[0], 100);" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "id": "9a4fb86c-4b74-43f8-97d8-eba60529abaf", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Plot ML fit for pEpoR (all regularization strengths)\n", + "fig, ax = plt.subplots(figsize=(6.5, 3.5))\n", + "for regstrength in sorted(regproblems.keys()):\n", + " t, pEpoR = simulate_pEpoR(\n", + " problem=regproblems[regstrength], result=regresults[regstrength]\n", + " )\n", + " if regstrength == chosen_regstrength:\n", + " kwargs = dict(\n", + " color=\"black\",\n", + " label=f\"$\\\\mathbf{{\\\\lambda = {regstrength}}}$\",\n", + " zorder=2,\n", + " )\n", + " else:\n", + " kwargs = dict(label=f\"$\\\\lambda = {regstrength}$\", alpha=0.5)\n", + " ax.plot(t, pEpoR, **kwargs)\n", + "ax.plot(\n", + " df_pEpoR[\"time\"],\n", + " df_pEpoR[\"measurement\"],\n", + " \"o\",\n", + " color=\"black\",\n", + " markerfacecolor=\"none\",\n", + " label=\"experimental data\",\n", + ")\n", + "ylim1 = ax.get_ylim()[0]\n", + "ax.plot(\n", + " nodes,\n", + " len(nodes) * [ylim1],\n", + " \"x\",\n", + " color=\"black\",\n", + " label=\"spline nodes\",\n", + " zorder=10,\n", + " clip_on=False,\n", + ")\n", + "ax.set_ylim(ylim1, ax.get_ylim()[1])\n", + "ax.set_xlabel(\"time\")\n", + "ax.set_ylabel(\"pEpoR\")\n", + "ax.set_xlim(-3.0, 63.0)\n", + "ax.set_ylim(-0.05299052022388704, 1.126290214024833)\n", + "ax.legend()\n", + "ax.figure.tight_layout()\n", + "# ax.set_ylabel(\"input function\")\n", + "# print(f\"xlim = {ax.get_xlim()}, ylim = {ax.get_ylim()}\")\n", + "# ax.figure.savefig('fit_15nodes_lambdas.pdf')" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "id": "18f79f00-265a-4cb8-a462-e4606636612b", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Plot ML fit for pSTAT5 (all regularization strengths)\n", + "fig, ax = plt.subplots(figsize=(6.5, 3.5))\n", + "for regstrength in sorted(regproblems.keys()):\n", + " t, pSTAT5 = simulate_pSTAT5(\n", + " problem=regproblems[regstrength], result=regresults[regstrength]\n", + " )\n", + " if regstrength == chosen_regstrength:\n", + " kwargs = dict(\n", + " color=\"black\",\n", + " label=f\"$\\\\mathbf{{\\\\lambda = {regstrength}}}$\",\n", + " zorder=2,\n", + " )\n", + " else:\n", + " kwargs = dict(label=f\"$\\\\lambda = {regstrength}$\", alpha=0.5)\n", + " ax.plot(t, pSTAT5, **kwargs)\n", + "ax.plot(\n", + " df_pSTAT5[\"time\"],\n", + " df_pSTAT5[\"measurement\"],\n", + " \"o\",\n", + " color=\"black\",\n", + " markerfacecolor=\"none\",\n", + " label=\"experimental data\",\n", + ")\n", + "ylim1 = ax.get_ylim()[0]\n", + "ax.plot(\n", + " nodes,\n", + " len(nodes) * [ylim1],\n", + " \"x\",\n", + " color=\"black\",\n", + " label=\"spline nodes\",\n", + " zorder=10,\n", + " clip_on=False,\n", + ")\n", + "ax.set_ylim(ylim1, ax.get_ylim()[1])\n", + "ax.set_xlabel(\"time\")\n", + "ax.set_ylabel(\"pSTAT5\");\n", + "# ax.legend();" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "id": "ac882963-7714-4536-b974-da0b642d79a9", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "image/png": "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du1asrKyupenpaURCASw2+09th/MbabX68nPz2fSpEksWbKECRMm8Pjjj0tbHcfWrVtpbm7m/PPPR6vVotVqWb9+PX/84x/RarWkpqZKm32BuLg4RowYwcGDB+V37DjS09MZM2ZMj2WjR4/uvlXYHz73Jfz0Ar1ez6RJk1izZk33skgkwpo1a5g2bVoUK+v/8vLySEtL69F2TqeTjRs3Dtq2UxSFO++8k1dffZUPPviAvLy8HusnTZqETqfr0Wbl5eXU1NQM2jb7b5FIBL/fL211HJdeeim7d+9mx44d3a8LLriAb37zm91/lzY7uc7OTg4dOkR6err8jh3HjBkzjhme48CBAwwZMgToJ5/7fdKtehB4+eWXFYPBoDz//PPK3r17le9///tKXFyc0tjYGO3Sos7lcinbt29Xtm/frgDKH/7wB2X79u3K4cOHFUVRlN/97ndKXFyc8tprrym7du1S5s+fr+Tl5SlerzfKlUfH7bffrthsNmXdunVKQ0ND98vj8XRv84Mf/EDJyclRPvjgA2XLli3KtGnTlGnTpkWx6ui57777lPXr1ytVVVXKrl27lPvuu09RqVTKu+++qyiKtNWp+PzTXooibfbf7rnnHmXdunVKVVWV8vHHHytz5sxRkpKSlObmZkVRpL3+26ZNmxStVqv89re/VSoqKpR//etfitlsVl588cXubaL9uS/hpxc98cQTSk5OjqLX65XJkycrGzZsiHZJ/cLatWsV4JjXzTffrChK12OPDzzwgJKamqoYDAbl0ksvVcrLy6NbdBQdr60A5bnnnuvexuv1Kj/84Q+V+Ph4xWw2K9dee63S0NAQvaKj6Lvf/a4yZMgQRa/XK8nJycqll17aHXwURdrqVPx3+JE26+n6669X0tPTFb1er2RmZirXX3+9cvDgwe710l7HeuONN5Rx48YpBoNBGTVqlPKXv/ylx/pof+6rFEVR+uYakxBCCCFE9EmfHyGEEEIMKhJ+hBBCCDGoSPgRQgghxKAi4UcIIYQQg4qEHyGEEEIMKhJ+hBBCCDGoSPgRQgghxKAi4UcIIYQQg4qEHyHEOWndunWoVKpjJpQUQogvIiM8CyHOCbNmzeK8887jscceAyAQCNDe3k5qaioqlSq6xQkhzinaaBcghBBfhl6vJy0tLdplCCHOQXLbSwjR733nO99h/fr1PP7446hUKlQqFc8//3yP217PP/88cXFxrFq1ipEjR2I2mykqKsLj8fDCCy+Qm5tLfHw8P/rRjwiHw93H9vv93HvvvWRmZhITE8OUKVNYt25ddE5UCNEn5MqPEKLfe/zxxzlw4ADjxo3j4YcfBmDPnj3HbOfxePjjH//Iyy+/jMvlYsGCBVx77bXExcWxevVqKisrue6665gxYwbXX389AHfeeSd79+7l5ZdfJiMjg1dffZUrrriC3bt3M3z48D49TyFE35DwI4To92w2G3q9HrPZ3H2ra//+/cdsFwwGefrppxk2bBgARUVF/POf/6SpqQmLxcKYMWOYPXs2a9eu5frrr6empobnnnuOmpoaMjIyALj33nt5++23ee6551i8eHHfnaQQos9I+BFCDBhms7k7+ACkpqaSm5uLxWLpsay5uRmA3bt3Ew6HGTFiRI/j+P1+EhMT+6ZoIUSfk/AjhBgwdDpdj59VKtVxl0UiEQA6OzvRaDRs3boVjUbTY7vPByYhxMAi4UcIcU7Q6/U9Oir3hokTJxIOh2lubmbmzJm9emwhRP8lT3sJIc4Jubm5bNy4kerqalpbW7uv3pyJESNG8M1vfpObbrqJ4uJiqqqq2LRpE0uWLOHNN9/shaqFEP2RhB8hxDnh3nvvRaPRMGbMGJKTk6mpqemV4z733HPcdNNN3HPPPYwcOZLCwkI2b95MTk5OrxxfCNH/yAjPQgghhBhU5MqPEEIIIQYVCT9CCCGEGFQk/AghhBBiUJHwI4QQQohBRcKPEEIIIQYVCT9CCCGEGFQk/AghhBBiUJHwI4QQQohBRcKPEEIIIQYVCT9CCCGEGFQk/AghhBBiUPn/dvf2grhNOkgAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Plot ML fit for tSTAT5 (all regularization strengths)\n", + "fig, ax = plt.subplots(figsize=(6.5, 3.5))\n", + "for regstrength in sorted(regproblems.keys()):\n", + " t, tSTAT5 = simulate_tSTAT5(\n", + " problem=regproblems[regstrength], result=regresults[regstrength]\n", + " )\n", + " if regstrength == chosen_regstrength:\n", + " kwargs = dict(\n", + " color=\"black\",\n", + " label=f\"$\\\\mathbf{{\\\\lambda = {regstrength}}}$\",\n", + " zorder=2,\n", + " )\n", + " else:\n", + " kwargs = dict(label=f\"$\\\\lambda = {regstrength}$\", alpha=0.5)\n", + " ax.plot(t, tSTAT5, **kwargs)\n", + "ax.plot(\n", + " df_tSTAT5[\"time\"],\n", + " df_tSTAT5[\"measurement\"],\n", + " \"o\",\n", + " color=\"black\",\n", + " markerfacecolor=\"none\",\n", + " label=\"experimental data\",\n", + ")\n", + "ylim1 = ax.get_ylim()[0]\n", + "ax.plot(\n", + " nodes,\n", + " len(nodes) * [ylim1],\n", + " \"x\",\n", + " color=\"black\",\n", + " label=\"spline nodes\",\n", + " zorder=10,\n", + " clip_on=False,\n", + ")\n", + "ax.set_ylim(ylim1, ax.get_ylim()[1])\n", + "ax.set_xlabel(\"time\")\n", + "ax.set_ylabel(\"tSTAT5\");\n", + "# ax.legend();" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "id": "c4d6fe0b-335f-4e69-ba72-6cc2b2b1af70", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAj8AAAFjCAYAAADSCGomAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAACcVElEQVR4nOzdd3xT1f8/8FeSNqtp0r0XCFL2KFABmVbZMhSQIVNRoaBUHKCUpRQF/CBDFJThYAiylIKykSFSpuxZWkp3m6RpdnJ/f/DL/TY0HWnTpuP9fDzyoLm599xzQ9r7zjnvcw6HYRgGhBBCCCH1BNfZFSCEEEIIqU4U/BBCCCGkXqHghxBCCCH1CgU/hBBCCKlXKPghhBBCSL1CwQ8hhBBC6hUKfgghhBBSr1DwQwghhJB6hYIfQgghhNQrFPyQeuPAgQNo06YNhEIhOBwO5HI5xo8fj4iIiEqVe+fOHbz00kuQyWTgcDjYvXu3Q+pbm2zcuBEcDgfJyckOLbdHjx7o0aOHQ8usyeetbyIiIjBgwABnV4PUQxT8kGpnuVFyOBycPHmy2OsMwyA0NBQcDqfYH0YOh4PY2Fi7z5mbm4vhw4dDJBJh9erV+Omnn+Dm5lZsP7VajXnz5uHYsWPlLnvcuHH477//8Pnnn+Onn35C+/bt7a4fqX7Xr1/HvHnzHB6wVbXExETMmzfP2dUot9rwPicnJ7N/k2w93nzzTXbfY8eOlbjfP//848SrIPZwcXYFSP0lFAqxefNmPP/881bbjx8/jkePHkEgEDjsXOfOnUNBQQEWLlyImJgYdvu6detgNpvZ52q1GvPnzweAcn3z12g0OHPmDD755JMKBWWkdH/99VeVlX39+nXMnz8fPXr0KNb6V5XnrazExESsXr261gRApb3PNYWvry9++umnYtsPHDiAX375BS+99FKx16ZPn44OHTpYbWvUqFGV1ZE4FgU/xGn69euH7du3Y8WKFXBx+b+P4ubNmxEVFYWcnByHnSsrKwsA4OHhYbXd1dW1UuVmZ2fbLLcyCgsLbbZKlUSr1YLP54PLrTsNuWq1GmKxGHw+3ynnd9Z5Hc1oNMJsNteZ66kqbm5uGDNmTLHtGzduhFQqxcCBA4u91rVrV7z66qvVUT1SBerOX0tS64wcORK5ubk4ePAgu02v12PHjh0YNWqUw87To0cPjBs3DgDQoUMHcDgcjB8/HgCscn6Sk5Ph6+sLAJg/fz7blF3SN+x58+YhPDwcAPDBBx+Aw+FYfbO9ePEi+vbtC6lUColEghdeeKFYs7ilC/D48eOYMmUK/Pz8EBISUuK1WJrct27dik8//RTBwcEQi8VQKpUAgLNnz6JPnz6QyWQQi8Xo3r07Tp06ZbOc9u3bQygU4plnnsF3332HefPmgcPhsPtYugI2btxY7PjS3heLPXv2oH///ggKCoJAIMAzzzyDhQsXwmQyWe3Xo0cPtGjRAufPn0e3bt0gFosxe/Zs9rWiLXAREREldjlYuiofPnyIKVOmoEmTJhCJRPD29sawYcOsul02btyIYcOGAQB69uxZrAxbOT9ZWVmYNGkS/P39IRQK0bp1a2zatMlqH8t7tnTpUqxduxbPPPMMBAIBOnTogHPnzpX6fgGAwWDA/Pnz0bhxYwiFQnh7e+P5559nf0fGjx+P1atXA4DVtT997uXLl7Pnvn79OgDg5s2bePXVV+Hl5QWhUIj27dtj7969Vue3fB5PnTqFuLg4+Pr6ws3NDUOGDGEDfQuz2Yx58+YhKCgIYrEYPXv2xPXr1xEREcH+fpX1PlucPHkSHTt2hFAoRMOGDfHjjz+W+V5VtfT0dBw9ehRDhw6FUCi0uU9BQQGMRmM114w4ArX8EKeJiIhAp06dsGXLFvTt2xcAsH//figUCrz22mtYsWKFQ87zySefoEmTJli7di0WLFiABg0a4Jlnnim2n6+vL9asWYN33nkHQ4YMwdChQwEArVq1slnu0KFD4eHhgRkzZmDkyJHo168fJBIJAODatWvo2rUrpFIpPvzwQ7i6uuK7775Djx49cPz4cURHR1uVNWXKFPj6+iI+Ph6FhYVlXtPChQvB5/Mxc+ZM6HQ68Pl8HDlyBH379kVUVBTmzp0LLpeLDRs2oFevXvj777/RsWNHAE+Csj59+iAwMBDz58+HyWTCggUL2MDPUTZu3AiJRIK4uDhIJBIcOXIE8fHxUCqVWLJkidW+ubm56Nu3L1577TWMGTMG/v7+Nstcvnw5VCqV1bb//e9/uHTpEry9vQE86eI8ffo0XnvtNYSEhCA5ORlr1qxBjx49cP36dYjFYnTr1g3Tp0/HihUrMHv2bDRt2hQA2H+fptFo0KNHD9y9exexsbFo0KABtm/fjvHjx0Mul+Pdd9+12n/z5s0oKCjAW2+9BQ6Hgy+//BJDhw7F/fv3S21tnDdvHhISEvDGG2+gY8eOUCqVSEpKwoULF/Diiy/irbfewuPHj3Hw4EGb3TQAsGHDBmi1WkyePBkCgQBeXl64du0aunTpguDgYHz88cdwc3PDr7/+isGDB+O3337DkCFDrMqYNm0aPD09MXfuXCQnJ2P58uWIjY3Ftm3b2H1mzZqFL7/8EgMHDkTv3r1x+fJl9O7dG1qtlt2nPO/z3bt38eqrr2LSpEkYN24c1q9fj/HjxyMqKgrNmzcv8b0CgPz8/GLBtC1isRhisbjM/YraunUrzGYzRo8ebfP1CRMmQKVSgcfjoWvXrliyZAnl+9UmDCHVbMOGDQwA5ty5c8yqVasYd3d3Rq1WMwzDMMOGDWN69uzJMAzDhIeHM/3797c6FgAzderUSp2zqHHjxjHh4eHs8+zsbAYAM3fu3HKV++DBAwYAs2TJEqvtgwcPZvh8PnPv3j122+PHjxl3d3emW7duxer1/PPPM0ajsczzHT16lAHANGzYkH3PGIZhzGYz07hxY6Z3796M2Wxmt6vVaqZBgwbMiy++yG4bOHAgIxaLmbS0NHbbnTt3GBcXF6bonwTLtW3YsKFYPZ5+jyzX8eDBA6tzP+2tt95ixGIxo9Vq2W3du3dnADDffvttsf27d+/OdO/evcT349dff2UAMAsWLCj1vGfOnGEAMD/++CO7bfv27QwA5ujRo2Wed/ny5QwA5ueff2a36fV6plOnToxEImGUSiXDMP/3nnl7ezN5eXnsvnv27GEAML///nuJ18IwDNO6detin/mnTZ06lbH1p9tybqlUymRlZVm99sILLzAtW7a0et/NZjPTuXNnpnHjxuw2y/9jTEyM1edoxowZDI/HY+RyOcMwDJORkcG4uLgwgwcPtjrPvHnzGADMuHHj2G2lvc/h4eEMAObEiRPstqysLEYgEDDvv/9+qe9D0ePLepT397moqKgoJjAwkDGZTFbbT506xbzyyivMDz/8wOzZs4dJSEhgvL29GaFQyFy4cMHu8xDnoG4v4lTDhw+HRqPBH3/8gYKCAvzxxx8O7fJyBpPJhL/++guDBw9Gw4YN2e2BgYEYNWoUTp48yXZTWbz55pvg8XjlPse4ceMgEonY55cuXcKdO3cwatQo5ObmIicnBzk5OSgsLMQLL7yAEydOwGw2w2Qy4dChQxg8eDCCgoLY4xs1asS2vjlK0foVFBQgJycHXbt2hVqtxs2bN632FQgEmDBhgl3lX79+HRMnTsSgQYPw6aef2jyvwWBAbm4uGjVqBA8PD1y4cKFC15KYmIiAgACMHDmS3ebq6orp06dDpVLh+PHjVvuPGDECnp6e7POuXbsCAO7fv1/qeTw8PHDt2jXcuXOnQvUEgFdeecWqFS8vLw9HjhzB8OHD2f+HnJwc5Obmonfv3rhz5w7S0tKsypg8ebJVF2jXrl1hMpnw8OFDAMDhw4dhNBoxZcoUq+OmTZtmd32bNWvGvj/AkxbYJk2alPleAcAvv/yCgwcPlvkYO3asXXW6ffs2zp8/j9dee61YLl3nzp2xY8cOTJw4ES+//DI+/vhj/PPPP+BwOJg1a5Zd5yHOQ91exKl8fX0RExODzZs3Q61Ww2Qy1fokwuzsbKjVajRp0qTYa02bNoXZbEZqaqpVk36DBg3sOsfT+1tulpbcJlsUCgW0Wi00Go3NUSmOHqly7do1fPrppzhy5EixYE+hUFg9Dw4OtispV6lUYujQoQgODsaPP/5odaPWaDRISEjAhg0bkJaWBoZhSjxveT18+BCNGzcudiO0dN9YggKLsLAwq+eWQCg/P7/U8yxYsACDBg3Cs88+ixYtWqBPnz54/fXXS+x6teXpz8bdu3fBMAzmzJmDOXPm2DwmKysLwcHB5a6/5Xqf/sx4eXlZBX3l8fS5LOcr670CgC5duth1rvL65ZdfAKDELq+nNWrUCIMGDcLOnTthMpns+iJDnIOCH+J0o0aNwptvvomMjAz07dvXoSOnaouirRUV2d8yXH/JkiVo06aNzWMkEolVPkZZigYURZUnx0Iul6N79+6QSqVYsGABnnnmGQiFQly4cAEfffSR1fQCgP3XP378eDx+/Bj//vsvpFKp1WvTpk3Dhg0b8N5776FTp07s5JOvvfZasfNWlZJufkUDMVu6deuGe/fuYc+ePfjrr7/w/fff43//+x++/fZbvPHGG+U6d0mfjZkzZ6J37942j3k6iKlo/SuiMufKzs4u1+dRIpGw+XjlsXnzZjRp0gRRUVHlPiY0NBR6vR6FhYXFPpOk5qHghzjdkCFD8NZbb+Gff/6xSqh0hpJu+Pbw9fWFWCzGrVu3ir128+ZNcLlchIaGVvo8RVkSuKVSqdU8Rk/z8/ODUCjE3bt3i7329DbLN3i5XG61/elWDluOHTuG3Nxc7Ny5E926dWO3P3jwoMxjy7J48WLs3r0bO3fuRGRkZLHXd+zYgXHjxmHZsmXsNq1WW+w67Pm/Dg8Px5UrV2A2m61afyzdd5ZRf47g5eWFCRMmsAm13bp1w7x589jgx97PqKXr1dXVtdTPhj0s13v37l2rlqbc3NxiLTaO+J0qSYcOHcr1eZw7d26550U6e/Ys7t69iwULFthVl/v370MoFNoVZBHnoeCHOJ1EIsGaNWuQnJxscz6N6mQZEfL0jdIePB4PL730Evbs2YPk5GR2+HtmZiY7qaOjvxlGRUXhmWeewdKlSzFq1Khif4Czs7Ph6+sLHo+HmJgY7N69G48fP2bzfu7evYv9+/dbHSOVSuHj44MTJ07gvffeY7d/8803ZdbH8m2+6Ld3vV5frmNLc+jQIXz66af45JNPMHjw4BLP/XSrwcqVK4u1EFjmUirP/3W/fv3w119/Ydu2bWzej9FoxMqVKyGRSNC9e3f7L8aG3NxcdtQa8OR3o1GjRkhNTbVZ7/K0kvr5+aFHjx747rvvMG3aNAQGBlq9bvls2OOFF16Ai4sL1qxZgxdffJHdvmrVqmL72vM+2+uXX36BRqMpc7+iuXdl2bx5MwCUmHto6/26fPky9u7di759+9ap+bbqMgp+SI1QWq7K05KSkvDZZ58V296jR49is0XbSyQSoVmzZti2bRueffZZeHl5oUWLFmjRooVd5Xz22Wc4ePAgnn/+eUyZMgUuLi747rvvoNPp8OWXX1aqjrZwuVx8//336Nu3L5o3b44JEyYgODgYaWlpOHr0KKRSKX7//XcAT4ZT//XXX+jSpQveeecdmEwmrFq1Ci1atMClS5esyn3jjTewePFivPHGG2jfvj1OnDiB27dvl1mfzp07w9PTE+PGjcP06dPB4XDw008/VbrbZOTIkfD19UXjxo3x888/W7324osvwt/fHwMGDMBPP/0EmUyGZs2a4cyZMzh06JBVUAEAbdq0AY/HwxdffAGFQgGBQIBevXrBz8+v2HknT56M7777DuPHj8f58+cRERGBHTt24NSpU1i+fDnc3d0rdV0WzZo1Q48ePRAVFQUvLy8kJSVhx44dVrOHW7pipk+fjt69e4PH4+G1114rtdzVq1fj+eefR8uWLfHmm2+iYcOGyMzMxJkzZ/Do0SNcvnzZrnr6+/vj3XffxbJly/Dyyy+jT58+uHz5Mvbv3w8fHx+r1h573md7OTrnx2QyYdu2bXjuuedsTocBPElmF4lE6Ny5M/z8/HD9+nWsXbsWYrEYixcvdmh9SBVy3kAzUl+VNOz8aSUNdS/psXDhQrvP+fRQd4ZhmNOnTzNRUVEMn88vc5hsSUPdGYZhLly4wPTu3ZuRSCSMWCxmevbsyZw+fbpc9SqJZaj79u3bbb5+8eJFZujQoYy3tzcjEAiY8PBwZvjw4czhw4et9jt8+DDTtm1bhs/nM8888wzz/fffM++//z4jFAqt9lOr1cykSZMYmUzGuLu7M8OHD2eysrLKNdT91KlTzHPPPceIRCImKCiI+fDDD5k///yz2LDn7t27M82bN7d5PU8POS/t/99SZn5+PjNhwgTGx8eHkUgkTO/evZmbN28y4eHhVkOwGYZh1q1bxzRs2JDh8XhWZdgaYp+ZmcmWy+fzmZYtWxabBqC0z0NZnyWGYZjPPvuM6dixI+Ph4cGIRCImMjKS+fzzzxm9Xs/uYzQamWnTpjG+vr4Mh8Nhh72Xdm6GYZh79+4xY8eOZQICAhhXV1cmODiYGTBgALNjxw52n5I+j5bPXdH/N6PRyMyZM4cJCAhgRCIR06tXL+bGjRuMt7c38/bbb1sdX9L7bOt3nGHKnuKgqhw4cIABwKxYsaLEfb7++mumY8eOjJeXF+Pi4sIEBgYyY8aMYe7cuVONNSWVxWGYKshgI4TUOoMHD670MGtSv8nlcnh6euKzzz7DJ5984uzqEFIi6pwkpB56Ok/izp07SExMLNdiroQAxT9DwJMZuIHyLQpMiDNRyw8h9VBgYCDGjx+Phg0b4uHDh1izZg10Oh0uXryIxo0bO7t6pBbYuHEjNm7cyC7rcvLkSWzZsgUvvfQS/vzzT2dXj5BSUcIzIfVQnz59sGXLFmRkZEAgEKBTp05YtGgRBT6k3Fq1agUXFxd8+eWXUCqVbBK0rcEIhNQ01PJDCCGEkHqFcn4IIYQQUq9Q8EMIIYSQeqXe5fyYzWY8fvwY7u7uVTrtOiGEEEKqD8MwKCgoQFBQUJkzbde74Ofx48cOX1eJEEIIITVDamoqQkJCSt2n3gU/lmnoU1NTaeVdQgghpI5QKpUIDQ0t13Iz9S74sXR1SaVSCn4IIYSQOqY8KS2U8EwIIYSQeoWCH0IIIYTUK04Nfk6cOIGBAwciKCgIHA4Hu3fvLnX/nTt34sUXX4Svry+kUik6depE06gTQgghxC5OzfkpLCxE69atMXHiRAwdOrTM/U+cOIEXX3wRixYtgoeHBzZs2ICBAwfi7NmzaNu2bTXUmBBCahaz2Qy9Xu/sahBS5VxdXcHj8RxSVo1Z3oLD4WDXrl0YPHiwXcc1b94cI0aMQHx8fLn2VyqVkMlkUCgUlPBMCKnV9Ho9Hjx4ALPZ7OyqEFItPDw8EBAQYDOp2Z77e60e7WU2m1FQUAAvL68S99HpdNDpdOxzpVJZHVUjhJAqxTAM0tPTwePxEBoaWuakboTUZgzDQK1WIysrCwAQGBhYqfJqdfCzdOlSqFQqDB8+vMR9EhISMH/+/GqsFSGEVD2j0Qi1Wo2goCCIxWJnV4eQKicSiQAAWVlZ8PPzq1QXWK39qrB582bMnz8fv/76K/z8/Ercb9asWVAoFOwjNTW1GmtJCCFVw2QyAQD4fL6Ta0JI9bEE+gaDoVLl1MqWn61bt+KNN97A9u3bERMTU+q+AoEAAoGgmmpGCCHVi9YoJPWJoz7vta7lZ8uWLZgwYQK2bNmC/v37O7s6xAaGYWAymVBDcukJIYQQK05t+VGpVLh79y77/MGDB7h06RK8vLwQFhaGWbNmIS0tDT/++COAJ11d48aNw9dff43o6GhkZGQAeNIPKJPJnHIN5P8YjUa2e9FsNoPL5UIqlcLd3Z1a3wipJiaTqVpHf3G5XIcNP66oio4Wrs/Gjx8PuVxe5vx6VWHjxo147733IJfLq/3cFk4NfpKSktCzZ0/2eVxcHABg3Lhx2LhxI9LT05GSksK+vnbtWhiNRkydOhVTp05lt1v2J86j1WqRkZEBlUoFoVAIHo8Hk8mEjIwM5OXlwdvbG56enjQihZAqZDKZkJqaWq3z/vD5fISGhpY7AEpISMDOnTtx8+ZNiEQidO7cGV988QWaNGlS4Tqkp6fD09OzwseT+sepwU+PHj1K7Rp5OqA5duxY1VaIVIher0d6ejrUajU8PT2t+mTFYjE0Gg3S09Oh0+ng5+cHF5damWpGSI1nmfCQx+NVy++Z0WiEXq+H2Wwud/Bz/PhxTJ06FR06dIDRaMTs2bPx0ksv4fr163Bzc6tQPQICAip0XE1kSRt4+v9Pr9dXKLm9osfVdfQ1nFSK2WxGdnY21Go1PDw8bCajWbol8/LykJ6eTrPRElLFXFxc4OrqWuWPigRYBw4cwPjx49G8eXO0bt0aGzduREpKCs6fP1/iMXq9HrGxsQgMDIRQKER4eDgSEhLY159eHun06dNo06YNhEIh2rdvj927d4PD4eDSpUsAnnyR5nA4+PPPP9G2bVuIRCL06tULWVlZ2L9/P5o2bQqpVIpRo0ZBrVZb1f3555+Hh4cHvL29MWDAANy7d6/U6zWbzUhISECDBg0gEonQunVr7Nixg33dUpf9+/cjKioKAoEAJ0+eRI8ePRAbG4v33nsPPj4+6N27N4AnwWPHjh0hEAgQGBiIjz/+GEajkS2vpONKMn/+fHbJqLffftvq73NZ15ucnAwOh4OdO3eiZ8+eEIvFaN26Nc6cOWN1jo0bNyIsLAxisRhDhgxBbm6u1euXL19Gz5494e7uDqlUiqioKCQlJZVa78qi4IdUilKpRH5+PqRSaalZ+DweDx4eHlAoFBQAEUJYCoUCAEqdrHbFihXYu3cvfv31V9y6dQu//PILIiIibO6rVCoxcOBAtGzZEhcuXMDChQvx0Ucf2dx33rx5WLVqFU6fPo3U1FQMHz4cy5cvx+bNm7Fv3z789ddfWLlyJbt/YWEh4uLikJSUhMOHD4PL5WLIkCGl5lglJCTgxx9/xLfffotr165hxowZGDNmDI4fP26138cff4zFixfjxo0baNWqFQBg06ZN4PP5OHXqFL799lukpaWhX79+6NChAy5fvow1a9bghx9+wGeffWZV1tPHleTw4cO4ceMGjh07hi1btmDnzp1W8+KV93o/+eQTzJw5E5cuXcKzzz6LkSNHsgHZ2bNnMWnSJMTGxuLSpUvo2bNnsfqOHj0aISEhOHfuHM6fP4+PP/4Yrq6uJdbbIZh6RqFQMAAYhULh7KrUenq9nrl79y5z584dJi0trVyPR48eMVevXmWSk5MZnU7n7EsgpNbSaDTM9evXGY1Gw27T6/XMrVu3mOTk5HL/TlbmkZyczNy6dYvR6/UVugaTycT079+f6dKlS6n7TZs2jenVqxdjNpttvg6A2bVrF8MwDLNmzRrG29vb6n1Zt24dA4C5ePEiwzAMc/ToUQYAc+jQIXafhIQEBgBz7949dttbb73F9O7du8R6ZWdnMwCY//77z+brWq2WEYvFzOnTp622T5o0iRk5cqRVXXbv3m21T/fu3Zm2bdtabZs9ezbTpEkTq/dh9erVjEQiYUwmU4nH2TJu3DjGy8uLKSwsZLetWbPGqqyyrvfBgwcMAOb7779n97l27RoDgLlx4wbDMAwzcuRIpl+/flbljBgxgpHJZOxzd3d3ZuPGjWXWmWFsf+4t7Lm/U8sPqTClUgmNRmPX7LIcDgceHh5QqVTUAkRIPTd16lRcvXoVW7duZbe9/fbbkEgk7AN4MjLp0qVLaNKkCaZPn46//vqrxDJv3bqFVq1aQSgUsts6duxoc19LCwsA+Pv7QywWo2HDhlbbLMspAMCdO3cwcuRINGzYEFKplG19Kjowp6i7d+9CrVbjxRdftLqmH3/8sVh3Wfv27YsdHxUVZfX8xo0b6NSpk1Ure5cuXaBSqfDo0aMSjytJ69atrf5+d+rUCSqVip0MuLzXW/R9tCw7YXnfbty4gejoaKv9O3XqZPU8Li4Ob7zxBmJiYrB48eIyuxIdgTJPSYUYjUbI5XJ2unF7WAKg/Px8AE9+WSghj5D6JTY2Fn/88QdOnDiBkJAQdvuCBQswc+ZMq33btWuHBw8eYP/+/Th06BCGDx+OmJgYq9yZiijatcLhcIp1tXA4HKsunoEDByI8PBzr1q1DUFAQzGYzWrRoUeKXOJVKBQDYt28fgoODrV57evoPW8neFU0Ar+hxTyvv9T79PgKwa7qFefPmYdSoUdi3bx/279+PuXPnYuvWrRgyZIhDrsMWCn5IhRQWFkKj0VR4eCmHw4Gnpyfy8/PB5XIRGBhIo8AIqQcYhsG0adOwa9cuHDt2DA0aNLB63c/Pz+aSRVKpFCNGjMCIESPw6quvok+fPsjLyyuWK9SkSRP8/PPP0Ol0bIBx7ty5Stc7NzcXt27dwrp169C1a1cAwMmTJ0s9plmzZhAIBEhJSUH37t0rXYemTZvit99+A8MwbJBx6tQpuLu7WwWQ5XX58mVoNBr2S+w///wDiUSC0NDQCl1vSXU+e/as1bZ//vmn2H7PPvssnn32WcyYMQMjR47Ehg0bqjT4oW4vYjeGYSCXy+Hq6lqpqcYtLUAKhQLZ2dnVOjEbIcQ5pk6dip9//hmbN2+Gu7s7MjIykJGRAY1GU+IxX331FbZs2YKbN2/i9u3b2L59OwICAuDh4VFs31GjRsFsNmPy5Mm4ceMG/vzzTyxduhRA5ZZG8PT0hLe3N9auXYu7d+/iyJEj7Nx0JXF3d8fMmTMxY8YMbNq0Cffu3cOFCxewcuVKbNq0ye46TJkyBampqZg2bRpu3ryJPXv2YO7cuYiLi6vQHGp6vR6TJk3C9evXkZiYiLlz5yI2NhZcLrdC12vL9OnTceDAASxduhR37tzBqlWrcODAAfZ1jUaD2NhYHDt2DA8fPsSpU6dw7tw5NG3a1O5z2YO+ahO7abVaqNVqtj++MiyzQOfm5kIkEtn8Y0YIsU/Roc817Txr1qwB8GRIdlEbNmzA+PHjbR7j7u6OL7/8Enfu3AGPx0OHDh2QmJho84YvlUrx+++/45133kGbNm3QsmVLxMfHY9SoUVZ5QPbicrnYunUrpk+fjhYtWqBJkyZYsWJFset42sKFC+Hr64uEhATcv38fHh4eaNeuHWbPnm13HYKDg5GYmIgPPvgArVu3hpeXFyZNmoRPP/20Qtf0wgsvoHHjxujWrRt0Oh1GjhyJefPmAaj49T7tueeew7p16zB37lzEx8cjJiYGn376KRYuXAjgyUjg3NxcjB07FpmZmfDx8cHQoUOtRp1VBQ7D1K8FmJRKJWQyGRQKBaRSqbOrUyvl5OQgIyOj1KGp9tJoNDCbzQgLC6OlMAgpB61WiwcPHqBBgwbsTb02zPDsDL/88gsmTJgAhUJRoTxFUnPY+txb2HN/p5YfYhez2QylUlmpb1C2iEQi5OXlIS8vDwEBAbRSNSEVwOPxEBoaWu/W9nrajz/+iIYNGyI4OBiXL1/GRx99hOHDh1PgQ1gU/BC76HQ6aLVauLu7O7xsd3d3yOVySKVSh41WIKS+4fF4NS4YqW4ZGRmIj49HRkYGAgMDMWzYMHz++efOrhapQSj4IXbRarV2reNjD8twyfz8fIjFYmr9IYRUyIcffogPP/zQ2dUgNRiN9iJ2UalUVTrtuEQigUKhQGFhYZWdgxBCSP1GwQ8pN4PBAK1WW6UTEvJ4PHC5XMjlctSzXHxCCCHVhIIfUm56vR4Gg6HKZ2N2c3ODUqm0Wk2ZEEIIcRQKfki56fV6q5lFq4plpmelUlml5yGEEFI/UfBDyq2wsLDalqCwtP5otdpqOR8hhJD6g4IfUi4mkwk6na5Kk52LcnV1hcFgQEFBQbWcjxBCSP1BwQ8pF4PBAL1eX23BD/Bk4kOFQgGDwVBt5ySEEFL3UfBDykWv11fZ/D4lEYlE0Gq1NOydkDpm/Pjx4HA4ePvtt4u9NnXqVHA4HHadr/Hjx2Pw4MEllhUREQEOh1PssXjx4iqqPakLKPgh5VKdawUVJRAIIJfLacV3QuqY0NBQbN261Wo1d61Wi82bNyMsLMyushYsWID09HSrx7Rp0xxdZVKHUPBDykWtVldrl5eFSCSCWq2mYe+E1DHt2rVDaGgodu7cyW7buXMnwsLC0LZtW7vKcnd3R0BAgNWDlsghpaHlLUiZTCZTtef7WHC5T+LzgoICSCSSaj8/IbUFwzBO+5JQ0eVoJk6ciA0bNmD06NEAgPXr12PChAk4duyYg2tIiDUKfkiZDAYDDAaDw1dyLy+xWIyCggJ4eXlBIBA4pQ6E1HRqtdppXxBUKlWFWlrGjBmDWbNm4eHDhwCAU6dOYevWrXYHPx999BE+/fRTq2379+9H165d7a4TqR8o+CFlMhgMMJlMbCtMdePz+VCpVFCr1RT8EFKH+Pr6on///ti4cSMYhkH//v3h4+NjdzkffPABmyBtERwc7KBakrqIgh9SJoPB4PQV1oVCIeRyOWQymdOCMEJqMrFYDJVK5bRzV9TEiRMRGxsLAFi9enWFyvDx8UGjRo0qXAdS/1DwQ8qk0WiqbWbnkgiFQigUCqc27RNSk3E4nFqZ5NunTx/o9XpwOBz07t3b2dUh9QQFP6RUZrMZWq3W6cEPl8sFh8OhxGdC6hgej4cbN26wP9uiUChw6dIlq23e3t4IDQ0F8GRAREZGhtXrYrEYUqnU8RUmdQL1H5BSGY1GGI1Gm8GPyWTC6dOnsXv3bpw+fRomk6lK62JJfNbpdFV6HkJI9ZJKpaUGKseOHUPbtm2tHvPnz2dfj4+PR2BgoNXjww8/rI6qk1qKwzAM46yTnzhxAkuWLMH58+eRnp6OXbt2lTqTJ/DklyAuLg7Xrl1DaGgoPv3002KJbqVRKpWQyWRQKBT0raAcCgsLkZycDA8PD6u8n8TERCxYsACpqansttDQUMTHx6Nfv35VVp+8vDwEBwfD09Ozys5BSG2g1Wrx4MEDNGjQwGkjMQmpbqV97u25vzu15aewsBCtW7cud5LbgwcP0L9/f/Ts2ROXLl3Ce++9hzfeeAN//vlnFde0/jIajWAYpljgM3nyZERGRmLv3r24ffs29u7di8jISEyePBmJiYlVVh9L4jPN+EwIIaSinNryUxSHwymz5eejjz7Cvn37cPXqVXbba6+9BrlcjgMHDpTrPNTyY5/s7GxkZ2fDw8MDwJOuri5duiAyMhLr16+3GnllNpsxceJE3Lp1CydPnqySdcDMZjMUCgXCw8Mp94fUa9TyQ+qjOtHyY68zZ84gJibGalvv3r1x5syZEo/R6XRQKpVWD1J+T4/0Onv2LFJTUzFt2rRiQ865XC5iY2ORkpKCs2fPVkl9LInP9P9ICCGkompV8JORkQF/f3+rbf7+/lAqlVaL4xWVkJAAmUzGPiyjA0jZTCYTDAaDVfCTlZUFAIiMjLR5jGW7Zb+qQInPhBBCKqNWBT8VMWvWLCgUCvZRNEGXlM7WSC8/Pz8AwM2bN20eY9lu2a8q8Pl8GAwGp03oRgghpHarVcFPQEAAMjMzrbZlZmZCKpVCJBLZPEYgELDDKMsaTkms2Qp+oqOjERoaipUrVxZLOjabzVi1ahXCwsIQHR1dpXWzJD5X9fB6QgghdU+tCn46deqEw4cPW207ePAgOnXq5KQa1W1Go7HYNh6Ph/j4eBw6dAgTJ05EUlISVCoVkpKSMHHiRBw6dAhz5sypkmTnokQiETQajdNWsSaEEFJ7OTX4UalUuHTpEjtz54MHD3Dp0iWkpKQAeNJlNXbsWHb/t99+G/fv38eHH36Imzdv4ptvvsGvv/6KGTNmOKP6dZ5lyvmn9evXD2vXrsXNmzcxaNAgNGnSBIMGDcKtW7ewdu3aKp3nx4LD4cDFxQUKhQI1ZMAiIYSQWsKpaxYkJSWhZ8+e7PO4uDgAwLhx47Bx40akp6ezgRAANGjQAPv27cOMGTPw9ddfIyQkBN9//z2tB1NFSlvTq1+/fujduzfOnj2LrKws+Pn5ITo6uspbfIqyLOSo1WpL7PYkhBBCnubU4KdHjx6lfmvfuHGjzWMuXrxYhbUiwJP8HYPBUGoww+Px0Llz52qslTUXFxeYTCYUFBRQ8EMIqbTk5GQ0aNAAFy9eRJs2bZxdnQrZuHEj3nvvPcjl8nIfUxeu2161KueHVB9LsrOrq6uzq1IqkUgEhUIBvV7v7KoQUiuZTCYcO3YMW7ZswbFjx+r1IILQ0FCkp6ejRYsWzq4K5s2bV6MDkfHjx5e5HFVNRsEPsckS/FRnN1ZFCIVC6HQ6FBYWOrsqhNQ6O3fuRKNGjdCzZ0+MGjUKPXv2RKNGjbBz505nV63a6fV68Hg8BAQElNjdT+oOCn6ITQaDAQBsJjzXNEKhEHl5eTZHpxFCbNu5cydeffVVtGzZEmfOnEFBQQHOnDmDli1b4tVXX62yAMhsNiMhIQENGjSASCRC69atsWPHDgAAwzCIiYlB79692ZSIvLw8hISEID4+HsCTxa05HA727duHVq1aQSgU4rnnnrNa9ggATp48ia5du0IkEiE0NBTTp0+3+pIUERGBhQsXYuzYsZBKpZg8eTKSk5PB4XDYQTiWc/35559o27YtRCIRevXqhaysLOzfvx9NmzaFVCrFqFGjrEaelnaNRcs9fPgw2rdvD7FYjM6dO+PWrVsAnnRdzZ8/H5cvXwaHwwGHw2HTQL766iu0bNkSbm5uCA0NxZQpU+ye8+zff/9F27ZtIRQK0b59+2KpJCaTCZMmTWLr36RJE3z99dfs6/PmzcOmTZuwZ88etn7Hjh0D8GQZqmeffRZisRgNGzbEnDlz2PtJTULBD7GpJn5YS2IZ9k6tP4SUj8lkwvvvv48BAwZg9+7deO655yCRSPDcc89h9+7dGDBgAGbOnFklXWAJCQn48ccf8e233+LatWuYMWMGxowZg+PHj4PD4WDTpk04d+4cVqxYAeDJKN/g4GA2+LH44IMPsGzZMpw7dw6+vr4YOHAg+3fr3r176NOnD1555RVcuXIF27Ztw8mTJxEbG2tVxtKlS9G6dWtcvHgRc+bMKbHO8+bNw6pVq3D69GmkpqZi+PDhWL58OTZv3ox9+/bhr7/+wsqVK8t1jUV98sknWLZsGZKSkuDi4oKJEycCAEaMGIH3338fzZs3R3p6OtLT0zFixAgAT5b4WbFiBa5du4ZNmzbhyJEj+PDDD8v9/qtUKgwYMADNmjXD+fPnMW/ePMycOdNqH7PZjJCQEGzfvh3Xr19HfHw8Zs+ejV9//RUAMHPmTAwfPhx9+vRh62fJ/3R3d8fGjRtx/fp1fP3111i3bh3+97//lbt+1YapZxQKBQOAUSgUzq5Kjfbo0SPm5s2bTFpaWrke9+/fZxYvXsx07NiR8fDwYGQyGdOuXTtm1qxZzPXr18tdTkUft2/fZu7fv88YjUZnv3WEVAuNRsNcv36d0Wg0dh979OhRBgBz5swZm6+fPn2aAcAcPXq0krW0ptVqGbFYzJw+fdpq+6RJk5iRI0eyz3/99VdGKBQyH3/8MePm5sbcvn27WN23bt3KbsvNzWVEIhGzbds2trzJkydbnePvv/9muFwu+36Fh4czgwcPttrnwYMHDADm4sWLVuc6dOgQu09CQgIDgLl37x677a233mJ69+5d7mu0Ve6+ffsYAGz95s6dy7Ru3bqkt5K1fft2xtvbm32+YcMGRiaTlbj/d999x3h7e1t9btasWWN13bZMnTqVeeWVV9jn48aNYwYNGlRm/ZYsWcJERUWVuV95lfa5t+f+Th2bpBiGYaDT6crd733v3j289dZbuHHjhtX2Cxcu4MKFC1i1ahXeeecdTJkypcoSqC2Jz4WFhTSLNyFlSE9PB4ASE3st2y37Ocrdu3ehVqvx4osvWm3X6/Vo27Yt+3zYsGHYtWsXFi9ejDVr1qBx48bFyio6ua2XlxeaNGnC/g26fPkyrly5gl9++YXdh2EYmM1mPHjwAE2bNgUAtG/fvlz1btWqFfuzv78/26VTdNu///5r1zU+XW5gYCCAJ+sihoWFlViXQ4cOISEhATdv3oRSqYTRaIRWq4VarYZYLC7zWm7cuMF2F1rYmih49erVWL9+PVJSUqDRaKDX68uVgL1t2zasWLEC9+7dg0qlgtForJF/kyn4IcVYkp35fH6Z+96+fRuvvPIK8vLy4O3tjdjYWHTt2hUcDgfnz5/HDz/8gFu3buHLL7/EgQMHsGLFCpt/yCqLy+XC1dUVeXl5kEgkxVacJ4T8H8uN9urVq3juueeKvW7Jn7Hs5yiW3JR9+/YhODjY6jWBQMD+rFarcf78efB4PNy5c6dC53nrrbcwffr0Yq8VDSzc3NzKVV7RL20cDqfYlzgOh8Mu91Pea7RVLoBiywYVlZycjAEDBuCdd97B559/Di8vL5w8eRKTJk2CXq8vV/BTHlu3bsXMmTOxbNkydOrUCe7u7liyZAnOnj1b6nFnzpzB6NGjMX/+fPTu3RsymQxbt27FsmXLHFIvR6LghxRjCX7K+kXKy8vD2LFjkZeXh5YtW+Knn36Cr68v+3pkZCRGjhyJ3bt3Y86cObhy5Qr69++PVatW4aWXXnJ4vcViMRQKBQoKCiCTyRxePiF1RdeuXREREYFFixZh9+7dVl8Wiibrdu3a1aHnbdasGQQCAVJSUtC9e/cS93v//ffB5XKxf/9+9OvXD/3790evXr2s9vnnn3/YQCY/Px+3b99mW3TatWuH69evo1GjRg6tf3mU9xrLwufzi+VcnT9/HmazGcuWLWP/zyx5OOXVtGlT/PTTT9BqtWzrzz///GO1z6lTp9C5c2dMmTKF3Xbv3r0y63f69GmEh4fjk08+Ybc9fPjQrvpVF/p6TIoxGo0wm82ltp4wDIOPPvoIqampiIiIwObNm60CHwsul4uhQ4fi8OHD6NSpEwoLCzFx4kR8/fXXDl+WomjrT32eq4SQsvB4PCxbtgx//PEHBg8ebDXaa/Dgwfjjjz+wdOlSh0914e7ujpkzZ2LGjBnYtGkT7t27hwsXLmDlypXYtGkTgCctJuvXr8cvv/yCF198ER988AHGjRuH/Px8q7IWLFiAw4cP4+rVqxg/fjx8fHzYeWc++ugjnD59GrGxsbh06RLu3LmDPXv2FEt4rgrlucbyiIiIYJd8ysnJgU6nQ6NGjWAwGLBy5Urcv38fP/30E7799lu76jdq1ChwOBy8+eabuH79OhITE7F06VKrfRo3boykpCT8+eefuH37NubMmYNz584Vq9+VK1dw69Yt5OTkwGAwoHHjxkhJScHWrVtx7949rFixArt27bKrftWFgh9STHmGjB88eBCJiYlwcXHBmjVr4OXlVer+AQEB2LJlC8aNGweGYfDll19i2rRp0Ol0jqo2gCfN2IWFhXYP/SSkvhk6dCh27NiB//77D507d4ZUKkXnzp1x9epV7NixA0OHDq2S8y5cuBBz5sxBQkICmjZtij59+mDfvn1o0KABsrOzMWnSJMybNw/t2rUDAMyfPx/+/v54++23rcpZvHgx3n33XURFRSEjIwO///4721XfqlUrHD9+HLdv30bXrl3Rtm1bxMfHIygoqEquyZ5rLK9XXnkFffr0Qc+ePeHr64stW7agdevW+Oqrr/DFF1+gRYsW+OWXX5CQkGBX3SQSCX7//Xf8999/aNu2LT755BN88cUXVvu89dZbGDp0KEaMGIHo6Gjk5uZatQIBwJtvvokmTZqgffv28PX1xalTp/Dyyy9jxowZiI2NRZs2bXD69OlSR9E5E4dx9NfvGk6pVEImk0GhUNTIJKyaICMjA/n5+SV2Hen1evTs2RPJycmYOnUqZs+ebVf5P/30Ez799FMYjUZ07NgRP/zwQ5nBkz0KCwvB5XIRHh5Ok5WROkur1eLBgwdo0KCBVfKqvUwmE/7++2+kp6cjMDAQXbt2rdGTmx47dgw9e/ZEfn4+PDw8nF0dUs1K+9zbc3+nOwMpRqvVlho0/Pbbb0hOToavr6/NhMKyvP766wgPD8fkyZPx77//YuDAgfj111+LJQdWlFgsRn5+PgoKCuDp6emQMgmpq3g8Hnr06OHsahBSrajbi1gxmUylLmthMpnYybymTJkCiURSofN069YNe/bsQUhICJKTkzFy5Ei7FuIrDYfDgUgkQl5eXq2arJEQQkj1oOCHWLGM9Cqp5efgwYN4+PAhPD09MWbMmEqdq0mTJti5cyeCgoJw7949zJw502FJ0JZZnxUKhUPKI4TUDD169ADDMNTlRSqFgh9ixWg0wmQylRj8bNiwAQAwevRoh8wpERwcjO+//x6urq7Yv38/W74jWLq/HJ1UTQghpHaj4IdYMRqNJba+pKWl4eTJk+BwOHj99dcdds7WrVuzIwIWLlxYbD6JirKs+O6o7jRCCCF1AwU/xIrBYChxJffff/8dABAdHY2QkJBKn8tkMuH06dPYvXs3IiMj0b17d+j1esyePdth3V8SiQT5+flWKy4TQgip32i0F7FS2kgvS/Dz8ssvV/o8iYmJWLBgAVJTU9ltgYGBcHV1xcmTJ7Fnzx52wrLK4PP5UKvVyM/Ph0gkKjGwI4QQUn9Qyw9hlbagaVZWFi5dugQA6Nu3b6XOk5iYiMmTJyMyMhJ79+7F7du3sXfvXrRo0YIdnbVgwQKHtdZIJBJ20VNCCCGEgh/CsiQ72xrmfuzYMQBPZk718/Or8DlMJhMWLFiAmJgYrF+/HlFRUXBzc0NUVBTWr1+PF154ATweD5mZmVi3bl2Fz1OUi4sLuFwucnNzS100kBBCSP1AwQ9hlTbM/ciRIwBQbHFBe509exapqamYNm1asbXDuFwupk+fzq7L9c033yA3N7dS57OQSCQoKChAQUGBQ8ojhDhHjx498N5777HPIyIisHz5cqfVpzKSk5PB4XDYVnVSfSj4IaySFjRlGAanT58GgErPBJuVlQXgyYrvtli2h4aGQqVS4euvv67U+Sy4XC4EAgFycnLKtXYZIaR2OHfuHCZPnuzsapBahoIfwiopKLh37x5yc3MhFArRunXrSp3D0mV28+ZNm69bto8fPx4A8PPPPyMjI6NS57QQi8XQaDQ09J0QAPPmzcPChQttvrZw4ULMmzeveitUQb6+vg6Zc4zULxT8EJZOp7OZ73P27FkAQNu2bdlVkysqOjoaoaGhWLlyZbH8G7PZjFWrViEsLAxvvPEGOnbsCJ1Oh2+++aZS57TgcDg08SEh/x+Px0N8fHyxAGjhwoWIj4+vssVNd+zYgZYtW0IkEsHb2xsxMTHsYITx48dj8ODBmD9/Pnx9fSGVSvH2229Dr9eXWN7T3V4cDgfff/89hgwZArFYjMaNG2Pv3r1Wx1y9ehV9+/aFRCKBv78/Xn/9deTk5JR4jo0bN8LDwwN//vknmjZtColEgj59+iA9PZ3dx2w2Y8GCBQgJCYFAIECbNm1w4MABq3L+/fdftG3bFkKhEO3bt8fFixeLnausupX2/pHyo+CHsEoa5m4JfqKjoyt9Dssf3EOHDmHixIlISkqCSqVCUlISJk6ciEOHDmHOnDlwcXHBjBkzADi29YcmPiTkiTlz5mDBggVWAZAl8FmwYAE78agjpaenY+TIkZg4cSJu3LiBY8eOYejQoVbzeh0+fJh9bcuWLdi5cyfmz59v13nmz5+P4cOH48qVK+jXrx9Gjx6NvLw8AIBcLkevXr3Qtm1bJCUl4cCBA8jMzMTw4cNLLVOtVmPp0qX46aefcOLECaSkpGDmzJns619//TWWLVuGpUuX4sqVK+jduzdefvll3LlzBwCgUqkwYMAANGvWDOfPn8e8efOsji9P3crz/pFyYuoZhULBAGAUCoWzq1KjGI1G5s6dO8y9e/eYtLQ0q0dISAgDgNmyZUux1yr6WLduHRMaGsoAYB9hYWHMunXr2H0ePXrEdOjQgQHATJo0yWHnfvDgAXPjxg2msLDQ2W87IRWm0WiY69evMxqNplLlLFiwgAHA8Pl8BgCzYMECB9WwuPPnzzMAmOTkZJuvjxs3jvHy8rL63VyzZg0jkUgYk8nEMAzDdO/enXn33XfZ18PDw5n//e9/7HMAzKeffso+V6lUDABm//79DMMwzMKFC5mXXnrJ6rypqakMAObWrVs267VhwwYGAHP37l122+rVqxl/f3/2eVBQEPP5559bHdehQwdmypQpDMMwzHfffcd4e3tb/X+tWbOGAcBcvHixXHUr6/2rD0r73Ntzf6eWHwKg5JFeaWlpePToEXg8HqKiohx2vn79+uHUqVPYvn07Vq9eje3bt+PkyZPo168fuw+Hw0FcXBwA4JdffkFmZqZDzs3n82E2m5GXl0ffmEi9N2fOHPD5fOj1evD5/Cpp8bFo3bo1XnjhBbRs2RLDhg3DunXrkJ+fX2yfojk8nTp1gkqlspoQtSytWrVif3Zzc4NUKmUHW1y+fBlHjx6FRCJhH5aBFqUtrSMWi/HMM8+wzwMDA9kylUolHj9+jC5dulgd06VLF9y4cQMAcOPGDbRq1QpCodDq2ooqq27lef9I+VDwQwCUvKDpv//+CwBo0aIF3NzcHHpOHo+Hzp07Y/DgwejcubPNHIOuXbuiffv20Gq1Dsv9AQB3d3colUqoVCqHlUlIbbRw4UI28NHr9SUmQTsCj8fDwYMHsX//fjRr1gwrV65EkyZN8ODBA4eex9XV1eo5h8NhcwxVKhUGDhyIS5cuWT3u3LmDbt262VWmo788lVW36nr/6gOnBz+rV69GREQEhEIhoqOj2ZttSZYvX44mTZpAJBIhNDQUM2bMgFarraba1l0lLWhqyffp2LFjdVcJwJM/MEVzf7Kzsx1SLo/HA4/HQ25uLjuvECH1TdEcH51OVywHqCpwOBx06dIF8+fPx8WLF8Hn87Fr1y729cuXL0Oj0bDP//nnH0gkEoSGhjrk/O3atcO1a9cQERGBRo0aWT0q+gVPKpUiKCgIp06dstp+6tQpNGvWDADQtGlTXLlyxep+9c8//9hdt7LeP1I+Tg1+tm3bhri4OMydOxcXLlxA69at0bt3b7Yp8WmbN2/Gxx9/jLlz5+LGjRv44YcfsG3bNsyePbuaa173lLSgqWXyrfbt21dzjf5P9+7d0bZtW2i1Wnz33XcOK9fNzQ0qlYomPiT1kq3kZltJ0I509uxZLFq0CElJSUhJScHOnTuRnZ2Npk2bsvvo9XpMmjQJ169fR2JiIubOnYvY2Nhi849V1NSpU5GXl4eRI0fi3LlzuHfvHv78809MmDChUl+EPvjgA3zxxRfYtm0bbt26hY8//hiXLl3Cu+++CwAYNWoUOBwO3nzzTfbali5dalfdyvP+kfJx6sKmX331Fd58801MmDABAPDtt99i3759WL9+PT7++ONi+58+fRpdunTBqFGjADwZ4jhy5Ei2dYJUnFarLdasq9Vq2f7qNm3aOKFWT3A4HLz33nsYN24cNm3ahClTpsDLy6vS5XK5XAiFQuTm5kIikZS4oCshdZFlqZmnc3wsz6uiRVQqleLEiRNYvnw5lEolwsPDsWzZMqv1Al944QU0btwY3bp1g06nw8iRIx0655Clheajjz7CSy+9BJ1Oh/DwcPTp06dSAdb06dOhUCjw/vvvIysrC82aNcPevXvRuHFjAE9mmf/999/x9ttvo23btmjWrBm++OILvPLKK+WuW3neP1I+HMZJGZ96vR5isRg7duywWr173LhxkMvl2LNnT7FjNm/ejClTpuCvv/5Cx44dcf/+ffTv3x+vv/56uVt/lEolZDIZFAoFpFKpoy6nVmMYBvfv3wcAiEQidvuFCxcwcOBAeHl54cqVK05dEZ1hGPTt2xf//fcfpk2bhg8++ABnz55FVlYW/Pz8EB0dXaF5SRiGQX5+PgIDA+Ht7V0FNSekami1Wjx48AANGjSwSqKtzcaPHw+5XI7du3c7uyqkhirtc2/P/d1pX3VzcnJgMpng7+9vtd3f37/E2X9HjRqFnJwcPP/882AYBkajEW+//XapgY9Op7Oa0E6pVDrmAuoQS7KzQCCw2n7lyhUAT1p9nBn4AP/X+jNp0iSsW7cOO3fuRFpaGvt6aGgo4uPjrUaLlbdckUiE/Px8uLu7V3oSR0IIITWf0xOe7XHs2DEsWrQI33zzDS5cuICdO3di3759pfZNJyQkQCaTsQ9HJc3VJQaDweYwd0u+T2WXtHCUl156CcHBwdBqtRAIBNi7dy9u376NvXv3IjIyEpMnT0ZiYqLd5YpEImi1WigUiiqoNSGEkJrGacGPj48PeDxesblbMjMzERAQYPOYOXPm4PXXX8cbb7yBli1bYsiQIVi0aBESEhKKLZVgMWvWLCgUCvZhz1wR9YHJZMLRo0exb98+nDlzxqqf//LlywCs58xwJoZh2JES2dnZaNy4Mdzc3BAVFYX169cjJiYGCxcurFCugpubG/Lz82nkICFOtHHjRuryItXCacEPn89HVFQUDh8+zG4zm804fPhwsYmfLNRqdbGENEueR0mpSwKBAFKp1OpBnti5cycaNWqEAQMG4MMPP8SwYcPQpUsXJCYmorCwkJ2W3ZnJzkWdPXsWubm5CA0NRUFBATZs2MC+xuVyERsbi5SUlAolwAsEAhgMBmr9IYSQesCp3V5xcXFYt24dNm3ahBs3buCdd95BYWEhO/pr7NixmDVrFrv/wIEDsWbNGmzduhUPHjzAwYMHMWfOHAwcOLDKFuGrq3bu3IlXX30VLVu2xN69e5GUlGTVfbRu3TowDIPAwEB2JXZns0yB8N577wEA1q5dazVJoWUm1JKmSiiLpfWn6BwjhNR0NEs5qU8c9Xl36tjeESNGIDs7G/Hx8cjIyGBXwbUkQaekpFi19Hz66afgcDj49NNPkZaWBl9fXwwcOBCff/65sy6hVjKZTHj//fcxYMAA7Nq1Cw8fPoTZbEZgYCDWr1+PiRMnYt26dQBqTr4PADYIe+aZZ9CwYUPcv38fP/74I6ZMmQIAbKJ8RYM1Pp8PtVoNuVwOoVDo9CRvQkpj+cKn1+utRmkSUpep1WoAxWfctpfThro7Cw11f5I43rNnT5w5cwbt27fHgwcP4Orqyo50SkpKwqBBgwA8mbjL0tLibCaTCV26dEFkZCT69u2LuLg4eHt7459//oFQKMTEiRNx69YtnDx5ssItgQaDAWq1GuHh4VbrCxFS0zAMg5SUFBgMBgQFBTlsEkBCaiKGYaBWq5GVlQUPDw8EBgYW26dWDHUnzpOeng7gyXpdlgVNi35ztHQfAUDz5s2rvX4l4fF4iI+Px+TJk8EwDAICApCRkYGEhASkpqbi0KFDWLt2baW6QF1dXdm5f0QiEbX+kBqLw+EgMDAQDx48wMOHD51dHUKqhYeHR4mDouxBwU89ZImYr169iubNm8NsNlt9a7TM7wM8CZBqkn79+mHt2rVYsGABMjIyAADr169HSEgI1q5da/c8P7ZIJBIolUp4eHg4fDFXQhyJz+ejcePG0Ov1zq4KIVXO1dXVYfm9FPzUQ127dkVERAQWLVqEDRs2WCWQmc1mdr0ZT09Ph0TYjtavXz/07t0bp06dwrRp05CTk4PBgwc7JPABABcXFzAMA7lcDrFYTK0/pEazLNNCCCk/6iSuh3g8HpYtW4Y//vgDo0aNwn///QeVSoWkpCRMnDgR//77L4AnrT419cbP4/HQrVs3LF68GADw/fffW834XFkSiQQKhYJNriOEEFJ3UPBTTw0dOhQ7duzA9evXMWrUKDRp0gSDBg3CrVu30KNHDwA1K9+nJH369EF0dDS0Wi2++OILh5Vrme1aoVDQUGJCCKljKPipx15++WUcPHgQmzdvxurVq7F9+3acPHmSnTunNgQ/HA4Hc+fOBQD89ttvVvlKleXm5galUkmzPhNCSB1DwU89ZjQawTAMunbtisGDB6Nz587gcDi4fv06gNoR/ABP5iIaOnQogCdzQZW01Im9XF1dYTKZaDFcQgipYyj4qccMBkOxkV4PHz5EYWEhhEIhnnnmGSfWzj6zZs2Cm5sbzp8/j19++cVh5YrFYigUCuh0OoeVSQghxLko+KnHLC0/RV27dg3Ak7l+nl7lvSYLCgrCRx99BABYtGhRsQVzK0ogEECv16OgoMAh5RFCCHE+Cn7qMa1WWyzAuXr1KoDa0+VV1Pjx49GqVSsolUrMmzfPYeWKRCLI5XIYDAaHlUkIIcR5KPippxiGgUajKRb8WFp+mjVr5oxqVQqPx8OXX34JLpeLvXv34siRIw4pVygUQqvVorCw0CHlEUIIcS4Kfuopo9EIk8lULPipbcnOT2vZsiUmTZoEAJg9e7ZD5unhcDgQCATIz893WDI1IYQQ56Hgp54yGo0wGAxWwU9OTg4yMjLA4XBqZcuPxQcffICgoCCkpqbiyy+/dEiZIpEIarWaWn8IIaQOoOCnnjIYDGAYxmqkl6XVJyIiolJrWjEMA7PZ7LTJAd3c3NgJD7///nt2xurK4HK54PF4NOkhIYTUART81FMGg6HY0hWWfJ+KLmaq0WiQn58PuVyOgoICyOVy5OfnQ6VSwWQyVbrO9ujVqxdGjBgBhmEQFxcHjUZT6TLFYjEKCgpo0kNCCKnlKPippzQaTbHVcSua7GwymZCXlwcACAgIQEREBBo0aIAGDRogKCgIAoEASqUSSqWyWnNm5s6di4CAADx48MAh3V8uLi4wm8006SEhhNRyFPzUQ2azGTqdrsSRXvYkOxuNRigUCnh5eSE0NBTe3t5wc3ODUCiEWCyGp6cnwsLCEB4eDjc3NygUCoe0wpSHTCZjg55169bh3LlzlS5TJBJBqVRCr9dXuixCCCHOQcFPPWQwGGA0GuHq6spu02g0uHv3LoDyBz+WVhAfHx8EBASAz+fb3I/D4UAikSA4OBjBwcFgGAb5+fnV0hX2wgsvYPjw4WAYBjNmzKh04CUUCqHT6SjxmRBCajEKfuoho9EIo9Fo1fJz69YtmM1meHt7w9/fv1zlKBQKyGQy+Pr6WiVOl4TL5cLDwwOhoaHw8PCotlagot1fS5YsqXR5lmHv1Z3HRAghxDEo+KmHLCO9iio6v8/TidC2aDQaCAQC+Pn5FcsdKotAIEBgYCBCQkJgNpshl8urNBfIw8ODHf21du3aSnd/iUQiaDQah8whRAghpPo5PPiprnwOUnFarbbEZOfydHmZzWZoNBr4+PhAIBBUqA5FW4Hc3d0hl8thNBorVFZ5xMTEYNiwYQ4Z/UXD3gkhpHZzWPCj0+mwbNkyNGjQwFFFkipgWdaiaL4PYN9Ir8LCQri7u0MqlVa6PkKhEEFBQfD29oZSqazSrqR58+bB398f9+/fx9KlSytVFg17J4SQ2suu4Een02HWrFlo3749OnfujN27dwMANmzYgAYNGmD58uWYMWNGVdSTOIitmZ3NZnO5l7UwmUwwmUzw8vIqV55PefB4PPj7+8Pb2xsKhaLKusCe7v66cOFChcuiYe+EEFJ72XX3io+Px5o1axAREYHk5GQMGzYMkydPxv/+9z989dVXSE5OxkcffVRVdSUOYDAYYDAYrFp+Hj58iMLCQggEAjzzzDOlHq/RaCAWiys1A7QtXC4Xvr6+kMlkUCgUDi27qBdffBFDhw6F2WzG+++/D51OV+GyaNg7IYTUTnYFP9u3b8ePP/6IHTt24K+//oLJZILRaMTly5fx2muv2Z34SqqfJdm5aFKzpcsrMjKy2Nw/RTEMA71eD09PT4e1+hTl4uICPz8/CIVCqFQqh5dvMX/+fPj4+OD27dv4+uuv7TrWZDLh9OnT2L17Ny5cuEDrfRFCSC1k1x3s0aNHiIqKAvBkCQSBQIAZM2aUa3QQqRl0Ol2xwKW8yc46nY6dvLCqWEaQGY3GKmtR8fLywqJFiwAAq1atwtWrV8t1XGJiIrp06YJhw4Zh6tSpGDZsGAYMGIAtW7bQsHdCCKlF7Ap+TCaT1UR2Li4ukEgkDq8UqRoMw0CtVhebjLC8wY9Wq4W7u3uxZGlHc3d3h4+PD1QqVZWNpurfvz/69+8Pk8mEuLg4GAyGUvdPTEzE5MmTERkZib179+L27dvYu3cvmjZtitjYWGzZsqVK6kkIIcTxOIwddxcul4u+ffuyw5t///139OrVq1j+x86dOx1bSwdSKpVsXokjRivVJgaDAcnJyXB1dbUKgNq3b4/09HTs2rULHTt2tHkswzCQy+UIDw+vloDXaDTi0aNH0Gq1Vfb/lJ2djR49ekAul+PDDz/Eu+++a3M/k8mELl26IDIyEuvXr7dqOTObzRg7dizu3r2Le/fuVXlgSAghxDZ77u92tfyMGzcOfn5+kMlkkMlkGDNmDIKCgtjnlgepmQwGA/R6vdUNOi8vD+np6QCApk2blnisTqeDQCCAUCis8noCT1oVfXx82DyjquDr64uFCxcCAJYvX45bt27Z3O/s2bNITU3FtGnTinUZcrlcTJ8+HampqTh06FCV1JMQQohjlZzdasOGDRscXoHVq1djyZIlyMjIQOvWrbFy5coSWx8AQC6X45NPPsHOnTuRl5eH8PBwLF++HP369XN43eoavV5fYrJzREQE3N3dSzxWq9XC09Oz1IRoR5NIJPDy8kJ2djY8PT2rJLdsyJAh2Lt3Lw4ePIj3338fu3fvLnaNWVlZAJ4khNtimRvp/v37xd5fQgghNU+lh+w8evQIjx49qtCx27ZtQ1xcHObOnYsLFy6gdevW6N27N3uzeZper8eLL76I5ORk7NixA7du3cK6desQHBxcmUuoN0qb2bmsyQ3NZrNT8ru8vLwgEomqbCkJDoeDhIQESKVSXLx4EZs2bSq2j5+fHwDg5s2bNsuwbJdIJDTDOSGE1AIVCn7MZjMWLFgAmUyG8PBwhIeHw8PDAwsXLrRrgrqvvvoKb775JiZMmIBmzZrh22+/hVgsxvr1623uv379euTl5WH37t3o0qULIiIi0L17d7Ru3boil1GvVCbZWa/Xg8/nV3gpi8pwdXWFr68v9Hp9lY2oCgwMxOzZswEAX375JdsNaBEdHY3Q0FCsXLmy2OfbbDZj1apVCAsLQ7t27ap0jiJCCCGOUaHg55NPPsGqVauwePFiXLx4ERcvXsSiRYuwcuVKzJkzp1xl6PV6nD9/HjExMf9XGS4XMTExOHPmjM1j9u7di06dOmHq1Knw9/dHixYtsGjRolJvijqdDkql0upRH1kmN3y6S6c8Mzvr9XoIhcJigVN1cXd3h4eHBwoKCqrsHKNHj0a7du2gUqkwd+5cq9d4PB7i4+Nx6NAhTJw4EUlJSVCpVEhKSsLEiRNx6NAhzJkzB+7u7lAqlbTkBSGE1HAVCn42bdqE77//Hu+88w5atWqFVq1aYcqUKVi3bh02btxYrjJycnJgMpng7+9vtd3f3x8ZGRk2j7l//z527NgBk8mExMREzJkzB8uWLcNnn31W4nkSEhKskrFDQ0PLfZ11iV6vh9FotEp21mg0uHPnDoCyg5/S8oGqGofDgZeXF3g8XpUFFlwuF1988QV4PB727duHw4cPW73er18/rF27Fjdv3sSgQYPQpEkTDBo0CLdu3cLatWvRr18/8Pl8GI1Gav0hhJAarkLZq3l5eTaTPyMjI5GXl1fpSpXEbDbDz88Pa9euBY/HQ1RUFNLS0rBkyZJi39YtZs2ahbi4OPa5UqmslwGQrRFT165dg8lkgp+fHwIDA20eZzabweVyndLlVZRIJIK3tzcyMjIgEAgqlVRsMplw9uxZZGVlwc/PD9HR0eDxeGjWrBnefPNNfPvtt/jkk0/QuXNniEQi9rh+/fqhd+/eNo+1EIvFUCgUkMlk1TYyjhBCiH0q1PLTunVrrFq1qtj2VatWlTv/xsfHBzweD5mZmVbbMzMzERAQYPOYwMBAPPvss1Y3m6ZNmyIjI6PE4dACgQBSqdTqUR8VFhYWm4PmypUrAIBWrVqVGEw4M9/naR4eHhCLxZVaTsLWLM1dunRBYmIiACAuLg5BQUFITU3F8uXLix3P4/HQuXNnDB48GJ07dy6WQC4QCKDX66n1hxBCarAKBT9ffvkl1q9fj2bNmmHSpEmYNGkSmjVrho0bN2LJkiXlKoPP5yMqKsqqe8FsNuPw4cPo1KmTzWO6dOmCu3fvWiWd3r59G4GBgU7LR6kNjEYjdDpdseDn8uXLAFBqwKrX6yEWi2vEum2WuX8MBkOFkp9LmqU5MjISkydPRmJiItzc3PD5558DAL777jskJyfbfR43NzfI5XLK/SGEkBqqQsFP9+7dcfv2bQwZMgRyuRxyuRxDhw7FrVu30LVr13KXExcXh3Xr1mHTpk24ceMG3nnnHRQWFmLChAkAgLFjx2LWrFns/u+88w7y8vLw7rvv4vbt29i3bx8WLVqEqVOnVuQy6g29Xl9sckPAuuWnJEajsUrX8rJXRZOfTSYTFixYgJiYGKxfvx5RUVFwc3NDVFQU1q9fj5iYGCxcuBAmkwkvvfQSevbsCYPBwAZC9hAIBJT7QwghNViFZ6wLCgqq0I2hqBEjRiA7Oxvx8fHIyMhAmzZtcODAATYJOiUlxWpG3dDQUPz555+YMWMGWrVqheDgYLz77rv46KOPKlWPuk6v17O5OxaFhYVssnNJwY/ZbAaPx6sRXV4WluTnwsJCaLXacufVWGZpXr16tc1ZmmNjYzFo0CCcPXsWnTt3xpw5c3D8+HEkJibi7NmziI6Otquebm5uyM/Ph1QqtcobIoQQ4nwVDn7y8/Pxww8/4MaNGwCeTJI3YcIEeHl52VVObGwsYmNjbb527NixYts6deqEf/75x+761mdqtbrYEPerV6+CYRgEBgayk/g9TafTgc/n17guRZFIBC8vL7uSn8uapdmy3bJfkyZNMGrUKPz888+YP38+/vjjj2JBU2n4fD7UajXy8vIQFBREsz4TQkgNUqFurxMnTiAiIgIrVqxAfn4+8vPzsWLFCjRo0AAnTpxwdB1JJZhMJmg0mgrl+xgMhhqT7/M0Dw8PSCQSqFSqcu1f3lmaiwaCM2fOhEQiweXLl7Fr1y676yiRSCCXyyuVoE0IIcTxKhT8TJ06FSNGjMCDBw+wc+dO7Ny5E/fv38drr71G+Tc1jCXf5+muq/Lm+9TULhsXFxd4e3vDZDLBYDCUuX95Z2ku2r3l6+uLadOmAQCWLFli9wKrLi4ucHFxYee0IoQQUjNUKPi5e/cu3n//fasWAR6Ph7i4ONy9e9dhlSOVZ1kW4ukum7JafmrK/D6lkUgk8Pb2RkFBARiGKXXf8s7S/HQr16RJk+Dn54fU1FRs3rzZ7jq6ublBpVJR8jMhhNQgFQp+2rVrx+b6FHXjxg1aZ6uGUavVxW7oSqUS9+/fB1Byy49lfp+alu9TlCX5ubwLn5ZnluaniUQivPvuuwCAFStW2L1wKZfLhUgkQm5uLg19J4SQGqJCCc/Tp0/Hu+++i7t37+K5554DAPzzzz9YvXo1Fi9ezHapAKV3q5CqZTabbS5m+t9//wF4MnqupAR1g8EAiURSI/N9irIsfPro0SMYDIZiuU1PK88szU8bNWoUvv32W6SmpmLTpk14++237aqjSCRCfn4+cnNzKfmZEEJqAA5TVn+BDWWNeuFwOGAYBhwOp8blOiiVSshkMigUijo/27NGo0FycnKxIGbNmjX47LPP0L9/f6xdu9bmsfn5+QgKCoKnp2d1VbfCGIZBVlYWsrOz4enpWSXBxbZt2xAXFwcPDw/8888/dq91ZjKZoFAoEBISAg8PD4fXjxBC6jt77u8Vavl58OBBhSpGqpcl3+fpVo1Lly4BKLlVzhIP1+Qur6I4HA68vb2h0WhQUFBQJUHtK6+8gtWrV+PevXtYt26d1Xpx5cHj8SAUCpGTkwOhUEjrfhFCiBNVKPgJDw93dD1IFbCV7wMA58+fBwBERUXZPK4mredVXi4uLmxiskajcfgoNRcXF8ycORPvvPMOvvvuO4wfP97uOa3EYjHy8/ORk5ODoKAgu+YNIoQQ4jh2/fWdMmWK1bwqW7ZssZrDRC6X20waJdWvpHyftLQ0pKeng8fjlZicbjAYIBAIik2MWNOJxWL4+flBq9XCaDQ6vPwBAwagWbNmUKlUWLNmTYXKkMlkkMvlyMvLc3DtCCGElJddwc93331nNarmrbfeslqVXafT4c8//3Rc7UiF6XQ66HS6Yq03SUlJAIDmzZuXuGaXwWCAm5tbldexKnh4eMDb2xtKpbLYfD6VxeVy8eGHHwIA1q9fb/XZt6cMNzc3ZGdn270+GSGEEMewK/h5Oje6ArnSpJrodLpi63kB/9fl1b59+xKPZRimVnV5FcXhcODj4wOpVAqlUunw8mNiYhAVFQWtVouvv/66QmUIBALweDxkZWVBp9M5uIaEEELKQkkHdVRhYaHNbitLy09JwY9luHhtSXa2xZL/IxAIyr38RXlxOBx8/PHHAIBffvkFDx8+rFA5EokEOp0OWVlZVdJFRwghpGQU/NRBRqMRGo2mWACj0Whw7do1AKUHP3w+v8z5cmo6oVAIf39/mM1muycmLEvnzp3Ro0cPGI1GLF26tMLlSKVSKBQKZGdnO7yLjhBCSMnszmiNj49nc0X0ej0+//xzyGQyACjXLLuk6ul0Ouj1evb/xeLy5cswGo0ICAhAUFCQzWP1ej18fHzqxER8EokE/v7+ePz4MVxcXBwa0H388cc4duwYdu3ahSlTpqBp06Z2l8HlciGVSpGbmwsXF5c6874TQkhNZ1fw061bN9y6dYt93rlzZ3aZhKL7EOey5Ps8fSM9d+4cgCfLk5R0kzWbzXVqDhoPDw/o9XpkZ2fDw8PDYcPLW7ZsiYEDB+L333/HF198gY0bN1aoHBcXF0gkEmRlZYHL5cLLy4sCIEIIqWJ2BT/Hjh0rts2S9Ex/sGsGhmFQUFBgM2fnzJkzAIBOnTrZPNYyIWJtzvd5miUB2mg0Qi6Xw8PDw2Gf1Q8++ACJiYk4ePAgzp07hw4dOlSoHD6fD4ZhkJGRAS6XWytm1SaEkNqswl+Df/jhB7Ro0YKdrbZFixb4/vvvHVk3UgEGgwFarbbYaC29Xo9///0XQMnBT21YzLQieDwe/Pz84Obm5tDV1Z955hmMGDECALB48eJKjX4UCAQQCoVIT0+HXC53UA0JIYTYUqHgJz4+Hu+++y4GDhyI7du3Y/v27Rg4cCBmzJiB+Ph4R9eR2EGr1dpc4PPy5cvQaDTw8vJCkyZNbB5rMBggEonq5MzDrq6u8Pf3B5/Pd+gIsBkzZkAgEOCff/6x2TJqD5FIBIFAgMePHyM/P5+mkiCEkCpSobvcmjVrsG7dOiQkJODll1/Gyy+/jISEBKxduxbffPONo+tI7KDRaGwGL6dPnwYAPPfccyUGN0ajscSJD+sCkUjk8BFgQUFBGD9+PABg0aJFlV7IVyQSsS1AeXl5FAARQkgVqFDwYzAYbA6VjoqKojlLnMhkMkGlUtmcoNCS79OlSxebxzIMAw6HU+e6vJ7m7u4Of39/toXMEWJjYyGTyXD9+nVs3bq10uUJhUKIRCJkZGTQMHhCCKkCFQp+Xn/9dZtrG61duxajR4+udKVIxZS0pIVOp2NHetW3fB9bPDw84Ovri4KCgkq31ACAl5cXu8r7F1984ZCZpQUCATsKLCMjg75UEEKIA1U64fmNN97AG2+8gZYtW2LdunXgcrmIi4tjH6T6aLVaMAxTrFvr0qVL0Gq18Pb2xrPPPmvz2Nq6mGlFWEaAeXp6QqFQOKRrady4cWjUqBFyc3OxfPnyylcST/KUZDIZ8vLykJaWBq1W65ByCSGkvqvQne7q1ato164dAODevXsAAB8fH/j4+ODq1avsfjT8vfpYhrjbmsjPkojbpUuXEv9PDAYDvL29q7KKNQqXy4Wfnx8MBgMUCgU8PDwqVZ6rqyvmzZuHMWPGYP369Rg9ejSeeeaZSteTx+OxQdqjR4/g5+cHqVRa6XIJIaQ+q1Dwc/ToUUfXg1SSXq+HVqu1OUGh5f+rZ8+eJR5fmxczrSjLCLC0tDQUFhZWeiX7nj17olevXjhy5Ag+/fRTbN682SFfADgcDjw8PFBYWIhHjx7B19cXXl5e4PF4lS6bEELqo7o3prmeKmmIe1ZWFv777z8AJQc/lvW86kO+z9MsI8CMRqNDupXmz58PgUCAEydOYMeOHQ6o4f9xc3ODWCxGZmYmHj16RMvJEEJIBVHwU0eoVCqb+TqWLq9WrVrB19fX5rGWZOfavphpRbm7u8PPzw8ajabSicUNGzZkc93mzZuHnJwcR1SRxefz4eHhAbVajZSUFGRmZkKn0zn0HIQQUtdR8FMHGAwGqNVqm91W5eny0uv1cHNzq9c5Wl5eXvDy8oJSqax0AvRbb72F5s2bQy6XY+7cuQ6q4f/hcrmQyWQQCoXIzs5GSkoKsrOzKSGaEELKiYKfOkCr1UKn0xXrtjIajThx4gSAsvN96tJiphXB4XDg6+sLd3f3Si+B4erqiqVLl4LL5WL37t3Yv3+/g2ppjc/ns7k/WVlZePjwIdLS0qBUKh02hxEhhNRFNSL4Wb16NSIiIiAUChEdHc2uQVWWrVu3gsPhYPDgwVVbwRpOrVaDy+UWa7m5ePEiu5inZXTe04xGI1xdXetlvs/TXFxc2CUwKptP06pVK7zzzjsAgPfffx+PHj1yRBVtEgqF8PT0hFAohFKpRGpqKpKTk5GWlgaFQgGtVksTJRJCSBFOD362bduGuLg4zJ07FxcuXEDr1q3Ru3dvZGVllXpccnIyZs6cia5du1ZTTWum0mZ1PnLkCACgW7duJY4M0uv1FPwUIRQK4efnB71eD71eX6myZs6cibZt20KhUGDKlClV3hpjmRfIw8MDLi4uKCgoYAOhBw8esIumFhYWwmAw0NIZhJB6y+nBz1dffYU333wTEyZMQLNmzfDtt99CLBZj/fr1JR5jMpkwevRozJ8/Hw0bNqzG2tY8li6vig5x1+v1kEgk9Trf52nu7u7w9fWFSqWqVIsJn8/HN998A6lUivPnz2Pp0qUOrGXJOBwOBAIBpFIpvLy82CH8crkcaWlpbDD08OFDZGZmQqFQQK1W0yzShJB6w6nBj16vx/nz5xETE8Nu43K5iImJYdeismXBggXw8/PDpEmTqqOaNZqle+bp4CUzM7PMIe4A5fvYwuFw4OXlBZlMVumlKsLCwrBkyRIAwKpVq/DHH384oop24fF4EIlEkMlk8PT0hIeHB/h8PoxGI/Ly8qxah1JSUpCXl4fCwkIKhgghdZZT1zLIycmByWSCv7+/1XZ/f3/cvHnT5jEnT57EDz/8gEuXLpXrHJb1riwcse5STWEymaBUKm12ef35558AgLZt25Y4xN1oNILH41GXlw08Hg++vr7QarXQaDQQiUQVLmvAgAGYNGkSfvjhB7z77rsICQlBmzZtHFdZO1kWsC36/242m2EwGKDVaqFUKsHlcsHn8+Hm5gaJRAKRSFQvlj4hhNQPTu/2skdBQQFef/11rFu3Dj4+PuU6JiEhATKZjH2EhoZWcS2rj6XLy1bwc+DAAQBA3759SzzeMr9PfZvZubyEQiEbAFW2FWTu3Lno1asXtFotJkyYgLS0NAfV0jG4XC67mKql1YvL5UIulyMlJQXJycnIysqCRqOhXCFCSK3n1ODHx8cHPB4PmZmZVtszMzMREBBQbP979+4hOTkZAwcOhIuLC1xcXPDjjz9i7969cHFxYdcZK2rWrFlQKBTsIzU1tcqup7qp1WqbC5kqFAqcOnUKANCnT58Sj6d8n7JZuooq22LI4/GwZs0aNG3aFFlZWRg7dizy8vIcVEvH43A4EAqFbAI1h8NBdnY2Hj58iMePH6OwsJCCIEJIreXU4IfP5yMqKgqHDx9mt5nNZhw+fBidOnUqtn9kZCT+++8/XLp0iX28/PLL6NmzJy5dumSzVceS+Fn0URdYurxs5escOXIERqMRjRs3LnVxTbPZTPk+ZbCsAC8UCis9/F0ikWDjxo1st+6YMWNqRTcsh8OBSCSCp6cnxGIxFAoFHj58iPT0dGg0GmdXjxBC7Ob0bq+4uDisW7cOmzZtwo0bN/DOO++gsLAQEyZMAACMHTsWs2bNAvCkG6JFixZWDw8PD7i7u6NFixb1KndFo9GU2OVlmVSvtFYfy/w+1OVVNj6fD19fX+h0OphMpkqVFRISgq1bt8LLywuXL1/G2LFja9UaXS4uLvDw8IBEImG7xHJycig5mhBSqzg9+BkxYgSWLl2K+Ph4tGnTBpcuXcKBAwfYJOiUlBSkp6c7uZY1j6Xb4ekuL41Gww5xLyvfRyAQ1KuAsTKkUqlDur8A4Nlnn8WWLVsglUpx7tw5jB49utKzSlc3SxDE5/ORkZGBR48eobCw0NnVIoSQcuEw9azjXqlUQiaTQaFQ1NouMKPRiOTkZLY7oqi//voLEyZMQGBgIM6dO1diPo9cLoefn1+5E8fJk5GDKSkp4HK5lRr9ZXH+/Hm266t58+b45ZdfShyZV5MxDIOCggK2i9DLy4vyyAgh1c6e+7vTW36I/TQaDbRarc18HcsQ9z59+pR4A2IYhub3qQCBQMCO/qps9xcAREVFYfv27fDx8cG1a9cwePBgJCcnV76i1YzD4UAqlcLV1RXp6enIyspyyPtDCCFVhYKfWkilUoHH4xULbvR6PTvEvbR8H4PBQEPcK0gqlUImk6GgoMAh5bVo0QK7du1CSEgIkpOT0b9/f3ak3tNMJhNOnz6N3bt34/Tp0zUuwBAKhZBKpcjOzkZGRgblARFCaiwKfmoZvV6PgoICm602J06cYLuzbI2Ws7Ash+Hq6lqVVa2TuFwuvL294eLiAq1Wa/fxtgKYhg0bYs+ePWjTpg3kcjlGjRqFjRs3Wg0lT0xMRJcuXTBs2DBMnToVw4YNQ5cuXZCYmOjIy6s0FxcXyGQy5OfnIz09nQIgQkiNRMFPLaNWq9lk5aft2bMHADBw4MASFzIFnrT8SCSSKqtjXScSieDt7Q21Wm3X2l+lBTABAQHYsWMHhgwZAqPRiE8++QSxsbEoKChAYmIiJk+ejMjISOzduxe3b9/G3r17ERkZicmTJ9e4AIjH48HDwwMKhYJagAghNRIlPNciDMMgJSUFOp2uWPCi0WjQqlUrqNVq7N27F1FRUTbLsKwCHxER4ZCk3frKZDLh0aNHUKvVkMlkZe5vCWBiYmIwbdo0REZG4ubNm1i5ciUOHTqEtWvXol+/fmAYBt9++y0SEhJgMpkQHh4OrVaLVq1aYf369Vaj+8xmMyZOnIhbt27h5MmTpQa8zmA2myGXy+Hp6YmAgIAaVz9CSN1CCc91lFarhVqtttnldfDgQajVaoSGhqJdu3YllkFLWjgGj8eDj48POByO1dpxtphMJixYsAAxMTFYv349oqKi4ObmhqioKKxfvx4xMTFYuHAhTCYTOBwO3nnnHfz2228IDg5mV1739PSEwWCwKpfL5SI2NhYpKSk4e/ZsVV5uhXC5XLYLLDs7265WMkIIqUoU/NQihYWFMJlMNheYtHR5DRo0qNRhxlqtFu7u7sXmByL2c3Nzg4+PT5lLPZw9exapqamYNm1asfe9pACmQ4cO+PPPP9G2bVsAwK+//oq+ffsWW9A3MjISAJCVleWgq7JfaYnYPB4PUqkUOTk5yM3NpSUxCCE1At0Bawmj0QiFQmGzq0qhUODIkSMAngQ/JbHceMRicdVUsh7y9PSERCIpdfSXJTCxBCpPKymA8fT0xOzZswE8GWV269YtDBw4EJ988glyc3MBADdv3gQA+Pn5Ve5CKqg8idguLi6QSCTIzs6udZM5EkLqJgp+agm1Wl3i3D4HDhyAXq/Hs88+i6ZNm5ZYhqXLi+b3cRwej8dOTFhS95clMLEEKk8rLYCJjo5muzIHDx4Ms9mMjRs3okuXLli5ciWWL1+OsLAwREdHO+Jy7GJPIralqzUzMxMqlara60oIIUVR8FMLMAwDhUJhc24fANi+fTsAYPDgwWV2ebm5udnsNiMVV7T7y1ZeiyWAWblyZbHXzWYzVq1aVWIAw+PxEB8fj+PHj6OwsBCfffYZmjZtioKCAixevBhHjx5F48aN8fDhwyq7PlvsyWOyEIlE4HA4yMzMrNA0AYQQ4igU/NQCWq0WhYWFNrurkpOTcebMGXA4HLz66qullmMymWiIexXx9PSEVCq1ufaXJYA5dOgQJk6ciKSkJKhUKiQlJWHixIk4dOgQ5syZU+JoqH79+mHt2rW4efMmPv30U9y4cQMA2CD28OHD6Nq1K/r27YtvvvkGd+/erfLcmorkMQFPVrbX6XTIzMykIfCEEKehJoBaQKVSwWg02myx+fXXXwEA3bp1Q3BwcIllUJdX1bJ0f2m1Wmg0mmK5WZYAZsGCBVZ5WWFhYeww99L069cPvXv3xtmzZ5GVlQU/Pz906NABp06dwg8//IDjx4/jypUruHLlCj7//HP4+/ujU6dO6Ny5M1q3bo1nn33WoYvYVjSPCYDVCDB/f39KvieEVDsKfmo4g8FQYqKzyWRiu7xGjBhRajmWUV60invVEYlE8PX1RVpaGlxdXYsFq7YCmOjo6HLPf8Pj8dC5c2erbT169ECPHj2Qm5uLffv2Yd++fTh37hwyMzOxe/du7N69GwDg6uqKZ599Fi1atEDz5s3RtGlTNG3aFJ6enhW61qJ5TLbmlCotj8myFlheXh74fD68vb0rVAdCCKkomuSwhpPL5Xj06BE8PT2L5fMcP34co0aNgoeHB86fP19qq05eXh7CwsJqxTXXZgzDICMjA7m5uTb/z6qDVqvFhQsXcOrUKfz777+4du1aiaOsAgMD0bRpUzRr1gzNmjVD06ZN0bBhwzLzwkwmE7p06YLIyMgKT76o1Wqh0+kQEhICd3f3il8wIYTAvvs7tfzUYJYZcvl8vs2b6NatWwEAQ4YMKTXw0el0EAgENKNzNeBwOPDx8YFOp2N/EaubUChE586d2VYihmHw6NEjXL16FVevXsX169dx48YNpKamIj09Henp6exUCZbj27dvz5bRunXrYi2GljymyZMnY+LEiYiNjWVnrV61ahU7a3VprVpCoRBGoxFZWVk08SYhpFpRy08NplKp8PDhQ8hksmJ5Efn5+WjXrh30ej3+/PNPtGjRosRyFAoFPDw8EBgYWNVVJv+fWq1GWloaOBxOjZ1XSalU4ubNm2wwdP36ddy8eRNqtdpqP3d3d7z44ovo27cvevbsaRVEJyYmYsGCBUhNTWW3hYWFYc6cOWXmMVnI5XK4u7sjKCiIlsAghFSYPfd3Cn5qKIZhkJaWhoKCAputB2vXrsX8+fPRrFkzHDx4sNRy8vPzER4eTl0L1UyhUCAtLQ0ikajWtGqYzWbcuXMHZ86cwenTp3HmzBnk5eWxr8tkMrz66qsYM2YMnn32WQBPusAqmsdkOadcLoevry/8/Pyc0lVICKn9KPgpRW0JftRqNR4+fGhzXh6z2YyuXbsiOTkZixcvxuuvv15iORqNBgzDICIigub3cYKcnBxkZGTA3d0drq6uzq6O3cxmM86fP4/9+/fjjz/+QFpaGvtar1698O6776J9+/aVPo/BYIBKpUJQUFCFk7AJIfUbLWxayzEMA7lcDgA2A5bjx48jOTkZUqkUQ4cOLbUsrVYLqVRKgY+TeHt7w9fXFwUFBbVyXhsul4sOHTogPj4eZ86cwc8//4y+ffuCy+XiyJEjGDRoEEaOHMnOPVRRrq6uEAqFyMrKQmFhoYNqTwghtlHwUwOp1WooFAq4ubnZfH3Dhg0AgOHDh5e4D/DkWzuHwyl1H1K1OBwOfH194e3tDaVSaTXjcW3D4/HQs2dPfP/99zhx4gRee+01uLi44MSJE+jduzfi4+MrtXaXJZcoIyMDer3eUdUmhJBiKPipYcxmM5tjYau1Jjk5mR2ZM27cOAAlr6ptmWyPRnk5F5fLhZ+fH7y9vaFQKGplC9DTGjRogGXLluHvv/9Gv379YDKZ8MMPP6Bnz574+++/K1yuu7s7tFotMjMza3WgSAip2Sj4qWEKCgqgUChKXIbixx9/BMMw6NmzJxo2bFjqqto6nQ4eHh40g24NwOPx2ABIqVTCYDA4u0oOERYWhnXr1mHLli1o2LAhMjMzMXLkSCxevNjmOmflYemzz8nJqfJlOggh9RPdFWsQvV6PnJwcCIVCm6Nl1Go1tm3bBgAYP358matqHz16tMYOs66PeDwe/P392RygklaBr426deuGv/76C2PGjAHDMFi5ciXeeOONCuXvcLlcuLu7Iycnh819I4QQR6LRXjWEZWbgvLy8Eke7rF+/HnPmzEFERASOHj2Kbt26lTjD7tixY3Hv3j3cv3+f5k6pYRiGQW5uLju5X10LUHfu3ImZM2dCp9OhadOm2LRpU6nrzpVEo9HAYDAgODiYpmkghJSJRnvVQnK5HLm5uSX+kTcYDPjuu+8AAG+99RaSkpJKXFUbACZNmoSUlJRK5V+QqmGZBTo4OBhmsxkKhaJOde8MHToUO3bsgK+vL27cuIHBgwfj7t27dpcjEonA5XKRmZkJjUZTBTUlhNRXFPzUAIWFhcjKyoJIJCpxSPoff/yBR48ewcfHB8OGDSt1VW2tVsvO+Jyenl51FSeVIpPJEBISApFIhPz8/DqRCG3Rrl077Nu3D40bN8bjx48xdOhQXL161e5yJBIJ9Ho9MjMzaQQYIcRhKPhxMp1Oh4yMDDAMU+KoLIZh8M033wAAJk6cCJFIZLWq9tO0Wi0yMjIAgJa0qOHEYjGCg4PZROi61MIRHByM3377DS1btkRubi5effVVXLhwwe5yZDIZVCoVjQAjhDgMBT9OZDQakZmZCa1WW2pOw/Hjx3H9+nWIxWJ2eHt0dDRCQ0OxcuVKq1E1er0eLi4uWLlyJRo0aICuXbtW+XWQynF1dUVAQABCQkJgNpuRn59fZ27y3t7e+PXXXxEdHY2CggKMHj0aV65csasMDocDDw8PKBQKZGVlVXgUGSGEWFDw4yQmkwmZmZlsglZp6xmtWrUKADB69Gh4eHgA+L9VtQ8dOoSJEyciKSkJKpUKp0+fxvTp05GYmIilS5dSsnMtYbnBh4aGsjf6pxcYra2kUil+/vlnREdHQ6lUYuTIkbh27ZpdZXC5XEilUuTm5tIQeEJIpdWI4Gf16tWIiIiAUChEdHQ0/v333xL3XbduHbp27QpPT094enoiJiam1P1rIrPZjKysLOTn59tcsb2oU6dO4cyZM+Dz+XjzzTetXuvXrx/Wrl2LmzdvYtCgQWjSpAlGjx6NO3fuYMeOHWUufUFqHqFQiMDAQISGhoLD4SAvL69ODIkXi8X48ccfERUVBblcjtdee81ml21pXFxc4O7ujqysLOTm5lIARAipMKcHP9u2bUNcXBzmzp2LCxcuoHXr1ujduzeb0Pu0Y8eOYeTIkTh69CjOnDmD0NBQvPTSS1YLLtZkZrMZ2dnZyM3NhVQqLbVlhmEYLF26FAAwatQom8OF+/Xrh1OnTmH79u1YunQptmzZgjt37lDgU4txuVzIZDKEhYXB398fer0ecrm81k+MKJFI8PPPP6N169bIy8vDiBEj7B4F5urqCjc3N2RlZVmtNk8IIfZw+jw/0dHR6NChA9u1YzabERoaimnTpuHjjz8u83iTyQRPT0+sWrUKY8eOLXN/Z87zwzAMsrOzkZWVVa7FRo8fP45Ro0ZBIBDg9OnTCAgIKLXs/Px8hIWF1aj5i0jlaTQayOVyyOVyMAwDiURSqxeqlcvlGD58OK5duwZ/f3/89ttvaNCggV1laLVaaDQaWgWeEMKqNfP86PV6nD9/HjExMew2LpeLmJgYnDlzplxlqNVqGAwGeHl52Xxdp9NBqVRaPZyh6MR27u7uZd68GIbBkiVLAACvv/56qYEP8H/reNEipnWPSCRCQEAAwsLCIJPJUFhYCLlcXmuHfnt4eGDr1q2IjIxEZmYmRowYgUePHtlVhlAohEgkQnp6OvLz86uopoSQusqpwU9OTg5MJhP8/f2ttvv7+7NDtcvy0UcfISgoyCqAKiohIQEymYx9hIaGVrre9mIYBnl5ecjMzIREIoGrq2uZxxw6dAgXL16EUChEbGxsmftrtVp4eXlRgnMdxeFw4ObmhqCgIISHh8PT0xN6vR55eXlQqVS1bo4gLy8vbN26FQ0bNkRaWhpGjBhR7t95C6FQCIFAgPT0dOTl5VEOECGk3Jye81MZixcvxtatW7Fr1y4IhUKb+8yaNQsKhYJ9pKamVnMtnzTzZ2ZmQiwWg8/nl7m/Xq/HZ599BgCYMGECfH19S91fo9FAKBSWuBgqqTs4HA7EYjECAwMRHh6O4OBgCAQCqNVq5OXlsXMF1Yah8r6+vti2bRvCwsKQnJyMESNGICcnx64yRCIRhEIhMjIyKAmaEFJuTk0c8PHxAY/HQ2ZmptX2zMzMMrt5li5disWLF+PQoUNo1apVifsJBAIIBAKH1LcilEolMjIy7KrH+vXrcffuXXh7e2PatGll7q/RaBAQEFCuFiVSd1g+Ux4eHtDpdNDpdFCr1VCr1VCpVDCZTOBwOHBxcYGLiwt4PB5cXFxKHV1Y3YKCgvDrr79iyJAhuHv3Ll577TVs377drjweoVAIDoeDjIwMmM1m+Pj41KhrJITUPE79C8Hn8xEVFYXDhw+z28xmMw4fPoxOnTqVeNyXX36JhQsX4sCBA2jfvn11VLVCCgsLkZGRAVdX1xJnb35aVlYW/ve//wEAZs+eDZlMVur+Wq0WfD6fFn6sxzgcDoRCIWQyGQIDA9GgQQM0aNAA4eHhCAgIYFsE9Xo9lEol8vPzkZeXB7lcjoKCAmg0Guh0Oqe1FoWGhuLXX3+Fn58fbty4gdGjR9udmycQCNhh8JmZmbWuG5AQUr2cPmQkLi4O48aNQ/v27dGxY0csX74chYWFmDBhAgBg7NixCA4ORkJCAgDgiy++QHx8PDZv3oyIiAg2T0AikdSobh/LshVms7nc9TKbzXj//fehUqnQtm1bDB8+vMxjNBoNfH19ndq6RWoWLpcLoVBo1RXMMAyMRiOMRiNMJhP7s1arhV6vZ3+2tBZxuVy4urrCxcUFrq6upU7C6QgNGzbE1q1b8corr+Dy5csYO3YsNm/ebNeK966urpDJZMjNzYXRaISfnx/9XhBCbHJ68DNixAhkZ2cjPj4eGRkZaNOmDQ4cOMAmQaekpFg1Ya9ZswZ6vR6vvvqqVTlz587FvHnzqrPqJTIajcjIyIBOp2NnZC6PdevW4ciRIxAKhViyZEmZTfc6nY79g09IaTgcDlxdXW12jTIMYxUQWQIhjUYDvV6PwsJCMAwDPp/PPqpCkyZNsHXrVgwbNgznzp3D+PHjsWnTpnK3mgJPZj63zJCt1+vh7+9fo74UEUJqBqfP81PdqnqeH7PZjMzMTOTm5sLT07Nc35hNJhM2bdqEefPmwWQy4bPPPmNbvkqTn58PX19fdpFTQhzJ0lqk1+uh0+mgUqnYliIej8eOtnK08+fPY+TIkSgsLESvXr3w/fffV+g8BQUFYBgGvr6+8PT0pDwgQuq4WjPPT11kyaeQSqXlCnwSExPRsWNHzJkzh825+O6775CYmFjqcVqtllp9SJWytBa5ubnBy8sLYWFhiIiIYCfSNBgMyMvLQ2FhoUMXG42KisKmTZsgFApx5MgRTJ06tUJzGrm7u4PP5yM9PR3p6el1YpkQQohjUPDjQAzDQKFQQCAQlGsG3sTEREyePBkKhQIA0LJlS2zfvh2RkZGYPHlyqQGQWq2Gp6cn5TSQasXn8yGVShEcHIyIiAiEhITA1dUVCoUCSqXSYUnTnTp1wvr168Hn87F//368+eab0Gq1dpdjSQSXy+VITU2FXC6nVeEJIdTt5UgMw+DBgwdgGKbMPAWTyYTnnnsOcrkcarUaYWFh2Lt3L3x9fWE2mzFx4kTcunULJ0+eLDZxoUajgdlsRnh4eJXlXxBSXmazmZ11uqCgADweDxKJxCFJ0kePHsUbb7wBrVaL559/HuvXr6/wLOZqtRparRYymQze3t52JVMTQmo+6vaqBXbv3o3Hjx9DrVazQ30tkxlyuVzExsYiJSUFZ8+etTqOYRhoNBp4eXlR4ENqBC6XC3d3d4SEhCA0NBQCgQD5+fnQaDSVLrtnz574+eef4ebmhpMnT2LUqFEVXqJGLBZDJpOhoKAAKSkpyMzMpK4wQuopCn6c4NKlS5g9ezYAICQkBNu3by+27EZkZCQAFFvd3rKGF+X6kJqGw+HA3d0doaGhCAwMhMlkgkKhqHQ3U6dOnbB161bIZDIkJSXhlVdeQXp6eoXKsowGEwqFyM7OxsOHD5GdnU1BECH1DAU/1ezIkSN49dVXoVKpAACLFi2yud7YzZs3AcBqJJfZbIZWq4W3t3etXtWb1G08Hg/e3t4IDQ2FRCKBXC6vdHDRrl07bN++Hb6+vrh+/ToGDhyIGzduVLg8Pp8PLy8vuLi4IDMzEw8fPkRmZqZDWqsIITUfBT/VaNu2bRg/fjw0Gg26deuG4OBg/PTTT8W+GZvNZqxatQphYWGIjo5mt6tUKri7u1fJEH1CHE0kEiEoKAgBAQHQarVswF9RzZs3x969e9GoUSOkp6djyJAh+PvvvytVplAohJeXF1xdXZGTk4OHDx8iLS0NKpWKEqMJqcMo+KkGDMNgxYoViIuLg8lkwiuvvMLO63Po0CFMnDgRSUlJUKlUSEpKwsSJE3Ho0CHMmTOHTXY2Go0wm83w9vam+UpIrcHj8eDj44OQkBDweLxKj7YKCwvD7t278dxzz6GgoABjxozBjz/+WOkFTQUCATw9PSESiaBUKvHw4UM8fPgQ+fn51CVGSB1Eo70cyNZoL7PZjLlz52L9+vUAgNjYWHz88cfsSJjExEQsWLDAarX5sLAwzJkzB/369WO35eXlwdvbGwEBAVW+1AAhVUGr1SIrKwtKpRJSqbRSXbc6nQ5xcXHYvXs3AGD48OFYtGiRXbNBl8bSxWyZT0sikcDd3R0ikYgWECakhrLn/k7BjwM9Hfzo9XrMmDGD/QO9YMECTJo0qdhxJpMJZ8+eRVZWFvz8/BAdHW01vN0ytD0sLIzm9SG1mtFoRE5ODnJzcyEWiyv1eWYYBt988w0WL14Ms9mMFi1aYN26dQgLC3NgjQGDwQCNRgOj0QiBQACJRAI3NzcKhAipYSj4KUV1BT8A8MYbb+DYsWNwcXHB8uXLMWTIELvLNJvNkMvlCA4Ohqenp0PrS4gzMAyDvLw8ZGZmgs/nV3q+nb///htTpkxBXl4eZDIZEhISMGjQIAfV1ppOp4NWq4XZbAafz4ebmxskEgmEQiFNPUGIk9E8P06m1WoxYcIEHDt2DCKRCBs3bqxQ4AM8WZ9IJpPR0HZSZ3A4HHh7eyMkJARmsxlKpbJSOTtdu3bFgQMH0LZtWygUCkyZMgXvvPMO8vPzHVjrJwQCAWQyGTw8PODi4gKlUomUlBQkJycjNTWVnd+IkqUJqdko+HEwnU6Ht99+G3///TfEYjG2bNmCnj17VrgsLpdLSc6kTpJKpezyGJVNhA4ODsauXbsQFxcHHo+HvXv34oUXXsChQ4ccWOP/w+FwIBAIIJVK2QlH1Wo10tLSkJycjOTkZGRnZ0OlUsFgMFRJHQghFUfdXg6k1WrRp08fHD9+HGKxGD///LPVUHV7WLq7AgIC4OPj49B6ElKT6HQ6ZGVlQaFQQCaTFVvOxV6XLl3C9OnTce/ePQBAnz59sGDBAgQHBzuiumUymUzQ6XTQ6/VgGAZ8Ph9CoRASiQQCgQACgaDS10gIKY5yfkpRVcGPTqfDK6+8gn379kEoFOLnn39Gp06dKlyeXC6Hu7s7goKC6A8lqfOMRiOys7ORm5sLiURS6fwZjUaDpUuXYt26dTCZTBCJRHj77bfx1ltvwd3d3UG1LhvDMNDr9dDr9TAYDOByuRAIBBCLxWzCN5/Pp5ZdQhyAgp9SVFXwk5+fjxdeeAHXr1/H999/j169elW4rMLCQnA4HISEhEAoFDqsjoTUZGazGbm5ucjOzoZAIHDIsPWbN29i9uzZ7Bp5np6eiI2Nxeuvv17hBVIrw2w2s61CJpMJLi4ucHV1hVgsZkeP8fl8uLi40JQWhNiJgp9SVGW3V25uLg4fPoyoqKgK/+HWaDQwGAwIDg6u1m+ohNQEDMNAoVAgMzMTABzyO8AwDPbt24cvv/yS7QqTyWR4/fXXMWHCBAQEBFT6HBVlMpnYViGTyQQAcHV1hYuLC0QiEYRCIVxcXKweFBQRYhsFP6Wo7kkO7WGZVC0oKAgeHh4OrRshtUlhYSEyMjKg1Wohk8kc0i1kNBqxY8cOrFq1Cg8ePADwJNB4+eWXMWrUKERHRzs9sGAYBgaDAUajEUajESaTCQzDgMvlwsXFBTweD66urhAKhXB1dQWPxyv2oC40Ul9R8FOKmhr8aDQa6HQ6BAYG0nw+hMA6Edrd3d3uCQVLmjzUZDLh4MGD+O677/Dvv/+y+zdo0ACvvfYahgwZUm3J0eVlNpvZYKjow8LFxQVcLpcNgFxdXdkWJEtAZPm36M/ODvYIcSQKfkpRE4OfgoICAEBAQADN50NIESaTCTk5OcjJyYFQKCz375WtZWNCQ0MRHx9vtWzMxYsXsXnzZuzZsweFhYXs9qioKAwYMAD9+/evcYGQLZZgyGw2w2w2W/0MPPnbxOFwrIIfDocDHo9n1aVWNECyPCzHFf3X8iCkJqHgpxQ1Kfgxm81QKBQQCoXw9/eHRCJxaH0IqQsYhoFcLkdWVhYYhoG7u3upN97ExERMnjwZMTExmDZtGiIjI3Hz5k2sXLkShw4dwtq1a60CIOBJN9vvv/+OX3/9Ff/++6/VpIvt2rVDTEwMXnjhBTRv3rxW3/SfDowYhmF/tjy3BEoAigU8T/9c0qNogFS0rKI/2/OvI38u6zVSe1HwU4qaEvzodDoUFhZCJpPBz8+P1uwipAxqtRpZWVlQqVQlLoxqMpnQpUsXREZGYv369Vb5L2azGRMnTsStW7dw8uTJEqeQyMjIQGJiIv74449igZC/vz969eqFXr16oUuXLnW+pdYSHBX9156HJbAo6eeSVDYIKuu10vYr+rzo56e0QM7Wz08fb+v1supXnsDMEcFbdQeAHA4Hbm5uDs9Po+CnFM4OfhiGgUqlgtlshq+vLzw9PWkeH0LKyWAwICcnB3l5eex8OUWdPn0aw4YNw969exEVFVXs+KSkJAwaNAjbt29H586dyzxfRkYGDh06hCNHjuDvv/+GWq1mX+NyuWjZsiU6d+6Mzp07o2PHjtR66wCWW1LRW1Nlfi7rtfLu58g6FN1mK/AoK0C0lFXeoKW0a67uwIdhGLi6uiI8PNzhU7nYc38v/tWJVBmDwYCCggK4ubnB19eX/lASYidXV1cEBARALBYjOzsb+fn5kEql7BeIrKwsAEBkZKTN4y3bLfuVJSAgAGPGjMGYMWOg0+lw9uxZHD58GEePHsW9e/dw+fJlXL58GWvWrAGPx0Pr1q3RpUsXdOrUCe3atatV01WUlCBe3Wy19pC6w2w2s3muzkTBTzVRq9XQ6/Xw8fGBt7e33SNXCCFPcDgcyGQyCIVC5OTkID8/H0KhEGKxGH5+fgCeTG5oq+Xn5s2bAMDuZw+BQIBu3bqhW7dumD9/PtLT03HmzBmcPn0ap06dQkpKCi5cuIALFy5g5cqV4HK5iIyMRPv27dGhQwe0b98eoaGhNfKmXt4EcULqCur2chCTyYQTJ07g8uXL8PX1Rbdu3cDj8dhVq/l8Pnx9fSGVSmvkHz9CaiPL71dOTg60Wi3EYjF69OhRqZyfinr06BFOnTqF06dP499//0VKSkqxffz9/REVFYUOHTqgXbt2aN68uUNmsq6MiiSIE1JRlpafBg0aOLXbi4IfB9i5cyfef/99JCcns9tCQ0Mxe/ZsPP/885DJZPD19aWlKgipInq9Hrm5ucjPz8ehQ4fw3nvvISYmBrGxsezNfNWqVdV6M8/MzERSUhKSkpJw7tw5XL16tdgK71wuF40aNUKLFi3QvHlzREZGomHDhggODq6WLidHJIjXZzWlq7A2oeDHSRwd/OzcuROvvvoqBgwYgFmzZkEikeDWrVtYs2YNjh49ih9++AFjx46lXwhCqhjDMCgsLERubi527dqFZcuW4dGjR+zrYWFhmDNnjtNaMTQaDa5cuYJz584hKSkJFy9eRE5Ojs19BQIBIiIi0LBhQ0RERCA4OBhBQUEICgpCYGAgvL29HdKC7OgE8fqEugorhoIfJ3Fk8GMymdCoUSO0bNkSu3fvBofDwYMHD6BQKCAWi/Hee+/h1q1buHPnDgU/hFQTyx/XrKwsnDx5EnK5HCEhIXjuuedq1O8hwzDIzMzE1atXcfXqVVy7dg13795FcnIy9Hp9qccKBAIEBgYiMDAQfn5+bC6hr68v+7OPjw98fHwgFotLDJR2796NqVOn4vbt2zYXelWpVGjSpAlWr16NwYMHO+Ky6wTqKqw4Cn6KWL16NZYsWYKMjAy0bt0aK1euRMeOHUvcf/v27ZgzZw6Sk5PRuHFjfPHFF+X+oDky+Dl27Bh69uyJM2fO4LnnngPDMEhOTgbDMAgICMDly5fRuXNnHD16FD169KjUuQgh9jEajVCpVMjNzYVGowGfz4dYLK7xa1+ZTCakpaXh/v37uH//PpKTk/H48WOkp6fj8ePH5R6pZiEUCuHp6QkPDw/2IZPJIJPJoFQqsWXLFnzwwQdo27at1Wvu7u64dOkStfw8hboKK6emBD9OH+21bds2xMXF4dtvv0V0dDSWL1+O3r1749atWzZHZJw+fRojR45EQkICBgwYgM2bN2Pw4MG4cOECWrRoUa11T09PBwD2vBwOB97e3hAKheDz+ex2y36EkOrj4uICDw8PSCQSFBYWIj8/H0qlEgAgFovB5/OdXEPbeDwewsLCEBYWZvNLk16vR0ZGBh4/fozHjx8jJycHubm57DIglp+zs7PZxZLT09NL/Tu0ZMkSm9stMza/9957cHNzg5ubG8RiMfuv5W/d0w+BQAA+nw9XV9diz11dXa0WarU8LOuTPb3d1uPpfatzuY2zZ88iNTUVq1evLhZIc7lcxMbGYtCgQTh79iwFjDWY01t+oqOj0aFDB6xatQrAk6gwNDQU06ZNw8cff1xs/xEjRqCwsBB//PEHu+25555DmzZt8O2335Z5vqps+XnamTNnqOWHkBrCbDZDrVZDqVRCpVJBr9eDz+dDKBTanC26LlCr1cjJyYFcLrd6KBQK9t+bN2/i4sWLkEgkEAqFKCwshEajcXbV7fb0khuW50UXcS1p3TJ79snPz0dycjKioqLg6upabM00s9mMEydOoEWLFggODi5WriVYK+k8Ze1jz0zWFd23vDNwV+QcDMNAp9NhxowZCA0NLfU89qo13V56vR5isRg7duyw6k8eN24c5HI59uzZU+yYsLAwxMXF4b333mO3zZ07F7t378bly5eL7a/T6aDT6djnSqUSoaGhVZLz83Tz5+DBg3H16lXK+SGkhtHpdNBoNFAoFNBoNDAajVYtF/WNreTdkJAQTJs2DdHR0SgsLGQfarWa/Vn3/9q7+6CoqjcO4N9lYVcQYUlxARGhQo1INEiGyLGSydJpULThDxup/nA0GDV1UjPRcHQdGxu1cTBzBpzeKJmwtLQYFHoZ30AdUcqXpLDkJadEWF522T2/P/rtnV1Y0NWVe+F+PzM77D337OHhab09e+/Zczs7YbFYYLFY0NnZCavVKj23WCw9ti0WC7q6utzepd65/XZ3saeBr7Ky0u0k+3sxYC573bhxAzabDUaj0aXdaDRKi5F119DQ4LZ/Q0OD2/4mkwnvvPOOdwLuRqvVYuvWrZg7dy5mzZqF1atXIz4+HufPn4fJZMLBgwdRXFzMwodIYfR6PfR6PYKDg6VCqKWlBR0dHWhtbYWPj490qUYN/35nzJiB6dOnK/pr2477izkXR11dXT1uztr9hq2OG7m6u4mr87bNZrvtWI5+K1euREREBBYuXNgjtj179qC+vh6rVq1yee3d/H53ffrKj7vnfe272369veZOxhBCwGq1yn5fvMF5rtfJ6tWrsWzZMmnbcebHWzIyMlBcXIzly5e7XN+NiYlBcXExMjIyvPa7iMi7NBoNhgwZIk0Ktlgs6OjoQHt7O1pbW9Ha2gqbzQatViudFVJSQeBNWq1W0XNUNBqNNOdHbna7HQsWLEBJSUmPtaRqamr4ba8+OCY8R0ZGyhqHrMXPiBEjoNVq0djY6NLe2NiIsLAwt68JCwvzqL/jE979lJGRgfT0dPz444+or69HeHg4pkyZooh/pER05xwFTlBQEEJDQ6XLNW1tbdLlHsenb+cJvFy1XV1mzJiB3bt3Iy8vD+np6VJ7VFQUC58BQtbiR6fTITExEWVlZdKcH7vdjrKyMuTk5Lh9TUpKCsrKylzm/JSWliIlJaUfIu6dVqvlpGaiQcTHxwf+/v7w9/dHcHAw7Ha7y/wWs9kMq9UKs9ks9ffz84Ovry8LIhUYCJcKqXeyX/ZatmwZsrKykJSUhMmTJ2Pbtm0wm8149dVXAQDz58/HqFGjYDKZAABLlizB1KlTsXXrVsycORNFRUWorKzE7t275fwziGiQ8/HxkS6RAUBoaCisVqvLpF7HDYzNZjOEED0KIqWvMUSeUfqlQuqd7MVPZmYm/v77b+Tm5qKhoQETJ07E4cOHpUnNdXV1LgeMJ598Ep9++inefvttvPXWW4iNjcX+/fv7fY0fIiLHZa+AgAAA/03m7OrqkooiR0HU1dWFjo4O2Gw2aDQa+Pr6ujx4loiof8m+zk9/u193dSci6o2jIHL8bG9vl4ohxzeWAEgL9znWelHKBF8ib+EKz0REKuE4w+PM+SyRY00b5/VxrFarVCA5+t/pQnhE1DcWP0REMtBoNNJls+4ca7o41qhxft7V1SUtFuhot1qtLuvBuFtZ11EoOf/s3u78nGgwY/FDRKQwjrM47gojZ+4W5ettgbzuKyg7L6pntVohhJC2nRekc3DcmsDx3PlWC309d369s972eXKLBnfbvbV52pcGNxY/REQD1L0s/Ne92OnreW8P50LLsQ2g15+9rf7r2N9XH3fb96ute0F0J2299QHcF1h97XMX390Wad3/RrmLPSGEIuaxsfghIlIh58tc/cXdGaW7ud2CN9pu9/s8abvT8W/Xfqf7b0fp32NyXPKVE4sfIiLqF+4uhxHJgStuERERkaqw+CEiIiJVYfFDREREqsLih4iIiFSFxQ8RERGpCosfIiIiUhUWP/do/fr12LBhg9t9GzZswPr16znWAIlNDWN5E+PyDOMaPJgzzygxXyx+7pFWq0Vubm6P/7AbNmxAbm6uRytZqmEsJcemhrG8iXExLrVizjyjyHwJlWlubhYARHNzs9fGzMvLEwBEXl6e222ONXBiU8NY3sS4GJdaMWee6Y98efL/d40QCl8H28uam5thMBhw7do1BAUFeW3cLVu2YOPGjdDpdLBYLFizZg3efPNNjjUAY1PDWN7EuBiXWjFnnrnf+bp16xZGjx6NmzdvIjg4uM++qit+/vzzT4wePVruMIiIiOg+uHbtGiIjI/vso7rix2634/r16xg2bJhX7y/jqGgdlPIJX6ljOY/noJTYlD6Wg1I+ZSr10y/zdXdxOSglLiVjzjxzv/MlhEBLSwsiIiJuf8Ner11sUzHHtcs1a9a4/JR7bodSx3J+PXPm2VjeyJc3KXXeA/N1d3EpLV9Kxpx5Rmn5YvFzj5wPXs6Tre7moNbbawbTWN1fx5x5Nta95subvP2+uB9xMV+exaWkfCkZc+YZJebLt+/zQnQ7NpsNeXl5WLt2LW7duiW1r127Vtp/N2M5G0xjdR+POfNsrHvNlzd5+33hLcyXZ5SaLyVjzjyjyHz1e7k1iHV0dIh169aJjo4OuUMZMJgzzzBfnmG+PMN8eY4584xS8qW6Cc9ERESkblzhmYiIiFSFxQ8RERGpCosfIiIiUhUWP0RERKQqLH68aOfOnYiOjsaQIUOQnJyMkydPyh2SIvzwww948cUXERERAY1Gg/3797vsF0IgNzcX4eHh8Pf3R1paGi5fvixPsApgMpnwxBNPYNiwYRg5ciRmzZqFixcvuvTp6OhAdnY2hg8fjsDAQMyZMweNjY0yRSyv/Px8TJgwAUFBQQgKCkJKSgoOHTok7Weu+rZ582ZoNBosXbpUamPOXK1fvx4ajcblMX78eGk/89XTX3/9hZdffhnDhw+Hv78/HnvsMVRWVkr75T7us/jxks8//xzLli3DunXrcPr0aSQkJGD69OloamqSOzTZmc1mJCQkYOfOnW73b9myBTt27MCuXbtw4sQJDB06FNOnT0dHR0c/R6oMFRUVyM7OxvHjx1FaWgqr1YrnnnsOZrNZ6vPGG2/gwIED2LdvHyoqKnD9+nVkZGTIGLV8IiMjsXnzZlRVVaGyshLPPvss0tPTceHCBQDMVV9OnTqFDz74ABMmTHBpZ856evTRR1FfXy89fvrpJ2kf8+Xq33//RWpqKvz8/HDo0CHU1NRg69atCAkJkfrIftyX9Yv2g8jkyZNFdna2tG2z2URERIQwmUwyRqU8AERJSYm0bbfbRVhYmHj33Xeltps3bwq9Xi8+++wzGSJUnqamJgFAVFRUCCH+y4+fn5/Yt2+f1OeXX34RAMSxY8fkClNRQkJCxJ49e5irPrS0tIjY2FhRWloqpk6dKpYsWSKE4PvLnXXr1omEhAS3+5ivnlauXCmeeuqpXvcr4bjPMz9eYLFYUFVVhbS0NKnNx8cHaWlpOHbsmIyRKV9tbS0aGhpcchccHIzk5GTm7v+am5sBAA888AAAoKqqClar1SVn48ePR1RUlOpzZrPZUFRUBLPZjJSUFOaqD9nZ2Zg5c6ZLbgC+v3pz+fJlRERE4MEHH8S8efNQV1cHgPly5+uvv0ZSUhJeeukljBw5EpMmTcKHH34o7VfCcZ/FjxfcuHEDNpsNRqPRpd1oNKKhoUGmqAYGR36YO/fsdjuWLl2K1NRUxMfHA/gvZzqdDgaDwaWvmnNWXV2NwMBA6PV6LFy4ECUlJYiLi2OuelFUVITTp0/DZDL12Mec9ZScnIzCwkIcPnwY+fn5qK2txZQpU9DS0sJ8uXH16lXk5+cjNjYW3333HRYtWoTFixdj7969AJRx3Oe9vYgULDs7G+fPn3eZX0A9jRs3DmfPnkVzczOKi4uRlZWFiooKucNSpGvXrmHJkiUoLS3FkCFD5A5nQHjhhRek5xMmTEBycjLGjBmDL774Av7+/jJGpkx2ux1JSUnYtGkTAGDSpEk4f/48du3ahaysLJmj+w/P/HjBiBEjoNVqe8zub2xsRFhYmExRDQyO/DB3PeXk5ODgwYM4evQoIiMjpfawsDBYLBbcvHnTpb+ac6bT6fDwww8jMTERJpMJCQkJ2L59O3PlRlVVFZqamvD444/D19cXvr6+qKiowI4dO+Dr6wuj0cic3YbBYMDYsWNx5coVvsfcCA8PR1xcnEvbI488Il0qVMJxn8WPF+h0OiQmJqKsrExqs9vtKCsrQ0pKioyRKV9MTAzCwsJccnfr1i2cOHFCtbkTQiAnJwclJSU4cuQIYmJiXPYnJibCz8/PJWcXL15EXV2danPWnd1uR2dnJ3PlxrRp01BdXY2zZ89Kj6SkJMybN096zpz1rbW1Fb/99hvCw8P5HnMjNTW1x/Icly5dwpgxYwAo5LjfL9OqVaCoqEjo9XpRWFgoampqxIIFC4TBYBANDQ1yhya7lpYWcebMGXHmzBkBQLz33nvizJkz4o8//hBCCLF582ZhMBjEV199Jc6dOyfS09NFTEyMaG9vlzlyeSxatEgEBweL8vJyUV9fLz3a2tqkPgsXLhRRUVHiyJEjorKyUqSkpIiUlBQZo5bPqlWrREVFhaitrRXnzp0Tq1atEhqNRnz//fdCCObqTjh/20sI5qy75cuXi/LyclFbWyt+/vlnkZaWJkaMGCGampqEEMxXdydPnhS+vr5i48aN4vLly+KTTz4RAQEB4uOPP5b6yH3cZ/HjRe+//76IiooSOp1OTJ48WRw/flzukBTh6NGjAkCPR1ZWlhDiv689rl27VhiNRqHX68W0adPExYsX5Q1aRu5yBUAUFBRIfdrb28Xrr78uQkJCREBAgJg9e7aor6+XL2gZvfbaa2LMmDFCp9OJ0NBQMW3aNKnwEYK5uhPdix/mzFVmZqYIDw8XOp1OjBo1SmRmZoorV65I+5mvng4cOCDi4+OFXq8X48ePF7t373bZL/dxXyOEEP1zjomIiIhIfpzzQ0RERKrC4oeIiIhUhcUPERERqQqLHyIiIlIVFj9ERESkKix+iIiISFVY/BAREZGqsPghogGpvLwcGo2mxz2ViIhuh4scEtGA8PTTT2PixInYtm0bAMBiseCff/6B0WiERqORNzgiGlB85Q6AiOhu6HQ61d41m4juDS97EZHivfLKK6ioqMD27duh0Wig0WhQWFjoctmrsLAQBoMBBw8exLhx4xAQEIC5c+eira0Ne/fuRXR0NEJCQrB48WLYbDZp7M7OTqxYsQKjRo3C0KFDkZycjPLycnn+UCLqFzzzQ0SKt337dly6dAnx8fHIy8sDAFy4cKFHv7a2NuzYsQNFRUVoaWlBRkYGZs+eDYPBgG+//RZXr17FnDlzkJqaiszMTABATk4OampqUFRUhIiICJSUlOD5559HdXU1YmNj+/XvJKL+weKHiBQvODgYOp0OAQEB0qWuX3/9tUc/q9WK/Px8PPTQQwCAuXPn4qOPPkJjYyMCAwMRFxeHZ555BkePHkVmZibq6upQUFCAuro6REREAABWrFiBw4cPo6CgAJs2beq/P5KI+g2LHyIaNAICAqTCBwCMRiOio6MRGBjo0tbU1AQAqK6uhs1mw9ixY13G6ezsxPDhw/snaCLqdyx+iGjQ8PPzc9nWaDRu2+x2OwCgtbUVWq0WVVVV0Gq1Lv2cCyYiGlxY/BDRgKDT6VwmKnvDpEmTYLPZ0NTUhClTpnh1bCJSLn7bi4gGhOjoaJw4cQK///47bty4IZ29uRdjx47FvHnzMH/+fHz55Zeora3FyZMnYTKZ8M0333ghaiJSIhY/RDQgrFixAlqtFnFxcQgNDUVdXZ1Xxi0oKMD8+fOxfPlyjBs3DrNmzcKpU6cQFRXllfGJSHm4wjMRERGpCs/8EBERkaqw+CEiIiJVYfFDREREqsLih4iIiFSFxQ8RERGpCosfIiIiUhUWP0RERKQqLH6IiIhIVVj8EBERkaqw+CEiIiJVYfFDREREqsLih4iIiFTlf2vFV0MSX4bLAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Plot ML fit for pEpoR (single regularization strength with noise model)\n", + "fig, ax = plt.subplots(figsize=(6.5, 3.5))\n", + "t, pEpoR = simulate_pEpoR(\n", + " problem=regproblems[chosen_regstrength],\n", + " result=regresults[chosen_regstrength],\n", + ")\n", + "sigma_pEpoR = 0.0274 + 0.1 * pEpoR\n", + "ax.fill_between(\n", + " t,\n", + " pEpoR - 2 * sigma_pEpoR,\n", + " pEpoR + 2 * sigma_pEpoR,\n", + " color=\"black\",\n", + " alpha=0.10,\n", + " interpolate=True,\n", + " label=\"2-sigma error bands\",\n", + ")\n", + "ax.plot(t, pEpoR, color=\"black\", label=\"MLE\")\n", + "ax.plot(\n", + " df_pEpoR[\"time\"],\n", + " df_pEpoR[\"measurement\"],\n", + " \"o\",\n", + " color=\"black\",\n", + " markerfacecolor=\"none\",\n", + " label=\"experimental data\",\n", + ")\n", + "ylim1 = ax.get_ylim()[0]\n", + "ax.plot(\n", + " nodes,\n", + " len(nodes) * [ylim1],\n", + " \"x\",\n", + " color=\"black\",\n", + " label=\"spline nodes\",\n", + " zorder=10,\n", + " clip_on=False,\n", + ")\n", + "ax.set_ylim(ylim1, ax.get_ylim()[1])\n", + "ax.set_xlabel(\"time\")\n", + "ax.set_ylabel(\"pEpoR\")\n", + "ax.set_title(f\"ML fit for regularization strength = {chosen_regstrength}\")\n", + "ax.legend();" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "id": "658cd182-8f00-4eb7-8d14-30b839cd9e38", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Store results for later\n", + "all_results[\"15 nodes, FD\"] = (\n", + " regproblems[chosen_regstrength],\n", + " regresults[chosen_regstrength],\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "3c33c2c6-b299-40a6-83b1-a3724d72dfc8", + "metadata": { + "tags": [] + }, + "source": [ + "## Spline approximation with few nodes, optimizing derivatives explicitly\n", + "An alternative way to achieve higher expressivity, while not increasing the number of nodes, is to optimize the derivatives of the spline at the nodes instead of computing them by finite differencing. The risk of overfitting is still present, so we will include regularization as in the above example." + ] + }, + { + "cell_type": "markdown", + "id": "8ab36ca3-4555-4fd0-9a98-8ed8fc899fee", + "metadata": {}, + "source": [ + "### Creating the PEtab model" + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "id": "ec220c5d-ec0b-44f3-bfae-129ffe3ee26b", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Problem name\n", + "name = \"Swameye_PNAS2003_5nodes\"" + ] + }, + { + "cell_type": "markdown", + "id": "a3ef383a-c34c-40cb-bdd9-4012554ba0fe", + "metadata": {}, + "source": [ + "We now need to create additional parameters for the spline derivatives too." + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "id": "e686b762-b485-4624-bbd6-27220bdb9c03", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Create spline for pEpoR\n", + "nodes = [0, 5, 10, 20, 60]\n", + "values_at_nodes = [\n", + " sp.Symbol(f\"pEpoR_t{str(t).replace('.', '_dot_')}\") for t in nodes\n", + "]\n", + "derivatives_at_nodes = [\n", + " sp.Symbol(f\"derivative_pEpoR_t{str(t).replace('.', '_dot_')}\")\n", + " for t in nodes[:-1]\n", + "]\n", + "spline = amici.splines.CubicHermiteSpline(\n", + " sbml_id=\"pEpoR\",\n", + " evaluate_at=amici.sbml_utils.amici_time_symbol,\n", + " nodes=nodes,\n", + " values_at_nodes=values_at_nodes,\n", + " derivatives_at_nodes=derivatives_at_nodes\n", + " + [0], # last value is zero because steady state is reached\n", + " extrapolate=(None, \"constant\"),\n", + " bc=\"auto\",\n", + " logarithmic_parametrization=True,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "id": "8b0924bf-ec72-4631-a3db-3325945e3ccb", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Compute L2 norm of the curvature of pEpoR\n", + "regularization = spline.squared_L2_norm_of_curvature()" + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "id": "80ca3be0-a3b2-446d-96fc-edbf405215db", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Add a parameter for regularization strength\n", + "reg_parameters_df = pd.DataFrame(\n", + " dict(\n", + " parameterScale=\"log10\",\n", + " lowerBound=1e-6,\n", + " upperBound=1e6,\n", + " nominalValue=1.0,\n", + " estimate=0,\n", + " ),\n", + " index=pd.Series([\"regularization_strength\"], name=\"parameterId\"),\n", + ")\n", + "# Encode regularization term as an additional observable\n", + "reg_observables_df = pd.DataFrame(\n", + " dict(\n", + " observableFormula=f\"sqrt({regularization})\".replace(\"**\", \"^\"),\n", + " observableTransformation=\"lin\",\n", + " noiseFormula=\"1/sqrt(regularization_strength)\",\n", + " noiseDistribution=\"normal\",\n", + " ),\n", + " index=pd.Series([\"regularization\"], name=\"observableId\"),\n", + ")\n", + "# and correspoding measurement\n", + "reg_measurements_df = pd.DataFrame(\n", + " dict(\n", + " observableId=\"regularization\",\n", + " simulationConditionId=\"condition1\",\n", + " measurement=0,\n", + " time=0,\n", + " observableTransformation=\"lin\",\n", + " ),\n", + " index=pd.Series([0]),\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "id": "6c9af000-624f-46a6-907b-fa1875a87986", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Add spline formula to SBML model\n", + "sbml_doc = libsbml.SBMLReader().readSBML(\n", + " os.path.join(\"Swameye_PNAS2003\", \"swameye2003_model.xml\")\n", + ")\n", + "sbml_model = sbml_doc.getModel()\n", + "spline.add_to_sbml_model(\n", + " sbml_model, auto_add=True, y_nominal=0.1, y_constant=True\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "id": "266ea27b-4eff-4fdf-98db-0009389cca81", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Derivative parameters must be added separately\n", + "for p in derivatives_at_nodes:\n", + " amici.sbml_utils.add_parameter(sbml_model, p, value=0.0, constant=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "id": "223e47a3-6d7b-49af-94ef-f536aeea3e18", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Extra parameters associated to the spline\n", + "spline_parameters_df1 = pd.DataFrame(\n", + " dict(\n", + " parameterScale=\"log\",\n", + " lowerBound=0.001,\n", + " upperBound=10,\n", + " nominalValue=0.1,\n", + " estimate=1,\n", + " ),\n", + " index=pd.Series(list(map(str, values_at_nodes)), name=\"parameterId\"),\n", + ")\n", + "spline_parameters_df2 = pd.DataFrame(\n", + " dict(\n", + " parameterScale=\"lin\",\n", + " lowerBound=-0.666,\n", + " upperBound=0.666,\n", + " nominalValue=0.0,\n", + " estimate=1,\n", + " ),\n", + " index=pd.Series(list(map(str, derivatives_at_nodes)), name=\"parameterId\"),\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "id": "5afa675e-0f72-4ff4-b18a-538a8a5f1d4e", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Create PEtab problem\n", + "petab_problem = petab.Problem(\n", + " sbml_model,\n", + " condition_df=petab.conditions.get_condition_df(\n", + " os.path.join(\"Swameye_PNAS2003\", \"swameye2003_conditions.tsv\")\n", + " ),\n", + " measurement_df=petab.core.concat_tables(\n", + " [\n", + " os.path.join(\"Swameye_PNAS2003\", \"swameye2003_measurements.tsv\"),\n", + " reg_measurements_df,\n", + " ],\n", + " petab.measurements.get_measurement_df,\n", + " ).reset_index(drop=True),\n", + " parameter_df=petab.core.concat_tables(\n", + " [\n", + " os.path.join(\"Swameye_PNAS2003\", \"swameye2003_parameters.tsv\"),\n", + " spline_parameters_df1,\n", + " spline_parameters_df2,\n", + " reg_parameters_df,\n", + " ],\n", + " petab.parameters.get_parameter_df,\n", + " ),\n", + " observable_df=petab.core.concat_tables(\n", + " [\n", + " os.path.join(\"Swameye_PNAS2003\", \"swameye2003_observables.tsv\"),\n", + " reg_observables_df,\n", + " ],\n", + " petab.observables.get_observable_df,\n", + " ),\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "id": "acb52953-ab44-4beb-9378-3ab8f743c1f8", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Check whether PEtab model is valid\n", + "assert not petab.lint_problem(petab_problem)" + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "id": "2b4d2215-0551-4f9c-a092-c22259405582", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Save PEtab problem to disk\n", + "# import shutil\n", + "# shutil.rmtree(name, ignore_errors=True)\n", + "# os.mkdir(name)\n", + "# petab_problem.to_files_generic(prefix_path=name)" + ] + }, + { + "cell_type": "markdown", + "id": "5466e058-089c-4bc7-a7bb-1f88649fb29f", + "metadata": {}, + "source": [ + "### Creating the pyPESTO problem" + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "id": "6fc63a63-d5f3-45a3-b8a2-284e07b229e0", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Problem must be \"flattened\" to be used with AMICI\n", + "petab.core.flatten_timepoint_specific_output_overrides(petab_problem)" + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "id": "4c55449b-b877-451d-8a40-0547c9f88e12", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Check whether simulation from the PEtab problem works\n", + "# import amici.petab_simulate\n", + "# simulator = amici.petab_simulate.PetabSimulator(petab_problem)\n", + "# simulator.simulate(noise=False)" + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "id": "bbb28c44-11d3-4a74-996f-cc13870b091b", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Import PEtab problem into pyPESTO\n", + "pypesto_problem = pypesto.petab.PetabImporter(\n", + " petab_problem, model_name=name\n", + ").create_problem()" + ] + }, + { + "cell_type": "markdown", + "id": "ca2f1ac5-4616-43bd-88ce-3e424461ccd2", + "metadata": {}, + "source": [ + "### Maximum Likelihood estimation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a745c509-6e28-4e98-aefc-12db835eb0ed", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Try different regularization strengths\n", + "regstrengths = np.asarray([1, 175, 500, 1000])\n", + "if os.getenv(\"GITHUB_ACTIONS\") is not None:\n", + " regstrengths = np.asarray([175])\n", + "regproblems = {}\n", + "regresults = {}\n", + "\n", + "for regstrength in regstrengths:\n", + " # Fix parameter in pypesto problem\n", + " name = f\"Swameye_PNAS2003_5nodes_reg{regstrength}\"\n", + " pypesto_problem.fix_parameters(\n", + " pypesto_problem.x_names.index(\"regularization_strength\"),\n", + " np.log10(\n", + " regstrength\n", + " ), # parameter is specified as log10 scale in PEtab\n", + " )\n", + " regproblem = copy.deepcopy(pypesto_problem)\n", + "\n", + " # Load existing results if available\n", + " if os.path.exists(f\"{name}.h5\"):\n", + " regresult = pypesto.store.read_result(f\"{name}.h5\", problem=regproblem)\n", + " else:\n", + " regresult = None\n", + " # Overwrite\n", + " # regresult = None\n", + "\n", + " # Parallel multistart optimization with pyPESTO and FIDES\n", + " if n_starts > 0:\n", + " if regresult is None:\n", + " new_ids = [str(i) for i in range(n_starts)]\n", + " else:\n", + " last_id = max(int(i) for i in regresult.optimize_result.id)\n", + " new_ids = [\n", + " str(i) for i in range(last_id + 1, last_id + n_starts + 1)\n", + " ]\n", + " regresult = pypesto.optimize.minimize(\n", + " regproblem,\n", + " n_starts=n_starts,\n", + " ids=new_ids,\n", + " optimizer=pypesto_optimizer,\n", + " engine=pypesto_engine,\n", + " result=regresult,\n", + " )\n", + " regresult.optimize_result.sort()\n", + " if regresult.optimize_result.x[0] is None:\n", + " raise Exception(\n", + " \"All multistarts failed (n_starts is probably too small)! If this error occurred during CI, just run the workflow again.\"\n", + " )\n", + "\n", + " # Save results to disk\n", + " # pypesto.store.write_result(regresult, f'{name}.h5', overwrite=True)\n", + "\n", + " # Store result\n", + " regproblems[regstrength] = regproblem\n", + " regresults[regstrength] = regresult" + ] + }, + { + "cell_type": "code", + "execution_count": 65, + "id": "efa1b2f7-347a-4fba-9e22-e375aeb06d30", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Target value is 15\n", + "Regularization strength: 1. Statistic is 9.638207938045252\n", + "Regularization strength: 175. Statistic is 15.115255701660317\n", + "Regularization strength: 500. Statistic is 19.156287450444093\n", + "Regularization strength: 1000. Statistic is 25.09224919998158\n" + ] + } + ], + "source": [ + "# Compute sum of squared normalized residuals\n", + "print(f\"Target value is {len(df_pEpoR['time'])}\")\n", + "regstrengths = sorted(regproblems.keys())\n", + "stats = []\n", + "for regstrength in regstrengths:\n", + " t, pEpoR = simulate_pEpoR(\n", + " N=None,\n", + " problem=regproblems[regstrength],\n", + " result=regresults[regstrength],\n", + " )\n", + " assert np.array_equal(df_pEpoR[\"time\"], t[:-1])\n", + " pEpoR = pEpoR[:-1]\n", + " sigma_pEpoR = 0.0274 + 0.1 * pEpoR\n", + " stat = np.sum(((pEpoR - df_pEpoR[\"measurement\"]) / sigma_pEpoR) ** 2)\n", + " print(f\"Regularization strength: {regstrength}. Statistic is {stat}\")\n", + " stats.append(stat)\n", + "# Select best regularization strength\n", + "chosen_regstrength = regstrengths[\n", + " np.abs(np.asarray(stats) - len(df_pEpoR[\"time\"])).argmin()\n", + "]" + ] + }, + { + "cell_type": "code", + "execution_count": 66, + "id": "07e7e94c-2017-424a-a7ab-cd42d9b454b4", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Visualize the results of the multistarts for a chosen regularization strength\n", + "ax = pypesto.visualize.waterfall(\n", + " regresults[chosen_regstrength], size=[6.5, 3.5]\n", + ")\n", + "ax.set_title(\n", + " f\"Waterfall plot (regularization strength = {chosen_regstrength})\"\n", + ")\n", + "ax.set_ylim(ax.get_ylim()[0], 100);" + ] + }, + { + "cell_type": "code", + "execution_count": 76, + "id": "bd083fae-d3f6-4de8-bb57-fa9f94ca3157", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Plot ML fit for pEpoR (all regularization strengths)\n", + "fig, ax = plt.subplots(figsize=(6.5, 3.5))\n", + "for regstrength in sorted(regproblems.keys()):\n", + " t, pEpoR = simulate_pEpoR(\n", + " problem=regproblems[regstrength], result=regresults[regstrength]\n", + " )\n", + " if regstrength == chosen_regstrength:\n", + " kwargs = dict(\n", + " color=\"black\",\n", + " label=f\"$\\\\mathbf{{\\\\lambda = {regstrength}}}$\",\n", + " zorder=2,\n", + " )\n", + " else:\n", + " kwargs = dict(label=f\"$\\\\lambda = {regstrength}$\", alpha=0.5)\n", + " ax.plot(t, pEpoR, **kwargs)\n", + "ax.plot(\n", + " df_pEpoR[\"time\"],\n", + " df_pEpoR[\"measurement\"],\n", + " \"o\",\n", + " color=\"black\",\n", + " markerfacecolor=\"none\",\n", + " label=\"experimental data\",\n", + ")\n", + "ylim1 = ax.get_ylim()[0]\n", + "ax.plot(\n", + " nodes,\n", + " len(nodes) * [ylim1],\n", + " \"x\",\n", + " color=\"black\",\n", + " label=\"spline nodes\",\n", + " zorder=10,\n", + " clip_on=False,\n", + ")\n", + "ax.set_ylim(ylim1, ax.get_ylim()[1])\n", + "ax.set_xlabel(\"time\")\n", + "ax.set_ylabel(\"pEpoR\")\n", + "ax.set_xlim(-3.0, 63.0)\n", + "ax.set_ylim(-0.05299052022388704, 1.126290214024833)\n", + "ax.legend()\n", + "ax.figure.tight_layout()\n", + "# ax.set_ylabel(\"input function\")\n", + "# ax.figure.savefig('fit_5nodes_lambdas.pdf')" + ] + }, + { + "cell_type": "code", + "execution_count": 68, + "id": "fb75829f-ff65-4d92-b4e6-7a7670fc829a", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Plot ML fit for pSTAT5 (all regularization strengths)\n", + "fig, ax = plt.subplots(figsize=(6.5, 3.5))\n", + "for regstrength in sorted(regproblems.keys()):\n", + " t, pSTAT5 = simulate_pSTAT5(\n", + " problem=regproblems[regstrength], result=regresults[regstrength]\n", + " )\n", + " if regstrength == chosen_regstrength:\n", + " kwargs = dict(\n", + " color=\"black\",\n", + " label=f\"$\\\\mathbf{{\\\\lambda = {regstrength}}}$\",\n", + " zorder=2,\n", + " )\n", + " else:\n", + " kwargs = dict(label=f\"$\\\\lambda = {regstrength}$\", alpha=0.5)\n", + " ax.plot(t, pSTAT5, **kwargs)\n", + "ax.plot(\n", + " df_pSTAT5[\"time\"],\n", + " df_pSTAT5[\"measurement\"],\n", + " \"o\",\n", + " color=\"black\",\n", + " markerfacecolor=\"none\",\n", + " label=\"experimental data\",\n", + ")\n", + "ylim1 = ax.get_ylim()[0]\n", + "ax.plot(\n", + " nodes,\n", + " len(nodes) * [ylim1],\n", + " \"x\",\n", + " color=\"black\",\n", + " label=\"spline nodes\",\n", + " zorder=10,\n", + " clip_on=False,\n", + ")\n", + "ax.set_ylim(ylim1, ax.get_ylim()[1])\n", + "ax.set_xlabel(\"time\")\n", + "ax.set_ylabel(\"pSTAT5\");\n", + "# ax.legend();" + ] + }, + { + "cell_type": "code", + "execution_count": 69, + "id": "c68e4f72-bd06-48db-bebf-7079df51a616", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Plot ML fit for tSTAT5 (all regularization strengths)\n", + "fig, ax = plt.subplots(figsize=(6.5, 3.5))\n", + "for regstrength in sorted(regproblems.keys()):\n", + " t, tSTAT5 = simulate_tSTAT5(\n", + " problem=regproblems[regstrength], result=regresults[regstrength]\n", + " )\n", + " if regstrength == chosen_regstrength:\n", + " kwargs = dict(\n", + " color=\"black\",\n", + " label=f\"$\\\\mathbf{{\\\\lambda = {regstrength}}}$\",\n", + " zorder=2,\n", + " )\n", + " else:\n", + " kwargs = dict(label=f\"$\\\\lambda = {regstrength}$\", alpha=0.5)\n", + " ax.plot(t, tSTAT5, **kwargs)\n", + "ax.plot(\n", + " df_tSTAT5[\"time\"],\n", + " df_tSTAT5[\"measurement\"],\n", + " \"o\",\n", + " color=\"black\",\n", + " markerfacecolor=\"none\",\n", + " label=\"experimental data\",\n", + ")\n", + "ylim1 = ax.get_ylim()[0]\n", + "ax.plot(\n", + " nodes,\n", + " len(nodes) * [ylim1],\n", + " \"x\",\n", + " color=\"black\",\n", + " label=\"spline nodes\",\n", + " zorder=10,\n", + " clip_on=False,\n", + ")\n", + "ax.set_ylim(ylim1, ax.get_ylim()[1])\n", + "ax.set_xlabel(\"time\")\n", + "ax.set_ylabel(\"tSTAT5\");\n", + "# ax.legend();" + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "id": "7c0ed0ba-8870-470e-8009-0c69dc4a48df", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAj8AAAFjCAYAAADSCGomAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAACcxklEQVR4nOzdd1yTV/s/8E8GWYS9RGW4KlpnseKodZQWRa3jcdsqarW27tXWWhdW0VatdTxa7YOjdRVr1Vbco1ZRWncdOEERZQgkIWQn9+8Pfrm/RAIECPt6v155KXfucRJCcuWc61yHwzAMA0IIIYSQWoJb2Q0ghBBCCKlIFPwQQgghpFah4IcQQgghtQoFP4QQQgipVSj4IYQQQkitQsEPIYQQQmoVCn4IIYQQUqtQ8EMIIYSQWoWCH0IIIYTUKhT8kBrp6NGjaNOmDUQiETgcDmQyGSIiIhAYGFim8z548ADvvfceXFxcwOFwcODAAbu0tzrZtm0bOBwOkpKS7Hrebt26oVu3bnY9Z1W+bm0TGBiIPn36VHYzCAFAwQ8pZ+YPSg6Hg/Pnzxe4n2EY+Pn5gcPhFHhj5HA4mDx5comvmZmZiSFDhkAsFmPDhg346aef4OjoWGA/lUqFRYsW4ezZszafe/To0fj333+xdOlS/PTTT2jXrl2J20cq3p07d7Bo0SK7B2zlLTY2FosWLarsZtisujzPx48fx7hx49CiRQvweLxCvxQtWrSIff+ydrtw4QK7b0REhNV9goKCKuhRkZLgV3YDSO0gEomwa9cuvPXWWxbb//zzTzx79gxCodBu1/rnn3+Qk5ODJUuWIDQ0lN2+ZcsWmEwm9meVSoXFixcDgE3f/NVqNS5evIh58+aVKigjRTt+/Hi5nfvOnTtYvHgxunXrVuCDrjyvW1axsbHYsGFDtQmAinqeq5Jdu3Zh7969eOONN1C3bt1C9xs4cCAaN25cYPuXX34JpVKJN99802K7UCjEjz/+aLHNxcXFPo0mdkXBD6kQ4eHhiImJwdq1a8Hn/9/LbteuXQgODsbLly/tdq309HQAgKurq8V2BweHMp03IyPD6nnLIjc312qvVGE0Gg0EAgG43JrTaatSqSCRSCAQCCrl+pV1XXszGAwwmUw15vGUp2XLlmHLli1wcHBAnz59cOvWLav7tWrVCq1atbLYlpycjGfPnuGjjz4q8Fzz+Xx88MEH5dZuYj815x2UVGnDhw9HZmYmTpw4wW7T6XTYt28fRowYYbfrdOvWDaNHjwYAvPnmm+BwOIiIiAAAi5yfpKQkeHl5AQAWL17MdlEX9g170aJFCAgIAADMmTMHHA7H4pvttWvX0KtXLzg7O0MqleKdd97BpUuXLM5hHgL8888/8emnn8Lb2xv169cv9LGcPXsWHA4He/bswVdffYV69epBIpFAoVAAAOLj49GzZ0+4uLhAIpGga9euFt3w+c/Trl07iEQiNGrUCD/88APbnW+WlJQEDoeDbdu2FTi+qOfF7ODBg+jduzfq1q0LoVCIRo0aYcmSJTAajRb7devWDS1atMCVK1fw9ttvQyKR4Msvv2Tvy98DFxgYWOhwg3mo8smTJ/j000/RtGlTiMVieHh4YPDgwRbDLtu2bcPgwYMBAN27dy9wDms5P+np6Rg3bhx8fHwgEonQunVrbN++3WIf83O2cuVKbN68GY0aNYJQKMSbb76Jf/75p8jnCwD0ej0WL16MJk2aQCQSwcPDA2+99Rb7NxIREYENGzYAgMVjf/Xaa9asYa99584dAEBCQgIGDRoEd3d3iEQitGvXDocOHbK4vvn1eOHCBcycORNeXl5wdHTEgAED2EDfzGQyYdGiRahbty4kEgm6d++OO3fuIDAwkP37Ku55Njt//jzat28PkUiEhg0bYseOHcU+V/ZWt27dUn8Z2r17NxiGwciRI63ebzQa2b9RUnVRzw+pEIGBgejYsSN2796NXr16AQCOHDkCuVyOYcOGYe3atXa5zrx589C0aVNs3rwZkZGRaNCgARo1alRgPy8vL2zcuBGffPIJBgwYgIEDBwJAgW95ZgMHDoSrqytmzJiB4cOHIzw8HFKpFABw+/ZtdOnSBc7Ozvjss8/g4OCAH374Ad26dcOff/6JkJAQi3N9+umn8PLywoIFC5Cbm1vsY1qyZAkEAgFmz54NrVYLgUCA06dPo1evXggODsbChQvB5XKxdetW9OjRA3/99Rfat28PIC8o69mzJ3x9fbF48WIYjUZERkaygZ+9bNu2DVKpFDNnzoRUKsXp06exYMECKBQKfPvttxb7ZmZmolevXhg2bBg++OAD+Pj4WD3nmjVroFQqLbZ99913uH79Ojw8PADkDXHGxcVh2LBhqF+/PpKSkrBx40Z069YNd+7cgUQiwdtvv42pU6di7dq1+PLLL9GsWTMAYP99lVqtRrdu3fDw4UNMnjwZDRo0QExMDCIiIiCTyTBt2jSL/Xft2oWcnBx8/PHH4HA4+OabbzBw4EA8fvy4yA/YRYsWISoqCh999BHat28PhUKBy5cv4+rVq3j33Xfx8ccf4/nz5zhx4gR++uknq+fYunUrNBoNJkyYAKFQCHd3d9y+fRudO3dGvXr18MUXX8DR0RG//PIL+vfvj19//RUDBgywOMeUKVPg5uaGhQsXIikpCWvWrMHkyZOxd+9edp+5c+fim2++Qd++fREWFoYbN24gLCwMGo2G3ceW5/nhw4cYNGgQxo0bh9GjRyM6OhoREREIDg7G66+/XuhzBQDZ2dkFgmlrJBIJJBJJsfuV1s6dO+Hn54e33367wH0qlQrOzs5QqVRwc3PD8OHDsWLFCva9glQhDCHlaOvWrQwA5p9//mHWr1/PODk5MSqVimEYhhk8eDDTvXt3hmEYJiAggOndu7fFsQCYSZMmlema+Y0ePZoJCAhgf87IyGAAMAsXLrTpvImJiQwA5ttvv7XY3r9/f0YgEDCPHj1itz1//pxxcnJi3n777QLteuuttxiDwVDs9c6cOcMAYBo2bMg+ZwzDMCaTiWnSpAkTFhbGmEwmdrtKpWIaNGjAvPvuu+y2vn37MhKJhElJSWG3PXjwgOHz+Uz+P3/zY9u6dWuBdrz6HJkfR2JiosW1X/Xxxx8zEomE0Wg07LauXbsyAJhNmzYV2L9r165M165dC30+fvnlFwYAExkZWeR1L168yABgduzYwW6LiYlhADBnzpwp9rpr1qxhADA///wzu02n0zEdO3ZkpFIpo1AoGIb5v+fMw8ODycrKYvc9ePAgA4D5/fffC30sDMMwrVu3LvCaf9WkSZMYa2/T5ms7Ozsz6enpFve98847TMuWLS2ed5PJxHTq1Ilp0qQJu838ewwNDbV4Hc2YMYPh8XiMTCZjGIZhUlNTGT6fz/Tv39/iOosWLWIAMKNHj2a3FfU8BwQEMACYc+fOsdvS09MZoVDIzJo1q8jnIf/xxd1s/Xs26927t8X7QlFu3brFAGA+++yzAvd98cUXzOeff87s3buX2b17NzN69GgGANO5c2dGr9eXqE2k/NGwF6kwQ4YMgVqtxh9//IGcnBz88ccfdh3yqgxGoxHHjx9H//790bBhQ3a7r68vRowYgfPnzxfoAh8/fjx4PJ7N1xg9ejTEYjH78/Xr1/HgwQOMGDECmZmZePnyJV6+fInc3Fy88847OHfuHEwmE4xGI06ePIn+/ftbJHU2btyY7X2zl/zty8nJwcuXL9GlSxeoVCokJCRY7CsUCjFmzJgSnf/OnTsYO3Ys+vXrh6+++srqdfV6PTIzM9G4cWO4urri6tWrpXossbGxqFOnDoYPH85uc3BwwNSpU6FUKvHnn39a7D906FC4ubmxP3fp0gUA8Pjx4yKv4+rqitu3b+PBgwelaicA/Oc//7HoxcvKysLp06cxZMgQ9vfw8uVLZGZmIiwsDA8ePEBKSorFOSZMmGAxBNqlSxcYjUY8efIEAHDq1CkYDAZ8+umnFsdNmTKlxO1t3rw5+/wAeT2wTZs2Lfa5AvJ6XE6cOFHsbdSoUSVul6127twJAFaHvKKiorB8+XIMGTIEw4YNw7Zt27B06VJcuHAB+/btK7c2kdKhYS9SYby8vBAaGopdu3ZBpVLBaDRi0KBBld2sMsnIyIBKpULTpk0L3NesWTOYTCYkJydbdOk3aNCgRNd4dX/zh6U5t8kauVwOjUYDtVptdbaKtW1lcfv2bXz11Vc4ffp0gWBPLpdb/FyvXr0SJeUqFAoMHDgQ9erVw44dOyw+qNVqNaKiorB161akpKSAYZhCr2urJ0+eoEmTJgWSys3DN+agwMzf39/iZ3MglJ2dXeR1IiMj0a9fP7z22mto0aIFevbsiQ8//LDQoVdrXn1tPHz4EAzDYP78+Zg/f77VY9LT01GvXj2b229+vK++Ztzd3S2CPlu8ei3z9Yp7rgCgc+fOJbqWvTEMg127dqFFixY2/45mzJiB+fPn4+TJkxg2bFg5t5CUBAU/pEKNGDEC48ePR2pqKnr16mXXmVPVRf7eitLsb56u/+2336JNmzZWj5FKpRb5GMXJH1DkZ0uOhUwmQ9euXeHs7IzIyEg0atQIIpEIV69exeeff25RXgAo+eOPiIjA8+fP8ffff8PZ2dnivilTpmDr1q2YPn06OnbsyBafHDZsWIHrlpfCevHyB2LWvP3223j06BEOHjyI48eP48cff8R3332HTZs24aOPPrLp2oW9NmbPno2wsDCrx7waxJS2/aVRlmtlZGTY9HqUSqXlkmNz4cIFPHnyBFFRUTYfY07Cz8rKsnt7SNlQ8EMq1IABA/Dxxx/j0qVLFgmVlaGwD/yS8PLygkQiwb179wrcl5CQAC6XCz8/vzJfJz9zArezs7NFHaNXeXt7QyQS4eHDhwXue3Wb+Ru8TCaz2P5qL4c1Z8+eRWZmJvbv32+RBJqYmFjsscVZvnw5Dhw4gP3791stFrdv3z6MHj0aq1atYrdpNJoCj6Mkv+uAgADcvHkTJpPJovfHPHxnnvVnD+7u7hgzZgzGjBkDpVKJt99+G4sWLWKDn5K+Rs1Drw4ODkW+NkrC/HgfPnxo0dOUmZlZoMfGHn9ThXnzzTdtej0uXLiwXOoi7dy5ExwOp0RD9eahR3tPMCBlR8EPqVBSqRQbN25EUlIS+vbtW6ltMc8IefWDsiR4PB7ee+89HDx4EElJSez097S0NLao46u9FWUVHByMRo0aYeXKlRgxYkSBb7kZGRnw8vICj8dDaGgoDhw4gOfPn7N5Pw8fPsSRI0csjnF2doanpyfOnTuH6dOns9v/+9//Ftse87f5/N/edTqdTccW5eTJk/jqq68wb9489O/fv9Brv9prsG7dugI9BOZaSrb8rsPDw3H8+HHs3buXzfsxGAxYt24dpFIpunbtWvIHY0VmZiY7aw3I+9to3LgxkpOTrbbbll5Sb29vdOvWDT/88AOmTJkCX19fi/vNr42SeOedd8Dn87Fx40a8++677Pb169cX2Lckz3NJ7dy5E2q1utj98ufe2Yter0dMTAzeeustq0N3Go0Ger0eTk5OFtuXLFkChmHQs2dPu7eJlA0FP6TCFZWr8qrLly/j66+/LrC9W7duBapFl5RYLEbz5s2xd+9evPbaa3B3d0eLFi3QokWLEp3n66+/xokTJ/DWW2/h008/BZ/Pxw8//ACtVotvvvmmTG20hsvl4scff0SvXr3w+uuvY8yYMahXrx5SUlJw5swZODs74/fffweQN536+PHj6Ny5Mz755BMYjUasX78eLVq0wPXr1y3O+9FHH2H58uX46KOP0K5dO5w7dw73798vtj2dOnWCm5sbRo8ejalTp4LD4eCnn34q87DJ8OHD4eXlhSZNmuDnn3+2uO/dd9+Fj48P+vTpg59++gkuLi5o3rw5Ll68iJMnT1oEFQDQpk0b8Hg8rFixAnK5HEKhED169IC3t3eB606YMAE//PADIiIicOXKFQQGBmLfvn24cOEC1qxZU+ADrrSaN2+Obt26ITg4GO7u7rh8+TL27dtnUT08ODgYADB16lSEhYWBx+MVmzuyYcMGvPXWW2jZsiXGjx+Phg0bIi0tDRcvXsSzZ89w48aNErXTx8cH06ZNw6pVq/D++++jZ8+euHHjBo4cOQJPT0+L3p6SPM8lZc+cn5s3b7J1jx4+fAi5XM6+z7Ru3brAF7Njx44hMzOz0No+qampaNu2LYYPH872UB47dgyxsbHo2bMn+vXrZ7e2EzuprGlmpHYobNr5qwqb6l7YbcmSJSW+5qtT3RmGYeLi4pjg4GBGIBAUO022sKnuDMMwV69eZcLCwhipVMpIJBKme/fuTFxcnE3tKox5qntMTIzV+69du8YMHDiQ8fDwYIRCIRMQEMAMGTKEOXXqlMV+p06dYtq2bcsIBAKmUaNGzI8//sjMmjWLEYlEFvupVCpm3LhxjIuLC+Pk5MQMGTKESU9Pt2mq+4ULF5gOHTowYrGYqVu3LvPZZ58xx44dKzDtuWvXrszrr79u9fG8OuW8qN+/+ZzZ2dnMmDFjGE9PT0YqlTJhYWFMQkICExAQYDEFm2EYZsuWLUzDhg0ZHo9ncQ5rU+zT0tLY8woEAqZly5YFygAU9Xoo7rXEMAzz9ddfM+3bt2dcXV0ZsVjMBAUFMUuXLmV0Oh27j8FgYKZMmcJ4eXkxHA6HnfZe1LUZhmEePXrEjBo1iqlTpw7j4ODA1KtXj+nTpw+zb98+dp/CXo/m113+35vBYGDmz5/P1KlThxGLxUyPHj2Yu3fvMh4eHszEiRMtji/sebb2N84wxZc4KA/mx27t9urrhmEYZtiwYYyDgwOTmZlp9XzZ2dnMBx98wDRu3JiRSCSMUChkXn/9dWbZsmUWv09SdXAYphyy2gghVVr//v3LPM2a1G4ymQxubm74+uuvMW/evMpuDiElQnV+CKnhXs2TePDgAWJjY21azJUQoOBrCMirwA3YtigwIVUN9fwQUsP5+voiIiICDRs2xJMnT7Bx40ZotVpcu3YNTZo0qezmkWpg27Zt2LZtG7usy/nz57F792689957OHbsWGU3j5ASo4RnQmq4nj17Yvfu3UhNTYVQKETHjh2xbNkyCnyIzVq1agU+n49vvvkGCoWCTYK2NhmBkOqAen4IIYQQUqtQzg8hhBBCahUKfgghhBBSq9S6nB+TyYTnz5/DycmpXEuxE0IIIaTiMAyDnJwc1K1bt8DCxK+qdcHP8+fP7b7WEiGEEEKqhuTkZNSvX7/IfWpd8GMuTZ+cnGz3NZcIIYQQUjkUCgX8/PxsWoKm1gU/5qEuZ2dnCn4IIYSQGsaWlBZKeCaEEEJIrULBDyGEEEJqFQp+CCGEEFKr1LqcH0IIqUlMJhN0Ol1lN4OQcufg4AAej2eXc1Vq8HPu3Dl8++23uHLlCl68eIHffvsN/fv3L3T//fv3Y+PGjbh+/Tq0Wi1ef/11LFq0CGFhYRXXaEIIqSJ0Oh0SExNhMpkquymEVAhXV1fUqVOnzHX6KjX4yc3NRevWrTF27FgMHDiw2P3PnTuHd999F8uWLYOrqyu2bt2Kvn37Ij4+Hm3btq2AFhNCSNXAMAxevHgBHo8HPz+/You6EVKdMQwDlUqF9PR0AICvr2+ZzlepwU+vXr3Qq1cvm/dfs2aNxc/Lli3DwYMH8fvvv1PwQwipVQwGA1QqFerWrQuJRFLZzSGk3InFYgBAeno6vL29yzQEVq1zfkwmE3JycuDu7l7oPlqtFlqtlv1ZoVBURNMIIaRcGY1GAIBAIKjklhBSccyBvl6vL1PwU637SVeuXAmlUokhQ4YUuk9UVBRcXFzYGy1tQQipSWiNQlKb2Ov1Xm2Dn127dmHx4sX45Zdf4O3tXeh+c+fOhVwuZ2/JyckV2EoC5H1DNX9LJYQQQipbtRz22rNnDz766CPExMQgNDS0yH2FQiGEQmEFtYzkp1QqIZfLoVarAeSN17q6usLR0bGSW0ZIzWU0Git09heXy7Xb9OPS4nA4xc4WJpYiIiIgk8lw4MCBCr/2tm3bMH36dMhksgq/tlm1C352796NsWPHYs+ePejdu3dlN4dYwTAMMjMzkZGRAQBs8KlQKKBQKODl5QV3d3eanUKInRmNRiQnJ1do3R+BQAA/Pz+bA6CoqCjs378fCQkJEIvF6NSpE1asWIGmTZuWug0vXryAm5tbqY8ntU+lBj9KpRIPHz5kf05MTMT169fh7u4Of39/zJ07FykpKdixYweAvKGu0aNH4/vvv0dISAhSU1MB5PUouLi4VMpjIJYYhsHLly+Rnp4OR0dHi2RMkUgErVaL1NRUGI1GeHt7U74CIXZkLnjI4/HA55f/27vBYIBOp4PJZLI5+Pnzzz8xadIkvPnmmzAYDPjyyy/x3nvv4c6dO6XuFa5Tp06pjquKGIaB0Wgs8PvT6XSlSm4v7XE1XaV+9b58+TLatm3LTlOfOXMm2rZtiwULFgDIi+afPn3K7r9582YYDAZMmjQJvr6+7G3atGmV0n5SkEKhQEZGRoHAx0woFMLJyQkZGRnIysqqhBYSUvPx+Xw4ODiU+600AdbRo0cRERGB119/Ha1bt8a2bdvw9OlTXLlypdBjdDodJk+eDF9fX4hEIgQEBCAqKoq9n8PhWAzfxMXFoU2bNhCJRGjXrh0OHDgADoeD69evAwDOnj0LDoeDY8eOoW3bthCLxejRowfS09Nx5MgRNGvWDM7OzhgxYgRUKpVF29966y24urrCw8MDffr0waNHj4p8vCaTCVFRUWjQoAHEYjFat26Nffv2sfeb23LkyBEEBwdDKBTi/Pnz6NatGyZPnozp06fD09OTLeb7559/on379hAKhfD19cUXX3wBg8HAnq+w4wqzePFieHl5wdnZGRMnTrToNSzu8SYlJYHD4WD//v3o3r07JBIJWrdujYsXL1pcY9u2bfD394dEIsGAAQOQmZlpcf+NGzfQvXt3ODk5wdnZGcHBwbh8+XKR7S6rSu356datGxiGKfT+bdu2Wfx89uzZ8m0QKROtVouMjAwIBIIiv2k4ODhAIpHg5cuXEIlElANESC0ml8sBoMiSJWvXrsWhQ4fwyy+/wN/fH8nJyYVOXlEoFOjbty/Cw8Oxa9cuPHnyBNOnT7e676JFi7B+/XpIJBIMGTIEQ4YMgVAoxK5du6BUKjFgwACsW7cOn3/+OYC8wrwzZ85Eq1atoFQqsWDBAgwYMADXr18vdBg/KioKP//8MzZt2oQmTZrg3Llz+OCDD+Dl5YWuXbuy+33xxRdYuXIlGjZsyA7hbd++HZ988gkuXLgAAEhJSUF4eDgiIiKwY8cOJCQkYPz48RCJRFi0aBF7rlePK8ypU6cgEolw9uxZJCUlYcyYMfDw8MDSpUtL9HjnzZuHlStXokmTJpg3bx6GDx+Ohw8fgs/nIz4+HuPGjUNUVBT69++Po0ePYuHChRbtGDlyJNq2bYuNGzeCx+Ph+vXrcHBwKLLtZcbUMnK5nAHAyOXyym5KjWIymZiUlBTm1q1bTEpKik23u3fvMklJSYzBYKjs5hNS7ajVaubOnTuMWq1mt+l0OubevXtMUlKSzX+HZbklJSUx9+7dY3Q6Xakeg9FoZHr37s107ty5yP2mTJnC9OjRgzGZTFbvB8D89ttvDMMwzMaNGxkPDw+L52XLli0MAObatWsMwzDMmTNnGADMyZMn2X2ioqIYAMyjR4/YbR9//DETFhZWaLsyMjIYAMy///5r9X6NRsNIJBImLi7OYvu4ceOY4cOHW7TlwIEDFvt07dqVadu2rcW2L7/8kmnatKnF87BhwwZGKpUyRqOx0OOsGT16NOPu7s7k5uay2zZu3GhxruIeb2JiIgOA+fHHH9l9bt++zQBg7t69yzAMwwwfPpwJDw+3OM/QoUMZFxcX9mcnJydm27ZtxbaZYay/7s1K8vlOGafELnJzcyGTyeDk5GTzMVKpFDk5OZWa8U8IqTyTJk3CrVu3sGfPHnbbxIkTIZVK2RuQNzPp+vXraNq0KaZOnYrjx48Xes579+6hVatWEIlE7Lb27dtb3bdVq1bs/318fCCRSNCwYUOLbeblFADgwYMHGD58OBo2bAhnZ2cEBgYCgEV6Rn4PHz6ESqXCu+++a/GYduzYUWC4rF27dgWODw4Otvj57t276Nixo0WuZOfOnaFUKvHs2bNCjytM69atLaqDd+zYEUqlku1Vs/Xx5n8ezctOmJ+3u3fvIiQkxGL/jh07Wvw8c+ZMfPTRRwgNDcXy5cuLHUq0h2o324tUPQzDIDs7G1wut0Q5AFwuF2KxGFlZWZBKpVSSgJBaZPLkyfjjjz9w7tw51K9fn90eGRmJ2bNnW+z7xhtvIDExEUeOHMHJkycxZMgQhIaGWuTOlEb+oRUOh1NgqIXD4ViUDejbty8CAgKwZcsW1K1bFyaTCS1atCh0dp1SqQQAHD58GPXq1bO479X3O2vD/6VNCbBXKoGtj/fV5xFAicotLFq0CCNGjMDhw4dx5MgRLFy4EHv27MGAAQPs8jisoeCHlFlubi5ycnJK1OtjZg5+ZDIZfHx8yqF1hJCqhGEYTJkyBb/99hvOnj2LBg0aWNzv7e1ttXCts7Mzhg4diqFDh2LQoEHo2bMnsrKyCuQKNW3aFD///DO0Wi0bYPzzzz9lbndmZibu3buHLVu2oEuXLgCA8+fPF3lM8+bNIRQK8fTpU4v8ntJq1qwZfv31VzAMwwYZFy5cgJOTk0UAaasbN25ArVaza2ZdunQJUqkUfn5+pXq8hbU5Pj7eYtulS5cK7Pfaa6/htddew4wZMzB8+HBs3bq1XIMfGvYiZcIwDGQyGTgcTqkLnUmlUmRnZ1vMqiCE1EyTJk3Czz//jF27dsHJyQmpqalITU1li6Fas3r1auzevRsJCQm4f/8+YmJiUKdOHbi6uhbYd8SIETCZTJgwYQLu3r2LY8eOYeXKlQDKtjSCm5sbPDw8sHnzZjx8+BCnT5/GzJkzizzGyckJs2fPxowZM7B9+3Y8evQIV69exbp167B9+/YSt+HTTz9FcnIypkyZgoSEBBw8eBALFy7EzJkzS1U3TafTYdy4cbhz5w5iY2OxcOFCTJ48GVwut1SP15qpU6fi6NGjWLlyJR48eID169fj6NGj7P1qtRqTJ0/G2bNn8eTJE1y4cAH//PMPmjVrVuJrlQT1/JAyUavVyMnJKVM3q0AggEqlQnZ2NsRiMdX+IaSM8k99rmrX2bhxI4C82b75bd26FREREVaPcXJywjfffIMHDx6Ax+PhzTffRGxsrNUPfGdnZ/z+++/45JNP0KZNG7Rs2RILFizAiBEjLPKASorL5WLPnj2YOnUqWrRogaZNm2Lt2rUFHserlixZAi8vL0RFReHx48dwdXXFG2+8gS+//LLEbahXrx5iY2MxZ84ctG7dGu7u7hg3bhy++uqrUj2md955B02aNMHbb78NrVaL4cOHs7PGSvt4X9WhQwds2bIFCxcuxIIFCxAaGoqvvvoKS5YsAQDweDxkZmZi1KhRSEtLg6enJwYOHIjFixeX6jHZisMwRcw1r4EUCgVcXFwgl8vh7Oxc2c2p9tLT05GRkVHm6qoGgwE5OTkICAhgkxwJIYXTaDRITExEgwYN2A/16lDhuTLs3LkTY8aMgVwuZ4d4SPVk7XVvVpLPd+r5IaWm1+uhUCjs8mbC5/PB4/GQnZ0NiURCS18QUgo8Hg9+fn61bm2vV+3YsQMNGzZEvXr1cOPGDXz++ecYMmQIBT6ERcEPKTWVSgWNRmO3NXUcHR2hUCjg6upaquRpQkheAFTVgpGKlpqaigULFiA1NRW+vr4YPHgwW7iPEICCH1JKDMNAoVDAwcHBbjk65vWIsrKy4OjoSL0/hJBS+eyzz/DZZ59VdjNIFUafLqRUtFotVCpVmRIIrXF0dIRSqURubq5dz0sIIYSYUfBDSkWtVkOv19t9/RVzocSsrKwKzVsghBBSe1DwQ0rMPORV1OKlZUG9P4QQQsoTBT+kxLRaLdRqtd2HvMzMs0dkMhlqWSUGQgghFYCCH1JiGo0GBoOhROt4lZSjoyNycnKo94cQQojdUfBDSiwnJ8fuuT6v4vF44HA4kMvl1PtDCCHErij4ISWi0+mgVqsrZAV2c92fotb8IYQQQkqKgh9SIhqNBjqdrtySnfPj8/lgGAZyubzcr0UIqTgRERHgcDiYOHFigfsmTZoEDofDrvMVERGB/v37F3quwMBAcDicArfly5eXU+tJTUDBDymR3NzcCq0eK5FIoFAooNFoKuyahJDy5+fnhz179lj07Go0GuzatQv+/v4lOldkZCRevHhhcZsyZYq9m0xqEAp+iM2MRiNUKlWF9PqYCQQCdg0xQkjN8cYbb8DPzw/79+9nt+3fvx/+/v5o27Ztic7l5OSEOnXqWNwcHR3t3WRSg9DyFsRmWq0WGo0GLi4uFXpdiUQCuVwOV1fXCg28CKlOGIaBSqWqlGtLJJJSLXMzduxYbN26FSNHjgQAREdHY8yYMTh79qydW0iIJQp+iM3MQ08VveaWSCRCVlYWcnJy4OHhUaHXJqS6UKlUkEqllXJtpVJZqp6WDz74AHPnzsWTJ08AABcuXMCePXtKHPx8/vnn+Oqrryy2HTlyBF26dClxm0jtQMEPsZlSqSz3Ke6FEYlEkMlkcHFxKdf6QoSQiuPl5YXevXtj27ZtYBgGvXv3hqenZ4nPM2fOHDZB2qxevXp2aiWpiehThNhEp9NBo9FU2rCTWCxGdnY2cnNzK3zYjZDqQCKRQKlUVtq1S2vs2LGYPHkyAGDDhg2lOoenpycaN25c6jaQ2oeCH2ITrVYLnU5Xad3qHA4HAoEAMpkMTk5OFT70RkhVx+FwqmWSb8+ePaHT6cDhcBAWFlbZzSG1BAU/xCZarbbSAw7ztPfKzG0ghNgXj8fD3bt32f9bI5fLcf36dYttHh4e8PPzA5BXdT41NdXifolEAmdnZ/s3mNQI9PWZFIthGCiVykqfaWUOvmjaOyE1i7Ozc5GBytmzZ9G2bVuL2+LFi9n7FyxYAF9fX4vbZ599VhFNJ9UUh6llCycpFAq4uLhALpfTtwIbabVaJCUlQSQSVVrCs5ler4darUZgYGC5rSpPSHWg0WiQmJiIBg0a0N8CqTWKet2X5PO9Unt+zp07h759+6Ju3brgcDg4cOBAscecPXsWb7zxBoRCIRo3boxt27aVeztrO51OB4PBUCDwMRqNiIuLw4EDBxAXFwej0VjubXFwcIDBYEBOTk65X4sQQkjNVKnBT25uLlq3bm1zhn9iYiJ69+6N7t274/r165g+fTo++ugjHDt2rJxbWrtZW1oiNjYWnTt3xuDBgzFp0iQMHjwYnTt3RmxsbLm3RywWQy6XQ6/Xl/u1CCGE1DyVGvz06tULX3/9NQYMGGDT/ps2bUKDBg2watUqNGvWDJMnT8agQYPw3XfflXNLay+GYZCbm2uxintsbCwmTJiAoKAgHDp0CPfv38ehQ4cQFBSECRMmlHsAJBaLodFokJubW67XIYQQUjNVq4TnixcvIjQ01GJbWFgYLl68WEktqvn0ej10Oh075GU0GhEZGYnQ0FBER0cjODgYjo6OCA4ORnR0NEJDQ7FkyZJyHwITCoXIzs6GyWQq1+sQQgipeapV8JOamgofHx+LbT4+PlAoFBYrA+en1WqhUCgsbsR2Op3OIviJj49HcnIypkyZUmDqO5fLxeTJk/H06VPEx8eXa7vEYjFUKlWlrWVECCGk+qpWwU9pREVFwcXFhb2Z60IQ22i1WosFC9PT0wEAQUFBVvc3bzfvV164XC64XC7kcnm5XocQQkjNU62Cnzp16iAtLc1iW1paGpydnSEWi60eM3fuXMjlcvaWnJxcEU2tMXJzcy1meXl7ewMAEhISrO5v3m7erzxJJBLk5ORYTcgmhBBCClOtgp+OHTvi1KlTFttOnDiBjh07FnqMUChkC2gVV0iLWDIYDNBqtRbBT0hICPz8/LBu3boC+TYmkwnr16+Hv78/QkJCyr19NO2dEEJIaVRq8KNUKnH9+nW2bHliYiKuX7+Op0+fAsjrtRk1ahS7/8SJE/H48WN89tlnSEhIwH//+1/88ssvmDFjRmU0v8Yzr+eVv7Izj8fDggULcPLkSYwdOxaXL1+GUqnE5cuXMXbsWJw8eRLz588vtEy9vdG0d0IIISVVqcHP5cuX2VLlADBz5ky0bdsWCxYsAAC8ePGCDYQAoEGDBjh8+DBOnDiB1q1bY9WqVfjxxx9pMbxyotPpwDCMRc4PAISHh2Pz5s1ISEhAv3790LRpU/Tr1w/37t3D5s2bER4eXmFtFIlENO2dEGIXSUlJ4HA4BdYRq062bdsGV1fXEh1TEx53SVXqwqbdunVDUatrWKve3K1bN1y7dq0cW0XM1Go1+HzrL5Hw8HCEhYUhPj4e6enp8Pb2RkhISIX1+JjlX+3d2dm50hdfJaS6MRqN+Ouvv/DixQv4+vqiS5cuFf53XFX4+fnhxYsX8PT0rOymYNGiRThw4ECVDUgiIiIgk8lsWpmhKqJV3YlVJpMJarW6yLW8eDweOnXqVIGtso5WeyekdPbv349Zs2YhKSmJ3RYYGIhVq1Zh4MCBldewSmAe4q9Tp05lN4VUAPqaTKzS6XTQ6/WVvpK7Lcy9PZT4TIjt9u/fj0GDBqFly5a4ePEicnJycPHiRbRs2RKDBg3C/v37y+W6JpMJUVFRaNCgAcRiMVq3bo19+/YByKsoHxoairCwMHZUICsrC/Xr12fTIc6ePQsOh4PDhw+jVatWEIlE6NChA27dumVxnfPnz6NLly4Qi8Xw8/PD1KlTLYbHAwMDsWTJEowaNQrOzs6YMGFCgeEf87WOHTuGtm3bQiwWo0ePHkhPT8eRI0fQrFkzODs7Y8SIERY1x4p6jPnPe+rUKbRr1w4SiQSdOnXCvXv3AOSNeixevBg3btwAh8MBh8NhR0JWr16Nli1bwtHREX5+fvj000+hVCpL9Dv4+++/0bZtW4hEIrRr167AaIrRaMS4cePY9jdt2hTff/89e/+iRYuwfft2HDx4kG3f2bNnAQCff/45XnvtNUgkEjRs2BDz58+vkjmZFPwQq8yLmZa1+9toNEKpVJZ7JWZz749Wqy3X6xBSExiNRsyaNQt9+vTBgQMH0KFDB0ilUnTo0AEHDhxAnz59MHv27HKp1B4VFYUdO3Zg06ZNuH37NmbMmIEPPvgAf/75JzgcDrZv345//vkHa9euBZA30aVevXps8GM2Z84crFq1Cv/88w+8vLzQt29f9kP20aNH6NmzJ/7zn//g5s2b2Lt3L86fP4/JkydbnGPlypVo3bo1rl27hvnz5xfa5kWLFmH9+vWIi4tDcnIyhgwZgjVr1mDXrl04fPgwjh8/jnXr1tn0GPObN28eVq1ahcuXL4PP52Ps2LEAgKFDh2LWrFl4/fXX8eLFC7x48QJDhw4FkPdlb+3atbh9+za2b9+O06dP47PPPrP5+VcqlejTpw+aN2+OK1euYNGiRZg9e7bFPiaTCfXr10dMTAzu3LmDBQsW4Msvv8Qvv/wCAJg9ezaGDBmCnj17su0zjwI4OTlh27ZtuHPnDr7//nts2bKlai5BxdQycrmcAcDI5fLKbkqVlpGRwdy6dYtJSUkp1S0mJoZ55513GJFIxABgJBIJExYWxuzfv7/U5yzu9u+//zIZGRmV/dQRUiHUajVz584dRq1Wl/jYM2fOMACYixcvWr0/Li6OAcCcOXOmjK20pNFoGIlEwsTFxVlsHzduHDN8+HD2519++YURiUTMF198wTg6OjL3798v0PY9e/aw2zIzMxmxWMzs3buXPd+ECRMsrvHXX38xXC6Xfb4CAgKY/v37W+yTmJjIAGCuXbtmca2TJ0+y+0RFRTEAmEePHrHbPv74YyYsLMzmx2jtvIcPH2YAsO1buHAh07p168KeSlZMTAzj4eHB/rx161bGxcWl0P1/+OEHxsPDw+J1s3HjRovHbc2kSZOY//znP+zPo0ePZvr161ds+7799lsmODi42P1sVdTrviSf75TzQ6zKzc0t1ZCXTqfD/Pnz8fPPP1tsV6lUOHbsGI4dO4YRI0YgMjKy0MKUpWWe9u7q6lpoojYhJG8mLQC0aNHC6v3m7eb97OXhw4dQqVR49913LbbrdDp21i8ADB48GL/99huWL1+OjRs3okmTJgXOlb++m7u7O5o2bYq7d+8CAG7cuIGbN29i586d7D4Mw8BkMiExMRHNmjUDALRr186mdrdq1Yr9v4+PDzukk3/b33//XaLH+Op5fX19AeRVx/f39y+0LSdPnkRUVBQSEhKgUChgMBig0WigUqkgkUiKfSx3795lhwvNrNXK27BhA6Kjo/H06VOo1WrodDq0adOm2PPv3bsXa9euxaNHj6BUKmEwGKpkfT36hCAFGAwG6HS6EgcQWq0WY8eOZcezR44ciYiICAQGBuLRo0fYsWMHdu3ahV27duHx48fYvn27XROURSIRsrOzoVKpquQfGyFVhfmD9tatW+jQoUOB+835M+b97MWcm3L48GHUq1fP4j6hUMj+X6VS4cqVK+DxeHjw4EGprvPxxx9j6tSpBe7LH1g4OjradL78Ez84HE6BiSAcDocd2rf1MVo7L4AiUwSSkpLQp08ffPLJJ1i6dCnc3d1x/vx5jBs3DjqdzqbgxxZ79uzB7NmzsWrVKnTs2BFOTk749ttvi12z8eLFixg5ciQWL16MsLAwuLi4YM+ePVi1apVd2mVPFPyQAsyLmZbkD4lhGMyYMQNnz56FWCzG5s2b0aNHD/b+Fi1a4JtvvkGfPn0wYcIEXLp0CRMnTsT27dvtNq3W/KYkk8ng5ORUoD4RISRPly5dEBgYiGXLluHAgQMWJSLyJ+t26dLFrtdt3rw5hEIhnj59iq5duxa636xZs8DlcnHkyBGEh4ejd+/eFu8nAHDp0iU2kMnOzsb9+/fZHp033ngDd+7cQePGje3aflvY+hiLIxAICuRcXblyBSaTCatWrWJ/Z+Y8HFs1a9YMP/30EzQaDdv7c+nSJYt9Lly4gE6dOuHTTz9ltz169KjY9sXFxSEgIADz5s1jtz158qRE7asolPBMCiisuGFRtm3bhoMHD4LP52Pr1q0F3qjM3n77bezevRsikQhnzpzBsmXL7NVsAHmJz0qlEmq12q7nJaQm4fF4WLVqFf744w/079/fYrZX//798ccff2DlypV2r/fj5OSE2bNnY8aMGdi+fTsePXqEq1evYt26ddi+fTuAvB6T6Oho7Ny5E++++y7mzJmD0aNHIzs72+JckZGROHXqFG7duoWIiAh4enqif//+APJmHMXFxWHy5Mm4fv06Hjx4gIMHDxZIeC4PtjxGWwQGBrKrHrx8+RJarRaNGzeGXq/HunXr8PjxY/z000/YtGlTido3YsQIcDgcjB8/Hnfu3EFsbCxWrlxpsU+TJk1w+fJlHDt2DPfv38f8+fPxzz//FGjfzZs3ce/ePbx8+RJ6vR5NmjTB06dPsWfPHjx69Ahr167Fb7/9VqL2VRQKfkgBWq22RG96ycnJbBCzYMGCYr8ttm3bls3+37RpE/7444/SN/YV5nbTtHdCijZw4EDs27cP//77Lzp16gRnZ2d06tQJt27dwr59+8qtzs+SJUswf/58REVFoVmzZujZsycOHz6MBg0aICMjA+PGjcOiRYvwxhtvAAAWL14MHx8fTJw40eI8y5cvx7Rp0xAcHIzU1FT8/vvvbJ5iq1at8Oeff+L+/fvo0qULu3JA3bp1y+UxleQx2uo///kPevbsie7du8PLywu7d+9G69atsXr1aqxYsQItWrTAzp07ERUVVaK2SaVS/P777/j333/Rtm1bzJs3DytWrLDY5+OPP8bAgQMxdOhQhISEIDMz06IXCADGjx+Ppk2bol27dvDy8sKFCxfw/vvvY8aMGZg8eTLatGmDuLi4ImfRVSYOwxRRYrkGUigUcHFxgVwup7wQKxiGQWJiIkwmk03DXgzD4IMPPsDZs2fRoUMHxMTE2FxlOSoqCuvXr4ebmxvOnDkDLy+vsjYfQF7wptfrERgYWC3qFBFSGhqNBomJiWjQoIFF8mpJVbcKz2fPnkX37t2RnZ1d4mUcSPVX1Ou+JJ/v1PNDLOj1ehgMhiIrO+d3+PBhnD17FkKhECtWrCjR8hKzZs1C8+bNkZ2djblz5xa51ElJCIVC6HQ6Wu+LEBvweDx069YNw4cPR7du3ap04EOIvVDwQyzo9Xro9XqbZnoZjUZ2rPjTTz8tcXKhQCDAmjVrwOfzceTIERw8eLBUbbbGPPOrPIq0EUIIqd4o+CEWdDodANiU7Hzw4EE8ePAArq6umDBhQqmu9/rrr2P69OkA8qqdZmRklOo8rxKJRFCr1RYl5wkh1Z95QWwa8iJlQcEPsaDRaGzq9jYYDFi9ejWAvOS4suRPTZ48GS1atIBMJsOSJUtKfZ78uFwueDwe5HK53YbTCCGE1AwU/BAWwzBQqVQ25fscOXIEiYmJcHNzY9ejKS0HBwesWLECHA4Hv/76K+Li4sp0PjPztHeNRmOX8xFCCKkZKPghLPNiprYEP1u3bgUAjBo1yi5Vmtu0aYMPP/wQAPDll1+yw29lwefzYTQaado7IYQQCxT8EJZ5pldxyc63b99GfHw8+Hw+G7CUhtFoRFxcHA4cOIC4uDjMnj0bnp6eePDgAX744YdSnzc/83pf5tWeCSGEEFregrBsDRC2bdsGAOjVq1ep1/6JjY1FZGQkkpOT2W1+fn54//33ER0dje+//x6DBg0q89pCIpEIWVlZyM3NpQRJQgghAKjnh+SjVquLTXZWKBTYv38/AGDMmDGluk5sbCwmTJiAoKAgHDp0CPfv38ehQ4cQFBSErVu3olGjRlCr1QWqjpaWUChEdnZ2kQsGEkIIqT0o+CEA8hYzVKvVxeb7HD58GBqNBk2aNEH79u1LfB2j0YjIyEiEhoYiOjoawcHBcHR0RHBwMKKjoxEaGsquihwTE4ObN2+W6vHkJxaLoVKpqOghITVAt27d2PIYQN4aU2vWrKm09pRFUlISOBwOrl+/XtlNqXUo+CEAbK/svG/fPgDAoEGDSrVqenx8PJKTkzFlypQC1aC5XC4mT56MtLQ0dn2wRYsWlXmqOpfLBZfLpWnvhNRA//zzT6nrjJHai4IfAsC2ZOfk5GRcunQJHA4HAwYMKNV10tPTAQBBQUFW7zdvf++99yASiRAfH4/Y2NhSXSs/R0dHmvZOSD6LFi0qtK7WkiVLsGjRooptUCl5eXnZtA4hIflR8EMA2Jbs/OuvvwIAOnXqhHr16pXqOt7e3gCAhIQEq/ebtwcFBbGrOC9duhRarbZU1zOjae+EWOLxeFiwYEGBAGjJkiVYsGBBua3xtW/fPrRs2RJisRgeHh4IDQ1lh6QjIiLQv39/LF68GF5eXnB2dsbEiROLLH3x6rAXh8PBjz/+iAEDBkAikaBJkyY4dOiQxTG3bt1Cr169IJVK4ePjgw8//BAvX74s9Brbtm2Dq6srjh07hmbNmkEqlaJnz5548eIFu4/JZEJkZCTq168PoVCINm3a4OjRoxbn+fvvv9G2bVuIRCK0a9cO165dK3Ct4tpW1PNHbEfBDwFQfGVnhmHYROdBgwaV+johISHw8/PDunXrCiQgm0wmrF+/Hv7+/ggJCcGnn34KHx8fPHnyhK0rVBbmae/2qCFESHU3f/58REZGWgRA5sAnMjIS8+fPt/s1X7x4geHDh2Ps2LG4e/cuzp49i4EDB1oMR586dYq9b/fu3di/fz8WL15coussXrwYQ4YMwc2bNxEeHo6RI0ciKysLACCTydCjRw+0bdsWly9fxtGjR5GWloYhQ4YUeU6VSoWVK1fip59+wrlz5/D06VPMnj2bvf/777/HqlWrsHLlSty8eRNhYWF4//338eDBAwCAUqlEnz590Lx5c1y5cgWLFi2yON6Wttny/BEbMbWMXC5nADByubyym1JlmEwm5uHDh8zDhw+ZlJQUq7eTJ08yABiBQMAkJCQUup8tty1btjAcDod59913mYMHDzL37t1jDh48yLz77rsMh8NhtmzZwu67evVqBgDj6urK3L17t0zXTUlJYf79918mKyursp9yQspMrVYzd+7cYdRqdZnOExkZyf5tA2AiIyPt1MKCrly5wgBgkpKSrN4/evRoxt3dncnNzWW3bdy4kZFKpYzRaGQYhmG6du3KTJs2jb0/ICCA+e6779ifATBfffUV+7NSqWQAMEeOHGEYhmGWLFnCvPfeexbXTU5OZgAw9+7ds9qurVu3MgCYhw8fsts2bNjA+Pj4sD/XrVuXWbp0qcVxb775JvPpp58yDMMwP/zwA+Ph4WHx+9q4cSMDgLl27ZpNbSvu+asNinrdl+TznXp+iE35Pua8m65du8LJyalM1wsPD8fmzZuRkJCAfv36oWnTpujXrx/u3buHzZs3Izw8nN130KBBeO211yCTybBp06YyXRf4v7o/tNo7IXnmz58PgUAAnU4HgUBQLj0+Zq1bt8Y777yDli1bYvDgwdiyZQuys7ML7JM/h6djx45QKpUWNcGK06pVK/b/jo6OcHZ2ZvMNb9y4gTNnzkAqlbI3c67ho0ePCj2nRCJBo0aN2J99fX3ZcyoUCjx//hydO3e2OKZz5864e/cuAODu3bto1aoVRCKRxWPLr7i22fL8EdtQ8ENsCn6OHDkCABaBSVmEh4fjwoULiImJwYYNGxATE4Pz588XOD+Px8Nnn30GANi8eXOZV30Xi8XQaDQ0Rk7I/7dkyRI28NHpdHZbXNgaHo+HEydO4MiRI2jevDnWrVuHpk2bIjEx0a7XeXXWKofDYYfZlUol+vbti+vXr1vcHjx4gLfffrtE52TsPNxUXNsq6vmrDSj4IdDr9WAYptCp648fP8bdu3fB5/Px3nvv2e26PB4PnTp1Qv/+/dGpU6dCc4569uyJtm3bQq1W4/vvvy/TNTkcDvh8PmQyGY2Tk1ovf46PVqstkANUHjgcDjp37ozFixfj2rVrEAgE+O2339j7b9y4AbVazf586dIlSKVS+Pn52eX6b7zxBm7fvo3AwEA0btzY4ubo6Fiqczo7O6Nu3bq4cOGCxfYLFy6gefPmAIBmzZrh5s2bFjNOL126VOK2Fff8EdtQ8EOKTXY2D3l17ty5UpaI4HA4mDt3LgDg559/xtOnT8t0PvNq7yqVyh7NI6RaspbcbC0J2p7i4+OxbNkyXL58GU+fPsX+/fuRkZGBZs2asfvodDqMGzcOd+7cQWxsLBYuXIjJkycXqAtWWpMmTUJWVhaGDx+Of/75B48ePcKxY8cwZsyYMg2Hz5kzBytWrMDevXtx7949fPHFF7h+/TqmTZsGABgxYgQ4HA7Gjx/PPraVK1eWqG22PH/ENpUe/GzYsAGBgYEQiUQICQnB33//XeT+a9asQdOmTSEWi+Hn54cZM2ZQ7ZYyYBgGKpWqyOKG5uma9hryKo3OnTuja9eu0Ov1Bd4wSsoc6CkUCns0jZBqyVxt/dUcH3MAVB55cc7Ozjh37hzCw8Px2muv4auvvsKqVavQq1cvdp933nkHTZo0wdtvv42hQ4fi/ffft2vNIXMPjdFoxHvvvYeWLVti+vTpcHV1LVOANXXqVMycOROzZs1Cy5YtcfToURw6dAhNmjQBAEilUvz+++/4999/0bZtW8ybN6/AEj7Ftc2W54/YhsNUYt//3r17MWrUKGzatAkhISFYs2YNYmJicO/ePbYeTH67du3C2LFjER0djU6dOuH+/fuIiIjAsGHDsHr1apuuqVAo4OLiArlcDmdnZ3s/pGpHr9cjMTERAoEAAoGgwP0vX75EmzZtwDAMrl69Ch8fn0poZZ6bN2+iV69e4HA4OHHiRJm+7eh0Omi1WgQEBFgkIBJSXWg0GiQmJqJBgwY15jUcEREBmUyGAwcOVHZTSBVV1Ou+JJ/vldrzs3r1aowfPx5jxoxB8+bNsWnTJkgkEkRHR1vdPy4uDp07d8aIESMQGBiI9957D8OHDy+2t4gUTq/XQ6/XF9rzc/r0aTAMg5YtW1Zq4APkzeDo06cPGIbBihUrEBcXhwMHDiAuLq7E31IFAgH0ej0VPSSEkFqo0oIfnU6HK1euIDQ09P8aw+UiNDQUFy9etHpMp06dcOXKFTbYefz4MWJjY4scjtFqtVAoFBY38n+KS3Y+efIkAFj8nirTnDlzwOVyceLECQwePBiTJk3C4MGD0blz5xIvgyGRSCCTyajoISGE1DKVFvy8fPkSRqOxQG+Cj48PUlNTrR4zYsQIREZG4q233oKDgwMaNWqEbt264csvvyz0OlFRUXBxcWFv9poxUFNotdpCx7n1ej3OnTsHIG8cviq4f/8+O2W1bdu2uH//Pg4dOoSgoCBMmDChRAGQSCSCVqul3h9Cqoht27bRkBepEJWe8FwSZ8+exbJly/Df//4XV69exf79+3H48OEiZyXMnTsXcrmcvZWkUFZtUFSy899//42cnBx4enqidevWFdyygswJmm+99RZ4PB6uXbuG27dvIzg4GNHR0QgNDcWSJUtKNAQmEokgk8lgMBjKseWEEEKqkkoLfjw9PcHj8ZCWlmaxPS0tDXXq1LF6zPz58/Hhhx/io48+QsuWLTFgwAAsW7YMUVFRBdaJMhMKhXB2dra4kTwGgwEajQaXL1+2mjtz+vRpAED37t3tNs20LOLj45GcnIzPPvsMw4YNAwB25heXy8XkyZPx9OlTxMfH23xOsVgMtVpNRQ9JtUX1qkhtYq/Xe6V9ogkEAgQHB+PUqVPsNpPJhFOnThUo+W2mUqkKfAibpy3TG0DJxcTEIDQ0FCNGjLCaO2Me8urevXtlNpNlLiUfFBSEadOmwcHBARcuXEBcXBy7Pf9+tuBwOBAIBMjOzi40gCakKjK/91HOGqlNzPXZiirPYovC1zOoADNnzsTo0aPRrl07tG/fHmvWrEFubi7GjBkDABg1ahTq1auHqKgoAEDfvn2xevVqtG3bFiEhIXj48CHmz5+Pvn37FlmkjxS0f/9+jBw5El27dsXGjRsRFBSEhIQErFu3DhMmTMDKlStx584dAMBbb71Vya3NYy5/kJCQgODgYIwYMQLbt2/HqlWr0LFjRyQkJFjsZyuJRAK5XI7c3Nwyr1tGSEXh8/mQSCTIyMiAg4NDleidJaS8mGvSpaenw9XVtcyf+ZVa5wcA1q9fj2+//Rapqalo06YN1q5di5CQEABAt27dEBgYiG3btgHIG6ZZunQpfvrpJ6SkpMDLywt9+/bF0qVLba48THV+8nJnGjdujKZNm+Lbb7+Fh4cHe5/JZMLYsWNx9epVZGZm4vXXX8fx48crsbX/x2g0onPnzggKCkJ0dDRSU1Px1ltvQavVYteuXdi6dSvu3buH8+fPl/gPIycnhy2cWdjMN0KqGp1Oh8TEROq1JLWGq6sr6tSpY/V9uiSf75Ue/FQ0Cn7yEse7d++OAwcOoFmzZpBKpRb3X758Gf369QMAfPLJJ/jqq68qo5lWxcbGYsKECQgNDcXkyZOxb98+/PTTT3BxcYFCoSiwKrytjEYjcnJy4O/vX+D5IKQqM5lMNPRFagUHB4civ9iW5PO9Uoe9SOV48eIFAKBBgwZWV3Jv2rQp+/8uXbpUWLtsER4ejs2bNyMyMpIN0ABALpdj+vTppV6Cg8fjgcPhQCaTwdHRkXp/SLXB5XJrTIVnQioKDRLXQr6+vgCAO3fuWE0aM8/ycnBwQPv27Su0bbYIDw/HhQsXEBMTgw0bNqBPnz4AgD///LNMie+Ojo5QKBS04CkhhNRwFPzUQl26dEFAQAB++OGHAj0cJpMJa9euBQC8+eabEIvFldHEYvF4PHTq1An9+/fH119/DbFYjGvXrlnMHiwpcy+YXC6n2YOEEFKDUfBTC/F4PERGRuLPP//E2LFjcfnyZSiVSly+fBljx45lZ029/fbbldxS23h5ebEzBFeuXFnm3h+5XA61Wm2v5hFCCKliKPippUJDQ7FmzRokJCSgX79+aNq0Kfr164eEhAQ2f6C6BD9AXmK2o6Mj/v333zLNTnNwcADDMJDL5XZsHSGEkKqEZnvVQgzD4PHjxwDyik3Gx8cjPT0d3t7e4PP5GDBgAFxdXXHz5s1qVT9p+fLlWLduHZo1a4bjx4+Xuu6JXq+HWq1GQEBAlR32I4QQYqkkn+/U81ML6fV6GAwG8Pl8i9yZTp064e+//wYAdOrUqVoFPgDw8ccfQyqV4u7duyVe4T0/BwcHmEwmyGQy+zWOEEJIlUHBTy2k1+thNBqtTnO/dOkSAKBDhw4V3awyc3Nzw/jx4wEAq1atKtECp6+i3B9CCKm5KPiphfR6PRiGKTDTy2AwsD0/1TH4AYDx48fD2dkZ9+/fx++//17q81DvDyGE1FwU/NRCOp3Oaj7MrVu3kJubCxcXF3aR0OrGxcUFEyZMAJDX+2MwGEp9LnPvD9X9IYSQmoWCn1pIpVJZLW5oHvJq3759tcv3ye+jjz6Cm5sbHj9+jP3795f6PPl7f2rZvABCCKnRKPipZQwGA/R6vdXg5uLFiwDsO+RlNBqh1Wqh1WrLlINTEk5OTpg0aRIA4LvvvivTukdSqZR6fwghpIah4KeW0ev10Ov1BXp+jEYjm+/TsWPHMl9Ho9EgKysLSqUSBoMBBoMBSqUS2dnZ0Gg0ZT5/cSIiIuDl5YWnT59i7969pT4Pn88HwzDU+0MIITUIBT+1jF6vh8lkKpDzc/fuXSgUCkilUrz++uulPr+5QKDBYECdOnUQGBiIBg0aoEGDBggMDISPjw8MBgNkMhlMJlNZH06hxGIxpkyZAgD4/vvvyxRwSaVSyGQy5Obm2qt5hBBCKhEFP7WMTqezumK5ecirffv2VqfA28JkMiE7OxsSiQR+fn7w9PSEWCwGn88Hn8+HWCyGp6cn/P394eTkBJlMVq5DYSNHjoSvry9evHiBnTt3lvo85npI2dnZ5RqwEUIIqRgU/NQyarW6yGTnkJCQUp3X3OPj4uKCunXrFlkZWSQSwdfXFx4eHpDL5eUWAIlEIkyfPh0AsG7dujLV7JFKpVAoFFAqlXZqHSGEkMpCwU8tYjQaodPpCvTsmEymMhc3lMvlcHR0RJ06dawGV6/i8/nw9vaGu7s75HJ5ufWoDB06FP7+/sjIyMC2bdtKfR4ulwsHBwdkZWVVWOI2IYSQ8kHBTy1inun1anDy8OFDyGQyiEQitGrVqsTnNfcm+fj42BT4mPF4PHh7e7NrsZRHQrGDgwNmzJgBANiwYQNycnJKfS5HR0colUooFAp7NY8QQkglsHvwQ8sBVF3mNb1eneZ++fJlAEDbtm0hEAhKdE6j0Qi1Wg0vL69SLQLK5/Ph4+MDsVhcpsCkKAMHDkSjRo2QnZ2NH3/8sdTn4XA4EIlEyMrKgl6vt2MLCSGEVCS7BT9arRarVq1CgwYN7HVKYmeFfWD/888/AIB27dqV+JwKhQJubm5wcXEpdbsEAgHq1KkDLpdbLsEzn8/HrFmzAACbN28u05IVEokEarWalr0ghJBqrETBj1arxdy5c9GuXTt06tQJBw4cAABs3boVDRo0wJo1a9ghBlL1qNVqqzO5zD0/JQ1+NBoNHBwc4OHhYXUGWUlIJBJ4e3tDq9WWS69K37590axZMygUCmzatKlM53J0dERWVhb1chJCSDVVouBnwYIF2LhxIwIDA5GUlITBgwdjwoQJ+O6777B69WokJSXh888/L6+2kjJgGMbqTK+srCw8fvwYABAcHFyi86lUKri7u0MkEtmljS4uLvDw8EBOTo7dE6C5XC5mz54NAPjxxx+Rnp5e6nMJhUIYjUZkZ2dT4UNCCKmGShT8xMTEYMeOHdi3bx+OHz8Oo9EIg8GAGzduYNiwYdV6PaiaTq/Xw2g0Fuj5Mff6NGnSBG5ubjafT6PRQCwWl2m461UcDgeenp5wcXEpl6TisLAwtG3bFmq1Gt99912ZzmUufEhT3wkhpPopUfDz7NkztnegRYsWEAqFmDFjRpmHPEj5Myc7Fxb8lGTIy9yL5O7uXqLZXbbg8Xjw8vKCg4OD3dfT4nA4mDdvHgBg165dbI9XSRiNRsTFxeGPP/7A5cuXkZ6eTlPfCSGkmilR8GM0Gi1mA/H5fEilUrs3itifXq8HwzAFAtXSBD/mXh8nJye7ttFMJBLB29sbOp3O7vk/HTt2xDvvvAODwYBvvvmmRMfGxsaic+fOGDx4MCZNmoTRo0eja9eu+Pnnn+3aRkIIIeWrROsYMAyDiIgICIVCAHkfghMnToSjo6PFfvv377dfC4ldaLXaAut56XQ63LhxA0DJgh+1Wo169eqVehkMWzg7O0OtVuPly5dwc3Oza+/i3Llzcfr0afz++++YOHEi2rRpU+wxsbGxmDBhAkJDQ7FhwwYEBQUhISEB33//PcaMGQOhUIhhw4bZrY2EEELKD4cpQcbmmDFjbNpv69atpW5QeVMoFGxRPWdn58puToVJSkqCwWCARCJht129ehV9+/aFq6srbt26ZVOAodFoYDKZEBAQYPchr1fp9Xo8e/YMOp3O7r1M06dPR0xMDDp16oRffvmlyMduNBrRuXNnBAUFITo62iKINJlM+PDDD/H48WM8evSoXANCQgghhSvJ53uJ3qmrclBDCldYZef8Q1629qyo1Wp4e3uXe+AD5FVn9vLyQnJyMrRaLdvjaA9z5szBoUOHEBcXhz///BPdunUrdN/4+HgkJydjw4YNBXrPuFwupk2bhgEDBuDo0aPo06eP3dpICCGkfJS5yOGzZ8/w7NmzUh+/YcMGBAYGQiQSISQkBH///XeR+8tkMkyaNAm+vr4QCoV47bXXEBsbW+rr1wZ6vR56vb7QZOc333zT5vPw+fxyy/WxRiqVwtPTE7m5uXadVl6vXj1EREQAAJYuXVrk1HrztPigoCCr9zdv3hxA3jIhOp3Obm0khBBSPkoV/JhMJkRGRsLFxQUBAQEICAiAq6srlixZUqL6LHv37sXMmTOxcOFCXL16Fa1bt0ZYWFihNVh0Oh3effddJCUlYd++fbh37x62bNmCevXqleZh1BrmZOf8vRYMw5Q42VmlUkEqldqtro+t3N3d4eTkZPfp75MnT4azszPu3LlTZJ6at7c3ACAhIcHq/ebtLi4uyMzMpNo/hBBSxZUq+Jk3bx7Wr1+P5cuX49q1a7h27RqWLVuGdevWYf78+TafZ/Xq1Rg/fjzGjBmD5s2bY9OmTZBIJIiOjra6f3R0NLKysnDgwAF07twZgYGB6Nq1K1q3bl2ah1FraLXaAsNaz549Q1paGvh8vk3Pn8lkgslksmtdH1vxeDx4enqCw+FAq9Xa7bzu7u6YPHkyACAqKqrQqfUhISHw8/PDunXrCgT3JpMJ69evh7+/P7p164bs7OxyW6OMEEKIfZQq+Nm+fTt+/PFHfPLJJ2jVqhVatWqFTz/9FFu2bMG2bdtsOodOp8OVK1cQGhr6f43hchEaGoqLFy9aPebQoUPo2LEjJk2aBB8fH7Ro0QLLli0rss6KVquFQqGwuNU2KpWq0Hyfli1b2rQgqUajgUgkskiYrkiOjo7lMvw1btw4+Pv7IzU1FRs3brS6D4/Hw4IFC3Dy5EmMHTsWly9fhlKpxOXLlzF27FicPHkS8+fPh1AohIODAzIyMmj4ixBCqrBSBT9ZWVlW8x+CgoKQlZVl0zlevnwJo9EIHx8fi+0+Pj5ITU21eszjx4+xb98+GI1GxMbGYv78+Vi1ahW+/vrrQq8TFRUFFxcX9ubn52dT+2oKc7JzYfk+ti5podFo4OrqWiDhtyK5ubnZffhLJBLhq6++AgD897//RUpKitX9wsPDsXnzZiQkJKBfv35o2rQp+vXrh3v37mHz5s0IDw8HkBekaTQaGv4ihJAqrFSfZK1bt8b69esLbF+/fn25DkGZTCZ4e3tj8+bNCA4OxtChQzFv3rwiF6qcO3cu5HI5e0tOTi639lVF5mTnV3t+SrKSu/n4V+s5VTR7DX+ZqzQfOHAAcXFxCAsLQ4cOHaDRaLB8+fJCjwsPD8eFCxcQExODDRs2ICYmBufPn2cDHzMnJydkZWVBLpeXuo2EEELKT6mKknzzzTfo3bs3Tp48iY4dOwIALl68iOTkZJtnXnl6eoLH4yEtLc1ie1paGurUqWP1GF9fXzg4OFisIdasWTOkpqZCp9NZVJ82EwqFdp0iXd2Y1/TK32OjVCpx9+5dALbN9FKr1XB0dKzwRGdrHB0d4eHhgbS0NAgEghIXP4yNjUVkZKRFEOzn54fRo0cjPj4e+/fvR0RERKE9YjweD506dSryGnw+HyKRCBkZGRAKhTYNKxJCCKk4per56dq1K+7fv48BAwZAJpNBJpNh4MCBuHfvHrp06WLTOQQCAYKDg3Hq1Cl2m8lkwqlTp9iA6lWdO3fGw4cPLZJO79+/D19fX6uBD8nLrXo1QLh27RpMJhPq169faKBpxjAMDAZDpSQ6F8bNzQ1SqbTEi4qaqzQHBQXh0KFDuH//Pg4dOoSgoCAsXbqUDWoWLlxY5lXlxWIx9Ho9MjIyaO0vQgipYkpdjrZu3bpYunRpmS4+c+ZMjB49Gu3atUP79u2xZs0a5ObmspWkR40ahXr16iEqKgoA8Mknn2D9+vWYNm0apkyZggcPHmDZsmWYOnVqmdpRkxWV7GzLkJe5uGBV6r3g8/nw9PTE06dPrQ7pWWM0GhEZGYnQ0FCLKs3BwcGIjo7G2LFjcefOHUgkEly7dg0HDhzAwIEDy9ROZ2dnyGQyCIVCeHt70wLAhBBSRZQ6+MnOzsb//vc/dvikefPmGDNmDNzd3W0+x9ChQ5GRkYEFCxYgNTUVbdq0wdGjR9kk6KdPn1oM1/j5+eHYsWOYMWMGWrVqhXr16mHatGn4/PPPS/swajSDwQCdTldo8GPLkJdGoymX1dvLSiqVwsPDAxkZGTat/VVclebJkyejX79+GD58OHbv3o2lS5ciLCysTHlOXC4XTk5OyMzMhEgkqlK9Z4QQUpuVatjr3LlzCAwMxNq1a5GdnY3s7GysXbsWDRo0wLlz50p0rsmTJ+PJkyfQarWIj49HSEgIe9/Zs2cLTJ3v2LEjLl26BI1Gg0ePHuHLL7+0yAEi/0ev1xcIfkwmE65cuQKg+J4fhmHAMAykUmm5trO03N3dIRaLC63Pk19xVZrN29u3b89Off/uu+/K3EYHBwcIhUKkpaXZ1E5CCCHlr1TBz6RJkzB06FAkJiZi//792L9/Px4/foxhw4Zh0qRJ9m4jKSW9Xg+TyWTR03H//n3k5ORAIpEUGgiYmWv7VKUhr/zMa3/pdDoYDIYi97W1SnP9+vXZ0glbtmwpdP+SEIvFMBqNSEtLo/o/hBBSBZQq+Hn48CFmzZpl0ePC4/Ewc+ZMPHz40G6NI2Wj1WoLDPGYp7i/8cYbxa5ArtVq4eTkVKV71pycnODm5lZsVWVbqzSHhITgnXfeQa9evWAwGPDll1/apV6Ps7MzcnNzkZ6eTgnQhBBSyUoV/Lzxxhtsrk9+d+/epaUmqpDc3NxSJzubA4TKquhsKw6HAw8PDwgEAqjV6kL3s7VKsznQW7x4McRiMeLj4xETE2OXdrq6ukImk+Hly5dUAJEQQipRqRKep06dimnTpuHhw4fo0KEDAODSpUvYsGEDli9fjps3b7L7tmrVyj4tJSVSWHFDW4OfqjjLqzBCoRCenp5ISUmBUCgstAq1uUpzZGQk+vXrx2739/e3qNIM5K36PnPmTCxduhRff/013n33Xbi5uZWpnVwuF87Oznj58iV4PB48PDxoBhghhFQCDlOKr6DFLXHA4XDAMAw4HE6V6+JXKBRwcXGBXC6Hs7NzZTen3KhUKiQmJsLV1ZX9gM3IyECbNm3A4XBw+/btImcfyWQyeHp6srkyVZ3JZEJKSgpycnLg6upa5L5GoxHx8fFIT0+Ht7c3QkJCrA7t6XQ6hIWF4f79+xg5ciS++eYbu7RVq9VCrVbD19e3zAEVIYSQPCX5fC9Vz09iYmKpGkYqjjmxNn/PgnmWV9OmTYsMfMyzvKr6kFd+XC4XHh4eyM3NZXutCmNLlWYgrxBnVFQU/vOf/2Dnzp3o168fOnfuXOa2CoVCMAyDFy9esMNhhBBCKk6pgp+AgAB7t4PYmUajKdBDZ+tiplV9lldhJBIJPD09kZqaWqqlL6zp0KEDPvzwQ/z000+YM2cOTp48aZegUCQSWQRAVAOIEEIqTokSnj/99FOLJQV2796N3Nxc9meZTFZgkUdS8RiGgUqlKrDkh63FDbVaLaRSaZWe5VUYV1dXODo6lnjpi6LMmzcPdevWxZMnT+w29AXkTYEXCAR4/vw5ZDKZ3c5LCCGkaCUKfn744QeLQm0ff/yxxcKkWq0Wx44ds1/rSKnodLoCyc5arZZNRC8q2ZlhGJhMpmo15JWfeekLo9EIvV5vl3M6OTlhxYoVAIAff/yRDSLtIX8AlJ2dTbPACCGkApQo+Hn1jZneqKsmc/CTv47Pv//+C61WCw8PDwQGBhZ5rFAorBIruJeWVCqFu7t7sbV/SqJHjx4YNGgQGIbB7NmzodFo7HZusVgMoVCIFy9eIDMzs8yLqhJCCClaqer8kKrN2kru+Ye8isqF0Wq1kEgkVW4tr5LgcDglWvrCVosWLYKXlxcePHiAb7/91m7nBfICIIlEgtTUVKSlpRVbsZoQQkjpUfBTA5VlJXeDwVBl1/IqCYFAAE9PT2g0GruVW3Bzc2Nzfn744QecP3/eLuc1EwgEcHZ2RmZmJp4/f27X3iVCCCH/p8SzvRYsWMDmg+h0OixdupSdqUILN1Y+g8EArVZrEfwwDGNT8GPOE6rOQ175OTs7w83NDXK53G7Tyd977z2MHDkSO3fuxLRp03Dy5Em71urh8/lwdXWFQqGATqeDl5cXnJ2dqRgiIYTYUYmKHHbr1s2mN+EzZ86UqVHlqaYXOVSpVEhKSoKzszM71f3Jkyfo1KkTBAIB7t69W2hwo1QqIRKJ4OfnV2M+bDUaDZKTk8Hlcu02dV+lUiEsLAyPHz9Gnz59sGnTpnJ5vlQqFXQ6Hdzd3eHu7l5g9h4hhJD/U25FDs+ePVtgmzl2qikfltWdTqcDwzAWNX7Mi5m2aNGiyF4dc09DTfpdikQieHh44Pnz50UufVESEokE69evx/vvv48//vgDMTExGDJkiB1aW/A6AoEAL1++RG5uLjw9PeHk5GSXx0AIIbVZqd9F//e//7EfpiKRCC1atMCPP/5oz7aRUlCr1QXq8/z9998A8lY2L4zRaASPx6sxQ175ubq6wsXFxa6zv1q3bo1Zs2YBAL766is8fPjQbufOj8/nw93dHQzD4NmzZ0hOTkZOTg7NtCSEkDIoVfCzYMECTJs2DX379kVMTAxiYmLQt29fzJgxAwsWLLB3G4mNTCaT1WRnc89PUcUNzVPci1oWoroyL33B5XKh1Wrtdt5JkyahY8eOyM3Nxfjx4y0KftqbRCKBi4sL1Go1kpOT8ezZM+Tm5lIQRAghpVCqhU29vLywdu1aDB8+3GL77t27MWXKFLx8+dJuDbS3mpzzo9FokJSUBEdHR7b3JysrCy1btgQA3Lx5Ex4eHlaPlclk8PLygpeXV4W1t6K9fPkSqampcHNzs9vQXkZGBsLCwpCWlob+/ftj/fr15T5saDQaoVQqwTAM3Nzc4ObmViN77AghpCRK8vleqp4fvV5vddZQcHAw1SepRDqdDgaDwWLYyzzLq3HjxoUGPuaFTKvbWl4l5ebmBqlUatfhLy8vL2zatAk8Hg8HDhzA9u3b7XbuwvB4PLi4uEAqlSIrKwtPnz5FZmam3ab0E0JITVeq4OfDDz/Exo0bC2zfvHkzRo4cWeZGkdKxtpipecirffv2hR6n0+kgEAhqfO8Bj8dje7bsOfzVvn17fPXVVwDyCiFeuXLFbucuCp/Ph5ubG/h8Pl68eIHk5GS7rmlGCCE1ValWdQfyEp6PHz+ODh06AADi4+Px9OlTjBo1CjNnzmT3W716ddlbSYrFMAyUSmWB6dDmZOei8n20Wi2cnJwslsOoqRwdHeHh4YG0tDS7rfwOAOPHj8fly5dx+PBhfPTRR/jjjz9Qr149u5y7OCKRCEKhEEqlEsnJyXB3d4eHh0et+H0SQkhplOrd8datW3jjjTcAAI8ePQIAeHp6wtPTE7du3WL3q0lTpqs6nU4HnU5nsSCpRqNhFzMtquenplR1tpWbmxtyc3ORk5Njt7wvDoeD1atX4/Hjx7h79y5Gjx6NAwcOVNjzyuFw4OTkBJ1Oh4yMDKhUKnh5edWq3yshhNiqVMFPVS5iWFtptVoYDAaLb/s3b96ETqeDt7c3AgICrB5nPqYmzvIqDJ/Ph5eXF5KTk9khP3uQSqXYvn07evfujbt37+KTTz7B1q1bK7QHRiAQwM3Nje0F8vT0hLu7e4HyB4QQUptRtbQawlq+T/4hr8J64bRaLTtsUpuYh7/Ms6bspV69eti2bRtEIhFOnz6NxYsX2+3ctjL3AolEIqSlpdE6YYQQ8goKfmqA4vJ9ikt2lkqltXKI0jz7S6FQ2PW8bdq0wbp16wAA0dHR+O9//2vX89tKKBTC1dUVOTk5SE5Ohlwup7pAhBACCn5qBHO+T/7gx2QysdPcCwt+GIYBh8Op8VPcC2Me/uJwOHad/QUA4eHhmD9/PgBg6dKl+Pnnn+16fltxuVy4urqCw+EgJSUF6enpNCWeEFLrUfBTA2i1WhiNRovckjt37kAul8PR0RHNmzcv9DiBQFDrhrzyc3R0hKenJ3Jzc2Eymex67okTJ2Ly5MkAgC+++AIHDhyw6/lLQiKRwNHRERkZGXj+/Lndgz1CCKlOKPipAdRqdYFhq7i4OAB563kVlnCr0+kgFotr/ZRoNzc3uLi42H34C8gLekaNGgWGYTB16lQcPHjQ7tewlYODA1xdXaFQKPDs2TOqCUQIqbWqRPCzYcMGBAYGQiQSISQkhM1VKc6ePXvA4XDQv3//8m1gFWYymaBUKgv03ly4cAEA0Llz50KPrW1T3AtjLn7o4OAAtVpt13NzOBwsXboUgwYNgtFoxOTJk/Hrr7/a9RolweVy4ebmBqPRiGfPniErK4vygAghtU6lBz979+7FzJkzsXDhQly9ehWtW7dGWFgY0tPTizwuKSkJs2fPRpcuXSqopVWTVqstkO9jMBhw6dIlAIUHP7VxintRRCIRvLy8oNFo7L5EC5fLxerVqzF8+HCYTCZMmzYNu3btsus1SkoqlUIgEOD58+eUB0QIqXUqPfhZvXo1xo8fjzFjxqB58+bYtGkTJBIJoqOjCz3GaDRi5MiRWLx4MRo2bFiBra16zPk++eu43Lx5E0qlEq6uroXm+9TkVdxLy8XFBe7u7lAoFHbvDeHxePjmm2/YIbA5c+bgu+++q9ReF5FIBGdnZzYPSKfTVVpbCCGkIlVq8KPT6XDlyhWEhoay27hcLkJDQ3Hx4sVCj4uMjIS3tzfGjRtX7DW0Wi0UCoXFrSbJzc0tkLNjHvLq2LFjocXtzEta1MYp7oXhcDjw9PSEo6NjueTDcLlcLFu2jE2CXrlyJebMmQO9Xm/3a9mKz+ezeUApKSlQqVSV1hZCCKkolRr8vHz5EkajET4+PhbbfXx8kJqaavWY8+fP43//+x+2bNli0zWioqLg4uLC3vz8/Mrc7qrCYDBArVYXqO9TXL6Pubehpi9kWhoODg7w8vICwzDlUhiQw+Fg7ty5iIqKApfLxe7duzFixAi8fPnS7teylXk6vEajQUpKSo37gkAIIa+q9GGvksjJycGHH36ILVu2wNPT06Zj5s6dC7lczt6Sk5PLuZUVx5zvk3/oSqvVsiu5Fxb81JZV3EtLKpXCy8sLKpWq3HJhRo0ahf/973+QSCSIi4tDz549ce3atXK5li04HA5cXFwAACkpKcjMzKREaEJIjVWpwY+npyd4PB7S0tIstqelpaFOnToF9n/06BGSkpLQt29f8Pl88Pl87NixA4cOHQKfz2cXWc1PKBTC2dnZ4lZTqNVqtlCh2dWrV6HRaODl5YUmTZpYPc68AGptn+JeFDc3N7i5uZVrVeT33nsPhw8fRsOGDfHixQsMHDgQmzdvtnu9oZJwdHSEUChEamoq0tPTK7UthBBSXio1+BEIBAgODsapU6fYbSaTCadOnULHjh0L7B8UFIR///0X169fZ2/vv/8+unfvjuvXr9eoIa3iFLakRf4hr8LyefR6PRwdHcu9jdUZl8uFl5cXJBJJudbDee211xAbG4tevXpBp9Nh8eLFGDp0KFJSUsrtmsURiUSQSqXIyMhAamqq3We/EUJIZav0Ya+ZM2diy5Yt2L59O7sSdm5uLsaMGQMgb3hg7ty5APLelFu0aGFxc3V1hZOTE1q0aGG31bmrA51OB41GU2h9n06dOlk9zjwzjGZ5FU8gELD5aPau/5Ofk5MTtmzZguXLl0MsFiMuLg49evTAli1bKi3wcHBwgIuLC7KysqgiNCGkxqn04Gfo0KFYuXIlFixYgDZt2uD69es4evQo+6Hz9OlTvHjxopJbWfWY69HkH7qSy+W4cuUKABRa/4imuJeMo6MjvL29oVaryzUQ4XA4+PDDD3H8+HEEBwdDqVRi0aJF6NmzJ1utu6LxeDy4ubkhJyeHZoIRQmoUDlPLshoVCgVcXFwgl8urdf5PSkoKcnJyLB7DH3/8gY8//hiNGjXCuXPnrB6XnZ0NLy8veHt7V1RTqz2GYZCeno6MjAy4urqCyy3f7wwmkwm7d+/GsmXLIJPJAABvvfUW5syZg3bt2pXrta1hGAYKhQIODg7w8fGBk5NThbeBEEKKU5LP90rv+SElp9froVKpCvTenDlzBgDQvXt3q8cxDAOGYWrtKu6lxeFw4OHhwf5RlTcul4uRI0fir7/+wujRo+Hg4IDz58+jX79+CAsLQ1RUFC5cuFBhVZnNM8FMJhNSUlIgk8loJhghpFqj4Kca0mg07IrsZgzD4OzZswCAHj16WD1Or9fTFPdS4vP58Pb2hkgkqrA6OO7u7li2bBn++usvdhjz1q1bWL9+PYYMGYK2bdvi999/r5C2AHklABwcHGgqPCGk2qPgpxpSqVTgcrkWs7nu3r2L1NRUiMVihISEWD1Oq9VCLBbDwcGhoppaowiFQtSpUwdcLrfU+S9GoxFxcXE4cOAA4uLibOq9+ffff3H+/Hl06dIF77//Pttzl5mZiYkTJ2LWrFkVlo8jFoshkUjYqfC0JhghpDqi4KeaMRqNVldxNw95derUqdCeHb1eT6u4l5GjoyN8fHyg0+lKPAMqNjYWnTt3xuDBgzFp0iQMHjwYnTt3RmxsbKHHGI1GREZGIjQ0FLt27cLGjRvxzz//YM6cOXB3dwcA7NmzB+3bt0d0dHSFrM8lFArh5OSEjIwMpKWl0VR4Qki1Q8FPNWMe8ios+ClsyMtkMoHL5dKQlx24uLjAx8cHubm5Nq/LFRsbiwkTJiAoKAiHDh3C/fv3cejQIQQFBWHChAmFBkDx8fFITk7GlClT2ERrNzc3TJ8+HX///TcmTpwIIC+Rff78+ejevTsOHTpU7kNS+afCv3jxghZFJYRUKxT8VDMqlQoMw1jMOMrJyWGXtCgs2dkcMNEUd/twd3eHl5cXcnJyih36yd97Ex0djeDgYDg6OiI4OBjR0dEIDQ3FkiVLrJ4nPT0dQF6Bz1eJxWLMmDEDQF7JCC8vLyQlJeGTTz5Bnz592NdEeeHxeBaLopZnLSRCCLEnCn6qEZPJBIVCUaD35q+//oLBYEDDhg0REBBg9VitVgupVFru07RrCw6HAy8vL3h4eEAulxe5DIS13hszLpeLyZMn4+nTp4iPjy9wrLkkQUJCgtVzm7cPGjQIFy5cwKxZsyCRSHD9+nX0798fM2fORGZmZmkfZrG4XC6cnJxw7tw5bNq0CUeOHKE8IEJIlUefhNWIWq22WtXZvDxIYb0+QF7gJJFIyrV9tQ2Xy4W3tzdcXV0hk8kKDYCK6r3Jv928X34hISHw8/PDunXrCpzfZDJh/fr18Pf3R0hICBwdHTFz5kxcuHABw4YNAwDs3bsXb7/9Nnbs2FEuQUlsbCzeeustREREYObMmQgPD0fDhg2xf/9+u1+LEELshYKfasQ8oyd/74HBYMDx48cB5C2UaY15FXca8rI/Ho8HHx8ftgaQtVwbW3tvrBWe5PF4WLBgAU6ePImxY8fi8uXLUCqVuHz5MsaOHYuTJ09i/vz54PF4FtdbtWoVDhw4gObNm0Mmk2Hu3Ll4//33cefOHXs8bADW85hiYmLQsGFDDBo0CL/++qvdrkUIIfZEFZ6rCaPRiKSkJACwKFIYFxeHwYMHw9XVFTdu3LC6UrtSqYRIJIK/v39FNbfW0ev1eP78OXJycuDm5mZRhsBoNKJz584ICgpCdHS0RfBqMpkwduxY3Lt3D+fPn7cIYvKLjY1FZGQkkpOT2W3+/v6YP38+wsPDC22XwWDA9u3b8e233yInJwd8Ph9Tp07FlClTyrQWXlGPSa1W46OPPsLjx4/x4MGDWrXmHiGk8lCF5xqosCGvo0ePAsjr9bEW+AB5PT+0JEH5cnBwgK+vL5ycnJCdnW3RA1Sa3ptXhYeH48KFC4iJicGGDRsQExOD8+fPFxn4AHnFGceNG4c///wTPXv2hMFgwOrVqxEeHo6bN2+W+vEWlcckFosxZcoUPH36FAcPHrR5RhwhhFQUCn6qidzcXACWQ14Mw+DIkSMAgF69elk9zmg00hT3CiIQCNgA6NUcoPDwcGzevBkJCQno168fmjZtin79+uHevXvYvHlzsUEMkBdEderUCf3790enTp2KDJZe5ePjgx9//BH//e9/4e7ujrt376JPnz6IioqCRqMp8WMtLo+pRYsWAIDExERaFZ4QUuVQ8FMNGAwG5OTkFFiT6+bNm3j+/DkkEgnefvttq8fSKu4VSyAQoG7dunB2drYaAJWm98ZeOBwO+vXrh7Nnz+L999+H0WjE+vXrER4ejtu3b5foXLbmMTVo0AC5ubl49uwZG8ATQkhlo+CnGlCpVFaHvMyF8Xr06FFoz45Wq4WzszNNca9A5iEwNzc3yGQyiwrIZem9sRcPDw9s3LgRP/74I7y8vHDv3j307t0bGzdutHlGmK2z0Dp06ABXV1fo9XqkpKRUyMKwhBBSHPpErAbMiar5k2iB/8v3KWzIi1Zxrzx8Ph916tSBh4cHFApFlayA3KtXL5w6dQphYWHQ6/X4+uuvMXToUDx79qzYY0uax2QOwFNSUvDy5UtaFJUQUqlotlcVp9Fo8OTJE4hEIosFSR88eIBu3bpBIBDg5s2bVhOaNRoNTCYTAgMDC02GJuXLZDIhMzMT6enpEIlEVTIQZRgGe/bswYIFC6BSqeDk5IRly5ZhwIABBQLuV5V0FppWq4VKpYKHhwe8vLwqpeeLEFIzleTznT4Rqzjz+lGvBjeHDh0CALz11luFzuTSarVwdXWlwKcScblceHp6gs/ns4uAVrWZdxwOB8OHD0fHjh0xZcoUXL16FVOmTMGJEyewfPlyuLi4FHpseHg4wsLCEB8fj/T0dHh7eyMkJKTQoEYoFILH4+Hly5fQ6/Xw9vamfDRCSIWjYa8qzGg0QiaTFcjnYRiGraA7cODAIo93dHQs1zaS4nE4HLi5uaF+/frg8/nIzs4ucjmMyhIYGIjffvsNs2fPBo/Hw6FDh/Dee+/h8uXLRR5X0jwmPp/PrglGidCEkMpAwU8VZk50fjX4uXr1KpKSkiCRSBAWFmb1WHNVZ5riXnVIpVLUr1+fnQlWFad/8/l8zJgxAwcPHkRAQACePXuGgQMHYu3atXZdHoPL5cLNzY1NhJbJZJQHRAipMBT8VGEKhQJcLrfATC1zr0/Pnj0hkUhgNBoRFxeHAwcOIC4uDkajEVqtFmKxmKrrVjFCoRB169aFj48PNBoNcnJyquSHftu2bXH06FH0798fRqMRK1aswPDhw5GammrX6zg7O4PH4+H58+fIyMigRVEJIRWCEp6rKLVajSdPnkAsFlskOuv1erRt2xbZ2dnYuXMnVCpVgYRTPz8/zJw5E2PGjCkyX4NULqVSiYyMDOTm5sLJycni91xVMAyDX375BfPmzYNarYa7uzu+++47hIaG2vU6Op0OSqUSrq6u8Pb2pqCdEFJitLxFDaBUKmEwGAp8IJ49exbZ2dnw8vJCTk5OgYUlDx06hKZNm2LmzJnsVHhSNZmHwby8vKBSqaBQKKpcLhCHw8HQoUNx9OhRvP7668jKysLo0aOxcOFCuw7bCQQCuLq6QiaTITk5mfKACCHlinp+qiC9Xo+kpCTweLwCOTuffPIJDh06hLFjx+LEiRNWF5aUy+WYOnUqHj16hAcPHtB04mpAqVQiMzMTOTk5EIlEkEgkld2kArRaLZYuXYr//e9/APKWsPjvf/+LRo0a2e0aDMNAqVQCyKsi7erqWux0e0IIAajnp9pTKpXQarUFAh+FQoHjx48DAF577bVCF5Y0Go2YM2cOEhMT8ddff1VYu0npmXuB6tevDw6Hg6ysLKjV6spulgWhUIjIyEhs27YNbm5uuHXrFnr27IlffvnFbnlLHA4HTk5O4PP5SElJQWpqKi2MSgixOwp+qhiDwYCsrCyrs7R+++03aDQavPbaa5BKpQAKLiyp0+ng4OCAN954AwDw4sWL8m80sQsejwdXV1f4+/ujTp06YBiGDYKqUgftu+++ixMnTqBjx45QqVSYMWMGpk6dipycHLtdQywWw9nZGZmZmUhJSYFKpbLbuQkhhIKfKkapVEKtVheoBMwwDH7++WcAwMiRI+Hj4wOg4MKSWq0WEokEDx48AAD4+vpWQKuJPTk4OMDT0xP+/v7s7y87Oxs5OTkW64RVJl9fX+zduxefffYZeDwe9u/fj549e+L69et2uwafz4ebmxvUajWSk5OrbH0kQkj1Q8FPFWI0GpGdnQ2hUFggz+HGjRu4c+cOhEIh/vOf/xS6sKRer4dUKkVUVBQaNGiALl26VPTDIHYiEAjg4eGBgIAA+Pv7QyKRQKVSISsrC7m5uZU+LZzH42HatGn49ddfUa9ePSQlJaFfv35Yv3693drG4XDg4uJiMQxWFddJI4RULxT8VCFKpRK5ublWk1137twJAOjduzfc3NysLiyZmZmJ27dv48MPP8Qff/yBlStXUrJzDcDn8+Hs7Iz69esjMDAQ9erVg4ODA5RKJbKzsyt9WOzNN9/E8ePH0bt3bxgMBkRFRWHo0KF4/vy53a4hFovh4uKC7OxsJCcn23WIjRBS+1SJ4GfDhg0IDAyESCRCSEgI/v7770L33bJlC7p06QI3Nze4ubkhNDS0yP2rC6PRiKysLKu9PjKZDL/99huAvCEvs/DwcGzevBkJCQno168fWrVqhWHDhuHOnTvYt29fkUtfkOqHw+FAJBLBzc0NAQEBCAgIYIc/K3tYzNXVFT/88ANWrVoFiUSCixcv4t1338Xhw4ftdg0ejwc3NzcYjUYkJycjPT29ygwDEkKql0oPfvbu3YuZM2di4cKFuHr1Klq3bo2wsDCkp6db3f/s2bMYPnw4zpw5g4sXL8LPzw/vvfceUlJSKrjl9pWTk1Nor8+ePXugVqvRrFkzhISEWNwXHh6OCxcu4JdffsGKFStw+PBhPHjwgAKfGo7D4UAikcDT09NiWEypVEImk1XK0BCHw8GwYcNw7NgxtG7dGjKZDBMmTMDs2bPtWrdHKpVCIpEgPT2d1gYjhJRKpdf5CQkJwZtvvon169cDAEwmE/z8/DBlyhR88cUXxR5vNBrh5uaG9evXY9SoUcXuXxXr/Oj1ejx9+hQMwxQIfgwGAzp37oxnz55h5cqVGD58uNVzmKdFBwQE0CrutRTDMFCr1VAoFFAoFNDr9RCLxZWyvptOp8OqVauwYcMGMAyDBg0aYMOGDWjdurXdrsEwDDv85enpyQ4HE0Jqp2pT50en0+HKlSsWpfK5XC5CQ0Nx8eJFm86hUqmg1+vh7u5eXs0sd3K53OoMLwA4fvw4nj17Bnd3d/Tv37/Qc2g0GrY+CqmdzL1BderUQUBAALy9vdnh1IquGSQQCDB37lzs3bsXderUQWJiIt5//31s2LDBbjO2OBwOnJ2dIRQKkZqaSpWhCSE2q9Tg5+XLlzAajWzegpmPj4/NCyh+/vnnqFu3bqFrDWm1WvabsPlWlWg0GmRlZUEikVitZLt582YAwAcffGA1OALyer84HA5b+4cQoVAILy8vBAQEwNfX16JmUEXq3LkzTp48ifDwcBgMBixbtgxDhw616zC1UCiEm5sbNBoNnj59SrlAhJBiVXrOT1ksX74ce/bswW+//VZo135UVBRcXFzYm5+fXwW3snDmDyS9Xm+1/fHx8fjnn38gEAgQERFR6Hk0Gg0kEkmhwRGpvfJPl88fBGk0mgprg5ubGzZv3oyVK1dCLBYjLi4OPXr0wJ49e+xaGdrZ2RlisRjp6el48uQJFApFlSoOSQipOio1+PH09ASPx0NaWprF9rS0NNSpU6fIY1euXInly5fj+PHjaNWqVaH7zZ07F3K5nL3lX/28suXk5EAmk8HJycnq/evWrQMADB06tEDvWH5arZbWQCJFcnBwgIeHB1s92jwcZs/FSYvC4XAwfPhwHD9+HMHBwVAqlZg1axZGjRplcy+vLQQCgcWMsBcvXlRooEcIqR4qNfgRCAQIDg7GqVOn2G0mkwmnTp1Cx44dCz3um2++wZIlS3D06FG0a9euyGsIhUI4Oztb3KoCvV6PjIwM8Pl8q3k6//77L86cOQMul4tPPvmk0PNotVoIhcIquRAmqXoEAgFbPdrb2xs6nQ7Z2dkVtn5Ww4YN8dtvv+Grr76CQCDA6dOn8c4772D//v127QWSSqVwcnJCdnY2nj59ipcvX9JQGCGEVenDXjNnzsSWLVuwfft23L17F5988glyc3MxZswYAMCoUaMwd+5cdv8VK1Zg/vz5iI6ORmBgIFJTU5GamsquBF0d5M+/cHR0tLrPmjVrAAD9+/dHQEBAoedSq9VwcnKCQCAoj6aSGkooFMLb2xsBAQFwd3eHSqWCXC6vkKrRPB4Pn3zyCY4ePYpWrVpBJpNhypQpGD9+PF6+fGm365iXx+Dz+UhNTcXTp08hl8tpiQxCSOUHP0OHDsXKlSuxYMECtGnTBtevX8fRo0fZYZ6nT59aLM65ceNG6HQ6DBo0CL6+vuxt5cqVlfUQSiwnJwcvX76Ek5OT1aEq83PA5XIxderUQs9j/qAqbNiMkOKIRCLUqVMH/v7+kEqlUCgUUCqVFZIr07RpUxw6dAizZ88Gn8/HkSNH0L17dxw8eNCu1zcXhjQPhaWkpCA3N5fygQipxSq9zk9Fq+w6P1qtFs+ePYPJZGJ7fYxGI+Lj45Geng5vb2+sW7cO586dw+DBg9keIGuUSiWEQiH8/f0p34eUmclkglKpRGZmJnJzcyEWiyssif7WrVuYPn067t69CwDo0aMHoqKiUL9+fbtex2QyIScnBwzDwNXVFW5ubjRRgJAaoiSf7xT8VCCTyYTnz59DLpfDzc0NABAbG4vIyMgCidg8Hg/nz5+Hv7+/1XMxDIPs7Gz4+fnBxcWl3NtOag+j0QiZTIasrCzodDo4OjpWyLCqTqfDhg0bsHbtWuh0OojFYsyZMwfjxo2ze/0qg8EApVIJLpcLV1dXuLq6VkoxSEKI/VSbIoe1CcMwyMzMhEwmY4OV2NhYTJgwAUFBQTh06BDu3r2Lhg0bAsj7ALp161ah59NoNBCLxYXmDBFSWjwej50Z5unpCY1GUyH5QAKBADNmzMCJEyfQoUMHqNVqREZGok+fPvj333/tei0+n88GPJmZmXjy5AnS0tJoZhghtQT1/FQQmUyG58+fQyKRQCAQwGg0onPnzggKCkJ0dDS4XC52796N2bNnw8nJCW+88QYSExNx/vx5qyX7s7Ky4OvrCw8Pjwp7DKR2ys3NRVZWFuRyOTuzsLyHWU0mE/bu3YslS5ZALpeDy+Vi3LhxmDVrVrnkuOl0OuTm5oLP57M1wWg4jJDqhXp+qhilUom0tDQIhUJ2+CA+Ph7JycmYMmUKuFwucnJysGLFCgDAjBkzMHPmTDx9+hTx8fEFzqfVaiEQCKiiM6kQjo6OqFevHvz8/MDlcpGdnV3uPSRcLhfDhw/Hn3/+if79+8NkMmHLli3o0qUL9u7da/cZW+b6QEKhkO0Jev78OSVGE1JDUfBTzlQqFVJTU8HhcCy+SZpXrQ8KCgKQN4U/IyMDDRo0wJgxY9jt1la3V6lUcHFxgVAorIBHQEheMOLi4oKAgADUqVMHBoMBMpms3GvneHl5YcOGDdi5cycaNmyIjIwMzJw5E++//z6uX79u9+uZgyCxWAyZTIYnT54gOTkZCoWiQsoAEEIqBgU/5UitVuPFixcwGAwFemm8vb0BAAkJCbh69Sq2bdsGAFi2bBkEAgESEhIs9jPT6/Xg8XhVplgjqV34fD5bJNHV1RVKpZKdPVWeunXrhlOnTmH+/PlwdHTEtWvX0KdPH8yePdvqF4SycnBwgKurK5ycnKBWq5GcnIykpCRkZmZWWFVsQkj5oZyfcqJSqfDixQvodDqrs7HMOT+vvfYaUlJSkJCQgEGDBuH777+HyWTC2LFjce/evQI5PzKZDG5ubvD19S23thNiC4Zh2KnxSqWywqbGp6WlYdmyZdi3bx8AQCKR4OOPP8bEiRPLbSiYYRio1WpoNBoIBAI4OTnByckJEokEXC59hySkKqCp7kWoiOBHqVQiNTUVer2+yGnosbGxGD9+PIC8QoUnTpxAWloa1q9fj5MnT2Lz5s0IDw9n99fr9VCr1fD396flLEiVYTQaIZfLkZmZCZ1OB6lUCgcHh3K/7uXLl7F48WJcvXoVQN5agTNmzMDIkSPL9fo6nQ4qlQoMw0AkEsHV1RUSiQQikYjqbRFSiSj4KUJ5Bz9yuZxdqLG4WSl///03Bg4cWGDIwN/fH/Pnz7cIfADq9SFVm1arZesDAXmv//LuFWEYBrGxsYiKikJiYiIAoEGDBvjss8/Qp0+fcr2+yWSCRqOBVqsFj8eDRCJhV5anfDxCKh4FP0Uoz+CHYRgkJiZCr9cXG/hkZ2ejV69eSE5OxqBBgzB06FC2wnNISEiB6e3mXp+AgACagkuqtMqYGq/X67Fz505899137PpgQUFBmD59Onr37l3uQZjBYIBGo4FOp4ODgwMbCIlEIggEAuoRIqQCUPBThIoIfhiGKTJAMZlMiIiIwKlTpxAQEIBjx47ZFCx5eHigTp06dm0zIeXBvIzEy5cvoVar2WGh8qZUKrF582Zs2bIFCoUCQN4aYjNmzKiQIAjIC8Q0Gg30ej0cHBwgFovh5OQEkUgEoVBIOUKElBMKfopQFYKf7777DitXroRQKMShQ4fQokWLIs+r1Wqh1+vh7+9PJfhJtWKeEp+VlQW9Xl9h+UByuRz/+9//CgRBn376Kd5///0KWa4DyAuEtFotdDodeDwehEIhGwiJRKIKeS4IqS0o+ClCZQc/v//+OyZOnAgAWLlyJYYPH17sebOysuDj4wMvLy+7tpeQiqLVapGVlQWZTAYAkEqlViuX25tcLkd0dDQ2b97MBkF16tTBRx99hJEjR1ZoyQij0cgGQiaTCQKBACKRCFKpFEKhEEKh0O5rmBFSm1DwU4TKDH4uX76MoUOHQqPRYNy4cYiMjCz2nCqVChwOB/7+/vQtkVRrDMNApVIhKysLCoUCDg4OcHR0rJB8GLlcjp9++gnR0dFIS0sDkBeAjRgxAuPGjbP76vHFYRgGer0eOp0Oer0eHA4HDg4OEAqFkEqlEAgEEAgEcHBwoHwhQmxEwU8RKiv4+ffffzFkyBAoFAqEhoYiOjq62G++JpMJMpkM9evXh6urq13bSkhlMZlMUCqVyMrKglKphEgkglgsrpAPea1WiwMHDmDTpk24f/8+gLzq1e+88w5GjRqFbt26VUpODsMwbCCk1+sB5BWUNAeI5qVxHBwcwOfzKSAixAoKfopQGcHP7du3MWTIEMhkMrRv3x4///yzTauxy+VySKVS1KtXj5IkSY1jNBqRk5ODzMxMqNXqCiuSCOT9rZ45cwabNm3ChQsX2O3+/v744IMPMGzYsEpfNNgcCOn1ehiNRrZ3iM/nQyKRQCgUwsHBgd1WEcOIhFRlFPwUoaKDn1u3bmHEiBHIzMxE27ZtsXv3bptWpdZqtdBqtVTQkNR4er0eCoUCWVlZ0Gq1bE9HRXn48CF27NiBffv2QS6XA8hb4ys0NBSDBw9Gt27dKixBuijmoTKDwQC9Xs8u7srj8dheIvPUej6fb3GjniJSG1DwU4SKDH7OnDmDjz/+GLm5uWjVqhX27NlTZMXn/OfJzs6mJGdSq+h0OsjlcmRnZ0On01V4EKRWq3Ho0CHs2LHDYtFUd3d39O/fH4MGDUKrVq2qXCBhNBrZ3iGDwcAGRVwuFzweDzwejw2MHBwc2G3moInL5VLPMqkRKPgpQkUFP7/99hu++OILdg2vLVu22BT4mNsoEolQv359mv1Bah1zpWjzqvGOjo6l6nkxGo2Ij48vsnhoYW7fvo1ff/0Vv/32m8XCqQ0bNkTv3r3Rp08fvP7661UuEMrPZDLBYDDAaDRa3ACAw+GwAZA5SDInWb+63RwcmW9V+TGT2o2CnyKUd/Bz584dLFq0iF10cdCgQfj2229tfvM2V4n18/OzKS+IkJpKo9FALpezQZA5z8UWsbGxiIyMRHJyMrvNz88PCxYsKLBsTFEMBgP++usv7Nu3D0ePHoVGo2HvCwgIQHh4OMLDw9GmTZtq13uSPyAymUwwmUzs/80BDofDYYMeHo8HDodjdUjt1eCosH8pcCLliYKfIpRn8HPr1i0MHDgQDx48AIfDwaxZszB9+nSb/+DNC0T6+vpWerIlIVVF/iBIr9cXOxwWGxuLCRMmIDQ0FFOmTEFQUBASEhKwbt06qwsG2yonJwenTp3C4cOHcfr0aYtAyMvLC927d0ePHj3QtWvXCq0fVJ4YhrEIisw/57+Z98v/PmctEAJg0ZOUv4cpf3BU3A1AkdvM/8//L6kdKPgpQnkFP+np6WjUqBGUSiW8vb2xfv16dO7cuUTnyMrKYhcurW7fIgkpbxqNBgqFAjKZDDqdzursMPMwc1BQEKKjoy3+jkwmE8aOHYt79+7h/PnzZZodpVKpcPr0aRw+fBinTp1Cbm4uex+Px0P79u3RtWtXdOrUCa1atapVNboYhmGDpPz/5r9Z25b/eKBg4FJUAJT/5/z7A2BfA/n/zb/vq/dbu+/V9hT2f1t+Lsm2st5Xln3L8zwcDqdcJhFQ8FOE8uz5mT9/Pv7880+sXLmyxEXTzEXf/Pz8qsTMEkKqKq1Wi5ycHMhkMmg0Gos6QXFxcRg8eDAOHTqE4ODgAsdevnwZ/fr1Q0xMDDp16mSX9uh0Ovz99984ffo0Tp8+jQcPHljc7+joiPbt26Njx47o1KkTWrZsSbl8pfBqoGQteHo1kHp1W2H/2nKf+QO/sP+X5ufCttlyn5mtgYg9AhZ7cXBwQL169ey+XBMFP0Uoz+DHYDAgKSkJHA6nRPVK1Go1DAYD6tevT3k+hNhIr9ezxRI1Gg34fD5OnDiBKVOm4P79+1b/lpRKJZo2bYoNGzagf//+5dKup0+f4vTp07hw4QLi4uLYJT3MHB0d0aZNG7Rt2xbBwcFo27ZtlZjVWZYEcVLxbP3oLutHvL1DBJPJBJVKhQYNGlRq8ENfP+zIPH5dkheLVquFRqNBvXr1KPAhpAQcHBzg5uYGZ2dn5ObmQiaTQSqVAsirqN6hQ4cCxyQkJAAAvL29y61d/v7+iIiIQEREBEwmE+7evYuLFy8iLi4Oly5dglwux4ULFyyKK9avXx9vvPEGWrVqhebNm6NZs2bl2sZX2StBnFSciurxsbeq0h7q+bETo9GIc+fO4caNG/Dy8sLbb79d7LcmvV6PnJwc1KlTBx4eHlXmRUFIdcQwDHJyctCiRQs0btwYa9asgUQigUgkApfLtWvOT2kZjUYkJCTg2rVr7O3+/ftWvzB5enqygVDz5s0RFBSERo0a2b0KdnkliBNijclkQk5OTqX3/FDwYwf79+/HrFmzkJSUxG4r7luTOfDx9vaGl5cXBT6E2Mn+/fsxaNAghIWFYdy4cfD398fDhw8RHR2N06dPV7kP85ycHFy/fh3Xrl3D7du3cffuXTx+/LjQHuS6deuiYcOGaNSokcW/devWLXFidUUliNdUNFRYchT8VBJ7Bz/mN9o+ffpg7ty5kEqluHfvHjZv3lzotyZz4OPl5QUvLy+a2UWInVn7QlK/fn3MmjUL4eHhEIvFVfpDSq1WIyEhAXfv3sWdO3dw584d3L9/H9nZ2YUew+VyUadOHdSvX9/i5ufnh3r16sHX17fAUjmVkSBeU9BQYelQ8FNJ7Bn8GI1GNG7cGC1btsSBAwfA4XDYCs9CodDqtyadTofc3Fx4enpS4ENIOTIajfjrr7/w4sUL+Pr6on379tDpdJDJZFCr1TAajRCLxRAKhdXm7zArKwuPHz/Go0eP8PjxY/aWmJgIrVZb7PFSqRReXl7w8fGBt7c3FAoFzp49i+XLl8Pf3x8eHh5wc3ODu7s7RCIRcnNzyz1BvDqiocLSo+Annw0bNuDbb79FamoqWrdujXXr1qF9+/aF7h8TE4P58+cjKSkJTZo0wYoVK2x+odkz+Dl79iy6d++OixcvokOHDgXW9nr1W5NarYZWq4W3tzfl+BBSSRiGgUajQW5uLuRyOTQaDTgcDoRCYbUKhPIzmUx4+fIlkpOT8ezZM6SkpBT4v0qlKtE5hUIhHB0dkZWVhebNmyMwMBBubm5wc3ODq6srpFIpnJyc4OjoCCcnJ/Zn8/9raskOGiosm6oS/FT6bK+9e/di5syZ2LRpE0JCQrBmzRqEhYXh3r17Vmc7xMXFYfjw4YiKikKfPn2wa9cu9O/fH1evXkWLFi0qtO0vXrwAgEKvGxQUBCCvAGJOTg4YhoGvry9cXV0p8CGkkphLUYjFYri7u0OtViM3Nxc5OTlQKBRgGAYikahaBUJcLhfe3t7w9va2OnzFMAyUSiXS09MtbqmpqdixYwdEIhG8vLyQlZWF7OxsGAwGaLVatjfJPPRWEgKBgA2IzInnYrG40H+tbRMKhXBwcICDgwMEAkGB/5vXI8u/rbxXsY+Pj0dycjI2bNhQ4PXB5XIxefJk9OvXD/Hx8TRUWIVVes9PSEgI3nzzTaxfvx5AXlTo5+eHKVOm4Isvviiw/9ChQ5Gbm4s//viD3dahQwe0adMGmzZtKvZ6ldHzEx0djbfffhs+Pj7sVFxCSNViNBqh0WigVqshl8uh1WrBMAwcHBzYD+GaKP8QzuTJk9G0aVNcu3YNGzZswIULFzBt2jQ0adIE2dnZ7E0ul0OpVLK3nJwcNoAsaQ9TecgfKL26kv2rK9rn/39h28xlTPh8Pp4/f45//vkH//nPf9iei/xLeRiNRvz000/o3r07GjdubHV9s/zroZn//+p5Xj2msPNYW9Yj//9t3ZZfWc5T3LmBvJy2kSNHwsfHp5DfYOlUm2EvnU4HiUSCffv2WYwnjx49GjKZDAcPHixwjL+/P2bOnInp06ez2xYuXIgDBw7gxo0bBfbP/+0FyHty/Pz8KiTnZ/To0bh37x7i4uJQp06dGtsNTEhNYzKZoNFooNFokJOTA41GA4PBAC6XC4FAUK16hWxhLXnX398f8+fPL3HuisFgQG5urkVgpFKp2MCyuH/z/1+n00Gv10Ov10Or1bL/f3U7qX4uX75stZeyLKrNsNfLly9hNBoLRH8+Pj5sMbJXpaamWt0/NTXV6v5RUVFYvHjx/2vv3oOiKv8/gL/XXRZFhCVRri5YoUYGmgSD5PgtmSidBkUbmtGR6g9Hg1FTpywTHRzFsbHxkoNZM+BURumEpqXlqGyX8QZq4iUviWEpkJWAXJfd5/eHnfPbhQVBV85Zzvs1s7N7znn27IcP8PDhnOc8xz0Bt6HX67F27VpMmzYNkydPxuLFi+Hr64vz589j8+bNsFgsKCgoQHh4eK/qKIl6uz59+sDHxwc+Pj4ICAhAS0sLmpubUV9fj4aGBtTW1sJutzudhvHk3/GJEyciJSXFLZdtGwwG+Pv7w9/f/wFE2p4QAjabzakoavtst9vR2trqdCd7x0dra6t8A1fpdUfrrFYrNm7ciEGDBjn90y7d6HXXrl34+++/MWPGDOh0uk7vcya9R3ot7aej93S0HykPjs/3ss7V8r3ut6P9S9+vtlce9jTFx/w8aG+//TYWLFggL0tHftwlLS0NO3bswMKFC51uZGo2m7Ft2za8/PLLbvssIup5joOh/fz8YLPZ0NzcLF+52djYKI8V0uv1cjHkaYNd9Xq9R45R0el0MBgMMBgMbp8AsiMRERGYNWsWfvnlF2RlZclXe33wwQe4evUqr/bqhOOAZyUpWvwEBgZCr9ejqqrKaX1VVRWCg4Ndvic4OLhb7aVO60FKS0tDamoqfvzxR1y8eBFhYWFISUnhzQuJeiG9Xi8fFTKZTE7FUENDgzyA2mazAbgz/sRgMMDLy8ujjw7R/5s4cSK2bNmCnJwcpKamyuvNZjMLHw+higHP8fHx2LhxI4A7VaHZbEZWVlaHA54bGhqwe/dued3YsWMRExPT4wOeiYjastvtaGlpkR/19fVOp190Op18VZI0GJc8E2d47j5e6v6fBQsWICMjA3FxcYiPj8e6detQX1+PV199FQAwc+ZMhIWFITc3FwAwb948jB8/HmvXrsWkSZNQWFiIkpISbNmyRckvg4gIwJ3xQn379pU79sDAQHmsiDQOpaGhAVarVT5CJJ0yk64skp45JYa6eeqpQlJB8ZOeno6//voL2dnZqKysxKhRo7Bv3z55UHNFRYXToeKxY8di27ZtePfdd/HOO+8gKioKO3fu7PE5foiIusrVmBRpIK3VakVraytaWlrQ2NiI1tZW+Vni6tJsFkZE907x0149jae9iEjNpKJIujrJccJBaVk6WiSR5q6R5o5xfE2kJjztRURE7UiFjKsLNTq6VFsaX2S1WuVl6TJq4M7lxY4T67WdYM/xQaQFLH6IiDyEVBh1RJoDRiqKpNdSIeR4ms1xHhtpuzR/jHRKTXrdlRmG27bjaTlSMxY/RES9hE6nu2uBJHGcZM9x8ry2y0IIp0LJsajqaBI+x8n3HD9PirFtzNJzV1539F5Xz3db19k+qXdj8UNEpEHdKZRccSxyXBU+nT0c399R8eR42q7tc9t9OMbk+Hy3dZ29lpYdi6G2y3fLT0dtOyoEu7rdVduutu/qvtyxv44+Qw3TAbD4ISKiblPq1FZ3budwtyKoozau2t3vuq5s68r27rbrqf10hzTXlZJY/BARkcfo6BQYUXdwaD8RERFpCosfIiIi0hQWP0RERKQpLH6IiIhIU1j8EBERkaaw+CEiIiJNYfFzn5YvX44VK1a43LZixQosX768ZwP6j1rjAtQdmxoxX0TkydTYh7H4uU96vR7Z2dntvrErVqxAdna2YjNZqjUuQN2xqRHzRUSeTJV9mNCYmpoaAUDU1NS4bZ85OTkCgMjJyXG5rBS1xuUqFjXFpkbMFxF5sp7ow7rz910nhAJzWyuopqYGJpMJ165dg5+fn9v2u2bNGqxcuRJGoxEtLS1YsmQJ3nzzTbftv7fFBag7NjVivojIkz3oPqy2thZDhgzBrVu34O/v32lbzRU/f/zxB4YMGaJ0GERERPQAXLt2DeHh4Z220VzxY7fbcf36dQwYMMCt94aRKlqJWv4rV/PRArXmTK2Yr+6T/hN095He3or56j7mrOsedB8mhEBdXR1CQ0PRp89dhjS77WSbhknnLpcsWeL0rPR4DDWPE1FrztSK+bo3D2KMX2/GfHUfc9Y1auvDWPzcJ8eCwvGXQOlCo6PPVzqutjGoKWdqxXzdO/5h6h7mq/uYs7tTYx9mcMehJi2z2WzIycnB0qVLUVtbK69funSpvF3puBwpHZf02WrMmVoxX0TkyVTZh/V4udWLNTU1iWXLlommpialQ/EYzFn3MF/dw3x1D/PVfcxZ96glX5ob8ExERETaxhmeiYiISFNY/BAREZGmsPghIiIiTWHxQ0RERJrC4seNNm3ahMjISPTt2xcJCQk4duyY0iGpwg8//IAXX3wRoaGh0Ol02Llzp9N2IQSys7MREhKCfv36ITk5GZcuXVImWBXIzc3FU089hQEDBmDw4MGYPHkyLly44NSmqakJmZmZGDhwIHx9fTF16lRUVVUpFLGy8vLyEBMTAz8/P/j5+SExMRF79+6VtzNXnVu9ejV0Oh3mz58vr2POnC1fvhw6nc7pMWLECHk789Xen3/+iRkzZmDgwIHo168fnnjiCZSUlMjble73Wfy4yRdffIEFCxZg2bJlOHHiBGJjY5GSkoLq6mqlQ1NcfX09YmNjsWnTJpfb16xZgw0bNmDz5s04evQo+vfvj5SUFDQ1NfVwpOpgsViQmZmJI0eOYP/+/bBarXjuuedQX18vt3njjTewe/dubN++HRaLBdevX0daWpqCUSsnPDwcq1evRmlpKUpKSvDss88iNTUVZ8+eBcBcdeb48eP48MMPERMT47SeOWvv8ccfx40bN+THTz/9JG9jvpz9+++/SEpKgpeXF/bu3Ytz585h7dq1CAgIkNso3u8reqF9LxIfHy8yMzPlZZvNJkJDQ0Vubq6CUakPAFFUVCQv2+12ERwcLN577z153a1bt4S3t7f4/PPPFYhQfaqrqwUAYbFYhBB38uPl5SW2b98utzl//rwAIA4fPqxUmKoSEBAgPv74Y+aqE3V1dSIqKkrs379fjB8/XsybN08IwZ8vV5YtWyZiY2NdbmO+2nvrrbfE008/3eF2NfT7PPLjBi0tLSgtLUVycrK8rk+fPkhOTsbhw4cVjEz9ysvLUVlZ6ZQ7f39/JCQkMHf/qampAQA89NBDAIDS0lJYrVannI0YMQJms1nzObPZbCgsLER9fT0SExOZq05kZmZi0qRJTrkB+PPVkUuXLiE0NBQPP/wwpk+fjoqKCgDMlytff/014uLi8NJLL2Hw4MEYPXo0PvroI3m7Gvp9Fj9ucPPmTdhsNgQFBTmtDwoKQmVlpUJReQYpP8yda3a7HfPnz0dSUhJGjhwJ4E7OjEYjTCaTU1st56ysrAy+vr7w9vbG7NmzUVRUhOjoaOaqA4WFhThx4gRyc3PbbWPO2ktISEBBQQH27duHvLw8lJeXY9y4cairq2O+XLhy5Qry8vIQFRWF7777DnPmzMHcuXOxdetWAOro93lvLyIVy8zMxJkzZ5zGF1B7w4cPx6lTp1BTU4MdO3YgIyMDFotF6bBU6dq1a5g3bx7279+Pvn37Kh2OR3jhhRfk1zExMUhISEBERAS+/PJL9OvXT8HI1MlutyMuLg6rVq0CAIwePRpnzpzB5s2bkZGRoXB0d/DIjxsEBgZCr9e3G91fVVWF4OBghaLyDFJ+mLv2srKysGfPHhw6dAjh4eHy+uDgYLS0tODWrVtO7bWcM6PRiEcffRRjxoxBbm4uYmNjsX79eubKhdLSUlRXV+PJJ5+EwWCAwWCAxWLBhg0bYDAYEBQUxJzdhclkwrBhw3D58mX+jLkQEhKC6Ohop3WPPfaYfKpQDf0+ix83MBqNGDNmDA4cOCCvs9vtOHDgABITExWMTP2GDh2K4OBgp9zV1tbi6NGjms2dEAJZWVkoKirCwYMHMXToUKftY8aMgZeXl1POLly4gIqKCs3mrC273Y7m5mbmyoUJEyagrKwMp06dkh9xcXGYPn26/Jo569zt27fx22+/ISQkhD9jLiQlJbWbnuPixYuIiIgAoJJ+v0eGVWtAYWGh8Pb2FgUFBeLcuXNi1qxZwmQyicrKSqVDU1xdXZ04efKkOHnypAAg3n//fXHy5Enx+++/CyGEWL16tTCZTGLXrl3i9OnTIjU1VQwdOlQ0NjYqHLky5syZI/z9/UVxcbG4ceOG/GhoaJDbzJ49W5jNZnHw4EFRUlIiEhMTRWJiooJRK2fx4sXCYrGI8vJycfr0abF48WKh0+nE999/L4RgrrrC8WovIZizthYuXCiKi4tFeXm5+Pnnn0VycrIIDAwU1dXVQgjmq61jx44Jg8EgVq5cKS5duiQ+++wz4ePjIz799FO5jdL9PosfN9q4caMwm83CaDSK+Ph4ceTIEaVDUoVDhw4JAO0eGRkZQog7lz0uXbpUBAUFCW9vbzFhwgRx4cIFZYNWkKtcARD5+flym8bGRvH666+LgIAA4ePjI6ZMmSJu3LihXNAKeu2110RERIQwGo1i0KBBYsKECXLhIwRz1RVtix/mzFl6eroICQkRRqNRhIWFifT0dHH58mV5O/PV3u7du8XIkSOFt7e3GDFihNiyZYvTdqX7fZ0QQvTMMSYiIiIi5XHMDxEREWkKix8iIiLSFBY/REREpCksfoiIiEhTWPwQERGRprD4ISIiIk1h8UNERESawuKHiDxScXExdDpdu3sqERHdDSc5JCKP8L///Q+jRo3CunXrAAAtLS34559/EBQUBJ1Op2xwRORRDEoHQER0L4xGo2bvmk1E94envYhI9V555RVYLBasX78eOp0OOp0OBQUFTqe9CgoKYDKZsGfPHgwfPhw+Pj6YNm0aGhoasHXrVkRGRiIgIABz586FzWaT993c3IxFixYhLCwM/fv3R0JCAoqLi5X5QomoR/DIDxGp3vr163Hx4kWMHDkSOTk5AICzZ8+2a9fQ0IANGzagsLAQdXV1SEtLw5QpU2AymfDtt9/iypUrmDp1KpKSkpCeng4AyMrKwrlz51BYWIjQ0FAUFRXh+eefR1lZGaKionr06ySinsHih4hUz9/fH0ajET4+PvKprl9//bVdO6vViry8PDzyyCMAgGnTpuGTTz5BVVUVfH19ER0djWeeeQaHDh1Ceno6KioqkJ+fj4qKCoSGhgIAFi1ahH379iE/Px+rVq3quS+SiHoMix8i6jV8fHzkwgcAgoKCEBkZCV9fX6d11dXVAICysjLYbDYMGzbMaT/Nzc0YOHBgzwRNRD2OxQ8R9RpeXl5OyzqdzuU6u90OALh9+zb0ej1KS0uh1+ud2jkWTETUu7D4ISKPYDQanQYqu8Po0aNhs9lQXV2NcePGuXXfRKRevNqLiDxCZGQkjh49iqtXr+LmzZvy0Zv7MWzYMEyfPh0zZ87EV199hfLychw7dgy5ubn45ptv3BA1EakRix8i8giLFi2CXq9HdHQ0Bg0ahIqKCrfsNz8/HzNnzsTChQsxfPhwTJ48GcePH4fZbHbL/olIfTjDMxEREWkKj/wQERGRprD4ISIiIk1h8UNERESawuKHiIiINIXFDxEREWkKix8iIiLSFBY/REREpCksfoiIiEhTWPwQERGRprD4ISIiIk1h8UNERESawuKHiIiINOX/AOxO4Toq8/C0AAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Plot ML fit for pEpoR (single regularization strength with noise model)\n", + "fig, ax = plt.subplots(figsize=(6.5, 3.5))\n", + "t, pEpoR = simulate_pEpoR(\n", + " problem=regproblems[chosen_regstrength],\n", + " result=regresults[chosen_regstrength],\n", + ")\n", + "sigma_pEpoR = 0.0274 + 0.1 * pEpoR\n", + "ax.fill_between(\n", + " t,\n", + " pEpoR - 2 * sigma_pEpoR,\n", + " pEpoR + 2 * sigma_pEpoR,\n", + " color=\"black\",\n", + " alpha=0.10,\n", + " interpolate=True,\n", + " label=\"2-sigma error bands\",\n", + ")\n", + "ax.plot(t, pEpoR, color=\"black\", label=\"MLE\")\n", + "ax.plot(\n", + " df_pEpoR[\"time\"],\n", + " df_pEpoR[\"measurement\"],\n", + " \"o\",\n", + " color=\"black\",\n", + " markerfacecolor=\"none\",\n", + " label=\"experimental data\",\n", + ")\n", + "ylim1 = ax.get_ylim()[0]\n", + "ax.plot(\n", + " nodes,\n", + " len(nodes) * [ylim1],\n", + " \"x\",\n", + " color=\"black\",\n", + " label=\"spline nodes\",\n", + " zorder=10,\n", + " clip_on=False,\n", + ")\n", + "ax.set_ylim(ylim1, ax.get_ylim()[1])\n", + "ax.set_xlabel(\"time\")\n", + "ax.set_ylabel(\"pEpoR\")\n", + "ax.set_title(f\"ML fit for regularization strength = {chosen_regstrength}\")\n", + "ax.legend();" + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "id": "809c240f-e3ca-45ec-906e-0e87d10b46dd", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Store results for later\n", + "all_results[\"5 nodes\"] = (\n", + " regproblems[chosen_regstrength],\n", + " regresults[chosen_regstrength],\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "6ae693f7-f3c1-43cd-8b0e-12b69b4ca6ef", + "metadata": {}, + "source": [ + "## Comparing the three approaches" + ] + }, + { + "cell_type": "code", + "execution_count": 72, + "id": "71b57527-51f6-479a-8d6d-895e9b2f4d34", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Plot ML fit for pEpoR\n", + "fig, ax = plt.subplots(figsize=(6.5, 3.5))\n", + "for label, (problem, result) in all_results.items():\n", + " t, pEpoR = simulate_pEpoR(problem=problem, result=result)\n", + " ax.plot(t, pEpoR, label=label)\n", + "ax.plot(\n", + " df_pEpoR[\"time\"],\n", + " df_pEpoR[\"measurement\"],\n", + " \"o\",\n", + " color=\"black\",\n", + " markerfacecolor=\"none\",\n", + " label=\"experimental data\",\n", + ")\n", + "ax.set_xlabel(\"time\")\n", + "ax.set_ylabel(\"pEpoR\")\n", + "ax.legend();" + ] + }, + { + "cell_type": "code", + "execution_count": 73, + "id": "214476c9-ecab-4201-a528-4507539b0b05", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Plot ML fit for pSTAT5\n", + "fig, ax = plt.subplots(figsize=(6.5, 3.5))\n", + "for label, (problem, result) in all_results.items():\n", + " t, pSTAT5 = simulate_pSTAT5(problem=problem, result=result)\n", + " ax.plot(t, pSTAT5, label=label)\n", + "ax.plot(\n", + " df_pSTAT5[\"time\"],\n", + " df_pSTAT5[\"measurement\"],\n", + " \"o\",\n", + " color=\"black\",\n", + " markerfacecolor=\"none\",\n", + " label=\"experimental data\",\n", + ")\n", + "ax.set_xlabel(\"time\")\n", + "ax.set_ylabel(\"pSTAT5\")\n", + "ax.legend();" + ] + }, + { + "cell_type": "code", + "execution_count": 74, + "id": "5776b49f-a3ba-401d-88a5-0a7674e4b14b", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Plot ML fit for tSTAT5\n", + "fig, ax = plt.subplots(figsize=(6.5, 3.5))\n", + "for label, (problem, result) in all_results.items():\n", + " t, tSTAT5 = simulate_tSTAT5(problem=problem, result=result)\n", + " ax.plot(t, tSTAT5, label=label)\n", + "ax.plot(\n", + " df_tSTAT5[\"time\"],\n", + " df_tSTAT5[\"measurement\"],\n", + " \"o\",\n", + " color=\"black\",\n", + " markerfacecolor=\"none\",\n", + " label=\"experimental data\",\n", + ")\n", + "ax.set_xlabel(\"time\")\n", + "ax.set_ylabel(\"tSTAT5\")\n", + "ax.legend();" + ] + }, + { + "cell_type": "code", + "execution_count": 75, + "id": "c2aeab6e-828c-4748-b3c8-2494ae89ef43", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "### 5 nodes, FD\n", + "k1 = -0.012344171128634264\n", + "k2 = -1.11975626735931\n", + "k3 = 5.999999816644789\n", + "k4 = 0.22576351403212522\n", + "scale_tSTAT5 = -0.020792663448966672\n", + "scale_pSTAT5 = 0.1422550065768319\n", + "sigma_pEpoR_abs = -1.562249437179612\n", + "sigma_pEpoR_rel = -1.0\n", + "pEpoR_t0 = -2.6870875267006804\n", + "pEpoR_t5 = -0.7797622417853871\n", + "pEpoR_t10 = -0.11820562755751975\n", + "pEpoR_t20 = -0.9974218537437654\n", + "pEpoR_t60 = -6.90775527898212\n", + "\n", + "### 15 nodes, FD\n", + "k1 = 0.1543170078851364\n", + "k2 = -1.0042579083153138\n", + "k3 = -0.17925294344845363\n", + "k4 = 0.31486258696137254\n", + "scale_tSTAT5 = -0.03364700359730668\n", + "scale_pSTAT5 = 0.11013784140762342\n", + "sigma_pEpoR_abs = -1.562249437179612\n", + "sigma_pEpoR_rel = -1.0\n", + "pEpoR_t0 = -2.40456191981547\n", + "pEpoR_t2_dot_5 = -1.6438670641678346\n", + "pEpoR_t5_dot_0 = -0.80437214623219\n", + "pEpoR_t7_dot_5 = -0.1144993579909219\n", + "pEpoR_t10_dot_0 = -0.09849380649928209\n", + "pEpoR_t12_dot_5 = -0.30861764847405077\n", + "pEpoR_t15_dot_0 = -0.535565172217061\n", + "pEpoR_t17_dot_5 = -0.8808659628360864\n", + "pEpoR_t20 = -1.1184724117332843\n", + "pEpoR_t25 = -1.3245209689075161\n", + "pEpoR_t30 = -2.3651756746835053\n", + "pEpoR_t35 = -3.4734027477458524\n", + "pEpoR_t40 = -4.578132101040909\n", + "pEpoR_t50 = -5.7968417139258435\n", + "pEpoR_t60 = -6.268875988124801\n", + "regularization_strength = 1.8750612633917\n", + "\n", + "### 5 nodes\n", + "k1 = 0.2486924371230916\n", + "k2 = -0.9010429810987043\n", + "k3 = -0.3408591074551208\n", + "k4 = 0.3594353532480489\n", + "scale_tSTAT5 = -0.03395751814386045\n", + "scale_pSTAT5 = 0.1008121903144357\n", + "sigma_pEpoR_abs = -1.562249437179612\n", + "sigma_pEpoR_rel = -1.0\n", + "pEpoR_t0 = -2.8601663957890175\n", + "pEpoR_t5 = -0.7275787612811422\n", + "pEpoR_t10 = -0.08172482568007049\n", + "pEpoR_t20 = -1.02532663950965\n", + "pEpoR_t60 = -6.907755278982137\n", + "derivative_pEpoR_t0 = 0.026587630472163528\n", + "derivative_pEpoR_t5 = 0.17154606724507934\n", + "derivative_pEpoR_t10 = -0.05503215878900286\n", + "derivative_pEpoR_t20 = -0.016352876798592663\n", + "regularization_strength = 2.2430380486862944\n" + ] + } + ], + "source": [ + "# Compare parameter values\n", + "for label, (problem, result) in all_results.items():\n", + " print(f\"\\n### {label}\")\n", + " x = result.optimize_result.x[0]\n", + " if len(x) == len(problem.x_free_indices):\n", + " names = problem.x_names[problem.x_free_indices]\n", + " else:\n", + " names = problem.x_names\n", + " for name, value in zip(names, x):\n", + " print(f\"{name} = {value}\")" + ] + }, + { + "cell_type": "markdown", + "id": "2ced065a-4b15-4403-91c6-6a46dc0b3e66", + "metadata": {}, + "source": [ + "## Bibliography\n", + "Schelker, M. et al. (2012). “Comprehensive estimation of input signals and dynamics in biochemical reaction networks”. In: Bioinformatics 28.18, pp. i529–i534. doi: [10.1093/bioinformatics/bts393](https://doi.org/10.1093/bioinformatics/bts393).\n", + "\n", + "Swameye, I. et al. (2003). “Identification of nucleocytoplasmic cycling as a remote sensor in cellular signaling by databased modeling”. In: Proceedings of the National Academy of Sciences 100.3, pp. 1028–1033. doi: [10.1073/pnas.0237333100](https://doi.org/10.1073/pnas.0237333100).\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/deps/AMICI/python/examples/example_splines_swameye/Swameye_PNAS2003/swameye2003_conditions.tsv b/deps/AMICI/python/examples/example_splines_swameye/Swameye_PNAS2003/swameye2003_conditions.tsv new file mode 100644 index 000000000..97ed38778 --- /dev/null +++ b/deps/AMICI/python/examples/example_splines_swameye/Swameye_PNAS2003/swameye2003_conditions.tsv @@ -0,0 +1,2 @@ +conditionId conditionName +condition1 diff --git a/deps/AMICI/python/examples/example_splines_swameye/Swameye_PNAS2003/swameye2003_measurements.tsv b/deps/AMICI/python/examples/example_splines_swameye/Swameye_PNAS2003/swameye2003_measurements.tsv new file mode 100644 index 000000000..8813d0aae --- /dev/null +++ b/deps/AMICI/python/examples/example_splines_swameye/Swameye_PNAS2003/swameye2003_measurements.tsv @@ -0,0 +1,47 @@ +observableId simulationConditionId measurement time noiseParameters observableTransformation +tSTAT5_au condition1 1.0000 0 0.084 lin +tSTAT5_au condition1 0.9275 2 0.046 lin +tSTAT5_au condition1 0.7923 4 0.038 lin +tSTAT5_au condition1 0.7778 6 0.032 lin +tSTAT5_au condition1 0.7053 8 0.033 lin +tSTAT5_au condition1 0.6522 10 0.037 lin +tSTAT5_au condition1 0.5894 12 0.039 lin +tSTAT5_au condition1 0.5894 14 0.040 lin +tSTAT5_au condition1 0.6377 16 0.030 lin +tSTAT5_au condition1 0.6425 18 0.028 lin +tSTAT5_au condition1 0.6908 20 0.030 lin +tSTAT5_au condition1 0.6908 25 0.031 lin +tSTAT5_au condition1 0.7585 30 0.032 lin +tSTAT5_au condition1 0.8068 40 0.040 lin +tSTAT5_au condition1 0.9275 50 0.046 lin +tSTAT5_au condition1 0.9710 60 0.082 lin +pSTAT5_au condition1 0.3315 2 0.050 lin +pSTAT5_au condition1 0.8645 4 0.066 lin +pSTAT5_au condition1 0.9635 6 0.070 lin +pSTAT5_au condition1 0.9279 8 0.065 lin +pSTAT5_au condition1 0.8162 10 0.051 lin +pSTAT5_au condition1 0.7553 12 0.053 lin +pSTAT5_au condition1 0.7680 14 0.051 lin +pSTAT5_au condition1 0.8416 16 0.040 lin +pSTAT5_au condition1 0.7680 18 0.040 lin +pSTAT5_au condition1 0.8010 20 0.048 lin +pSTAT5_au condition1 0.7832 25 0.052 lin +pSTAT5_au condition1 0.8086 30 0.054 lin +pSTAT5_au condition1 0.4888 40 0.055 lin +pSTAT5_au condition1 0.2782 50 0.044 lin +pSTAT5_au condition1 0.2553 60 0.071 lin +pEpoR_au condition1 0.01713 0 lin +pEpoR_au condition1 0.145 2 lin +pEpoR_au condition1 0.2442 4 lin +pEpoR_au condition1 0.7659 6 lin +pEpoR_au condition1 1 8 lin +pEpoR_au condition1 0.8605 10 lin +pEpoR_au condition1 0.7829 12 lin +pEpoR_au condition1 0.5705 14 lin +pEpoR_au condition1 0.6217 16 lin +pEpoR_au condition1 0.331 18 lin +pEpoR_au condition1 0.3388 20 lin +pEpoR_au condition1 0.3116 25 lin +pEpoR_au condition1 0.05062 30 lin +pEpoR_au condition1 0.02504 40 lin +pEpoR_au condition1 0.01163 50 lin diff --git a/deps/AMICI/python/examples/example_splines_swameye/Swameye_PNAS2003/swameye2003_model.xml b/deps/AMICI/python/examples/example_splines_swameye/Swameye_PNAS2003/swameye2003_model.xml new file mode 100644 index 000000000..1e8045a74 --- /dev/null +++ b/deps/AMICI/python/examples/example_splines_swameye/Swameye_PNAS2003/swameye2003_model.xml @@ -0,0 +1,282 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + cyt + k1 + STAT5 + pEpoR + + + + + + + + + + + + + + + + cyt + k2 + + + pSTAT5 + 2 + + + + + + + + + + + + + + + + + cyt + k3 + pSTAT5_pSTAT5 + + + + + + + + + + + + + + + + nuc + k4 + npSTAT5_npSTAT5 + + + + + + + + + + + + + + + + nuc + k4 + nSTAT5_1 + + + + + + + + + + + + + + + + nuc + k4 + nSTAT5_2 + + + + + + + + + + + + + + + + nuc + k4 + nSTAT5_3 + + + + + + + + + + + + + + + + nuc + k4 + nSTAT5_4 + + + + + + + + + + + + + + + + nuc + k4 + nSTAT5_5 + + + + + + + + + + + + + + + + nuc + k4 + nSTAT5_6 + + + + + + + + + + + + + + + + nuc + k4 + nSTAT5_7 + + + + + + + + + + + + + + + + nuc + k4 + nSTAT5_8 + + + + + + + + + + + + + + + + nuc + k4 + nSTAT5_9 + + + + + + + diff --git a/deps/AMICI/python/examples/example_splines_swameye/Swameye_PNAS2003/swameye2003_observables.tsv b/deps/AMICI/python/examples/example_splines_swameye/Swameye_PNAS2003/swameye2003_observables.tsv new file mode 100644 index 000000000..63d12243d --- /dev/null +++ b/deps/AMICI/python/examples/example_splines_swameye/Swameye_PNAS2003/swameye2003_observables.tsv @@ -0,0 +1,4 @@ +observableId observableFormula observableTransformation noiseFormula noiseDistribution +tSTAT5_au scale_tSTAT5 * (STAT5 + pSTAT5 + 2 * pSTAT5_pSTAT5) lin noiseParameter1_tSTAT5_au normal +pSTAT5_au scale_pSTAT5 * (pSTAT5 + 2 * pSTAT5_pSTAT5) lin noiseParameter1_pSTAT5_au normal +pEpoR_au pEpoR lin sigma_pEpoR_abs + sigma_pEpoR_rel * pEpoR normal diff --git a/deps/AMICI/python/examples/example_splines_swameye/Swameye_PNAS2003/swameye2003_parameters.tsv b/deps/AMICI/python/examples/example_splines_swameye/Swameye_PNAS2003/swameye2003_parameters.tsv new file mode 100644 index 000000000..e3858d011 --- /dev/null +++ b/deps/AMICI/python/examples/example_splines_swameye/Swameye_PNAS2003/swameye2003_parameters.tsv @@ -0,0 +1,9 @@ +parameterId parameterScale lowerBound upperBound nominalValue estimate +k1 log10 0.001 100000 1.95 1 +k2 log10 0.001 100000 0.11 1 +k3 log10 0.001 1000000 98400 1 +k4 log10 0.001 100000 1.49 1 +scale_tSTAT5 log10 0.01 100 0.95 1 +scale_pSTAT5 log10 0.01 100 1.25 1 +sigma_pEpoR_abs log10 0.001 1 0.0274 0 +sigma_pEpoR_rel log10 0.001 1 0.10 0 diff --git a/deps/AMICI/python/examples/example_steadystate/ExampleSteadystate.ipynb b/deps/AMICI/python/examples/example_steadystate/ExampleSteadystate.ipynb index d1acfe687..b57ed522a 100644 --- a/deps/AMICI/python/examples/example_steadystate/ExampleSteadystate.ipynb +++ b/deps/AMICI/python/examples/example_steadystate/ExampleSteadystate.ipynb @@ -4,9 +4,14 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# AMICI Python example \"steadystate\"\n", + "# SBML import, observation model, sensitivity analysis, data export and visualization\n", "\n", - "This is an example using the [model_steadystate_scaled.sbml] model to demonstrate and test SBML import and AMICI Python interface." + "This is an example using the [model_steadystate_scaled.sbml] model to demonstrate:\n", + "\n", + "* SBML import\n", + "* specifying the observation model\n", + "* performing sensitivity analysis\n", + "* exporting and visualizing simulation results" ] }, { @@ -16,17 +21,14 @@ "outputs": [], "source": [ "# SBML model we want to import\n", - "sbml_file = 'model_steadystate_scaled.xml'\n", + "sbml_file = \"model_steadystate_scaled_without_observables.xml\"\n", "# Name of the model that will also be the name of the python module\n", - "model_name = 'model_steadystate_scaled'\n", + "model_name = \"model_steadystate_scaled\"\n", "# Directory to which the generated model code is written\n", "model_output_dir = model_name\n", "\n", "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" ] @@ -67,18 +69,41 @@ "sbml_model = sbml_doc.getModel()\n", "dir(sbml_doc)\n", "\n", - "print('Species: ', [s.getId() for s in sbml_model.getListOfSpecies()])\n", + "print(\"Species: \", [s.getId() for s in sbml_model.getListOfSpecies()])\n", "\n", - "print('\\nReactions:')\n", + "print(\"\\nReactions:\")\n", "for reaction in sbml_model.getListOfReactions():\n", - " reactants = ' + '.join(['%s %s'%(int(r.getStoichiometry()) if r.getStoichiometry() > 1 else '', r.getSpecies()) for r in reaction.getListOfReactants()])\n", - " products = ' + '.join(['%s %s'%(int(r.getStoichiometry()) if r.getStoichiometry() > 1 else '', r.getSpecies()) for r in reaction.getListOfProducts()])\n", - " reversible = '<' if reaction.getReversible() else ''\n", - " print('%3s: %10s %1s->%10s\\t\\t[%s]' % (reaction.getId(),\n", - " reactants,\n", - " reversible,\n", - " products,\n", - " libsbml.formulaToL3String(reaction.getKineticLaw().getMath())))\n" + " reactants = \" + \".join(\n", + " [\n", + " \"%s %s\"\n", + " % (\n", + " int(r.getStoichiometry()) if r.getStoichiometry() > 1 else \"\",\n", + " r.getSpecies(),\n", + " )\n", + " for r in reaction.getListOfReactants()\n", + " ]\n", + " )\n", + " products = \" + \".join(\n", + " [\n", + " \"%s %s\"\n", + " % (\n", + " int(r.getStoichiometry()) if r.getStoichiometry() > 1 else \"\",\n", + " r.getSpecies(),\n", + " )\n", + " for r in reaction.getListOfProducts()\n", + " ]\n", + " )\n", + " reversible = \"<\" if reaction.getReversible() else \"\"\n", + " print(\n", + " \"%3s: %10s %1s->%10s\\t\\t[%s]\"\n", + " % (\n", + " reaction.getId(),\n", + " reactants,\n", + " reversible,\n", + " products,\n", + " libsbml.formulaToL3String(reaction.getKineticLaw().getMath()),\n", + " )\n", + " )" ] }, { @@ -122,7 +147,7 @@ "metadata": {}, "outputs": [], "source": [ - "constantParameters = ['k0']" + "constant_parameters = [\"k0\"]" ] }, { @@ -133,7 +158,7 @@ "\n", "Specifying observables is beyond the scope of SBML. Here we define them manually.\n", "\n", - "If you are looking for a more scalable way for defining observables, then checkout [PEtab](https://github.com/PEtab-dev/PEtab). Another possibility is using SBML's [`AssignmentRule`s](http://sbml.org/Software/libSBML/5.13.0/docs//python-api/classlibsbml_1_1_rule.html) to specify model outputs within the SBML file." + "If you are looking for a more scalable way for defining observables, then checkout [PEtab](https://github.com/PEtab-dev/PEtab). Another possibility is using SBML's [`AssignmentRule`s](https://sbml.org/software/libsbml/5.18.0/docs/formatted/python-api/classlibsbml_1_1_assignment_rule.html) to specify model outputs within the SBML file." ] }, { @@ -144,12 +169,12 @@ "source": [ "# Define observables\n", "observables = {\n", - " 'observable_x1': {'name': '', 'formula': 'x1'},\n", - " 'observable_x2': {'name': '', 'formula': 'x2'},\n", - " 'observable_x3': {'name': '', 'formula': 'x3'},\n", - " 'observable_x1_scaled': {'name': '', 'formula': 'scaling_x1 * x1'},\n", - " 'observable_x2_offsetted': {'name': '', 'formula': 'offset_x2 + x2'},\n", - " 'observable_x1withsigma': {'name': '', 'formula': 'x1'}\n", + " \"observable_x1\": {\"name\": \"\", \"formula\": \"x1\"},\n", + " \"observable_x2\": {\"name\": \"\", \"formula\": \"x2\"},\n", + " \"observable_x3\": {\"name\": \"\", \"formula\": \"x3\"},\n", + " \"observable_x1_scaled\": {\"name\": \"\", \"formula\": \"scaling_x1 * x1\"},\n", + " \"observable_x2_offsetted\": {\"name\": \"\", \"formula\": \"offset_x2 + x2\"},\n", + " \"observable_x1withsigma\": {\"name\": \"\", \"formula\": \"x1\"},\n", "}" ] }, @@ -168,7 +193,7 @@ "metadata": {}, "outputs": [], "source": [ - "sigmas = {'observable_x1withsigma': 'observable_x1withsigma_sigma'}" + "sigmas = {\"observable_x1withsigma\": \"observable_x1withsigma_sigma\"}" ] }, { @@ -312,12 +337,15 @@ ], "source": [ "import logging\n", - "sbml_importer.sbml2amici(model_name,\n", - " model_output_dir,\n", - " verbose=logging.INFO,\n", - " observables=observables,\n", - " constant_parameters=constantParameters,\n", - " sigmas=sigmas)" + "\n", + "sbml_importer.sbml2amici(\n", + " model_name,\n", + " model_output_dir,\n", + " verbose=logging.INFO,\n", + " observables=observables,\n", + " constant_parameters=constant_parameters,\n", + " sigmas=sigmas,\n", + ")" ] }, { @@ -326,7 +354,7 @@ "source": [ "### Importing the module and loading the model\n", "\n", - "If everything went well, we need to add the previously selected model output directory to our PYTHON_PATH and are then ready to load newly generated model:" + "If everything went well, we can now import the newly generated Python module containing our model:" ] }, { @@ -335,8 +363,7 @@ "metadata": {}, "outputs": [], "source": [ - "sys.path.insert(0, os.path.abspath(model_output_dir))\n", - "model_module = importlib.import_module(model_name)" + "model_module = amici.import_model_module(model_name, model_output_dir)" ] }, { @@ -365,7 +392,7 @@ "source": [ "model = model_module.getModel()\n", "\n", - "print(\"Model name:\", model.getName())\n", + "print(\"Model name: \", model.getName())\n", "print(\"Model parameters:\", model.getParameterIds())\n", "print(\"Model outputs: \", model.getObservableIds())\n", "print(\"Model states: \", model.getStateIds())" @@ -418,8 +445,10 @@ } ], "source": [ - "print('Simulation was run using model default parameters as specified in the SBML model:')\n", - "print(model.getParameters())" + "print(\n", + " \"Simulation was run using model default parameters as specified in the SBML model:\"\n", + ")\n", + "print(dict(zip(model.getParameterIds(), model.getParameters())))" ] }, { @@ -827,25 +856,21 @@ } ], "source": [ - "#np.set_printoptions(threshold=8, edgeitems=2)\n", + "# np.set_printoptions(threshold=8, edgeitems=2)\n", "for key, value in rdata.items():\n", - " print('%12s: ' % key, value)" + " print(\"%12s: \" % key, value)" ] }, { "cell_type": "code", - "execution_count": 13, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "(1.0, 0.5, 0.4, 2.0, 0.1, 2.0, 3.0, 0.2)\n" - ] - } - ], + "execution_count": null, + "outputs": [], "source": [ - "print(model.getParameters())" + "# In particular for interactive use, ReturnDataView.by_id() and amici.evaluate provides a more convenient way to access slices of the result:\n", + "# Time trajectory of observable observable_x1\n", + "print(f\"{rdata.by_id('observable_x1')=}\")\n", + "# Time trajectory of state variable x2\n", + "print(f\"{rdata.by_id('x2')=}\")" ], "metadata": { "collapsed": false, @@ -891,10 +916,33 @@ ], "source": [ "import amici.plotting\n", - "amici.plotting.plotStateTrajectories(rdata, model = None)\n", - "amici.plotting.plotObservableTrajectories(rdata, model = None)" + "\n", + "amici.plotting.plot_state_trajectories(rdata, model=None)\n", + "amici.plotting.plot_observable_trajectories(rdata, model=None)" ] }, + { + "cell_type": "markdown", + "source": [ + "We can also evaluate symbolic expressions of model quantities using `amici.numpy.evaluate`, or directly plot the results using `amici.plotting.plot_expressions`:" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "amici.plotting.plot_expressions(\n", + " \"observable_x1 + observable_x2 + observable_x3\", rdata=rdata\n", + ")" + ], + "metadata": { + "collapsed": false + } + }, { "cell_type": "markdown", "metadata": {}, @@ -934,7 +982,7 @@ "# Re-run simulation, this time passing \"experimental data\"\n", "rdata = amici.runAmiciSimulation(model, solver, edata)\n", "\n", - "print('Log-likelihood %f' % rdata['llh'])" + "print(\"Log-likelihood %f\" % rdata[\"llh\"])" ] }, { @@ -967,9 +1015,13 @@ "solver.setSensitivityOrder(amici.SensitivityOrder.none)\n", "rdata_ref = amici.runAmiciSimulation(model, solver, edata)\n", "\n", + "\n", "def get_simulation_error(solver):\n", " rdata = amici.runAmiciSimulation(model, solver, edata)\n", - " return np.mean(np.abs(rdata['x']-rdata_ref['x'])), np.mean(np.abs(rdata['llh']-rdata_ref['llh']))\n", + " return np.mean(np.abs(rdata[\"x\"] - rdata_ref[\"x\"])), np.mean(\n", + " np.abs(rdata[\"llh\"] - rdata_ref[\"llh\"])\n", + " )\n", + "\n", "\n", "def get_errors(tolfun, tols):\n", " solver.setRelativeTolerance(1e-16)\n", @@ -983,25 +1035,28 @@ " llh_errs.append(llh_err)\n", " return x_errs, llh_errs\n", "\n", - "atols = np.logspace(-5,-15, 100)\n", - "atol_x_errs, atol_llh_errs = get_errors('setAbsoluteTolerance', atols)\n", "\n", - "rtols = np.logspace(-5,-15, 100)\n", - "rtol_x_errs, rtol_llh_errs = get_errors('setRelativeTolerance', rtols)\n", + "atols = np.logspace(-5, -15, 100)\n", + "atol_x_errs, atol_llh_errs = get_errors(\"setAbsoluteTolerance\", atols)\n", + "\n", + "rtols = np.logspace(-5, -15, 100)\n", + "rtol_x_errs, rtol_llh_errs = get_errors(\"setRelativeTolerance\", rtols)\n", "\n", "fig, axes = plt.subplots(1, 2, figsize=(15, 5))\n", "\n", + "\n", "def plot_error(tols, x_errs, llh_errs, tolname, ax):\n", - " ax.plot(tols, x_errs, 'r-', label='x')\n", - " ax.plot(tols, llh_errs, 'b-', label='llh')\n", - " ax.set_xscale('log')\n", - " ax.set_yscale('log')\n", - " ax.set_xlabel(f'{tolname} tolerance')\n", - " ax.set_ylabel('average numerical error')\n", + " ax.plot(tols, x_errs, \"r-\", label=\"x\")\n", + " ax.plot(tols, llh_errs, \"b-\", label=\"llh\")\n", + " ax.set_xscale(\"log\")\n", + " ax.set_yscale(\"log\")\n", + " ax.set_xlabel(f\"{tolname} tolerance\")\n", + " ax.set_ylabel(\"average numerical error\")\n", " ax.legend()\n", "\n", - "plot_error(atols, atol_x_errs, atol_llh_errs, 'absolute', axes[0])\n", - "plot_error(rtols, rtol_x_errs, rtol_llh_errs, 'relative', axes[1])\n", + "\n", + "plot_error(atols, atol_x_errs, atol_llh_errs, \"absolute\", axes[0])\n", + "plot_error(rtols, rtol_x_errs, rtol_llh_errs, \"relative\", axes[1])\n", "\n", "# reset relative tolerance to default value\n", "solver.setRelativeTolerance(1e-8)\n", @@ -1523,21 +1578,27 @@ "source": [ "model = model_module.getModel()\n", "model.setTimepoints(np.linspace(0, 10, 11))\n", - "model.requireSensitivitiesForAllParameters() # sensitivities w.r.t. all parameters\n", + "model.requireSensitivitiesForAllParameters() # sensitivities w.r.t. all parameters\n", "# model.setParameterList([1, 2]) # sensitivities\n", "# w.r.t. the specified parameters\n", - "model.setParameterScale(amici.ParameterScaling.none) # parameters are used as-is (not log-transformed)\n", + "model.setParameterScale(\n", + " amici.ParameterScaling.none\n", + ") # parameters are used as-is (not log-transformed)\n", "\n", "solver = model.getSolver()\n", - "solver.setSensitivityMethod(amici.SensitivityMethod.forward) # forward sensitivity analysis\n", - "solver.setSensitivityOrder(amici.SensitivityOrder.first) # first-order sensitivities\n", + "solver.setSensitivityMethod(\n", + " amici.SensitivityMethod.forward\n", + ") # forward sensitivity analysis\n", + "solver.setSensitivityOrder(\n", + " amici.SensitivityOrder.first\n", + ") # first-order sensitivities\n", "\n", "rdata = amici.runAmiciSimulation(model, solver)\n", "\n", "# print sensitivity-related results\n", "for key, value in rdata.items():\n", - " if key.startswith('s'):\n", - " print('%12s: ' % key, value)" + " if key.startswith(\"s\"):\n", + " print(\"%12s: \" % key, value)" ] }, { @@ -1568,13 +1629,15 @@ "# Set model options\n", "model = model_module.getModel()\n", "p_orig = np.array(model.getParameters())\n", - "p_orig[list(model.getParameterIds()).index('observable_x1withsigma_sigma')] = 0.1 # Change default parameter\n", + "p_orig[\n", + " list(model.getParameterIds()).index(\"observable_x1withsigma_sigma\")\n", + "] = 0.1 # Change default parameter\n", "model.setParameters(p_orig)\n", "model.setParameterScale(amici.ParameterScaling.none)\n", "model.setTimepoints(np.linspace(0, 10, 21))\n", "\n", "solver = model.getSolver()\n", - "solver.setMaxSteps(10**4) # Set maximum number of steps for the solver\n", + "solver.setMaxSteps(10**4) # Set maximum number of steps for the solver\n", "\n", "# simulate time-course to get artificial data\n", "rdata = amici.runAmiciSimulation(model, solver)\n", @@ -1582,18 +1645,22 @@ "edata.fixedParameters = model.getFixedParameters()\n", "# set sigma to 1.0 except for observable 5, so that p[7] is used instead\n", "# (if we have sigma parameterized, the corresponding ExpData entries must NaN, otherwise they will override the parameter)\n", - "edata.setObservedDataStdDev(rdata['t']*0+np.nan,\n", - " list(model.getObservableIds()).index('observable_x1withsigma'))\n", + "edata.setObservedDataStdDev(\n", + " rdata[\"t\"] * 0 + np.nan,\n", + " list(model.getObservableIds()).index(\"observable_x1withsigma\"),\n", + ")\n", "\n", "# enable sensitivities\n", - "solver.setSensitivityOrder(amici.SensitivityOrder.first) # First-order ...\n", - "solver.setSensitivityMethod(amici.SensitivityMethod.adjoint) # ... adjoint sensitivities\n", - "model.requireSensitivitiesForAllParameters() # ... w.r.t. all parameters\n", + "solver.setSensitivityOrder(amici.SensitivityOrder.first) # First-order ...\n", + "solver.setSensitivityMethod(\n", + " amici.SensitivityMethod.adjoint\n", + ") # ... adjoint sensitivities\n", + "model.requireSensitivitiesForAllParameters() # ... w.r.t. all parameters\n", "\n", "# compute adjoint sensitivities\n", "rdata = amici.runAmiciSimulation(model, solver, edata)\n", - "#print(rdata['sigmay'])\n", - "print('Log-likelihood: %f\\nGradient: %s' % (rdata['llh'], rdata['sllh']))\n" + "# print(rdata['sigmay'])\n", + "print(\"Log-likelihood: %f\\nGradient: %s\" % (rdata[\"llh\"], rdata[\"sllh\"]))" ] }, { @@ -1657,12 +1724,13 @@ "source": [ "from scipy.optimize import check_grad\n", "\n", - "def func(x0, symbol='llh', x0full=None, plist=[], verbose=False):\n", + "\n", + "def func(x0, symbol=\"llh\", x0full=None, plist=[], verbose=False):\n", " p = x0[:]\n", " if len(plist):\n", " p = x0full[:]\n", " p[plist] = x0\n", - " verbose and print('f: p=%s' % p)\n", + " verbose and print(\"f: p=%s\" % p)\n", "\n", " old_parameters = model.getParameters()\n", " solver.setSensitivityOrder(amici.SensitivityOrder.none)\n", @@ -1675,7 +1743,8 @@ " verbose and print(res)\n", " return res\n", "\n", - "def grad(x0, symbol='llh', x0full=None, plist=[], verbose=False):\n", + "\n", + "def grad(x0, symbol=\"llh\", x0full=None, plist=[], verbose=False):\n", " p = x0[:]\n", " if len(plist):\n", " model.setParameterList(plist)\n", @@ -1683,7 +1752,7 @@ " p[plist] = x0\n", " else:\n", " model.requireSensitivitiesForAllParameters()\n", - " verbose and print('g: p=%s' % p)\n", + " verbose and print(\"g: p=%s\" % p)\n", "\n", " old_parameters = model.getParameters()\n", " solver.setSensitivityMethod(amici.SensitivityMethod.forward)\n", @@ -1693,45 +1762,50 @@ "\n", " model.setParameters(old_parameters)\n", "\n", - " res = rdata['s%s' % symbol]\n", + " res = rdata[\"s%s\" % symbol]\n", " if not isinstance(res, float):\n", " if len(res.shape) == 3:\n", " res = np.sum(res, axis=(0, 2))\n", " verbose and print(res)\n", " return res\n", "\n", + "\n", "epsilon = 1e-4\n", - "err_norm = check_grad(func, grad, p_orig, 'llh', epsilon=epsilon)\n", - "print('sllh: |error|_2: %f' % err_norm)\n", + "err_norm = check_grad(func, grad, p_orig, \"llh\", epsilon=epsilon)\n", + "print(\"sllh: |error|_2: %f\" % err_norm)\n", "# assert err_norm < 1e-6\n", "print()\n", "\n", "for ip in range(model.np()):\n", " plist = [ip]\n", " p = p_orig.copy()\n", - " err_norm = check_grad(func, grad, p[plist], 'llh', p, [ip], epsilon=epsilon)\n", - " print('sllh: p[%d]: |error|_2: %f' % (ip, err_norm))\n", + " err_norm = check_grad(\n", + " func, grad, p[plist], \"llh\", p, [ip], epsilon=epsilon\n", + " )\n", + " print(\"sllh: p[%d]: |error|_2: %f\" % (ip, err_norm))\n", "\n", "print()\n", "for ip in range(model.np()):\n", " plist = [ip]\n", " p = p_orig.copy()\n", - " err_norm = check_grad(func, grad, p[plist], 'y', p, [ip], epsilon=epsilon)\n", - " print('sy: p[%d]: |error|_2: %f' % (ip, err_norm))\n", + " err_norm = check_grad(func, grad, p[plist], \"y\", p, [ip], epsilon=epsilon)\n", + " print(\"sy: p[%d]: |error|_2: %f\" % (ip, err_norm))\n", "\n", "print()\n", "for ip in range(model.np()):\n", " plist = [ip]\n", " p = p_orig.copy()\n", - " err_norm = check_grad(func, grad, p[plist], 'x', p, [ip], epsilon=epsilon)\n", - " print('sx: p[%d]: |error|_2: %f' % (ip, err_norm))\n", + " err_norm = check_grad(func, grad, p[plist], \"x\", p, [ip], epsilon=epsilon)\n", + " print(\"sx: p[%d]: |error|_2: %f\" % (ip, err_norm))\n", "\n", "print()\n", "for ip in range(model.np()):\n", " plist = [ip]\n", " p = p_orig.copy()\n", - " err_norm = check_grad(func, grad, p[plist], 'sigmay', p, [ip], epsilon=epsilon)\n", - " print('ssigmay: p[%d]: |error|_2: %f' % (ip, err_norm))\n" + " err_norm = check_grad(\n", + " func, grad, p[plist], \"sigmay\", p, [ip], epsilon=epsilon\n", + " )\n", + " print(\"ssigmay: p[%d]: |error|_2: %f\" % (ip, err_norm))" ] }, { @@ -1742,45 +1816,56 @@ }, "outputs": [], "source": [ - "eps=1e-4\n", - "op=model.getParameters()\n", + "eps = 1e-4\n", + "op = model.getParameters()\n", "\n", "\n", - "solver.setSensitivityMethod(amici.SensitivityMethod.forward) # forward sensitivity analysis\n", - "solver.setSensitivityOrder(amici.SensitivityOrder.first) # first-order sensitivities\n", + "solver.setSensitivityMethod(\n", + " amici.SensitivityMethod.forward\n", + ") # forward sensitivity analysis\n", + "solver.setSensitivityOrder(\n", + " amici.SensitivityOrder.first\n", + ") # first-order sensitivities\n", "model.requireSensitivitiesForAllParameters()\n", "solver.setRelativeTolerance(1e-12)\n", "rdata = amici.runAmiciSimulation(model, solver, edata)\n", "\n", - "def fd(x0, ip, eps, symbol='llh'):\n", + "\n", + "def fd(x0, ip, eps, symbol=\"llh\"):\n", " p = list(x0[:])\n", " old_parameters = model.getParameters()\n", " solver.setSensitivityOrder(amici.SensitivityOrder.none)\n", - " p[ip]+=eps\n", + " p[ip] += eps\n", " model.setParameters(p)\n", " rdata_f = amici.runAmiciSimulation(model, solver, edata)\n", - " p[ip]-=2*eps\n", + " p[ip] -= 2 * eps\n", " model.setParameters(p)\n", " rdata_b = amici.runAmiciSimulation(model, solver, edata)\n", "\n", " model.setParameters(old_parameters)\n", - " return (rdata_f[symbol]-rdata_b[symbol])/(2*eps)\n", + " return (rdata_f[symbol] - rdata_b[symbol]) / (2 * eps)\n", + "\n", "\n", "def plot_sensitivities(symbol, eps):\n", - " fig, axes = plt.subplots(4,2, figsize=(15,10))\n", + " fig, axes = plt.subplots(4, 2, figsize=(15, 10))\n", " for ip in range(4):\n", " fd_approx = fd(model.getParameters(), ip, eps, symbol=symbol)\n", "\n", - " axes[ip,0].plot(edata.getTimepoints(), rdata[f's{symbol}'][:,ip,:], 'r-')\n", - " axes[ip,0].plot(edata.getTimepoints(), fd_approx, 'k--')\n", - " axes[ip,0].set_ylabel(f'sensitivity {symbol}')\n", - " axes[ip,0].set_xlabel('time')\n", - "\n", - "\n", - " axes[ip,1].plot(edata.getTimepoints(), np.abs(rdata[f's{symbol}'][:,ip,:]-fd_approx), 'k-')\n", - " axes[ip,1].set_ylabel('difference to fd')\n", - " axes[ip,1].set_xlabel('time')\n", - " axes[ip,1].set_yscale('log')\n", + " axes[ip, 0].plot(\n", + " edata.getTimepoints(), rdata[f\"s{symbol}\"][:, ip, :], \"r-\"\n", + " )\n", + " axes[ip, 0].plot(edata.getTimepoints(), fd_approx, \"k--\")\n", + " axes[ip, 0].set_ylabel(f\"sensitivity {symbol}\")\n", + " axes[ip, 0].set_xlabel(\"time\")\n", + "\n", + " axes[ip, 1].plot(\n", + " edata.getTimepoints(),\n", + " np.abs(rdata[f\"s{symbol}\"][:, ip, :] - fd_approx),\n", + " \"k-\",\n", + " )\n", + " axes[ip, 1].set_ylabel(\"difference to fd\")\n", + " axes[ip, 1].set_xlabel(\"time\")\n", + " axes[ip, 1].set_yscale(\"log\")\n", "\n", " plt.tight_layout()\n", " plt.show()" @@ -1803,7 +1888,7 @@ } ], "source": [ - "plot_sensitivities('x', eps)" + "plot_sensitivities(\"x\", eps)" ] }, { @@ -1823,7 +1908,7 @@ } ], "source": [ - "plot_sensitivities('y', eps)" + "plot_sensitivities(\"y\", eps)" ] }, { @@ -1971,8 +2056,11 @@ "toc_position": {}, "toc_section_display": true, "toc_window_display": false + }, + "nbsphinx": { + "execute": "always" } }, "nbformat": 4, "nbformat_minor": 2 -} \ No newline at end of file +} diff --git a/deps/AMICI/python/examples/example_steadystate/model_steadystate_scaled.xml b/deps/AMICI/python/examples/example_steadystate/model_steadystate_scaled.xml index dc5ae669f..980b7b72a 100644 --- a/deps/AMICI/python/examples/example_steadystate/model_steadystate_scaled.xml +++ b/deps/AMICI/python/examples/example_steadystate/model_steadystate_scaled.xml @@ -196,4 +196,3 @@ - diff --git a/deps/AMICI/python/examples/example_steadystate/model_steadystate_scaled_without_observables.xml b/deps/AMICI/python/examples/example_steadystate/model_steadystate_scaled_without_observables.xml new file mode 100644 index 000000000..e55a2c789 --- /dev/null +++ b/deps/AMICI/python/examples/example_steadystate/model_steadystate_scaled_without_observables.xml @@ -0,0 +1,152 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + p1 + + + x1 + 2 + + + + + + + + + + + + + + + + + + p2 + x1 + x2 + + + + + + + + + + + + + + + + p3 + x2 + + + + + + + + + + + + + + + + + p4 + x3 + + + + + + + + + + + + + k0 + x3 + + + + + + + + + + + p5 + + + + + + diff --git a/deps/AMICI/python/examples/example_units/model_units.xml b/deps/AMICI/python/examples/example_units/model_units.xml index 9108ad78c..09e2e40ac 100644 --- a/deps/AMICI/python/examples/example_units/model_units.xml +++ b/deps/AMICI/python/examples/example_units/model_units.xml @@ -86,4 +86,4 @@ - \ No newline at end of file + diff --git a/deps/AMICI/python/sdist/MANIFEST.in b/deps/AMICI/python/sdist/MANIFEST.in index f53efb3e6..762987665 100644 --- a/deps/AMICI/python/sdist/MANIFEST.in +++ b/deps/AMICI/python/sdist/MANIFEST.in @@ -1,15 +1,18 @@ -recursive-include amici/ThirdParty/SuiteSparse *.c *.h LICENSE.txt -recursive-include amici/ThirdParty/sundials *.c *.h LICENSE +recursive-include amici/ThirdParty/SuiteSparse * +recursive-include amici/ThirdParty/sundials * recursive-include amici/ThirdParty/gsl *.hpp -recursive-include amici/include *.h -recursive-include amici/swig *.i *.h +recursive-include amici/cmake * +recursive-include amici/include/amici *.h +recursive-include amici/src * +recursive-include amici/swig * include amici/* -include amici/src/*template* -include amici/src/hdf5.cpp -include amici/swig/CMakeLists_model.cmake -include setup_clibs.py include version.txt include LICENSE.md exclude amici/*.so exclude amici/*.dll - +prune **/build/ +prune amici/share +prune amici/lib +prune amici/ThirdParty/SuiteSparse/lib +prune amici/ThirdParty/SuiteSparse/include +prune __pycache__ diff --git a/deps/AMICI/python/sdist/amici/CMakeLists.txt b/deps/AMICI/python/sdist/amici/CMakeLists.txt new file mode 120000 index 000000000..d8cd15fc6 --- /dev/null +++ b/deps/AMICI/python/sdist/amici/CMakeLists.txt @@ -0,0 +1 @@ +../../../CMakeLists.txt \ No newline at end of file diff --git a/deps/AMICI/python/sdist/amici/MANIFEST.template.in b/deps/AMICI/python/sdist/amici/MANIFEST.template.in deleted file mode 120000 index 7615b2cce..000000000 --- a/deps/AMICI/python/sdist/amici/MANIFEST.template.in +++ /dev/null @@ -1 +0,0 @@ -../../amici/MANIFEST.template.in \ No newline at end of file diff --git a/deps/AMICI/python/sdist/amici/MANIFEST.template.in b/deps/AMICI/python/sdist/amici/MANIFEST.template.in new file mode 100644 index 000000000..eb3b1b450 --- /dev/null +++ b/deps/AMICI/python/sdist/amici/MANIFEST.template.in @@ -0,0 +1 @@ +include *.cpp *.h diff --git a/deps/AMICI/python/sdist/amici/__init__.py b/deps/AMICI/python/sdist/amici/__init__.py deleted file mode 120000 index ce45fc83f..000000000 --- a/deps/AMICI/python/sdist/amici/__init__.py +++ /dev/null @@ -1 +0,0 @@ -../../amici/__init__.py \ No newline at end of file diff --git a/deps/AMICI/python/sdist/amici/__init__.py b/deps/AMICI/python/sdist/amici/__init__.py new file mode 100644 index 000000000..bcb7387fb --- /dev/null +++ b/deps/AMICI/python/sdist/amici/__init__.py @@ -0,0 +1,210 @@ +""" +AMICI +----- + +The AMICI Python module provides functionality for importing SBML or PySB +models and turning them into C++ Python extensions. +""" + + +import contextlib +import importlib +import os +import re +import sys +from pathlib import Path +from types import ModuleType as ModelModule +from typing import Any, Callable, Optional, Union + + +def _get_amici_path(): + """ + Determine package installation path, or, if used directly from git + repository, get repository root + """ + basedir = os.path.dirname(os.path.dirname(os.path.dirname(__file__))) + if os.path.exists(os.path.join(basedir, ".git")): + return os.path.abspath(basedir) + return os.path.dirname(__file__) + + +def _get_commit_hash(): + """Get commit hash from file""" + basedir = os.path.dirname(os.path.dirname(os.path.dirname(amici_path))) + commitfile = next( + ( + file + for file in [ + os.path.join(basedir, ".git", "FETCH_HEAD"), + os.path.join(basedir, ".git", "ORIG_HEAD"), + ] + if os.path.isfile(file) + ), + None, + ) + + if commitfile: + with open(commitfile) as f: + return str(re.search(r"^([\w]*)", f.read().strip()).group()) + return "unknown" + + +def _imported_from_setup() -> bool: + """Check whether this module is imported from `setup.py`""" + + from inspect import currentframe, getouterframes + from os import sep + + # in case we are imported from setup.py, this will be the AMICI package + # root directory (otherwise it is most likely the Python library directory, + # we are not interested in) + package_root = os.path.realpath(os.path.dirname(os.path.dirname(__file__))) + + for frame in getouterframes(currentframe(), context=0): + # Need to compare the full path, in case a user tries to import AMICI + # from a module `*setup.py`. Will still cause trouble if some package + # requires the AMICI extension during its installation, but seems + # unlikely... + frame_path = os.path.realpath(os.path.expanduser(frame.filename)) + if frame_path == os.path.join( + package_root, "setup.py" + ) or frame_path.endswith(f"{sep}setuptools{sep}build_meta.py"): + return True + + return False + + +# Initialize AMICI paths +#: absolute root path of the amici repository or Python package +amici_path = _get_amici_path() +#: absolute path of the amici swig directory +amiciSwigPath = os.path.join(amici_path, "swig") +#: absolute path of the amici source directory +amiciSrcPath = os.path.join(amici_path, "src") +#: absolute root path of the amici module +amiciModulePath = os.path.dirname(__file__) +#: boolean indicating if this is the full package with swig interface or +# the raw package without extension +has_clibs: bool = any( + os.path.isfile(os.path.join(amici_path, wrapper)) + for wrapper in ["amici.py", "amici_without_hdf5.py"] +) +#: boolean indicating if amici was compiled with hdf5 support +hdf5_enabled: bool = False + +# Get version number from file +with open(os.path.join(amici_path, "version.txt")) as f: + __version__ = f.read().strip() + +__commit__ = _get_commit_hash() + +# Import SWIG module and swig-dependent submodules if required and available +if not _imported_from_setup(): + if has_clibs: + from . import amici + from .amici import * + + # has to be done before importing readSolverSettingsFromHDF5 + # from .swig_wrappers + hdf5_enabled = "readSolverSettingsFromHDF5" in dir() + # These modules require the swig interface and other dependencies + from .numpy import ExpDataView, ReturnDataView + 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 + + @runtime_checkable + class ModelModule(Protocol): + """Type of AMICI-generated model modules. + + To enable static type checking.""" + + def getModel(self) -> amici.Model: + """Create a model instance.""" + ... + + def get_model(self) -> amici.Model: + """Create a model instance.""" + ... + + +class add_path: + """Context manager for temporarily changing PYTHONPATH""" + + def __init__(self, path: Union[str, Path]): + self.path: str = str(path) + + def __enter__(self): + if self.path: + sys.path.insert(0, self.path) + + def __exit__(self, exc_type, exc_value, traceback): + with contextlib.suppress(ValueError): + sys.path.remove(self.path) + + +def import_model_module( + module_name: str, module_path: Optional[Union[Path, str]] = None +) -> ModelModule: + """ + Import Python module of an AMICI model + + :param module_name: + Name of the python package of the model + :param module_path: + Absolute or relative path of the package directory + :return: + The model module + """ + module_path = str(module_path) + + # ensure we will find the newly created module + importlib.invalidate_caches() + + if not os.path.isdir(module_path): + raise ValueError(f"module_path '{module_path}' is not a directory.") + + module_path = os.path.abspath(module_path) + + # module already loaded? + if module_name in sys.modules: + # if a module with that name is already in sys.modules, we remove it, + # along with all other modules from that package. otherwise, there + # will be trouble if two different models with the same name are to + # be imported. + del sys.modules[module_name] + # collect first, don't delete while iterating + to_unload = { + loaded_module_name + for loaded_module_name in sys.modules.keys() + if loaded_module_name.startswith(f"{module_name}.") + } + for m in to_unload: + del sys.modules[m] + + with add_path(module_path): + return importlib.import_module(module_name) + + +class AmiciVersionError(RuntimeError): + """Error thrown if an AMICI model is loaded that is incompatible with + the installed AMICI base package""" + + pass + + +def _get_default_argument(func: Callable, arg: str) -> Any: + """Get the default value of the given argument in the given function.""" + import inspect + + signature = inspect.signature(func) + if ( + default := signature.parameters[arg].default + ) is not inspect.Parameter.empty: + return default + raise ValueError(f"No default value for argument {arg} of {func}.") diff --git a/deps/AMICI/python/sdist/amici/__init__.template.py b/deps/AMICI/python/sdist/amici/__init__.template.py deleted file mode 120000 index 165798fd5..000000000 --- a/deps/AMICI/python/sdist/amici/__init__.template.py +++ /dev/null @@ -1 +0,0 @@ -../../amici/__init__.template.py \ No newline at end of file diff --git a/deps/AMICI/python/sdist/amici/__init__.template.py b/deps/AMICI/python/sdist/amici/__init__.template.py new file mode 100644 index 000000000..c37ac0f96 --- /dev/null +++ b/deps/AMICI/python/sdist/amici/__init__.template.py @@ -0,0 +1,21 @@ +"""AMICI-generated module for model TPL_MODELNAME""" + +from pathlib import Path + +import amici + +# Ensure we are binary-compatible, see #556 +if "TPL_AMICI_VERSION" != amici.__version__: + raise amici.AmiciVersionError( + f"Cannot use model `TPL_MODELNAME` in {Path(__file__).parent}, " + "generated with amici==TPL_AMICI_VERSION, " + f"together with amici=={amici.__version__} " + "which is currently installed. To use this model, install " + "amici==TPL_AMICI_VERSION or re-import the model with the amici " + "version currently installed." + ) + +from .TPL_MODELNAME import * +from .TPL_MODELNAME import getModel as get_model + +__version__ = "TPL_PACKAGE_VERSION" diff --git a/deps/AMICI/python/sdist/amici/__main__.py b/deps/AMICI/python/sdist/amici/__main__.py deleted file mode 120000 index cfa13d34d..000000000 --- a/deps/AMICI/python/sdist/amici/__main__.py +++ /dev/null @@ -1 +0,0 @@ -../../amici/__main__.py \ No newline at end of file diff --git a/deps/AMICI/python/sdist/amici/__main__.py b/deps/AMICI/python/sdist/amici/__main__.py new file mode 100644 index 000000000..165f5d951 --- /dev/null +++ b/deps/AMICI/python/sdist/amici/__main__.py @@ -0,0 +1,30 @@ +"""Package-level entrypoint""" + +import os +import sys + +from . import __version__, compiledWithOpenMP, has_clibs, hdf5_enabled + + +def print_info(): + """Displays information on the current AMICI installation. + + Useful for verifying package installation of submitting bug reports""" + features = [] + + if has_clibs: + features.append("extensions") + + if compiledWithOpenMP(): + features.append("OpenMP") + + if hdf5_enabled: + features.append("HDF5") + + print( + f"AMICI ({sys.platform}) version {__version__} ({','.join(features)})" + ) + + +if __name__ == "__main__": + print_info() diff --git a/deps/AMICI/python/sdist/amici/antimony_import.py b/deps/AMICI/python/sdist/amici/antimony_import.py new file mode 100644 index 000000000..cc2307978 --- /dev/null +++ b/deps/AMICI/python/sdist/amici/antimony_import.py @@ -0,0 +1,58 @@ +"""Import of Antimony models into AMICI. + +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: + """Convert Antimony model to SBML. + + :param ant_model: Antimony model as string or path to file + + :returns: + The SBML model as string. + """ + import antimony as ant + + # Unload everything / free memory + ant.clearPreviousLoads() + ant.freeAll() + + try: + # potentially fails because of too long file name + is_file = Path(ant_model).exists() + except OSError: + is_file = False + + if is_file: + status = ant.loadAntimonyFile(ant_model) + else: + status = ant.loadAntimonyString(ant_model) + if status < 0: + raise RuntimeError("Antimony model could not be loaded.") + + if (main_module_name := ant.getMainModuleName()) is None: + raise AssertionError("There is no Antimony module.") + + sbml_str = ant.getSBMLString(main_module_name) + if not sbml_str: + raise ValueError("Antimony model could not be converted to SBML.") + + return sbml_str + + +def antimony2amici(ant_model: Union[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. + + For documentation see :meth:`amici.sbml_import.SbmlImporter.sbml2amici`. + """ + from .sbml_import import SbmlImporter + + sbml_str = antimony2sbml(ant_model) + sbml_importer = SbmlImporter(sbml_str, from_file=False) + return sbml_importer.sbml2amici(*args, **kwargs) diff --git a/deps/AMICI/python/sdist/amici/bngl_import.py b/deps/AMICI/python/sdist/amici/bngl_import.py new file mode 100644 index 000000000..960413dbe --- /dev/null +++ b/deps/AMICI/python/sdist/amici/bngl_import.py @@ -0,0 +1,32 @@ +""" +BNGL Import +------------ +This module provides all necessary functionality to import a model specified +in the :term:`BNGL` format. +""" + + +from pysb.importers.bngl import model_from_bngl + +from .pysb_import import pysb2amici + + +def bngl2amici(bngl_model: str, *args, **kwargs) -> None: + r""" + Generate AMICI C++ files for the provided model. + + :param bngl_model: + bngl model file, model name will determine the name of the generated + module + + :param args: + see :func:`amici.pysb_import.pysb2amici` for additional arguments + + :param kwargs: + see :func:`amici.pysb_import.pysb2amici` for additional arguments + + """ + if "model" in kwargs: + raise ValueError("model argument not allowed") + pysb_model = model_from_bngl(bngl_model) + pysb2amici(pysb_model, *args, **kwargs) diff --git a/deps/AMICI/python/sdist/amici/cmake b/deps/AMICI/python/sdist/amici/cmake new file mode 120000 index 000000000..791713971 --- /dev/null +++ b/deps/AMICI/python/sdist/amici/cmake @@ -0,0 +1 @@ +../../../cmake/ \ No newline at end of file diff --git a/deps/AMICI/python/sdist/amici/conserved_quantities_demartino.py b/deps/AMICI/python/sdist/amici/conserved_quantities_demartino.py new file mode 100644 index 000000000..5b04fa147 --- /dev/null +++ b/deps/AMICI/python/sdist/amici/conserved_quantities_demartino.py @@ -0,0 +1,981 @@ +import logging +import math +import random +import sys +from typing import List, MutableSequence, Optional, Sequence, Tuple, Union + +from .logging import get_logger + +logger = get_logger(__name__, logging.ERROR) + +# increase recursion limit for recursive quicksort +sys.setrecursionlimit(3000) + +_MIN = 1e-9 +_MAX = 1e9 + + +def compute_moiety_conservation_laws( + stoichiometric_list: Sequence[float], + 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]]]: + """Compute moiety conservation laws. + + According to the algorithm proposed by De Martino et al. (2014) + https://doi.org/10.1371/journal.pone.0100750 + + :param stoichiometric_list: + the stoichiometric matrix as a list (species x reactions, + column-major ordering) + :param num_species: + total number of species in the reaction network + :param num_reactions: + total number of reactions in the reaction network + :param max_num_monte_carlo: + maximum number of MonteCarlo steps before changing to relaxation + :param rng_seed: + Seed for the random number generator. If `False`, the RNG will not be + re-initialized. Other values will be passed to :func:`random.seed`. + :param species_names: + Species names. Optional and only used for logging. + :returns: + Integer MCLs as list of lists of indices of involved species and + list of lists of corresponding coefficients. + """ + # compute semi-positive conservation laws + ( + kernel_dim, + engaged_species, + int_kernel_dim, + conserved_moieties, + cls_species_idxs, + cls_coefficients, + ) = _kernel(stoichiometric_list, num_species, num_reactions) + # if the number of integer MCLs equals total MCLS no MC relaxation + done = int_kernel_dim == kernel_dim + + if not done: + # construct interaction matrix + J, J2, fields = _fill( + stoichiometric_list, engaged_species, num_species + ) + + # seed random number generator + if rng_seed is not False: + random.seed(rng_seed) + + timer = 0 + # maximum number of montecarlo search before starting relaxation + while not done: + yes, int_kernel_dim, conserved_moieties = _monte_carlo( + engaged_species, + J, + J2, + fields, + conserved_moieties, + int_kernel_dim, + cls_species_idxs, + cls_coefficients, + num_species, + max_iter=max_num_monte_carlo, + ) + # if the number of integer MCLs equals total MCLS then MC done + done = int_kernel_dim == kernel_dim + timer = 0 if yes else timer + 1 + + if timer == max_num_monte_carlo: + done = _relax( + stoichiometric_list, + conserved_moieties, + num_reactions, + num_species, + ) + timer = 0 + _reduce(int_kernel_dim, cls_species_idxs, cls_coefficients, num_species) + _output( + int_kernel_dim, + kernel_dim, + engaged_species, + cls_species_idxs, + cls_coefficients, + species_names, + verbose=True, + ) + + return cls_species_idxs[:int_kernel_dim], cls_coefficients[:int_kernel_dim] + + +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, + verbose: bool = False, + log_level: int = logging.DEBUG, +): + """Log infos on identified conservation laws""" + + def log(*args, **kwargs): + logger.log(log_level, *args, **kwargs) + + log( + f"There are {int_kernel_dim} linearly independent conserved " + f"moieties, engaging {len(int_matched)} state variables." + ) + if int_kernel_dim == kernel_dim: + log("They generate all the conservation laws") + else: + log( + f"They don't generate all the conservation laws, " + f"{kernel_dim - int_kernel_dim} of them are not reducible to " + "moieties" + ) + # print all conserved quantities + if verbose: + for i, (coefficients, engaged_species_idxs) in enumerate( + zip(species_coefficients, species_indices) + ): + if not engaged_species_idxs: + continue + log( + f"Moiety number {i + 1} engages {len(engaged_species_idxs)} " + "species:" + ) + for species_idx, coefficient in zip( + engaged_species_idxs, coefficients + ): + name = ( + species_names[species_idx] + if species_names + else species_idx + ) + log(f"\t{name}\t{coefficient}") + + +def _qsort( + k: int, km: int, order: MutableSequence[int], pivots: Sequence[int] +) -> None: + """Quicksort + + Recursive implementation of the quicksort algorithm + + :param k: + number of elements to sort + :param km: + current center element + :param order: + ordering of the elements + :param pivots: + corresponding pivot elements from scaled partial pivoting strategy + """ + # TODO: Rewrite into an iterative algorithm with pivoting strategy + + if k - km < 1: + # nothing to sort + return + + pivot = km + int((k - km) / 2) + l = 0 + p = k - km - 1 + new_order = [0] * (k - km) + for i in range(km, k): + if i != pivot: + if pivots[order[i]] < pivots[order[pivot]]: + new_order[l] = order[i] + l += 1 + else: + new_order[p] = order[i] + p -= 1 + new_order[p] = order[pivot] + order[km:k] = new_order + + # calculate center, then recursive calls on left and right intervals + centre = p + km + _qsort(k, centre + 1, order, pivots) + _qsort(centre, km, order, pivots) + + +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]]]: + """ + Kernel (left nullspace of :math:`S`) calculation by Gaussian elimination + + To compute the left nullspace of the stoichiometric matrix :math:`S`, + a Gaussian elimination method with partial scaled pivoting is used to deal + effectively with a possibly ill-conditioned stoichiometric matrix + :math:`S`. + + Note that this is the Python reimplementation of the algorithm proposed by + `De Martino et al. (2014) `_ + and thus a direct adaption of the original implementation in C++. + + :param stoichiometric_list: + the stoichiometric matrix as a list (species x reactions, + col-major ordering) + :param num_species: + total number of species in the reaction network + :param num_reactions: + total number of reactions in the reaction network + :returns: + 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)] + i_reaction = 0 + i_species = 0 + for val in stoichiometric_list: + if val != 0: + matrix[i_species].append(i_reaction) + matrix2[i_species].append(val) + i_species += 1 + if i_species == num_species: + i_species = 0 + i_reaction += 1 + for i in range(num_species): + matrix[i].append(num_reactions + i) + matrix2[i].append(1) + + order: List[int] = list(range(num_species)) + pivots = [ + matrix[i][0] if len(matrix[i]) else _MAX for i in range(num_species) + ] + + done = False + while not done: + _qsort(num_species, 0, order, pivots) + for j in range(num_species - 1): + if pivots[order[j + 1]] == pivots[order[j]] != _MAX: + min1 = _MAX + if len(matrix[order[j]]) > 1: + for i in range(len(matrix[order[j]])): + min1 = min( + min1, + abs(matrix2[order[j]][0] / matrix2[order[j]][i]), + ) + + min2 = _MAX + if len(matrix[order[j + 1]]) > 1: + for i in range(len(matrix[order[j + 1]])): + min2 = min( + min2, + abs( + matrix2[order[j + 1]][0] + / matrix2[order[j + 1]][i] + ), + ) + + if min2 > min1: + # swap + k2 = order[j + 1] + order[j + 1] = order[j] + order[j] = k2 + done = True + + for j in range(num_species - 1): + if pivots[order[j + 1]] == pivots[order[j]] != _MAX: + k1 = order[j + 1] + k2 = order[j] + 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 + + for i in range(1, len(matrix[k2])): + column[matrix[k2][i]] -= matrix2[k2][i] + + matrix[k1] = [] + matrix2[k1] = [] + for col_idx, col_val in enumerate(column): + if abs(col_val) > _MIN: + matrix[k1].append(col_idx) + matrix2[k1].append(col_val) + + done = False + if len(matrix[order[j + 1]]): + pivots[order[j + 1]] = matrix[order[j + 1]][0] + else: + pivots[order[j + 1]] = _MAX + + RSolutions = [[] for _ in range(num_species)] + RSolutions2 = [[] for _ in range(num_species)] + kernel_dim = 0 + + for i in range(num_species): + done = all( + matrix[i][j] >= num_reactions for j in range(len(matrix[i])) + ) + if done and len(matrix[i]): + for j in range(len(matrix[i])): + RSolutions[kernel_dim].append(matrix[i][j] - num_reactions) + RSolutions2[kernel_dim].append(matrix2[i][j]) + kernel_dim += 1 + del matrix, matrix2 + + matched = [] + int_matched = [] + cls_species_idxs = [[] for _ in range(num_species)] + cls_coefficients = [[] for _ in range(num_species)] + + i2 = 0 + for i in range(kernel_dim): + ok2 = True + for j in range(len(RSolutions[i])): + if RSolutions2[i][j] * RSolutions2[i][0] < 0: + ok2 = False + if not matched or all( + cur_matched != RSolutions[i][j] for cur_matched in matched + ): + matched.append(RSolutions[i][j]) + if ok2 and len(RSolutions[i]): + min_value = _MAX + for j in range(len(RSolutions[i])): + cls_species_idxs[i2].append(RSolutions[i][j]) + cls_coefficients[i2].append(abs(RSolutions2[i][j])) + min_value = min(min_value, abs(RSolutions2[i][j])) + if not int_matched or all( + cur_int_matched != cls_species_idxs[i2][j] + for cur_int_matched in int_matched + ): + int_matched.append(cls_species_idxs[i2][j]) + for j in range(len(cls_species_idxs[i2])): + cls_coefficients[i2][j] /= min_value + i2 += 1 + int_kernel_dim = i2 + + assert int_kernel_dim <= kernel_dim + assert len(cls_species_idxs) == len(cls_coefficients), ( + "Inconsistent number of conserved quantities in coefficients and " + "species" + ) + return ( + kernel_dim, + matched, + int_kernel_dim, + int_matched, + cls_species_idxs, + cls_coefficients, + ) + + +def _fill( + stoichiometric_list: Sequence[float], + matched: Sequence[int], + num_species: int, +) -> Tuple[List[List[int]], List[List[int]], List[int]]: + """Construct interaction matrix + + Construct the interaction matrix out of the given stoichiometric matrix + :math:`S`. + + :param stoichiometric_list: + the stoichiometric matrix given as a flat list + :param matched: + found and independent moiety conservation laws (MCL) + :param num_species: + number of rows in :math:`S` + :returns: + interactions of metabolites and reactions, and matrix of interaction + """ + dim = len(matched) + + # for each entry in the stoichiometric matrix save interaction + i_reaction = 0 + i_species = 0 + matrix = [[] for _ in range(dim)] + matrix2 = [[] for _ in range(dim)] + for val in stoichiometric_list: + if val != 0: + take = dim + for matched_idx, matched_val in enumerate(matched): + if i_species == matched_val: + take = matched_idx + if take < dim: + matrix[take].append(i_reaction) + matrix2[take].append(val) + i_species += 1 + if i_species == num_species: + i_species = 0 + i_reaction += 1 + + J = [[] for _ in range(num_species)] + J2 = [[] for _ in range(num_species)] + fields = [0] * num_species + for i in range(dim): + for j in range(i, dim): + interactions = 0 + for po in range(len(matrix[i])): + for pu in range(len(matrix[j])): + if matrix[i][po] == matrix[j][pu]: + interactions += matrix2[i][po] * matrix2[j][pu] + if j == i: + fields[i] = interactions + elif abs(interactions) > _MIN: + J[i].append(j) + J2[i].append(interactions) + J[j].append(i) + J2[j].append(interactions) + return J, J2, fields + + +def _is_linearly_dependent( + vector: Sequence[float], + int_kernel_dim: int, + cls_species_idxs: Sequence[Sequence[int]], + cls_coefficients: Sequence[Sequence[float]], + matched: Sequence[int], + num_species: int, +) -> bool: + """Check for linear dependence between MCLs + + Check if the solutions found with Monte Carlo are linearly independent + with respect to the previous found solution for all MCLs involved + + :param vector: + found basis + :param int_kernel_dim: + number of integer conservative laws + :param cls_species_idxs: + NSolutions contains the species involved in the MCL + :param cls_coefficients: + NSolutions2 contains the corresponding coefficients in the MCL + :param matched: + actual found MCLs + :param num_species: + number of rows in :math:`S` + :returns: + 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)] + # Populate matrices with species ids and coefficients for CLs + for i in range(K - 1): + for j in range(len(cls_species_idxs[i])): + matrix[i].append(cls_species_idxs[i][j]) + matrix2[i].append(cls_coefficients[i][j]) + + order2 = list(range(len(matched))) + pivots2 = matched[:] + _qsort(len(matched), 0, order2, pivots2) + + # ensure positivity + for i in range(len(matched)): + if vector[order2[i]] > _MIN: + matrix[K - 1].append(matched[order2[i]]) + matrix2[K - 1].append(vector[order2[i]]) + + order = list(range(K)) + pivots = [matrix[i][0] if len(matrix[i]) else _MAX for i in range(K)] + + # check for linear independence of the solution + ok = False + while not ok: + _qsort(K, 0, order, pivots) + for j in range(K - 1): + if pivots[order[j + 1]] == pivots[order[j]] != _MAX: + min1 = _MAX + if len(matrix[order[j]]) > 1: + for i in range(len(matrix[order[j]])): + min1 = min( + min1, + abs(matrix2[order[j]][0] / matrix2[order[j]][i]), + ) + min2 = _MAX + if len(matrix[order[j + 1]]) > 1: + for i in range(len(matrix[order[j + 1]])): + min2 = min( + min2, + abs( + matrix2[order[j + 1]][0] + / matrix2[order[j + 1]][i] + ), + ) + if min2 > min1: + # swap + k2 = order[j + 1] + order[j + 1] = order[j] + order[j] = k2 + ok = True + for j in range(K - 1): + if pivots[order[j + 1]] == pivots[order[j]] != _MAX: + k1 = order[j + 1] + k2 = order[j] + 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 + for i in range(1, len(matrix[k2])): + column[matrix[k2][i]] -= matrix2[k2][i] + + matrix[k1] = [] + matrix2[k1] = [] + for i in range(num_species): + if abs(column[i]) > _MIN: + matrix[k1].append(i) + matrix2[k1].append(column[i]) + ok = False + pivots[k1] = matrix[k1][0] if len(matrix[k1]) else _MAX + K1 = sum(len(matrix[i]) > 0 for i in range(K)) + return K == K1 + + +def _monte_carlo( + matched: Sequence[int], + J: Sequence[Sequence[int]], + J2: Sequence[Sequence[float]], + fields: Sequence[float], + int_matched: MutableSequence[int], + int_kernel_dim: int, + cls_species_idxs: MutableSequence[MutableSequence[int]], + cls_coefficients: MutableSequence[MutableSequence[float]], + num_species: int, + initial_temperature: float = 1, + cool_rate: float = 1e-3, + max_iter: int = 10, +) -> 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 + the De Martino (2014) paper and Eqs. 11-13 in the publication + + :param matched: + number of found MCLs + :param J: + index of metabolites involved in a MCL + :param J2: + coefficients of metabolites involved in a MCL + :param fields: + actual number of metabolites involved in a MCL + :param int_matched: + actual matched MCLs + :param int_kernel_dim: + number of MCLs found in :math:`S` + :param cls_species_idxs: + Modified in-place. + :param cls_coefficients: + Modified in-place. + :param initial_temperature: + initial temperature + :param cool_rate: + cooling rate of simulated annealing + :param max_iter: + maximum number of MonteCarlo steps before changing to relaxation + :returns: + status of MC iteration, number of integer MCLs, number of MCLs, + metabolites and reaction indices, MCLs and integer MCLs as a tuple + + status indicates if the currently found moiety by the Monte Carlo + process is linearly dependent (False) or linearly independent (True) + in case of linear dependence, the current Monte Carlo cycle can be + considered otherwise the algorithm retries Monte Carlo up to max_iter + """ + dim = len(matched) + num = [ + int(2 * random.uniform(0, 1)) if len(J[i]) else 0 for i in range(dim) + ] + numtot = sum(num) + + def compute_h(): + H = 0 + for i in range(dim): + H += fields[i] * num[i] ** 2 + for j in range(len(J[i])): + H += J2[i][j] * num[i] * num[J[i][j]] + return H + + H = compute_h() + + count = 0 + howmany = 0 + T1 = initial_temperature + e = math.exp(-1 / T1) + while True: + en = int(random.uniform(0, 1) * dim) + while not len(J[en]): + en = int(random.uniform(0, 1) * dim) + + p = -1 if num[en] > 0 and random.uniform(0, 1) < 0.5 else 1 + delta = fields[en] * num[en] + for i in range(len(J[en])): + delta += J2[en][i] * num[J[en][i]] + delta = 2 * p * delta + fields[en] + + if delta < 0 or random.uniform(0, 1) < math.pow(e, delta): + num[en] += p + numtot += p + H += delta + + count += 1 + if count % dim == 0: + T1 -= cool_rate + if T1 <= 0: + T1 = cool_rate + e = math.exp(-1 / T1) + + if count == dim // cool_rate: + count = 0 + T1 = initial_temperature + e = math.exp(-1 / T1) + en = int(random.uniform(0, 1) * dim) + while not len(J[en]): + en = int(random.uniform(0, 1) * dim) + num = [0] * dim + num[en] = 1 + numtot = 1 + + H = compute_h() + howmany += 1 + + if (H < _MIN and numtot > 0) or (howmany == 10 * max_iter): + break + + if howmany >= 10 * max_iter: + return False, int_kernel_dim, int_matched + + # founds MCLS? need to check for linear independence + if len(int_matched) and not _is_linearly_dependent( + num, + int_kernel_dim, + cls_species_idxs, + cls_coefficients, + matched, + num_species, + ): + logger.debug("Found a moiety but it is linearly dependent... next.") + return False, int_kernel_dim, int_matched + + # reduce by MC procedure + order2 = list(range(len(matched))) + pivots2 = matched[:] + _qsort(len(matched), 0, order2, pivots2) + for i in range(len(matched)): + if num[order2[i]] > 0: + cls_species_idxs[int_kernel_dim].append(matched[order2[i]]) + cls_coefficients[int_kernel_dim].append(num[order2[i]]) + int_kernel_dim += 1 + _reduce(int_kernel_dim, cls_species_idxs, cls_coefficients, num_species) + min_value = 1000 + for i in range(len(cls_species_idxs[int_kernel_dim - 1])): + if not len(int_matched) or all( + cur_int_matched != cls_species_idxs[int_kernel_dim - 1][i] + for cur_int_matched in int_matched + ): + int_matched.append(cls_species_idxs[int_kernel_dim - 1][i]) + + min_value = min(min_value, cls_coefficients[int_kernel_dim - 1][i]) + for i in range(len(cls_species_idxs[int_kernel_dim - 1])): + cls_coefficients[int_kernel_dim - 1][i] /= min_value + + logger.debug( + f"Found linearly independent moiety, now there are " + f"{int_kernel_dim} engaging {len(int_matched)} species" + ) + + return True, int_kernel_dim, int_matched + + +def _relax( + stoichiometric_list: Sequence[float], + int_matched: Sequence[int], + num_reactions: int, + num_species: int, + relaxation_max: float = 1e6, + relaxation_step: float = 1.9, +) -> bool: + """Relaxation scheme for Monte Carlo final solution + + Checking for completeness using Motzkin's theorem. See Step (c) in + De Martino (2014) and the Eqs. 14-16 in the corresponding publication + + :param stoichiometric_list: + stoichiometric matrix :math:`S` as a flat list (column-major ordering) + :param int_matched: + number of matched integer CLs + :param num_reactions: + number of reactions in reaction network + :param num_species: + number of species in reaction network + :param relaxation_max: + maximum relaxation step + :param relaxation_step: + relaxation step width + :returns: + boolean indicating if relaxation has succeeded (``True``) or not + (``False``) + """ + K = len(int_matched) + 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: + if val != 0: + take = K + if K > 0: + for i in range(K): + if i_species == int_matched[i]: + take = i + if take < K: + matrix[take].append(i_reaction) + matrix2[take].append(val) + i_species += 1 + if i_species == num_species: + i_species = 0 + i_reaction += 1 + + # reducing the stoichiometric matrix of conserved moieties to row echelon + # form by Gaussian elimination + order = list(range(K)) + pivots = [matrix[i][0] if len(matrix[i]) else _MAX for i in range(K)] + done = False + while not done: + _qsort(K, 0, order, pivots) + for j in range(K - 1): + if pivots[order[j + 1]] == pivots[order[j]] != _MAX: + min1 = _MAX + if len(matrix[order[j]]) > 1: + for i in range(len(matrix[order[j]])): + min1 = min( + min1, + abs(matrix2[order[j]][0] / matrix2[order[j]][i]), + ) + min2 = _MAX + if len(matrix[order[j + 1]]) > 1: + for i in range(len(matrix[order[j + 1]])): + min2 = min( + min2, + abs( + matrix2[order[j + 1]][0] + / matrix2[order[j + 1]][i] + ), + ) + if min2 > min1: + # swap + k2 = order[j + 1] + order[j + 1] = order[j] + order[j] = k2 + done = True + for j in range(K - 1): + if pivots[order[j + 1]] == pivots[order[j]] != _MAX: + k1 = order[j + 1] + k2 = order[j] + 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 + for i in range(1, len(matrix[k2])): + column[matrix[k2][i]] -= matrix2[k2][i] + + matrix[k1] = [] + matrix2[k1] = [] + for col_idx, col_val in enumerate(column): + if abs(col_val) > _MIN: + matrix[k1].append(col_idx) + matrix2[k1].append(col_val) + done = False + if len(matrix[order[j + 1]]): + pivots[order[j + 1]] = matrix[order[j + 1]][0] + else: + pivots[order[j + 1]] = _MAX + + # normalize + for matrix2_i in matrix2: + if len(matrix2_i): + norm = matrix2_i[0] + for j in range(len(matrix2_i)): + matrix2_i[j] /= norm + + for k1 in reversed(range(K - 1)): + k = order[k1] + if len(matrix[k]) <= 1: + continue + + i = 0 + while i < len(matrix[k]): + for i_species in range(k1 + 1, K): + j = order[i_species] + if not len(matrix[j]) or matrix[j][0] != matrix[k][i]: + continue + + # subtract rows + # matrix2[k] = matrix2[k] - matrix2[j] * matrix2[k][i] + 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])): + row_k[matrix[j][a]] -= matrix2[j][a] * matrix2[k][i] + # filter + matrix[k] = [ + row_idx + for row_idx, row_val in enumerate(row_k) + if row_val != 0 + ] + matrix2[k] = [row_val for row_val in row_k if row_val != 0] + + if len(matrix[k]) <= i: + break + i += 1 + + indip = [K + 1] * num_reactions + for i in range(K): + if len(matrix[i]): + indip[matrix[i][0]] = i + M1 = 0 + for i in range(num_reactions): + if indip[i] == K + 1: + indip[i] = K + M1 + M1 += 1 + + matrixAus = [[] for _ in range(M1)] + matrixAus2 = [[] for _ in range(M1)] + i_reaction = 0 + for i in range(num_reactions): + if indip[i] >= K: + matrixAus[i_reaction].append(i) + matrixAus2[i_reaction].append(1) + i_reaction += 1 + else: + t = indip[i] + if len(matrix[t]) > 1: + for k in range(1, len(matrix[t])): + idx = indip[matrix[t][k]] - K + matrixAus[idx].append(i) + matrixAus2[idx].append(-matrix2[t][k]) + del matrix + + N1 = num_species - K + matrix_aus = [[] for _ in range(N1)] + matrix_aus2 = [[] for _ in range(N1)] + k1 = 0 + i_reaction = 0 + i_species = 0 + for val in stoichiometric_list: + take = 1 + for i in range(len(int_matched)): + if i_species == int_matched[i]: + take -= 1 + if val != 0 and take == 1: + matrix_aus[k1].append(i_reaction) + matrix_aus2[k1].append(val) + i_species += 1 + k1 += take + if i_species == num_species: + i_species = 0 + k1 = 0 + i_reaction += 1 + + matrixb = [[] for _ in range(N1)] + matrixb2 = [[] for _ in range(N1)] + for i in range(M1): + for j in range(N1): + if len(matrix_aus[j]) * len(matrixAus[i]): + prod = 0 + for ib in range(len(matrixAus[i])): + for jb in range(len(matrix_aus[j])): + if matrixAus[i][ib] == matrix_aus[j][jb]: + prod += matrixAus2[i][ib] * matrix_aus2[j][jb] + if abs(prod) > _MIN: + matrixb[j].append(i) + matrixb2[j].append(prod) + del matrixAus, matrixAus2, matrix_aus, matrix_aus2 + + var = [_MIN] * M1 + time = 0 + cmin_idx = 0 + while True: + cmin = 1000 + for j in range(N1): + constr = 0 + if len(matrixb[j]): + for i in range(len(matrixb[j])): + constr += matrixb2[j][i] * var[matrixb[j][i]] + if constr < cmin: + cmin_idx = j + cmin = constr + if cmin >= 0: + # constraints satisfied + break + + # Motzkin relaxation + alpha = -relaxation_step * cmin + fact = sum(val**2 for val in matrixb2[cmin_idx]) + alpha /= fact + alpha = max(1e-9 * _MIN, alpha) + for j in range(len(matrixb[cmin_idx])): + var[matrixb[cmin_idx][j]] += alpha * matrixb2[cmin_idx][j] + + time += 1 + if time >= relaxation_max: + # timeout + break + + return done + + +def _reduce( + int_kernel_dim: int, + cls_species_idxs: MutableSequence[MutableSequence[int]], + cls_coefficients: MutableSequence[MutableSequence[float]], + num_species: int, +) -> None: + """Reducing the solution which has been found by the Monte Carlo process + + In case of superpositions of independent MCLs one can reduce by + iteratively subtracting the other independent MCLs, taking care + to maintain then non-negativity constraint, see Eq. 13 in De Martino (2014) + + :param int_kernel_dim: + number of found MCLs + :param cls_species_idxs: + Species indices involved in each of the conservation laws. + Modified in-place. + :param cls_coefficients: + Coefficients for each of the species involved in each of the + conservation laws. Modified in-place. + :param num_species: + number of species / rows in :math:`S` + """ + K = int_kernel_dim + order = list(range(K)) + pivots = [-len(cls_species_idxs[i]) for i in range(K)] + + done = False + while not done: + _qsort(K, 0, order, pivots) + done = True + for i in range(K - 1): + k1 = order[i] + for j in range(i + 1, K): + k2 = order[j] + column: List[float] = [0] * num_species + for species_idx, coefficient in zip( + cls_species_idxs[k1], cls_coefficients[k1] + ): + column[species_idx] = coefficient + ok1 = True + for species_idx, coefficient in zip( + cls_species_idxs[k2], cls_coefficients[k2] + ): + column[species_idx] -= coefficient + if column[species_idx] < -_MIN: + ok1 = False + break + if not ok1: + continue + + done = False + cls_species_idxs[k1] = [] + cls_coefficients[k1] = [] + for col_idx, col_val in enumerate(column): + if abs(col_val) > _MIN: + cls_species_idxs[k1].append(col_idx) + cls_coefficients[k1].append(col_val) + pivots[k1] = -len(cls_species_idxs[k1]) diff --git a/deps/AMICI/python/sdist/amici/conserved_quantities_rref.py b/deps/AMICI/python/sdist/amici/conserved_quantities_rref.py new file mode 100644 index 000000000..46028e94b --- /dev/null +++ b/deps/AMICI/python/sdist/amici/conserved_quantities_rref.py @@ -0,0 +1,99 @@ +"""Find conserved quantities deterministically""" + +from typing import List, Literal, Optional, Union + +import numpy as np + + +def rref( + mat: np.array, round_ndigits: Optional[Union[Literal[False], int]] = None +) -> np.array: + """ + Bring matrix ``mat`` to reduced row echelon form + + see https://en.wikipedia.org/wiki/Row_echelon_form + + :param mat: Numpy float matrix to operate on (will be copied) + :param round_ndigits: Number of digits to round intermediary results to, + or ``False`` to disable rounding completely. + Helps to avoid numerical artifacts. + :returns: ``mat`` in rref form. + """ + # Rounding function + if round_ndigits is False: + # no-op + def _round(mat): + return mat + + else: + if round_ndigits is None: + # drop the least significant digit (more or less) + round_ndigits = -int(np.ceil(np.log10(np.spacing(1)))) + + def _round(mat): + mat = np.round(mat, round_ndigits) + mat[np.abs(mat) <= 10 ** (-round_ndigits)] = 0 + return mat + + # create a copy that will be modified + mat = mat.copy() + + lead = 0 + n_rows, n_columns = mat.shape + for r in range(n_rows): + if n_columns <= lead: + return mat + + i = r + while mat[i, lead] == 0: + i += 1 + if n_rows == i: + i = r + lead += 1 + if n_columns == lead: + return mat + + if i != r: + # Swap rows + mat[[i, r]] = mat[[r, i]] + # Divide row + mat[r] /= mat[r, lead] + for i in range(n_rows): + if i != r: + # Subtract multiple + mat[i] -= mat[i, lead] * mat[r] + mat = _round(mat) + lead += 1 + return mat + + +def pivots(mat: np.array) -> List[int]: + """Get indices of pivot columns in ``mat``, assumed to be in reduced row + echelon form""" + pivot_cols = [] + last_pivot_col = -1 + for i in range(mat.shape[0]): + for j in range(last_pivot_col + 1, mat.shape[1]): + if mat[i, j] != 0: + pivot_cols.append(j) + last_pivot_col = j + break + return pivot_cols + + +def nullspace_by_rref(mat: np.array) -> np.array: + """Compute basis of the nullspace of ``mat`` based on the reduced row + echelon form""" + rref_mat = rref(mat) + pivot_cols = pivots(rref_mat) + rows, cols = mat.shape + + basis = [] + for i in range(cols): + if i in pivot_cols: + continue + vec = [1.0 if i == j else 0.0 for j in range(cols)] + for pivot_row, pivot_col in enumerate(pivot_cols): + vec[pivot_col] -= rref_mat[pivot_row][i] + basis.append(vec) + return np.array(basis) diff --git a/deps/AMICI/python/sdist/amici/constants.py b/deps/AMICI/python/sdist/amici/constants.py deleted file mode 120000 index 5612f52bb..000000000 --- a/deps/AMICI/python/sdist/amici/constants.py +++ /dev/null @@ -1 +0,0 @@ -../../amici/constants.py \ No newline at end of file diff --git a/deps/AMICI/python/sdist/amici/constants.py b/deps/AMICI/python/sdist/amici/constants.py new file mode 100644 index 000000000..74b365889 --- /dev/null +++ b/deps/AMICI/python/sdist/amici/constants.py @@ -0,0 +1,36 @@ +""" +Constants +----------- +This module provides a central place to define native python enums and +constants that are used in multiple other modules +""" + +import enum + + +class SymbolId(str, enum.Enum): + """ + Defines the different fields in the symbol dict to which sbml entities + get parsed to. + + .. note:: This class inherits from str enabling direct comparison to + strings, which means that the species symbols can be accessed as + symbols['species'], which is convenient for debugging and symbols[ + SymbolId.SPECIES], which is how the field should be accessed + programmatically. + """ + + SPECIES = "species" + ALGEBRAIC_STATE = "algebraic_state" + ALGEBRAIC_EQUATION = "algebraic_equation" + PARAMETER = "parameter" + FIXED_PARAMETER = "fixed_parameter" + OBSERVABLE = "observable" + EXPRESSION = "expression" + SIGMAY = "sigmay" + LLHY = "llhy" + EVENT = "event" + EVENT_OBSERVABLE = "event_observable" + SIGMAZ = "sigmaz" + LLHZ = "llhz" + LLHRZ = "llhrz" diff --git a/deps/AMICI/python/sdist/amici/custom_commands.py b/deps/AMICI/python/sdist/amici/custom_commands.py deleted file mode 120000 index 531035884..000000000 --- a/deps/AMICI/python/sdist/amici/custom_commands.py +++ /dev/null @@ -1 +0,0 @@ -../../amici/custom_commands.py \ No newline at end of file diff --git a/deps/AMICI/python/sdist/amici/custom_commands.py b/deps/AMICI/python/sdist/amici/custom_commands.py new file mode 100644 index 000000000..46abfe329 --- /dev/null +++ b/deps/AMICI/python/sdist/amici/custom_commands.py @@ -0,0 +1,190 @@ +"""Custom setuptools commands for AMICI installation""" + +import os +import subprocess +import sys +from pathlib import Path + +from amici.swig import fix_typehints +from cmake_build_extension import BuildExtension, CMakeExtension +from setuptools.command.build_py import build_py +from setuptools.command.develop import develop +from setuptools.command.install import install +from setuptools.command.install_lib import install_lib +from setuptools.command.sdist import sdist + + +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 initialize_options(self): + super().initialize_options() + self.no_clibs = False + + def finalize_options(self): + if self.no_clibs: + self.no_clibs = True + super().finalize_options() + + +class AmiciDevelop(develop): + """Custom develop to build clibs""" + + # Passing --no-clibs allows to install the Python-only part of AMICI + user_options = develop.user_options + [ + ("no-clibs", None, "Don't build AMICI C++ extension"), + ] + + def initialize_options(self): + super().initialize_options() + self.no_clibs = False + + def finalize_options(self): + if self.no_clibs: + self.no_clibs = True + super().finalize_options() + + +class AmiciInstallLib(install_lib): + """Custom install to allow preserving of debug symbols""" + + def run(self): + """strip debug symbols + + Returns: + + """ + print("running AmiciInstallLib") + + if ( + os.environ.get("ENABLE_AMICI_DEBUGGING") == "TRUE" + and sys.platform == "darwin" + ): + search_dir = os.path.join(os.getcwd(), self.build_dir, "amici") + for file in os.listdir(search_dir): + if file.endswith(".so"): + subprocess.run( + [ + "dsymutil", + os.path.join(search_dir, file), + "-o", + os.path.join(search_dir, f"{file}.dSYM"), + ] + ) + + # Continue with the actual installation + super().run() + + +class AmiciSDist(sdist): + """Customized creation of source distribution""" + + def run(self): + """Setuptools entry-point""" + print(f"running {self.__class__.__name__}") + + save_git_version() + + super().run() + + +def save_git_version(): + """Create file with extended version string + + This requires git. We assume that whoever creates the sdist will work + inside a valid git repository. + """ + with open(os.path.join("amici", "git_version.txt"), "w") as f: + try: + cmd = [ + "git", + "describe", + "--abbrev=4", + "--dirty=-dirty", + "--always", + "--tags", + ] + subprocess.run(cmd, stdout=f) + except Exception as e: + print(e) + + +class AmiciBuildPy(build_py): + def run(self): + print(f"running {self.__class__.__name__}") + # We need build_ext before build_py, that all artifacts will be + # copied from the build dir + self.run_command("build_ext") + return super().run() + + +class AmiciBuildCMakeExtension(BuildExtension): + def finalize_options(self): + # Allow overriding the - since setuptools version 64 randomly named - + # setuptools/distutils temporary build directory via environment variable. + # This is useful for CI builds where we need the files in this directory + # for code coverage analysis. + if os.getenv("AMICI_BUILD_TEMP"): + self.build_temp = os.getenv("AMICI_BUILD_TEMP") + + super().finalize_options() + + def run(self): + """Copy the generated clibs to the extensions folder to be included in + the wheel + """ + print(f"running {self.__class__.__name__}") + + # custom flag to build without extensions + no_clibs = ( + "develop" in self.distribution.command_obj + and self.get_finalized_command("develop").no_clibs + ) + no_clibs |= ( + "install" in self.distribution.command_obj + and self.get_finalized_command("install").no_clibs + ) + + if no_clibs: + # Nothing to build + return + + # Continue with the actual extension building + result = super().run() + + if not self.dry_run: + # Fix SWIG-generated typehints + build_dir = self.build_lib if self.inplace == 0 else os.getcwd() + swig_py_module_path = Path(build_dir, "amici", "amici.py") + # this is class is used for the amici core extension, and any model + # extensions. if amici.py is present, this is the core extension. + if swig_py_module_path.is_file(): + print("updating typehints") + fix_typehints(swig_py_module_path, swig_py_module_path) + + return result + + def build_extension(self, ext: CMakeExtension) -> None: + # put some structure into CMake output + print("-" * 30, ext.name, "-" * 30, file=sys.stderr) + + # Some hack to be able to use distutils' potentially temporary build + # directory in CMake options: + # Any occurrence of `${build_dir}` will be replaced by said path. + build_dir = self.build_lib if self.inplace == 0 else os.getcwd() + build_dir = Path(build_dir).absolute().as_posix() + ext.cmake_configure_options = [ + x.replace("${build_dir}", build_dir) + for x in ext.cmake_configure_options + ] + + super().build_extension(ext) + + print("-" * 30, ext.name, "-" * 30, file=sys.stderr) diff --git a/deps/AMICI/python/sdist/amici/cxxcodeprinter.py b/deps/AMICI/python/sdist/amici/cxxcodeprinter.py deleted file mode 120000 index 4a7a4c588..000000000 --- a/deps/AMICI/python/sdist/amici/cxxcodeprinter.py +++ /dev/null @@ -1 +0,0 @@ -../../amici/cxxcodeprinter.py \ No newline at end of file diff --git a/deps/AMICI/python/sdist/amici/cxxcodeprinter.py b/deps/AMICI/python/sdist/amici/cxxcodeprinter.py new file mode 100644 index 000000000..e6e377b33 --- /dev/null +++ b/deps/AMICI/python/sdist/amici/cxxcodeprinter.py @@ -0,0 +1,346 @@ +"""C++ code generation""" +import itertools +import os +import re +from typing import Dict, Iterable, List, Optional, Tuple + +import sympy as sp +from sympy.codegen.rewriting import Optimization, optimize +from sympy.printing.cxx import CXX11CodePrinter +from sympy.utilities.iterables import numbered_symbols +from toposort import toposort + + +class AmiciCxxCodePrinter(CXX11CodePrinter): + """ + C++ code printer + + Attributes + ---------- + extract_cse: + Whether to extract common subexpression during code printing. + Currently controlled by environment variable ``AMICI_EXTRACT_CSE``. + optimizations: + Iterable of :class:`sympy.codegen.rewriting.Optimization`s to optimize + generated code (e.g. :data:`sympy.codegen.rewriting.Optimization` for + optimizations, such as ``log(1 + x)`` --> ``logp1(x)``). + Applying these optimizations is potentially quite costly. + """ + + optimizations: Iterable[Optimization] = () + + def __init__(self): + """Create code printer""" + super().__init__() + + # extract common subexpressions in matrix functions? + self.extract_cse = os.getenv("AMICI_EXTRACT_CSE", "0").lower() in ( + "1", + "on", + "true", + ) + + # Floating-point optimizations + # e.g., log(1 + x) --> logp1(x) + if self.optimizations: + self._fpoptimizer = lambda x: optimize(x, self.optimizations) + else: + self._fpoptimizer = None + + def doprint(self, expr: sp.Expr, assign_to: Optional[str] = None) -> str: + if self._fpoptimizer: + if isinstance(expr, list): + expr = list(map(self._fpoptimizer, expr)) + else: + expr = self._fpoptimizer(expr) + + try: + # floating point + code = super().doprint(expr, assign_to) + code = re.sub(r"(^|\W)M_PI(\W|$)", r"\1amici::pi\2", code) + + return code + except TypeError as e: + raise ValueError( + f'Encountered unsupported function in expression "{expr}"' + ) from e + + def _print_min_max(self, expr, cpp_fun: str, sympy_fun): + # C++ doesn't like mixing int and double for arguments for min/max, + # therefore, we just always convert to float + arg0 = ( + sp.Float(expr.args[0]) if expr.args[0].is_number else expr.args[0] + ) + if len(expr.args) == 1: + return self._print(arg0) + return "%s%s(%s, %s)" % ( + self._ns, + cpp_fun, + self._print(arg0), + self._print(sympy_fun(*expr.args[1:])), + ) + + def _print_Min(self, expr): + from sympy.functions.elementary.miscellaneous import Min + + return self._print_min_max(expr, "min", Min) + + def _print_Max(self, expr): + from sympy.functions.elementary.miscellaneous import Max + + return self._print_min_max(expr, "max", Max) + + def _get_sym_lines_array( + self, equations: sp.Matrix, variable: str, indent_level: int + ) -> List[str]: + """ + Generate C++ code for assigning symbolic terms in symbols to C++ array + `variable`. + + :param equations: + vectors of symbolic expressions + + :param variable: + name of the C++ array to assign to + + :param indent_level: + indentation level (number of leading blanks) + + :return: + C++ code as list of lines + """ + return [ + " " * indent_level + f"{variable}[{index}] = " + f"{self.doprint(math)};" + for index, math in enumerate(equations) + if math not in [0, 0.0] + ] + + def _get_sym_lines_symbols( + self, + symbols: sp.Matrix, + equations: sp.Matrix, + variable: str, + indent_level: int, + ) -> List[str]: + """ + Generate C++ code for where array elements are directly replaced with + their corresponding macro symbol + + :param symbols: + vectors of symbols that equations are assigned to + + :param equations: + vectors of expressions + + :param variable: + name of the C++ array to assign to, only used in comments + + :param indent_level: + indentation level (number of leading blanks) + + :return: + C++ code as list of lines + """ + indent = " " * indent_level + + def format_regular_line(symbol, math, index): + return ( + f"{indent}{self.doprint(symbol)} = {self.doprint(math)};" + f" // {variable}[{index}]".replace("\n", "\n" + indent) + ) + + if self.extract_cse: + # Extract common subexpressions + cse_sym_prefix = "__amici_cse_" + symbol_generator = numbered_symbols( + cls=sp.Symbol, prefix=cse_sym_prefix + ) + replacements, reduced_exprs = sp.cse( + equations, + symbols=symbol_generator, + order="none", + list=False, + ) + if replacements: + # we need toposort to handle the dependencies of extracted + # subexpressions + expr_dict = dict( + itertools.chain(zip(symbols, reduced_exprs), replacements) + ) + sorted_symbols = toposort( + { + identifier: { + s + for s in definition.free_symbols + if s in expr_dict + } + for (identifier, definition) in expr_dict.items() + } + ) + symbol_to_idx = {sym: idx for idx, sym in enumerate(symbols)} + + def format_line(symbol: sp.Symbol): + math = expr_dict[symbol] + if str(symbol).startswith(cse_sym_prefix): + return ( + f"{indent}const realtype " + f"{self.doprint(symbol)} " + f"= {self.doprint(math)};" + ) + elif math not in [0, 0.0]: + return format_regular_line( + symbol, math, symbol_to_idx[symbol] + ) + + return [ + line + for symbol_group in sorted_symbols + for symbol in sorted(symbol_group, key=str) + if (line := format_line(symbol)) + ] + + return [ + format_regular_line(sym, math, index) + for index, (sym, math) in enumerate(zip(symbols, equations)) + 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""" + return "true" if bool(expr) else "false" + + +def get_switch_statement( + condition: str, + cases: Dict[int, List[str]], + indentation_level: Optional[int] = 0, + indentation_step: Optional[str] = " " * 4, +): + """ + Generate code for switch statement + + :param condition: + Condition for switch + + :param cases: + Cases as dict with expressions as keys and statement as + list of strings + + :param indentation_level: + indentation level + + :param indentation_step: + indentation whitespace per level + + :return: + Code for switch expression as list of strings + """ + lines = [] + + if not cases: + return lines + + indent0 = indentation_level * indentation_step + indent1 = (indentation_level + 1) * indentation_step + indent2 = (indentation_level + 2) * indentation_step + for expression, statements in cases.items(): + if statements: + lines.extend( + [ + f"{indent1}case {expression}:", + *(f"{indent2}{statement}" for statement in statements), + f"{indent2}break;", + ] + ) + + if lines: + lines.insert(0, f"{indent0}switch({condition}) {{") + lines.append(indent0 + "}") + + return lines diff --git a/deps/AMICI/python/sdist/amici/de_export.py b/deps/AMICI/python/sdist/amici/de_export.py new file mode 100644 index 000000000..b1fa02c42 --- /dev/null +++ b/deps/AMICI/python/sdist/amici/de_export.py @@ -0,0 +1,4273 @@ +""" +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 +: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 + + :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] + + 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 + """ + + # 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)) + + if heavisides: + # only apply subs if necessary + for heaviside_sympy, heaviside_amici in heavisides: + dxdt = dxdt.subs(heaviside_sympy, heaviside_amici) + + return dxdt + + +class DEExporter: + """ + The DEExporter class generates AMICI C++ files for a model as + defined in symbolic expressions. + + :ivar model: + DE definition + + :ivar verbose: + more verbose output if True + + :ivar assume_pow_positivity: + if set to true, a special pow function is + used to avoid problems with state variables that may become negative + due to numerical errors + + :ivar compiler: + Absolute path to the compiler executable to be used to build the Python + extension, e.g. ``/usr/bin/clang``. + + :ivar functions: + carries C++ function signatures and other specifications + + :ivar model_name: + name of the model that will be used for compilation + + :ivar model_path: + path to the generated model specific files + + :ivar model_swig_path: + path to the generated swig files + + :ivar allow_reinit_fixpar_initcond: + indicates whether reinitialization of + initial states depending on fixedParameters is allowed for this model + + :ivar _build_hints: + If the given model uses special functions, this set contains hints for + model building. + + :ivar generate_sensitivity_code: + Specifies whether code for sensitivity computation is to be generated + + .. note:: + When importing large models (several hundreds of species or + parameters), import time can potentially be reduced by using multiple + CPU cores. This is controlled by setting the ``AMICI_IMPORT_NPROCS`` + environment variable to the number of parallel processes that are to be + used (default: 1). Note that for small models this may (slightly) + increase import times. + """ + + 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", + ): + """ + Generate AMICI C++ files for the DE provided to the constructor. + + :param de_model: + DE model definition + + :param outdir: + see :meth:`amici.de_export.DEExporter.set_paths` + + :param verbose: + verbosity level for logging, ``True``/``False`` default to + :data:`logging.Error`/:data:`logging.DEBUG` + + :param assume_pow_positivity: + if set to true, a special pow function is + used to avoid problems with state variables that may become + negative due to numerical errors + + :param compiler: Absolute path to the compiler executable to be used + to build the Python extension, e.g. ``/usr/bin/clang``. + + :param allow_reinit_fixpar_initcond: + see :class:`amici.de_export.DEExporter` + + :param generate_sensitivity_code: + specifies whether code required for sensitivity computation will be + generated + + :param model_name: + name of the model to be used during code generation + """ + set_log_level(logger, verbose) + + self.verbose: bool = logger.getEffectiveLevel() <= logging.DEBUG + self.assume_pow_positivity: bool = assume_pow_positivity + self.compiler: str = compiler + + self.model_path: str = "" + self.model_swig_path: str = "" + + self.set_name(model_name) + self.set_paths(outdir) + + # 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( + splines.spline_user_functions( + 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.allow_reinit_fixpar_initcond: bool = allow_reinit_fixpar_initcond + self._build_hints = set() + self.generate_sensitivity_code: bool = generate_sensitivity_code + + @log_execution_time("generating cpp code", logger) + def generate_model_code(self) -> None: + """ + Generates the native C++ code for the loaded model and a Matlab + script that can be run to compile a mex file from the C++ code + """ + with _monkeypatched( + sp.Pow, "_eval_derivative", _custom_pow_eval_derivative + ): + self._prepare_model_folder() + self._generate_c_code() + self._generate_m_code() + + @log_execution_time("compiling cpp code", logger) + def compile_model(self) -> None: + """ + Compiles the generated code it into a simulatable module + """ + self._compile_c_code(compiler=self.compiler, verbose=self.verbose) + + def _prepare_model_folder(self) -> None: + """ + Create model directory or remove all files if the output directory + already exists. + """ + os.makedirs(self.model_path, exist_ok=True) + + for file in os.listdir(self.model_path): + file_path = os.path.join(self.model_path, file) + if os.path.isfile(file_path): + os.remove(file_path) + + def _generate_c_code(self) -> None: + """ + Create C++ code files for the model based on + :attribute:`DEExporter.model`. + """ + for func_name, func_info in self.functions.items(): + if ( + func_name in sensi_functions + sparse_sensi_functions + and not self.generate_sensitivity_code + ): + continue + + if func_info.generate_body: + dec = log_execution_time(f"writing {func_name}.cpp", logger) + dec(self._write_function_file)(func_name) + + for name in self.model.sym_names(): + # only generate for those that have nontrivial implementation, + # check for both basic variables (not in functions) and function + # computed values + if ( + ( + name in self.functions + and not self.functions[name].body + and name not in nobody_functions + ) + or name not in self.functions + ) and len(self.model.sym(name)) == 0: + continue + self._write_index_files(name) + + self._write_wrapfunctions_cpp() + self._write_wrapfunctions_header() + self._write_model_header_cpp() + self._write_c_make_file() + self._write_swig_files() + self._write_module_setup() + + 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 + """ + + # Second order code is not yet implemented. Once this is done, + # those variables will have to be replaced by + # "self.model.true()", or the corresponding "model.self.o2flag" + nxtrue_rdata = self.model.num_states_rdata() + nytrue = self.model.num_obs() + nztrue = self.model.num_eventobs() + o2flag = 0 + + lines = [ + "% This compile script was automatically created from" + " Python SBML import.", + "% If mex compiler is set up within MATLAB, it can be run" + " from MATLAB ", + "% in order to compile a mex-file from the Python" + " generated C++ files.", + "", + f"modelName = '{self.model_name}';", + "amimodel.compileAndLinkModel(modelName, '', [], [], [], []);", + f"amimodel.generateMatlabWrapper({nxtrue_rdata}, " + f"{nytrue}, {self.model.num_par()}, " + f"{self.model.num_const()}, {nztrue}, {o2flag}, ...", + " [], ['simulate_' modelName '.m'], modelName, ...", + " 'lin', 1, 1);", + ] + + # write compile script (for mex) + compile_script = os.path.join(self.model_path, "compileMexFile.m") + with open(compile_script, "w") as fileout: + fileout.write("\n".join(lines)) + + def _get_index(self, name: str) -> Dict[sp.Symbol, int]: + """ + Compute indices for a symbolic array. + :param name: + key in self.model._syms for which to obtain the index. + :return: + a dictionary of symbol/index pairs. + """ + if name in self.model.sym_names(): + if name in sparse_functions: + symbols = self.model.sparsesym(name) + else: + symbols = self.model.sym(name).T + else: + raise ValueError(f"Unknown symbolic array: {name}") + + return { + strip_pysb(symbol).name: index + for index, symbol in enumerate(symbols) + } + + def _write_index_files(self, name: str) -> None: + """ + Write index file for a symbolic array. + + :param name: + key in ``self.model._syms`` for which the respective file should + be written + """ + if name not in self.model.sym_names(): + raise ValueError(f"Unknown symbolic array: {name}") + + symbols = ( + self.model.sparsesym(name) + if name in sparse_functions + else self.model.sym(name).T + ) + if not len(symbols): + return + + # flatten multiobs + if isinstance(next(iter(symbols), None), list): + symbols = [symbol for obs in symbols for symbol in obs] + + lines = [] + for index, symbol in enumerate(symbols): + symbol_name = strip_pysb(symbol) + if str(symbol) == "0": + continue + if str(symbol_name) == "": + raise ValueError(f'{name} contains a symbol called ""') + lines.append(f"#define {symbol_name} {name}[{index}]") + if name == "stau": + # we only need a single macro, as all entries have the same symbol + break + + filename = os.path.join(self.model_path, f"{name}.h") + with open(filename, "w") as fileout: + fileout.write("\n".join(lines)) + + def _write_function_file(self, function: str) -> None: + """ + Generate equations and write the C++ code for the function + ``function``. + + :param function: + name of the function to be written (see ``self.functions``) + """ + + # first generate the equations to make sure we have everything we + # need in subsequent steps + if function in sparse_functions: + equations = self.model.sparseeq(function) + elif ( + not self.allow_reinit_fixpar_initcond + and function == "sx0_fixedParameters" + ): + # Not required. Will create empty function body. + equations = sp.Matrix() + elif function == "create_splines": + # nothing to do + pass + else: + equations = self.model.eq(function) + + # function body + if function == "create_splines": + body = self._get_create_splines_body() + else: + body = self._get_function_body(function, equations) + if not body: + return + + # colptrs / rowvals for sparse matrices + if function in sparse_functions: + lines = self._generate_function_index(function, "colptrs") + lines.extend(self._generate_function_index(function, "rowvals")) + lines.append("\n\n") + else: + lines = [] + + # function header + lines.extend( + [ + '#include "amici/symbolic_functions.h"', + '#include "amici/defines.h"', + '#include "sundials/sundials_types.h"', + "", + "#include ", + "#include ", + "", + ] + ) + if function == "create_splines": + lines += [ + '#include "amici/splinefunctions.h"', + "#include ", + ] + + func_info = self.functions[function] + + # extract symbols that need definitions from signature + # don't add includes for files that won't be generated. + # Unfortunately we cannot check for `self.functions[sym].body` + # here since it may not have been generated yet. + for sym in re.findall( + r"const (?:realtype|double) \*([\w]+)[0]*(?:,|$)", + func_info.arguments(self.model.is_ode()), + ): + if sym not in self.model.sym_names(): + continue + + if sym in sparse_functions: + iszero = smart_is_zero_matrix(self.model.sparseeq(sym)) + elif sym in self.functions: + iszero = smart_is_zero_matrix(self.model.eq(sym)) + else: + iszero = len(self.model.sym(sym)) == 0 + + if iszero and not ( + (sym == "y" and "Jy" in function) + or ( + sym == "w" + and "xdot" in function + and len(self.model.sym(sym)) + ) + ): + continue + + lines.append(f'#include "{sym}.h"') + + # include return symbols + if ( + function in self.model.sym_names() + and function not in non_unique_id_symbols + ): + lines.append(f'#include "{function}.h"') + + lines.extend( + [ + "", + "namespace amici {", + f"namespace model_{self.model_name} {{", + "", + f"{func_info.return_type} {function}_{self.model_name}" + f"({func_info.arguments(self.model.is_ode())}){{", + ] + ) + + if self.assume_pow_positivity and func_info.assume_pow_positivity: + pow_rx = re.compile(r"(^|\W)std::pow\(") + body = [ + # execute this twice to catch cases where the ending '(' would + # be the starting (^|\W) for the following match + pow_rx.sub( + r"\1amici::pos_pow(", + pow_rx.sub(r"\1amici::pos_pow(", line), + ) + for line in body + ] + + self.functions[function].body = body + + lines += body + lines.extend( + [ + "}", + "", + f"}} // namespace model_{self.model_name}", + "} // namespace amici\n", + ] + ) + + # check custom functions + for fun in CUSTOM_FUNCTIONS: + if "include" in fun and any(fun["c++"] in line for line in lines): + if "build_hint" in fun: + self._build_hints.add(fun["build_hint"]) + lines.insert(0, fun["include"]) + + # if not body is None: + filename = os.path.join(self.model_path, f"{function}.cpp") + with open(filename, "w") as fileout: + fileout.write("\n".join(lines)) + + def _generate_function_index( + self, function: str, indextype: Literal["colptrs", "rowvals"] + ) -> List[str]: + """ + Generate equations and C++ code for the function ``function``. + + :param function: + name of the function to be written (see ``self.functions``) + + :param indextype: + type of index {'colptrs', 'rowvals'} + + :returns: + The code lines for the respective function index file + """ + if indextype == "colptrs": + values = self.model.colptrs(function) + setter = "indexptrs" + elif indextype == "rowvals": + values = self.model.rowvals(function) + setter = "indexvals" + else: + raise ValueError( + "Invalid value for indextype, must be colptrs or " + f"rowvals: {indextype}" + ) + + # function signature + if function in multiobs_functions: + signature = f"(SUNMatrixWrapper &{function}, int index)" + else: + signature = f"(SUNMatrixWrapper &{function})" + + lines = [ + '#include "amici/sundials_matrix_wrapper.h"', + '#include "sundials/sundials_types.h"', + "", + "#include ", + "#include ", + "", + "namespace amici {", + f"namespace model_{self.model_name} {{", + "", + ] + + # Generate static array with indices + if len(values): + static_array_name = f"{function}_{indextype}_{self.model_name}_" + if function in multiobs_functions: + # list of index vectors + lines.append( + "static constexpr std::array, {len(values)}> " + f"{static_array_name} = {{{{" + ) + lines.extend( + [ + " {" + ", ".join(map(str, index_vector)) + "}, " + for index_vector in values + ] + ) + lines.append("}};") + else: + # single index vector + lines.extend( + [ + "static constexpr std::array {static_array_name} = {{", + " " + ", ".join(map(str, values)), + "};", + ] + ) + + lines.extend( + [ + "", + f"void {function}_{indextype}_{self.model_name}{signature}{{", + ] + ) + + if len(values): + if function in multiobs_functions: + lines.append( + f" {function}.set_{setter}" + f"(gsl::make_span({static_array_name}[index]));" + ) + else: + lines.append( + f" {function}.set_{setter}" + f"(gsl::make_span({static_array_name}));" + ) + + lines.extend( + [ + "}" "", + f"}} // namespace model_{self.model_name}", + "} // namespace amici\n", + ] + ) + + return lines + + def _get_function_body( + self, function: str, equations: sp.Matrix + ) -> List[str]: + """ + Generate C++ code for body of function ``function``. + + :param function: + name of the function to be written (see ``self.functions``) + + :param equations: + symbolic definition of the function body + + :return: + generated C++ code + """ + lines = [] + + if len(equations) == 0 or ( + isinstance(equations, (sp.Matrix, sp.ImmutableDenseMatrix)) + and min(equations.shape) == 0 + ): + # dJydy is a list + return lines + + if not self.allow_reinit_fixpar_initcond and function in { + "sx0_fixedParameters", + "x0_fixedParameters", + }: + return lines + + if function == "sx0_fixedParameters": + # here we only want to overwrite values where x0_fixedParameters + # was applied + + lines.extend( + [ + # Keep list of indices of fixed parameters occurring in x0 + " static const std::array _x0_fixedParameters_idxs = {", + " " + + ", ".join( + str(x) for x in self.model._x0_fixedParameters_idx + ), + " };", + "", + # Set all parameters that are to be reset to 0, so that the + # switch statement below only needs to handle non-zero entries + # (which usually reduces file size and speeds up + # compilation significantly). + " for(auto idx: reinitialization_state_idxs) {", + " if(std::find(_x0_fixedParameters_idxs.cbegin(), " + "_x0_fixedParameters_idxs.cend(), idx) != " + "_x0_fixedParameters_idxs.cend())\n" + " sx0_fixedParameters[idx] = 0.0;", + " }", + ] + ) + + cases = {} + for ipar in range(self.model.num_par()): + expressions = [] + for index, formula in zip( + self.model._x0_fixedParameters_idx, equations[:, ipar] + ): + if not formula.is_zero: + expressions.extend( + [ + f"if(std::find(" + "reinitialization_state_idxs.cbegin(), " + f"reinitialization_state_idxs.cend(), {index}) != " + "reinitialization_state_idxs.cend())", + f" {function}[{index}] = " + f"{self.model._code_printer.doprint(formula)};", + ] + ) + cases[ipar] = expressions + lines.extend(get_switch_statement("ip", cases, 1)) + + elif function == "x0_fixedParameters": + for index, formula in zip( + self.model._x0_fixedParameters_idx, equations + ): + 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)};" + ) + + elif function in event_functions: + cases = { + ie: self.model._code_printer._get_sym_lines_array( + equations[ie], function, 0 + ) + for ie in range(self.model.num_events()) + if not smart_is_zero_matrix(equations[ie]) + } + lines.extend(get_switch_statement("ie", cases, 1)) + + elif function in event_sensi_functions: + outer_cases = {} + for ie, inner_equations in enumerate(equations): + inner_lines = [] + inner_cases = { + ipar: self.model._code_printer._get_sym_lines_array( + inner_equations[:, ipar], function, 0 + ) + for ipar in range(self.model.num_par()) + if not smart_is_zero_matrix(inner_equations[:, ipar]) + } + inner_lines.extend(get_switch_statement("ip", inner_cases, 0)) + outer_cases[ie] = copy.copy(inner_lines) + lines.extend(get_switch_statement("ie", outer_cases, 1)) + + elif ( + function in sensi_functions + and equations.shape[1] == self.model.num_par() + ): + cases = { + ipar: self.model._code_printer._get_sym_lines_array( + equations[:, ipar], function, 0 + ) + for ipar in range(self.model.num_par()) + if not smart_is_zero_matrix(equations[:, ipar]) + } + lines.extend(get_switch_statement("ip", cases, 1)) + elif function in multiobs_functions: + if function == "dJydy": + cases = { + iobs: self.model._code_printer._get_sym_lines_array( + equations[iobs], function, 0 + ) + for iobs in range(self.model.num_obs()) + if not smart_is_zero_matrix(equations[iobs]) + } + else: + cases = { + iobs: self.model._code_printer._get_sym_lines_array( + equations[:, iobs], function, 0 + ) + for iobs in range(equations.shape[1]) + if not smart_is_zero_matrix(equations[:, iobs]) + } + if function.startswith(("Jz", "dJz", "Jrz", "dJrz")): + iterator = "iz" + else: + iterator = "iy" + lines.extend(get_switch_statement(iterator, cases, 1)) + + elif ( + function in self.model.sym_names() + and function not in non_unique_id_symbols + ): + if function in sparse_functions: + 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 + ) + + else: + lines += self.model._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: + return [" return {};"] + + ind4 = " " * 4 + ind8 = " " * 8 + + body = ["return {"] + for ispl, spline in enumerate(self.model.splines): + if isinstance(spline.nodes, splines.UniformGrid): + nodes = ( + f"{ind8}{{{spline.nodes.start}, {spline.nodes.stop}}}, " + ) + else: + nodes = f"{ind8}{{{', '.join(map(str, spline.nodes))}}}, " + + # vector with the node values + values = ( + f"{ind8}{{{', '.join(map(str, spline.values_at_nodes))}}}, " + ) + # vector with the slopes + if spline.derivatives_by_fd: + slopes = f"{ind8}{{}}," + else: + slopes = f"{ind8}{{{', '.join(map(str, spline.derivatives_at_nodes))}}}," + + body.extend( + [ + f"{ind4}HermiteSpline(", + nodes, + values, + slopes, + ] + ) + + bc_to_cpp = { + None: "SplineBoundaryCondition::given, ", + "zeroderivative": "SplineBoundaryCondition::zeroDerivative, ", + "natural": "SplineBoundaryCondition::natural, ", + "zeroderivative+natural": "SplineBoundaryCondition::naturalZeroDerivative, ", + "periodic": "SplineBoundaryCondition::periodic, ", + } + for bc in spline.bc: + try: + body.append(ind8 + bc_to_cpp[bc]) + except KeyError: + raise ValueError( + f"Unknown boundary condition '{bc}' " + "found in spline object" + ) + extrapolate_to_cpp = { + None: "SplineExtrapolation::noExtrapolation, ", + "polynomial": "SplineExtrapolation::polynomial, ", + "constant": "SplineExtrapolation::constant, ", + "linear": "SplineExtrapolation::linear, ", + "periodic": "SplineExtrapolation::periodic, ", + } + for extr in spline.extrapolate: + try: + body.append(ind8 + extrapolate_to_cpp[extr]) + except KeyError: + raise ValueError( + f"Unknown extrapolation '{extr}' " + "found in spline object" + ) + line = ind8 + line += "true, " if spline.derivatives_by_fd else "false, " + line += ( + "true, " + if isinstance(spline.nodes, splines.UniformGrid) + else "false, " + ) + line += "true" if spline.logarithmic_parametrization else "false" + body.append(line) + body.append(f"{ind4}),") + + body.append("};") + return [" " + line for line in body] + + def _write_wrapfunctions_cpp(self) -> None: + """ + Write model-specific 'wrapper' file (``wrapfunctions.cpp``). + """ + template_data = {"MODELNAME": self.model_name} + apply_template( + os.path.join(amiciSrcPath, "wrapfunctions.template.cpp"), + os.path.join(self.model_path, "wrapfunctions.cpp"), + template_data, + ) + + def _write_wrapfunctions_header(self) -> None: + """ + Write model-specific header file (``wrapfunctions.h``). + """ + template_data = {"MODELNAME": str(self.model_name)} + apply_template( + os.path.join(amiciSrcPath, "wrapfunctions.template.h"), + os.path.join(self.model_path, "wrapfunctions.h"), + template_data, + ) + + def _write_model_header_cpp(self) -> None: + """ + Write model-specific header and cpp file (MODELNAME.{h,cpp}). + """ + model_type = "ODE" if self.model.is_ode() else "DAE" + tpl_data = { + "MODEL_TYPE_LOWER": model_type.lower(), + "MODEL_TYPE_UPPER": model_type, + "MODELNAME": self.model_name, + "NX_RDATA": self.model.num_states_rdata(), + "NXTRUE_RDATA": self.model.num_states_rdata(), + "NX_SOLVER": self.model.num_states_solver(), + "NXTRUE_SOLVER": self.model.num_states_solver(), + "NX_SOLVER_REINIT": self.model.num_state_reinits(), + "NY": self.model.num_obs(), + "NYTRUE": self.model.num_obs(), + "NZ": self.model.num_eventobs(), + "NZTRUE": self.model.num_eventobs(), + "NEVENT": self.model.num_events(), + "NOBJECTIVE": "1", + "NSPL": len(self.model.splines), + "NW": len(self.model.sym("w")), + "NDWDP": len( + self.model.sparsesym( + "dwdp", force_generate=self.generate_sensitivity_code + ) + ), + "NDWDX": len(self.model.sparsesym("dwdx")), + "NDWDW": len(self.model.sparsesym("dwdw")), + "NDXDOTDW": len(self.model.sparsesym("dxdotdw")), + "NDXDOTDP_EXPLICIT": len( + self.model.sparsesym( + "dxdotdp_explicit", + force_generate=self.generate_sensitivity_code, + ) + ), + "NDXDOTDX_EXPLICIT": len(self.model.sparsesym("dxdotdx_explicit")), + "NDJYDY": "std::vector{%s}" + % ",".join(str(len(x)) for x in self.model.sparsesym("dJydy")), + "NDXRDATADXSOLVER": len(self.model.sparsesym("dx_rdatadx_solver")), + "NDXRDATADTCL": len(self.model.sparsesym("dx_rdatadtcl")), + "NDTOTALCLDXRDATA": len(self.model.sparsesym("dtotal_cldx_rdata")), + "UBW": self.model.num_states_solver(), + "LBW": self.model.num_states_solver(), + "NP": self.model.num_par(), + "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( + self.model.val("k") + )[1:-1], + "PARAMETER_NAMES_INITIALIZER_LIST": self._get_symbol_name_initializer_list( + "p" + ), + "STATE_NAMES_INITIALIZER_LIST": self._get_symbol_name_initializer_list( + "x_rdata" + ), + "FIXED_PARAMETER_NAMES_INITIALIZER_LIST": self._get_symbol_name_initializer_list( + "k" + ), + "OBSERVABLE_NAMES_INITIALIZER_LIST": self._get_symbol_name_initializer_list( + "y" + ), + "OBSERVABLE_TRAFO_INITIALIZER_LIST": "\n".join( + f"ObservableScaling::{trafo.value}, // y[{idx}]" + for idx, trafo in enumerate( + self.model.get_observable_transformations() + ) + ), + "EXPRESSION_NAMES_INITIALIZER_LIST": self._get_symbol_name_initializer_list( + "w" + ), + "PARAMETER_IDS_INITIALIZER_LIST": self._get_symbol_id_initializer_list( + "p" + ), + "STATE_IDS_INITIALIZER_LIST": self._get_symbol_id_initializer_list( + "x_rdata" + ), + "FIXED_PARAMETER_IDS_INITIALIZER_LIST": self._get_symbol_id_initializer_list( + "k" + ), + "OBSERVABLE_IDS_INITIALIZER_LIST": self._get_symbol_id_initializer_list( + "y" + ), + "EXPRESSION_IDS_INITIALIZER_LIST": self._get_symbol_id_initializer_list( + "w" + ), + "STATE_IDXS_SOLVER_INITIALIZER_LIST": ", ".join( + str(idx) + for idx, state in enumerate(self.model.states()) + if not state.has_conservation_law() + ), + "REINIT_FIXPAR_INITCOND": AmiciCxxCodePrinter.print_bool( + self.allow_reinit_fixpar_initcond + ), + "AMICI_VERSION_STRING": __version__, + "AMICI_COMMIT_STRING": __commit__, + "W_RECURSION_DEPTH": self.model._w_recursion_depth, + "QUADRATIC_LLH": AmiciCxxCodePrinter.print_bool( + self.model._has_quadratic_nllh + ), + "ROOT_INITIAL_VALUES": ", ".join( + map( + lambda event: AmiciCxxCodePrinter.print_bool( + event.get_initial_value() + ), + self.model.events(), + ) + ), + "Z2EVENT": ", ".join(map(str, self.model._z2event)), + "ID": ", ".join( + ( + str(float(isinstance(s, DifferentialState))) + for s in self.model.states() + if not s.has_conservation_law() + ) + ), + } + + for func_name, func_info in self.functions.items(): + if func_name in nobody_functions: + continue + + if not func_info.body: + tpl_data[f"{func_name.upper()}_DEF"] = "" + + if ( + func_name in sensi_functions + sparse_sensi_functions + and not self.generate_sensitivity_code + ): + impl = "" + else: + impl = get_model_override_implementation( + func_name, + self.model_name, + self.model.is_ode(), + nobody=True, + ) + + tpl_data[f"{func_name.upper()}_IMPL"] = impl + + if func_name in sparse_functions: + for indexfield in ["colptrs", "rowvals"]: + if ( + func_name in sparse_sensi_functions + and not self.generate_sensitivity_code + ): + impl = "" + else: + impl = get_sunindex_override_implementation( + func_name, + self.model_name, + indexfield, + nobody=True, + ) + tpl_data[ + f"{func_name.upper()}_{indexfield.upper()}_DEF" + ] = "" + tpl_data[ + f"{func_name.upper()}_{indexfield.upper()}_IMPL" + ] = 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()}_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_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_IMPL" + ] = get_sunindex_override_implementation( + func_name, self.model_name, "rowvals" + ) + + if self.model.num_states_solver() == self.model.num_states_rdata(): + tpl_data["X_RDATA_DEF"] = "" + tpl_data["X_RDATA_IMPL"] = "" + + tpl_data = {k: str(v) for k, v in tpl_data.items()} + + apply_template( + os.path.join(amiciSrcPath, "model_header.template.h"), + os.path.join(self.model_path, f"{self.model_name}.h"), + tpl_data, + ) + + apply_template( + os.path.join(amiciSrcPath, "model.template.cpp"), + os.path.join(self.model_path, f"{self.model_name}.cpp"), + tpl_data, + ) + + def _get_symbol_name_initializer_list(self, name: str) -> str: + """ + Get SBML name initializer list for vector of names for the given + model entity + + :param name: + any key present in ``self.model._syms`` + + :return: + Template initializer list of names + """ + return "\n".join( + f'"{symbol}", // {name}[{idx}]' + for idx, symbol in enumerate(self.model.name(name)) + ) + + def _get_symbol_id_initializer_list(self, name: str) -> str: + """ + Get C++ initializer list for vector of names for the given model + entity + + :param name: + any key present in ``self.model._syms`` + + :return: + Template initializer list of ids + """ + return "\n".join( + f'"{self.model._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"), + ) + and f != "main.cpp" + ) + + template_data = { + "MODELNAME": self.model_name, + "SOURCES": sources, + "AMICI_VERSION": __version__, + } + apply_template( + MODEL_CMAKE_TEMPLATE_FILE, + Path(self.model_path, "CMakeLists.txt"), + template_data, + ) + + def _write_swig_files(self) -> None: + """Write SWIG interface files for this model.""" + Path(self.model_swig_path).mkdir(exist_ok=True) + template_data = {"MODELNAME": self.model_name} + apply_template( + Path(amiciSwigPath, "modelname.template.i"), + Path(self.model_swig_path, self.model_name + ".i"), + template_data, + ) + shutil.copy( + SWIG_CMAKE_TEMPLATE_FILE, + Path(self.model_swig_path, "CMakeLists.txt"), + ) + + def _write_module_setup(self) -> None: + """ + Create a setuptools ``setup.py`` file for compile the model module. + """ + + template_data = { + "MODELNAME": self.model_name, + "AMICI_VERSION": __version__, + "PACKAGE_VERSION": "0.1.0", + } + apply_template( + Path(amiciModulePath, "setup.template.py"), + Path(self.model_path, "setup.py"), + template_data, + ) + apply_template( + Path(amiciModulePath, "MANIFEST.template.in"), + Path(self.model_path, "MANIFEST.in"), + {}, + ) + # write __init__.py for the model module + Path(self.model_path, self.model_name).mkdir(exist_ok=True) + + apply_template( + Path(amiciModulePath, "__init__.template.py"), + Path(self.model_path, self.model_name, "__init__.py"), + template_data, + ) + + def set_paths(self, output_dir: Optional[Union[str, Path]] = None) -> None: + """ + Set output paths for the model and create if necessary + + :param output_dir: + relative or absolute path where the generated model + code is to be placed. If ``None``, this will default to + ``amici-{self.model_name}`` in the current working directory. + will be created if it does not exist. + + """ + if output_dir is None: + output_dir = os.path.join(os.getcwd(), f"amici-{self.model_name}") + + self.model_path = os.path.abspath(output_dir) + self.model_swig_path = os.path.join(self.model_path, "swig") + + def set_name(self, model_name: str) -> None: + """ + Sets the model name + + :param model_name: + name of the model (may only contain upper and lower case letters, + digits and underscores, and must not start with a digit) + """ + if not is_valid_identifier(model_name): + raise ValueError( + f"'{model_name}' is not a valid model name. " + "Model name may only contain upper and lower case letters, " + "digits and underscores, and must not start with a digit." + ) + + 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, + observables... . Identifiers may only contain upper and lower case letters, + digits and underscores, and must not start with a digit. + + :param x: + string to check + + :return: + ``True`` if valid, ``False`` otherwise + """ + + return IDENTIFIER_PATTERN.match(x) is not None + + +@contextlib.contextmanager +def _monkeypatched(obj: object, name: str, patch: Any): + """ + Temporarily monkeypatches an object. + + :param obj: + object to be patched + + :param name: + name of the attribute to be patched + + :param patch: + patched value + """ + pre_patched_value = getattr(obj, name) + setattr(obj, name, patch) + try: + yield object + finally: + setattr(obj, name, pre_patched_value) + + +def _custom_pow_eval_derivative(self, s): + """ + Custom Pow derivative that removes a removable singularity for + ``self.base == 0`` and ``self.base.diff(s) == 0``. This function is + intended to be monkeypatched into :py:method:`sympy.Pow._eval_derivative`. + + :param self: + sp.Pow class + + :param s: + variable with respect to which the derivative will be computed + """ + dbase = self.base.diff(s) + dexp = self.exp.diff(s) + part1 = sp.Pow(self.base, self.exp - 1) * self.exp * dbase + part2 = self * dexp * sp.log(self.base) + if self.base.is_nonzero or dbase.is_nonzero or part2.is_zero: + # first piece never applies or is zero anyways + return part1 + part2 + + return part1 + sp.Piecewise( + (self.base, sp.And(sp.Eq(self.base, 0), sp.Eq(dbase, 0))), + (part2, True), + ) + + +def _jacobian_element(i, j, eq_i, sym_var_j): + """Compute a single element of a jacobian""" + return (i, j), eq_i.diff(sym_var_j) + + +def _parallel_applyfunc(obj: sp.Matrix, func: Callable) -> sp.Matrix: + """Parallel implementation of sympy's Matrix.applyfunc""" + if (n_procs := int(os.environ.get("AMICI_IMPORT_NPROCS", 1))) == 1: + # serial + return obj.applyfunc(func) + + # parallel + from multiprocessing import get_context + from pickle import PicklingError + + from sympy.matrices.dense import DenseMatrix + + # "spawn" should avoid potential deadlocks occurring with fork + # see e.g. https://stackoverflow.com/a/66113051 + ctx = get_context("spawn") + with ctx.Pool(n_procs) as p: + try: + if isinstance(obj, DenseMatrix): + return obj._new(obj.rows, obj.cols, p.map(func, obj)) + elif isinstance(obj, sp.SparseMatrix): + dok = obj.todok() + mapped = p.map(func, dok.values()) + dok = {k: v for k, v in zip(dok.keys(), mapped) if v != 0} + return obj._new(obj.rows, obj.cols, dok) + else: + raise ValueError(f"Unsupported matrix type {type(obj)}") + except PicklingError as e: + raise ValueError( + f"Couldn't pickle {func}. This is likely because the argument " + "was not a module-level function. Either rewrite the argument " + "to a module-level function or disable parallelization by " + "setting `AMICI_IMPORT_NPROCS=1`." + ) from e diff --git a/deps/AMICI/python/sdist/amici/de_model.py b/deps/AMICI/python/sdist/amici/de_model.py new file mode 100644 index 000000000..77d9013ad --- /dev/null +++ b/deps/AMICI/python/sdist/amici/de_model.py @@ -0,0 +1,715 @@ +"""Objects for AMICI's internal differential equation model representation""" +import abc +import numbers +from typing import Dict, Optional, Set, SupportsFloat, Union + +import sympy as sp + +from .import_utils import ( + RESERVED_SYMBOLS, + ObservableTransformation, + cast_to_sym, + generate_measurement_symbol, + generate_regularization_symbol, +) + +__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: Union[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(ConservationLaw, self).__init__(identifier, name, value) + + def get_ncoeff(self, state_id) -> Union[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, solution is given by + ``formula == 0`` + """ + super(AlgebraicEquation, self).__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: Optional[ConservationLaw] = 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(AlgebraicState, self).__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(DifferentialState, self).__init__(identifier, name, init) + self._dt = cast_to_sym(dt, "dt") + self._conservation_law: Union[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: Union[sp.Symbol, None] = None + + def __init__( + self, + identifier: sp.Symbol, + name: str, + value: sp.Expr, + measurement_symbol: Optional[sp.Symbol] = None, + transformation: Optional[ + ObservableTransformation + ] = ObservableTransformation.LIN, + ): + """ + 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(Observable, self).__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: Optional[sp.Symbol] = None, + transformation: Optional[ObservableTransformation] = "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(EventObservable, self).__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(Sigma, self).__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(Expression, self).__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(Parameter, self).__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(Constant, self).__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(LogLikelihood, self).__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: Union[sp.Expr, None], + initial_value: Optional[bool] = 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(Event, self).__init__(identifier, name, value) + # add the Event specific components + self._state_update = state_update + self._initial_value = initial_value + + 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() + ) diff --git a/deps/AMICI/python/sdist/amici/gradient_check.py b/deps/AMICI/python/sdist/amici/gradient_check.py deleted file mode 120000 index 3402ef082..000000000 --- a/deps/AMICI/python/sdist/amici/gradient_check.py +++ /dev/null @@ -1 +0,0 @@ -../../amici/gradient_check.py \ No newline at end of file diff --git a/deps/AMICI/python/sdist/amici/gradient_check.py b/deps/AMICI/python/sdist/amici/gradient_check.py new file mode 100644 index 000000000..27e2d671d --- /dev/null +++ b/deps/AMICI/python/sdist/amici/gradient_check.py @@ -0,0 +1,339 @@ +""" +Finite Difference Check +----------------------- +This module provides functions to automatically check correctness of amici +computed sensitivities using finite difference approximations +""" + +import copy +from typing import Callable, List, Optional, Sequence + +import numpy as np + +from . import ( + AMICI_SUCCESS, + ExpData, + Model, + ParameterScaling, + ReturnData, + SensitivityMethod, + SensitivityOrder, + Solver, + runAmiciSimulation, +) + + +def check_finite_difference( + x0: Sequence[float], + model: Model, + solver: Solver, + edata: ExpData, + ip: int, + fields: List[str], + atol: Optional[float] = 1e-4, + rtol: Optional[float] = 1e-4, + epsilon: Optional[float] = 1e-3, +) -> None: + """ + Checks the computed sensitivity based derivatives against a finite + difference approximation. + + :param x0: + parameter value at which to check finite difference approximation + + :param model: + amici model + + :param solver: + amici solver + + :param edata: + exp data + + :param ip: + parameter index + + :param fields: + rdata fields for which to check the gradient + + :param atol: + absolute tolerance for comparison + + :param rtol: + relative tolerance for comparison + + :param epsilon: + finite difference step-size + + """ + og_sensitivity_order = solver.getSensitivityOrder() + og_parameters = model.getParameters() + og_plist = model.getParameterList() + if edata: + og_eplist = edata.plist + + # sensitivity + p = copy.deepcopy(x0) + plist = [ip] + + model.setParameters(p) + model.setParameterList(plist) + if edata: + edata.plist = plist + + # simulation with gradient + if int(og_sensitivity_order) < int(SensitivityOrder.first): + solver.setSensitivityOrder(SensitivityOrder.first) + rdata = runAmiciSimulation(model, solver, edata) + if rdata["status"] != AMICI_SUCCESS: + raise AssertionError(f"Simulation failed (status {rdata['status']}") + + # finite difference + solver.setSensitivityOrder(SensitivityOrder.none) + + pf = copy.deepcopy(x0) + pb = copy.deepcopy(x0) + pscale = model.getParameterScale()[ip] + if x0[ip] == 0 or pscale != int(ParameterScaling.none): + pf[ip] += epsilon / 2 + pb[ip] -= epsilon / 2 + else: + pf[ip] *= 1 + epsilon / 2 + pb[ip] /= 1 + epsilon / 2 + + # forward: + model.setParameters(pf) + rdataf = runAmiciSimulation(model, solver, edata) + if rdataf["status"] != AMICI_SUCCESS: + raise AssertionError(f"Simulation failed (status {rdataf['status']}") + + # backward: + model.setParameters(pb) + rdatab = runAmiciSimulation(model, solver, edata) + if rdatab["status"] != AMICI_SUCCESS: + raise AssertionError(f"Simulation failed (status {rdatab['status']}") + + for field in fields: + sensi_raw = rdata[f"s{field}"] + fd = (rdataf[field] - rdatab[field]) / (pf[ip] - pb[ip]) + if len(sensi_raw.shape) == 1: + sensi = sensi_raw[0] + elif len(sensi_raw.shape) == 2: + sensi = sensi_raw[:, 0] + elif len(sensi_raw.shape) == 3: + sensi = sensi_raw[:, 0, :] + else: + raise NotImplementedError() + + _check_close(sensi, fd, atol=atol, rtol=rtol, field=field, ip=ip) + + solver.setSensitivityOrder(og_sensitivity_order) + model.setParameters(og_parameters) + model.setParameterList(og_plist) + if edata: + edata.plist = og_eplist + + +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, + check_least_squares: bool = True, + skip_zero_pars: bool = False, +) -> None: + """ + Finite differences check for likelihood gradient. + + :param model: + amici model + + :param solver: + amici solver + + :param edata: + exp data + + :param atol: + absolute tolerance for comparison + + :param rtol: + relative tolerance for comparison + + :param epsilon: + finite difference step-size + + :param check_least_squares: + whether to check least squares related values. + + :param skip_zero_pars: + whether to perform FD checks for parameters that are zero + + """ + p = np.array(model.getParameters()) + + og_sens_order = solver.getSensitivityOrder() + + if int(og_sens_order) < int(SensitivityOrder.first): + solver.setSensitivityOrder(SensitivityOrder.first) + rdata = runAmiciSimulation(model, solver, edata) + solver.setSensitivityOrder(og_sens_order) + + if rdata["status"] != AMICI_SUCCESS: + raise AssertionError(f"Simulation failed (status {rdata['status']}") + + fields = [] + + if ( + solver.getSensitivityMethod() == SensitivityMethod.forward + and solver.getSensitivityOrder() <= SensitivityOrder.first + ): + fields.append("x") + + leastsquares_applicable = ( + solver.getSensitivityMethod() == SensitivityMethod.forward + and edata is not None + ) + + if ( + "ssigmay" in rdata.keys() + and rdata["ssigmay"] is not None + and rdata["ssigmay"].any() + and not model.getAddSigmaResiduals() + ): + leastsquares_applicable = False + + if check_least_squares and leastsquares_applicable: + fields += ["res", "y"] + + _check_results( + rdata, + "FIM", + np.dot(rdata["sres"].T, rdata["sres"]), + atol=1e-8, + rtol=1e-4, + ) + _check_results( + rdata, + "sllh", + -np.dot(rdata["res"].T, rdata["sres"]), + atol=1e-8, + rtol=1e-4, + ) + + if edata is not None: + fields.append("llh") + + for ip, pval in enumerate(p): + if pval == 0.0 and skip_zero_pars: + continue + check_finite_difference( + p, + model, + solver, + edata, + ip, + fields, + atol=atol, + rtol=rtol, + epsilon=epsilon, + ) + + +def _check_close( + result: np.array, + expected: np.array, + atol: float, + rtol: float, + field: str, + ip: Optional[int] = None, + verbose: Optional[bool] = True, +) -> None: + """ + Compares computed values against expected values and provides rich + output information. + + :param result: + computed values + + :param expected: + expected values + + :param field: + rdata field for which the gradient is checked, only for error reporting + + :param atol: + absolute tolerance for comparison + + :param rtol: + relative tolerance for comparison + + :param ip: + parameter index, for more informative output + + :param verbose: + produce a more verbose error message in case of unmatched expectations + """ + close = np.isclose(result, expected, atol=atol, rtol=rtol, equal_nan=True) + if close.all(): + return + + if ip is None: + index_str = "" + check_type = "Regression check" + else: + index_str = f"at index ip={ip} " + check_type = "FD check" + + lines = [ + f"{check_type} failed for {field} {index_str}for " + f"{close.size - close.sum()} indices:" + ] + if verbose: + for idx in np.argwhere(~close): + idx = tuple(idx) + if result.shape: + rr = result[idx] + else: + rr = result + lines.append(f"\tat {idx}: Expected {expected[idx]}, got {rr}") + adev = np.abs(result - expected) + rdev = np.abs((result - expected) / (expected + atol)) + lines.append(f"max(adev): {adev.max()}, max(rdev): {rdev.max()}") + + raise AssertionError("\n".join(lines)) + + +def _check_results( + rdata: ReturnData, field: str, expected: np.array, atol: float, rtol: float +) -> None: + """ + Checks whether rdata[field] agrees with expected according to provided + tolerances. + + :param rdata: + simulation results as returned by + :meth:`amici.amici.runAmiciSimulation` + + :param field: + name of the field to check + + :param expected: + expected values + + :param atol: + absolute tolerance for comparison + + :param rtol: + relative tolerance for comparison + """ + + result = rdata[field] + if type(result) is float: + result = np.array(result) + + _check_close( + result=result, expected=expected, atol=atol, rtol=rtol, field=field + ) diff --git a/deps/AMICI/python/sdist/amici/import_utils.py b/deps/AMICI/python/sdist/amici/import_utils.py deleted file mode 120000 index ca30c75bf..000000000 --- a/deps/AMICI/python/sdist/amici/import_utils.py +++ /dev/null @@ -1 +0,0 @@ -../../amici/import_utils.py \ No newline at end of file diff --git a/deps/AMICI/python/sdist/amici/import_utils.py b/deps/AMICI/python/sdist/amici/import_utils.py new file mode 100644 index 000000000..77a2add60 --- /dev/null +++ b/deps/AMICI/python/sdist/amici/import_utils.py @@ -0,0 +1,738 @@ +"""Miscellaneous functions related to model import, independent of any specific + model format""" +import enum +import itertools as itt +import numbers +import sys +from typing import ( + Any, + Callable, + Dict, + Iterable, + Optional, + Sequence, + SupportsFloat, + Tuple, + Union, +) + +import sympy as sp +from sympy.functions.elementary.piecewise import ExprCondPair +from sympy.logic.boolalg import BooleanAtom +from toposort import toposort + +RESERVED_SYMBOLS = ["x", "k", "p", "y", "w", "h", "t", "AMICI_EMPTY_BOLUS"] + +try: + import pysb +except ImportError: + pysb = None + + +class SBMLException(Exception): + pass + + +SymbolDef = Dict[sp.Symbol, Union[Dict[str, sp.Expr], sp.Expr]] + + +# Monkey-patch toposort CircularDependencyError to handle non-sortable objects, +# such as sympy objects +class CircularDependencyError(ValueError): + def __init__(self, data): + # Sort the data just to make the output consistent, for use in + # error messages. That's convenient for doctests. + s = "Circular dependencies exist among these items: {{{}}}".format( + ", ".join( + "{!r}:{!r}".format(key, value) + for key, value in sorted( + {str(k): v for k, v in data.items()}.items() + ) + ) + ) + super(CircularDependencyError, self).__init__(s) + self.data = data + + +setattr( + sys.modules["toposort"], "CircularDependencyError", CircularDependencyError +) + +annotation_namespace = "https://github.com/AMICI-dev/AMICI" + + +class ObservableTransformation(str, enum.Enum): + """ + Different modes of observable transformation. + """ + + LOG10 = "log10" + LOG = "log" + LIN = "lin" + + +def noise_distribution_to_observable_transformation( + noise_distribution: Union[str, Callable] +) -> ObservableTransformation: + """ + Parse noise distribution string and extract observable transformation + + :param noise_distribution: + see :func:`noise_distribution_to_cost_function` + + :return: + observable transformation + """ + if isinstance(noise_distribution, str): + if noise_distribution.startswith("log-"): + return ObservableTransformation.LOG + if noise_distribution.startswith("log10-"): + return ObservableTransformation.LOG10 + + return ObservableTransformation.LIN + + +def noise_distribution_to_cost_function( + noise_distribution: Union[str, Callable] +) -> Callable[[str], str]: + """ + Parse noise distribution string to a cost function definition amici can + work with. + + The noise distributions listed in the following are supported. :math:`m` + denotes the measurement, :math:`y` the simulation, and :math:`\\sigma` a + distribution scale parameter + (currently, AMICI only supports a single distribution parameter). + + - `'normal'`, `'lin-normal'`: A normal distribution: + + .. math:: + \\pi(m|y,\\sigma) = \\frac{1}{\\sqrt{2\\pi}\\sigma}\\ + exp\\left(-\\frac{(m-y)^2}{2\\sigma^2}\\right) + + - `'log-normal'`: A log-normal distribution (i.e. log(m) is + normally distributed): + + .. math:: + \\pi(m|y,\\sigma) = \\frac{1}{\\sqrt{2\\pi}\\sigma m}\\ + exp\\left(-\\frac{(\\log m - \\log y)^2}{2\\sigma^2}\\right) + + - `'log10-normal'`: A log10-normal distribution (i.e. log10(m) is + normally distributed): + + .. math:: + \\pi(m|y,\\sigma) = \\frac{1}{\\sqrt{2\\pi}\\sigma m \\log(10)}\\ + exp\\left(-\\frac{(\\log_{10} m - \\log_{10} y)^2}{2\\sigma^2}\\right) + + - `'laplace'`, `'lin-laplace'`: A laplace distribution: + + .. math:: + \\pi(m|y,\\sigma) = \\frac{1}{2\\sigma} + \\exp\\left(-\\frac{|m-y|}{\\sigma}\\right) + + - `'log-laplace'`: A log-Laplace distribution (i.e. log(m) is Laplace + distributed): + + .. math:: + \\pi(m|y,\\sigma) = \\frac{1}{2\\sigma m} + \\exp\\left(-\\frac{|\\log m - \\log y|}{\\sigma}\\right) + + - `'log10-laplace'`: A log10-Laplace distribution (i.e. log10(m) is + Laplace distributed): + + .. math:: + \\pi(m|y,\\sigma) = \\frac{1}{2\\sigma m \\log(10)} + \\exp\\left(-\\frac{|\\log_{10} m - \\log_{10} y|}{\\sigma}\\right) + + - `'binomial'`, `'lin-binomial'`: A (continuation of a discrete) binomial + distribution, parameterized via the success probability + :math:`p=\\sigma`: + + .. math:: + \\pi(m|y,\\sigma) = \\operatorname{Heaviside}(y-m) \\cdot + \\frac{\\Gamma(y+1)}{\\Gamma(m+1) \\Gamma(y-m+1)} + \\sigma^m (1-\\sigma)^{(y-m)} + + - `'negative-binomial'`, `'lin-negative-binomial'`: A (continuation of a + discrete) negative binomial distribution, with with `mean = y`, + parameterized via success probability `p`: + + .. math:: + + \\pi(m|y,\\sigma) = \\frac{\\Gamma(m+r)}{\\Gamma(m+1) \\Gamma(r)} + (1-\\sigma)^m \\sigma^r + + where + + .. math:: + r = \\frac{1-\\sigma}{\\sigma} y + + The distributions above are for a single data point. + For a collection :math:`D=\\{m_i\\}_i` of data points and corresponding + simulations :math:`Y=\\{y_i\\}_i` and noise parameters + :math:`\\Sigma=\\{\\sigma_i\\}_i`, AMICI assumes independence, + i.e. the full distributions is + + .. math:: + \\pi(D|Y,\\Sigma) = \\prod_i\\pi(m_i|y_i,\\sigma_i) + + AMICI uses the logarithm :math:`\\log(\\pi(m|y,\\sigma)`. + + In addition to the above mentioned distributions, it is also possible to + pass a function taking a symbol string and returning a log-distribution + string with variables '{str_symbol}', 'm{str_symbol}', 'sigma{str_symbol}' + for y, m, sigma, respectively. + + :param noise_distribution: An identifier specifying a noise model. + Possible values are + + {`'normal'`, `'lin-normal'`, `'log-normal'`, `'log10-normal'`, + `'laplace'`, `'lin-laplace'`, `'log-laplace'`, `'log10-laplace'`, + `'binomial'`, `'lin-binomial'`, `'negative-binomial'`, + `'lin-negative-binomial'`, ``} + + For the meaning of the values see above. + + :return: A function that takes a strSymbol and then creates a cost + function string (negative log-likelihood) from it, which can be + sympified. + """ + + if isinstance(noise_distribution, Callable): + return noise_distribution + + if noise_distribution in ["normal", "lin-normal"]: + y_string = "0.5*log(2*pi*{sigma}**2) + 0.5*(({y} - {m}) / {sigma})**2" + elif noise_distribution == "log-normal": + y_string = ( + "0.5*log(2*pi*{sigma}**2*{m}**2) " + "+ 0.5*((log({y}) - log({m})) / {sigma})**2" + ) + elif noise_distribution == "log10-normal": + y_string = ( + "0.5*log(2*pi*{sigma}**2*{m}**2*log(10)**2) " + "+ 0.5*((log({y}, 10) - log({m}, 10)) / {sigma})**2" + ) + elif noise_distribution in ["laplace", "lin-laplace"]: + y_string = "log(2*{sigma}) + Abs({y} - {m}) / {sigma}" + elif noise_distribution == "log-laplace": + y_string = "log(2*{sigma}*{m}) + Abs(log({y}) - log({m})) / {sigma}" + elif noise_distribution == "log10-laplace": + y_string = ( + "log(2*{sigma}*{m}*log(10)) " + "+ Abs(log({y}, 10) - log({m}, 10)) / {sigma}" + ) + elif noise_distribution in ["binomial", "lin-binomial"]: + # Binomial noise model parameterized via success probability p + y_string = ( + "- log(Heaviside({y} - {m})) - loggamma({y}+1) " + "+ loggamma({m}+1) + loggamma({y}-{m}+1) " + "- {m} * log({sigma}) - ({y} - {m}) * log(1-{sigma})" + ) + elif noise_distribution in ["negative-binomial", "lin-negative-binomial"]: + # Negative binomial noise model of the number of successes m + # (data) before r=(1-sigma)/sigma * y failures occur, + # with mean number of successes y (simulation), + # parameterized via success probability p = sigma. + r = "{y} * (1-{sigma}) / {sigma}" + y_string = ( + f"- loggamma({{m}}+{r}) + loggamma({{m}}+1) " + f"+ loggamma({r}) - {r} * log(1-{{sigma}}) " + f"- {{m}} * log({{sigma}})" + ) + else: + raise ValueError( + f"Cost identifier {noise_distribution} not recognized." + ) + + def nllh_y_string(str_symbol): + y, m, sigma = _get_str_symbol_identifiers(str_symbol) + return y_string.format(y=y, m=m, sigma=sigma) + + return nllh_y_string + + +def _get_str_symbol_identifiers(str_symbol: str) -> tuple: + """Get identifiers for simulation, measurement, and sigma.""" + y, m, sigma = f"{str_symbol}", f"m{str_symbol}", f"sigma{str_symbol}" + return y, m, sigma + + +def smart_subs_dict( + sym: sp.Expr, + subs: SymbolDef, + field: Optional[str] = None, + reverse: bool = True, +) -> sp.Expr: + """ + Substitutes expressions completely flattening them out. Requires + sorting of expressions with toposort. + + :param sym: + Symbolic expression in which expressions will be substituted + + :param subs: + Substitutions + + :param field: + Field of substitution expressions in subs.values(), if applicable + + :param reverse: + Whether ordering in subs should be reversed. Note that substitution + requires the reverse order of what is required for evaluation. + + :return: + Substituted symbolic expression + """ + s = [ + (eid, expr[field] if field is not None else expr) + for eid, expr in subs.items() + ] + if reverse: + s.reverse() + for substitution in s: + # note that substitution may change free symbols, so we have to do + # this recursively + if sym.has(substitution[0]): + sym = sym.subs(*substitution) + return sym + + +def smart_subs(element: sp.Expr, old: sp.Symbol, new: sp.Expr) -> sp.Expr: + """ + Optimized substitution that checks whether anything needs to be done first + + :param element: + substitution target + + :param old: + to be substituted + + :param new: + subsitution value + + :return: + substituted expression + """ + return element.subs(old, new) if element.has(old) else element + + +def toposort_symbols( + symbols: SymbolDef, field: Optional[str] = None +) -> SymbolDef: + """ + Topologically sort symbol definitions according to their interdependency + + :param symbols: + symbol definitions + + :param field: + field of definition.values() that is used to compute interdependency + + :return: + ordered symbol definitions + """ + sorted_symbols = toposort( + { + identifier: { + s + for s in ( + definition[field] if field is not None else definition + ).free_symbols + if s in symbols + } + for identifier, definition in symbols.items() + } + ) + return { + s: symbols[s] + for symbol_group in sorted_symbols + for s in sorted(symbol_group, key=str) + } + + +def _parse_special_functions(sym: sp.Expr, toplevel: bool = True) -> sp.Expr: + """ + Recursively checks the symbolic expression for functions which have be + to parsed in a special way, such as piecewise functions + + :param sym: + symbolic expressions + + :param toplevel: + as this is called recursively, are we in the top level expression? + """ + args = tuple( + arg + if arg.__class__.__name__ == "piecewise" + and sym.__class__.__name__ == "piecewise" + else _parse_special_functions(arg, False) + for arg in sym.args + ) + + fun_mappings = { + "times": sp.Mul, + "xor": sp.Xor, + "abs": sp.Abs, + "min": sp.Min, + "max": sp.Max, + "ceil": sp.functions.ceiling, + "floor": sp.functions.floor, + "factorial": sp.functions.factorial, + "arcsin": sp.functions.asin, + "arccos": sp.functions.acos, + "arctan": sp.functions.atan, + "arccot": sp.functions.acot, + "arcsec": sp.functions.asec, + "arccsc": sp.functions.acsc, + "arcsinh": sp.functions.asinh, + "arccosh": sp.functions.acosh, + "arctanh": sp.functions.atanh, + "arccoth": sp.functions.acoth, + "arcsech": sp.functions.asech, + "arccsch": sp.functions.acsch, + } + + if sym.__class__.__name__ in fun_mappings: + return fun_mappings[sym.__class__.__name__](*args) + + elif sym.__class__.__name__ == "piecewise" or isinstance( + sym, sp.Piecewise + ): + if isinstance(sym, sp.Piecewise): + # this is sympy piecewise, can't be nested + denested_args = args + else: + # this is sbml piecewise, can be nested + denested_args = _denest_piecewise(args) + return _parse_piecewise_to_heaviside(denested_args) + + if sym.__class__.__name__ == "plus" and not sym.args: + return sp.Float(0.0) + + if isinstance(sym, (sp.Function, sp.Mul, sp.Add, sp.Pow)): + sym._args = args + + elif toplevel and isinstance(sym, BooleanAtom): + # Replace boolean constants by numbers so they can be differentiated + # must not replace in Piecewise function. Therefore, we only replace + # it the complete expression consists only of a Boolean value. + sym = sp.Float(int(bool(sym))) + + return sym + + +def _denest_piecewise( + args: Sequence[Union[sp.Expr, sp.logic.boolalg.Boolean, bool]] +) -> Tuple[Union[sp.Expr, sp.logic.boolalg.Boolean, bool]]: + """ + Denest piecewise functions that contain piecewise as condition + + :param args: + Arguments to the piecewise function + + :return: + Arguments where conditions no longer contain piecewise functions and + the conditional dependency is flattened out + """ + args_out = [] + for coeff, cond in grouper(args, 2, True): + # handling of this case is explicitely disabled in + # _parse_special_functions as keeping track of coeff/cond + # arguments is tricky. Simpler to just parse them out here + if coeff.__class__.__name__ == "piecewise": + coeff = _parse_special_functions(coeff, False) + + # we can have conditions that are piecewise function + # returning True or False + if cond.__class__.__name__ == "piecewise": + # this keeps track of conditional that the previous + # piece was picked + previous_was_picked = sp.false + # recursively denest those first + for sub_coeff, sub_cond in grouper( + _denest_piecewise(cond.args), 2, True + ): + # flatten the individual pieces + pick_this = sp.And(sp.Not(previous_was_picked), sub_cond) + if sub_coeff == sp.true: + args_out.extend([coeff, pick_this]) + previous_was_picked = pick_this + + else: + args_out.extend([coeff, cond]) + # cut off last condition as that's the default + return tuple(args_out[:-1]) + + +def _parse_piecewise_to_heaviside(args: Iterable[sp.Expr]) -> sp.Expr: + """ + Piecewise functions cannot be transformed into C++ right away, but AMICI + has a special interface for Heaviside functions, so we transform them. + + :param args: + symbolic expressions for arguments of the piecewise function + """ + # how many condition-expression pairs will we have? + formula = sp.Float(0.0) + not_condition = sp.Float(1.0) + + if all(isinstance(arg, ExprCondPair) for arg in args): + # sympy piecewise + grouped_args = args + else: + # smbl piecewise + grouped_args = grouper(args, 2, True) + + for coeff, trigger in grouped_args: + if isinstance(coeff, BooleanAtom): + coeff = sp.Float(int(bool(coeff))) + + if trigger == sp.true: + return formula + coeff * not_condition + + if trigger == sp.false: + continue + + tmp = _parse_heaviside_trigger(trigger) + formula += coeff * sp.simplify(not_condition * tmp) + not_condition *= 1 - tmp + + return formula + + +def _parse_heaviside_trigger(trigger: sp.Expr) -> sp.Expr: + """ + Recursively translates a boolean trigger function into a real valued + root function + + :param trigger: + :return: real valued root function expression + """ + if trigger.is_Relational: + root = trigger.args[0] - trigger.args[1] + _check_unsupported_functions(root, "sympy.Expression") + + # normalize such that we always implement <, + # this ensures that we can correctly evaluate the condition if + # simulation starts at H(0). This is achieved by translating + # conditionals into Heaviside functions H that is implemented as unit + # step with H(0) = 1 + if isinstance(trigger, sp.core.relational.StrictLessThan): + # x < y => x - y < 0 => r < 0 + return 1 - sp.Heaviside(root) + if isinstance(trigger, sp.core.relational.LessThan): + # x <= y => not(y < x) => not(y - x < 0) => not -r < 0 + return sp.Heaviside(-root) + if isinstance(trigger, sp.core.relational.StrictGreaterThan): + # y > x => y - x < 0 => -r < 0 + return 1 - sp.Heaviside(-root) + if isinstance(trigger, sp.core.relational.GreaterThan): + # y >= x => not(x < y) => not(x - y < 0) => not r < 0 + return sp.Heaviside(root) + + # or(x,y) = not(and(not(x),not(y)) + if isinstance(trigger, sp.Or): + return 1 - sp.Mul( + *[1 - _parse_heaviside_trigger(arg) for arg in trigger.args] + ) + + if isinstance(trigger, sp.And): + return sp.Mul(*[_parse_heaviside_trigger(arg) for arg in trigger.args]) + + raise RuntimeError( + "AMICI can not parse piecewise/event trigger functions with argument " + f"{trigger}." + ) + + +def grouper( + iterable: Iterable, n: int, fillvalue: Any = None +) -> Iterable[Tuple[Any]]: + """ + Collect data into fixed-length chunks or blocks + + grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx" + + :param iterable: + any iterable + + :param n: + chunk length + + :param fillvalue: + padding for last chunk if length < n + + :return: itertools.zip_longest of requested chunks + """ + args = [iter(iterable)] * n + return itt.zip_longest(*args, fillvalue=fillvalue) + + +def _check_unsupported_functions( + sym: sp.Expr, expression_type: str, full_sym: Optional[sp.Expr] = None +): + """ + Recursively checks the symbolic expression for unsupported symbolic + functions + + :param sym: + symbolic expressions + + :param expression_type: + type of expression, only used when throwing errors + + :param full sym: + outermost symbolic expression in recursive checks, only used for errors + """ + if full_sym is None: + full_sym = sym + + # note that sp.functions.factorial, sp.functions.ceiling, + # sp.functions.floor applied to numbers should be simplified out and + # thus pass this test + unsupported_functions = ( + sp.functions.factorial, + sp.functions.ceiling, + sp.functions.floor, + sp.functions.sec, + sp.functions.csc, + sp.functions.cot, + sp.functions.asec, + sp.functions.acsc, + sp.functions.acot, + sp.functions.acsch, + sp.functions.acoth, + sp.Mod, + sp.core.function.UndefinedFunction, + ) + + if ( + isinstance(sym.func, unsupported_functions) + or isinstance(sym, unsupported_functions) + ) and getattr(sym.func, "name", "") != "rateOf": + raise RuntimeError( + f"Encountered unsupported expression " + f'"{sym.func}" of type ' + f'"{type(sym.func)}" as part of a ' + f'{expression_type}: "{full_sym}"!' + ) + for arg in list(sym.args): + _check_unsupported_functions(arg, expression_type) + + +def cast_to_sym( + value: Union[SupportsFloat, sp.Expr, BooleanAtom], input_name: str +) -> sp.Expr: + """ + Typecasts the value to :py:class:`sympy.Float` if possible, and ensures the + value is a symbolic expression. + + :param value: + value to be cast + + :param input_name: + name of input variable + + :return: + typecast value + """ + if isinstance(value, (sp.RealNumber, numbers.Number)): + value = sp.Float(float(value)) + elif isinstance(value, BooleanAtom): + value = sp.Float(float(bool(value))) + + if not isinstance(value, sp.Expr): + raise TypeError( + f"Couldn't cast {input_name} to sympy.Expr, was " f"{type(value)}" + ) + + return value + + +def generate_measurement_symbol(observable_id: Union[str, sp.Symbol]): + """ + Generates the appropriate measurement symbol for the provided observable + + :param observable_id: + symbol (or string representation) of the observable + + :return: + symbol for the corresponding measurement + """ + if not isinstance(observable_id, str): + observable_id = strip_pysb(observable_id) + return symbol_with_assumptions(f"m{observable_id}") + + +def generate_regularization_symbol(observable_id: Union[str, sp.Symbol]): + """ + Generates the appropriate regularization symbol for the provided observable + + :param observable_id: + symbol (or string representation) of the observable + + :return: + symbol for the corresponding regularization + """ + if not isinstance(observable_id, str): + observable_id = strip_pysb(observable_id) + return symbol_with_assumptions(f"r{observable_id}") + + +def generate_flux_symbol( + reaction_index: int, name: Optional[str] = None +) -> sp.Symbol: + """ + Generate identifier symbol for a reaction flux. + This function will always return the same unique python object for a + given entity. + + :param reaction_index: + index of the reaction to which the flux corresponds + :param name: + an optional identifier of the reaction to which the flux corresponds + :return: + identifier symbol + """ + if name is not None: + return symbol_with_assumptions(name) + + return symbol_with_assumptions(f"flux_r{reaction_index}") + + +def symbol_with_assumptions(name: str): + """ + Central function to create symbols with consistent, canonical assumptions + + :param name: + name of the symbol + + :return: + symbol with canonical assumptions + """ + return sp.Symbol(name, real=True) + + +def strip_pysb(symbol: sp.Basic) -> sp.Basic: + """ + Strips pysb info from a :class:`pysb.Component` object + + :param symbol: + symbolic expression + + :return: + stripped expression + """ + # strip pysb type and transform into a flat sympy.Symbol. + # this ensures that the pysb type specific __repr__ is used when converting + # to string + if pysb and isinstance(symbol, pysb.Component): + return sp.Symbol(symbol.name, real=True) + else: + # in this case we will use sympy specific transform anyways + return symbol + + +sbml_time_symbol = symbol_with_assumptions("time") +amici_time_symbol = symbol_with_assumptions("t") diff --git a/deps/AMICI/python/sdist/amici/include b/deps/AMICI/python/sdist/amici/include deleted file mode 120000 index b85a40983..000000000 --- a/deps/AMICI/python/sdist/amici/include +++ /dev/null @@ -1 +0,0 @@ -../../../include/ \ No newline at end of file diff --git a/deps/AMICI/python/sdist/amici/include/amici b/deps/AMICI/python/sdist/amici/include/amici new file mode 120000 index 000000000..4e5f8a216 --- /dev/null +++ b/deps/AMICI/python/sdist/amici/include/amici @@ -0,0 +1 @@ +../../../../include/amici/ \ No newline at end of file diff --git a/deps/AMICI/python/sdist/amici/logging.py b/deps/AMICI/python/sdist/amici/logging.py deleted file mode 120000 index b8d5e1b32..000000000 --- a/deps/AMICI/python/sdist/amici/logging.py +++ /dev/null @@ -1 +0,0 @@ -../../amici/logging.py \ No newline at end of file diff --git a/deps/AMICI/python/sdist/amici/logging.py b/deps/AMICI/python/sdist/amici/logging.py new file mode 100644 index 000000000..2648fc5b2 --- /dev/null +++ b/deps/AMICI/python/sdist/amici/logging.py @@ -0,0 +1,229 @@ +""" +Logging +------- +This module provides custom logging functionality for other amici modules +""" + +import functools +import logging +import os +import platform +import socket +import time +import warnings +from inspect import currentframe, getouterframes + +import amici + +LOG_LEVEL_ENV_VAR = "AMICI_LOG" +BASE_LOGGER_NAME = "amici" +# Supported values for LOG_LEVEL_ENV_VAR +NAMED_LOG_LEVELS = { + "NOTSET": logging.NOTSET, + "DEBUG": logging.DEBUG, + "INFO": logging.INFO, + "WARNING": logging.WARNING, + "ERROR": logging.ERROR, + "CRITICAL": logging.CRITICAL, +} + +from typing import Callable, Optional, Union + + +def _setup_logger( + level: Optional[int] = logging.WARNING, + console_output: Optional[bool] = True, + file_output: Optional[bool] = False, + capture_warnings: Optional[bool] = True, +) -> logging.Logger: + """ + Set up a new logging.Logger for AMICI logging + + :param level: + Logging level, typically using a constant like logging.INFO or + logging.DEBUG + + :param console_output: + 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) + + :param capture_warnings: + Capture warnings from Python's warnings module if True (default) + + :return: + A :class:`logging.Logger` object for AMICI logging. Note that other + AMICI modules + should use a logger specific to their namespace instead by calling + :func:`get_logger`. + """ + log = logging.getLogger(BASE_LOGGER_NAME) + + # Logging level can be overridden with environment variable + if LOG_LEVEL_ENV_VAR in os.environ: + try: + level = int(os.environ[LOG_LEVEL_ENV_VAR]) + except ValueError: + # Try parsing as a name + level_name = os.environ[LOG_LEVEL_ENV_VAR] + if level_name in NAMED_LOG_LEVELS.keys(): + level = NAMED_LOG_LEVELS[level_name] + else: + raise ValueError( + f"Environment variable {LOG_LEVEL_ENV_VAR} " + f'contains an invalid value "{level_name}".' + f" If set, its value must be one of " + f'{", ".join(NAMED_LOG_LEVELS.keys())}' + f" (case-sensitive) or an integer log level." + ) + + log.setLevel(level) + + # Remove default logging handler + log.handlers = [] + + log_fmt = logging.Formatter( + "%(asctime)s.%(msecs).3d - %(name)s - " "%(levelname)s - %(message)s", + datefmt="%Y-%m-%d %H:%M:%S", + ) + + if console_output: + stream_handler = logging.StreamHandler() + stream_handler.setFormatter(log_fmt) + log.addHandler(stream_handler) + + if file_output: + file_handler = logging.FileHandler(file_output) + file_handler.setFormatter(log_fmt) + log.addHandler(file_handler) + + log.info("Logging started on AMICI version %s", amici.__version__) + + log.debug("OS Platform: %s", platform.platform()) + log.debug("Python version: %s", platform.python_version()) + log.debug("Hostname: %s", socket.getfqdn()) + + logging.captureWarnings(capture_warnings) + + return log + + +def set_log_level(logger: logging.Logger, log_level: Union[int, bool]) -> None: + if log_level is not None and log_level is not False: + if isinstance(log_level, bool): + log_level = logging.DEBUG + elif not isinstance(log_level, int): + raise ValueError("log_level must be a boolean, integer or None") + + if logger.getEffectiveLevel() != log_level: + logger.debug( + "Changing log_level from %d to %d" + % (logger.getEffectiveLevel(), log_level) + ) + logger.setLevel(log_level) + + +def get_logger( + logger_name: Optional[str] = BASE_LOGGER_NAME, + log_level: Optional[int] = None, + **kwargs, +) -> logging.Logger: + """ + Returns (if extistant) or creates an AMICI logger + + If the AMICI base logger has already been set up, this method will + return it or any of its descendant loggers without overriding the + settings - i.e. any values supplied as kwargs will be ignored. + + :param logger_name: + Get a logger for a specific namespace, typically __name__ + for code outside of classes or self.__module__ inside a class + + :param log_level: + Override the default or preset log level for the requested logger. + None or False uses the default or preset value. True evaluates to + logging.DEBUG. Any integer is used directly. + + :param console_output: + Set up a default console log handler if True (default). Only used when + the AMICI logger hasn't been set up yet. + + :param file_output: + Supply a filename to copy all log output to that file, or set to + False to disable (default). Only used when the AMICI logger hasn't + been set up yet. + + :param capture_warnings: + Capture warnings from Python's warnings module if True (default). + Only used when the AMICI logger hasn't been set up yet.. + + :return: + A logging.Logger object with the requested name + """ + if BASE_LOGGER_NAME not in logging.Logger.manager.loggerDict.keys(): + _setup_logger(**kwargs) + elif kwargs: + warnings.warn( + "AMICI logger already exists, ignoring keyword " + "arguments to setup_logger" + ) + + logger = logging.getLogger(logger_name) + + set_log_level(logger, log_level) + + return logger + + +def log_execution_time(description: str, logger: logging.Logger) -> Callable: + """ + Parameterized function decorator that enables automatic execution time + tracking + + :param description: + Description of what the decorated function does + + :param logger: + Logger to which execution timing will be printed + """ + + def decorator_timer(func): + @functools.wraps(func) + def wrapper_timer(*args, **kwargs): + # append pluses to indicate recursion level + recursion_level = sum( + frame.function == "wrapper_timer" + and frame.filename == __file__ + for frame in getouterframes(currentframe(), context=0) + ) + + recursion = "" + level = logging.INFO + level_length = len("INFO") + if recursion_level > 1: + recursion = "+" * (recursion_level - 1) + level = logging.DEBUG + level_length = len("DEBUG") + + tstart = time.perf_counter() + rval = func(*args, **kwargs) + tend = time.perf_counter() + spacers = " " * max( + 59 + - len(description) + - len(logger.name) + - len(recursion) + - level_length, + 0, + ) + logger.log( + level, + f"Finished {description}{spacers}{recursion} ({(tend - tstart):.2E}s)", + ) + return rval + + return wrapper_timer + + return decorator_timer diff --git a/deps/AMICI/python/sdist/amici/numpy.py b/deps/AMICI/python/sdist/amici/numpy.py deleted file mode 120000 index 54495e029..000000000 --- a/deps/AMICI/python/sdist/amici/numpy.py +++ /dev/null @@ -1 +0,0 @@ -../../amici/numpy.py \ No newline at end of file diff --git a/deps/AMICI/python/sdist/amici/numpy.py b/deps/AMICI/python/sdist/amici/numpy.py new file mode 100644 index 000000000..b84e52cc2 --- /dev/null +++ b/deps/AMICI/python/sdist/amici/numpy.py @@ -0,0 +1,483 @@ +""" +C++ object views +---------------- +This module provides views on C++ objects for efficient access. +""" + +import collections +import copy +from typing import Dict, Iterator, List, Literal, Union + +import amici +import numpy as np +import sympy as sp + +from . import ExpData, ExpDataPtr, Model, ReturnData, ReturnDataPtr + +StrOrExpr = Union[str, sp.Expr] + + +class SwigPtrView(collections.abc.Mapping): + """ + Interface class to expose ``std::vector`` and scalar members of + swig wrapped C++ objects as numpy array attributes and fields. This + class is memory efficient as copies of the underlying C++ objects is + only created when respective fields are accessed for the first time. + Cached copies are used for all subsequent calls. + + :ivar _swigptr: pointer to the C++ object + :ivar _field_names: names of members that will be exposed as numpy arrays + :ivar _field_dimensions: dimensions of numpy arrays + :ivar _cache: dictionary with cached values + """ + + _swigptr = None + _field_names: List[str] = [] + _field_dimensions: Dict[str, List[int]] = dict() + + def __getitem__(self, item: str) -> Union[np.ndarray, float]: + """ + Access to field names, copies data from C++ object into numpy + array, reshapes according to field dimensions and stores values in + cache. + + :param item: field name + :return: value + """ + if self._swigptr is None: + raise NotImplementedError("Cannot get items from abstract class.") + + if item == "ptr": + return self._swigptr + + if item in self._cache: + return self._cache[item] + + if item == "id": + return getattr(self._swigptr, item) + + if item not in self._field_names: + self.__missing__(item) + + value = _field_as_numpy(self._field_dimensions, item, self._swigptr) + self._cache[item] = value + return value + + def __missing__(self, key: str) -> None: + """ + Default behaviour for missing keys + + :param key: field name + """ + raise KeyError(f"Unknown field name {key}.") + + def __getattr__(self, item) -> Union[np.ndarray, float]: + """ + Attribute accessor for field names + + :param item: field name + + :returns: value + """ + return self.__getitem__(item) + + def __init__(self, swigptr): + """ + Constructor + + :param swigptr: pointer to the C++ object + """ + self._swigptr = swigptr + self._cache = {} + super(SwigPtrView, self).__init__() + + def __len__(self) -> int: + """ + Returns the number of available keys/fields + + :returns: length of _field_names + """ + return len(self._field_names) + + def __iter__(self) -> Iterator: + """ + Create an iterator of the keys/fields + + :returns: iterator over _field_names + """ + return iter(self._field_names) + + def __copy__(self): + """ + Create a shallow copy + + :return: SwigPtrView shallow copy + """ + other = SwigPtrView(self._swigptr) + other._field_names = self._field_names + other._field_dimensions = self._field_dimensions + other._cache = self._cache + return other + + def __contains__(self, item) -> bool: + """ + Faster implementation of ``__contains__`` that avoids copy of the field + + :param item: item to check for + + :returns: whether item is available as key + """ + return item in self._field_names + + def __deepcopy__(self, memo): + """ + Create a deep copy + + :param memo: dict with id-to-object mapping + + :returns: SwigPtrView deep copy + """ + # We assume we have a copy-ctor for the swigptr object + other = self.__class__(copy.deepcopy(self._swigptr)) + other._field_names = copy.deepcopy(self._field_names) + other._field_dimensions = copy.deepcopy(self._field_dimensions) + other._cache = copy.deepcopy(self._cache) + return other + + def __repr__(self): + """ + String representation of the object + + :returns: string representation + """ + return f"<{self.__class__.__name__}({self._swigptr})>" + + def __eq__(self, other): + """ + Equality check + + :param other: other object + + :returns: whether other object is equal to this object + """ + if not isinstance(other, self.__class__): + return False + return self._swigptr == other._swigptr + + +class ReturnDataView(SwigPtrView): + """ + Interface class for C++ :class:`amici.ReturnData` objects that avoids + possibly costly copies of member data. + """ + + _field_names = [ + "ts", + "x", + "x0", + "x_ss", + "sx", + "sx0", + "sx_ss", + "y", + "sigmay", + "sy", + "ssigmay", + "z", + "rz", + "sigmaz", + "sz", + "srz", + "ssigmaz", + "sllh", + "s2llh", + "J", + "xdot", + "status", + "llh", + "chi2", + "res", + "sres", + "FIM", + "w", + "preeq_wrms", + "preeq_t", + "preeq_numsteps", + "preeq_numstepsB", + "preeq_status", + "preeq_cpu_time", + "preeq_cpu_timeB", + "posteq_wrms", + "posteq_t", + "posteq_numsteps", + "posteq_numstepsB", + "posteq_status", + "posteq_cpu_time", + "posteq_cpu_timeB", + "numsteps", + "numrhsevals", + "numerrtestfails", + "numnonlinsolvconvfails", + "order", + "cpu_time", + "numstepsB", + "numrhsevalsB", + "numerrtestfailsB", + "numnonlinsolvconvfailsB", + "cpu_timeB", + "cpu_time_total", + ] + + def __init__(self, rdata: Union[ReturnDataPtr, ReturnData]): + """ + Constructor + + :param rdata: pointer to the ``ReturnData`` instance + """ + if not isinstance(rdata, (ReturnDataPtr, ReturnData)): + raise TypeError( + f"Unsupported pointer {type(rdata)}, must be" + f"amici.ExpDataPtr!" + ) + self._field_dimensions = { + "ts": [rdata.nt], + "x": [rdata.nt, rdata.nx], + "x0": [rdata.nx], + "x_ss": [rdata.nx], + "sx": [rdata.nt, rdata.nplist, rdata.nx], + "sx0": [rdata.nplist, rdata.nx], + "sx_ss": [rdata.nplist, rdata.nx], + # observables + "y": [rdata.nt, rdata.ny], + "sigmay": [rdata.nt, rdata.ny], + "sy": [rdata.nt, rdata.nplist, rdata.ny], + "ssigmay": [rdata.nt, rdata.nplist, rdata.ny], + # event observables + "z": [rdata.nmaxevent, rdata.nz], + "rz": [rdata.nmaxevent, rdata.nz], + "sigmaz": [rdata.nmaxevent, rdata.nz], + "sz": [rdata.nmaxevent, rdata.nplist, rdata.nz], + "srz": [rdata.nmaxevent, rdata.nplist, rdata.nz], + "ssigmaz": [rdata.nmaxevent, rdata.nplist, rdata.nz], + # objective function + "sllh": [rdata.nplist], + "s2llh": [rdata.np, rdata.nplist], + "res": [rdata.nt * rdata.nytrue * (2 if rdata.sigma_res else 1)], + "sres": [ + rdata.nt * rdata.nytrue * (2 if rdata.sigma_res else 1), + rdata.nplist, + ], + "FIM": [rdata.nplist, rdata.nplist], + # diagnosis + "J": [rdata.nx_solver, rdata.nx_solver], + "w": [rdata.nt, rdata.nw], + "xdot": [rdata.nx_solver], + "preeq_numlinsteps": [rdata.newton_maxsteps, 2], + "preeq_numsteps": [1, 3], + "preeq_status": [1, 3], + "posteq_numlinsteps": [rdata.newton_maxsteps, 2], + "posteq_numsteps": [1, 3], + "posteq_status": [1, 3], + "numsteps": [rdata.nt], + "numrhsevals": [rdata.nt], + "numerrtestfails": [rdata.nt], + "numnonlinsolvconvfails": [rdata.nt], + "order": [rdata.nt], + "numstepsB": [rdata.nt], + "numrhsevalsB": [rdata.nt], + "numerrtestfailsB": [rdata.nt], + "numnonlinsolvconvfailsB": [rdata.nt], + } + super(ReturnDataView, self).__init__(rdata) + + def __getitem__( + self, item: str + ) -> Union[np.ndarray, ReturnDataPtr, ReturnData, float]: + """ + Access fields by name.s + + Custom ``__getitem__`` implementation shim to map ``t`` to ``ts``. + + :param item: field/attribute key + + :returns: self[item] + """ + if item == "status": + return int(super().__getitem__(item)) + + if item == "t": + item = "ts" + + return super().__getitem__(item) + + def __repr__(self): + status = amici.simulation_status_to_str(self._swigptr.status) + return f"<{self.__class__.__name__}(id={self._swigptr.id!r}, status={status})>" + + def by_id( + self, entity_id: str, field: str = None, model: Model = None + ) -> np.array: + """ + Get the value of a given field for a named entity. + + :param entity_id: The ID of the model entity that is to be extracted + from ``field`` (e.g. a state ID). + :param field: The requested field, e.g. 'x' for model states. This is + optional if field would be one of ``{'x', 'y', 'w'}`` + :param model: The model from which this ReturnDataView was generated. + This is optional if this ReturnData was generated with + ``solver.getReturnDataReportingMode() == amici.RDataReporting.full``. + """ + if field is None: + field = _entity_type_from_id(entity_id, self, model) + + if field in {"x", "x0", "x_ss", "sx", "sx0", "sx_ss"}: + ids = (model and model.getStateIds()) or self._swigptr.state_ids + elif field in {"w"}: + ids = ( + model and model.getExpressionIds() + ) or self._swigptr.expression_ids + elif field in {"y", "sy", "sigmay"}: + ids = ( + model and model.getObservableIds() + ) or self._swigptr.observable_ids + elif field in {"sllh"}: + ids = ( + model and model.getParameterIds() + ) or self._swigptr.parameter_ids + else: + raise NotImplementedError( + f"Subsetting {field} by ID is not implemented or not possible." + ) + col_index = ids.index(entity_id) + return getattr(self, field)[:, ..., col_index] + + +class ExpDataView(SwigPtrView): + """ + Interface class for C++ Exp Data objects that avoids possibly costly + copies of member data. + + NOTE: This currently assumes that the underlying :class:`ExpData` + does not change after instantiating an :class:`ExpDataView`. + """ + + _field_names = [ + "ts", + "observedData", + "observedDataStdDev", + "observedEvents", + "observedEventsStdDev", + "fixedParameters", + "fixedParametersPreequilibration", + "fixedParametersPresimulation", + ] + + def __init__(self, edata: Union[ExpDataPtr, ExpData]): + """ + Constructor + + :param edata: pointer to the ExpData instance + """ + if not isinstance(edata, (ExpDataPtr, ExpData)): + raise TypeError( + f"Unsupported pointer {type(edata)}, must be" + f"amici.ExpDataPtr!" + ) + self._field_dimensions = { + "ts": [edata.nt()], + # observables + "observedData": [edata.nt(), edata.nytrue()], + "observedDataStdDev": [edata.nt(), edata.nytrue()], + # event observables + "observedEvents": [edata.nmaxevent(), edata.nztrue()], + "observedEventsStdDev": [edata.nmaxevent(), edata.nztrue()], + # fixed parameters + "fixedParameters": [len(edata.fixedParameters)], + "fixedParametersPreequilibration": [ + len(edata.fixedParametersPreequilibration) + ], + "fixedParametersPresimulation": [ + len(edata.fixedParametersPreequilibration) + ], + } + edata.ts = edata.ts_ + edata.observedData = edata.getObservedData() + edata.observedDataStdDev = edata.getObservedDataStdDev() + edata.observedEvents = edata.getObservedEvents() + edata.observedEventsStdDev = edata.getObservedEventsStdDev() + super(ExpDataView, self).__init__(edata) + + +def _field_as_numpy( + field_dimensions: Dict[str, List[int]], field: str, data: SwigPtrView +) -> Union[np.ndarray, float, None]: + """ + Convert data object field to numpy array with dimensions according to + specified field dimensions + + :param field_dimensions: dimension specifications + ``dict({field: list([dim1, dim2, ...])})`` + :param data: object with fields + :param field: Name of field + + :returns: Field Data as numpy array with dimensions according to + specified field dimensions + """ + 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) + + +def _entity_type_from_id( + entity_id: str, + rdata: Union[amici.ReturnData, "amici.ReturnDataView"] = None, + model: amici.Model = None, +) -> Literal["x", "y", "w", "p", "k"]: + """Guess the type of some entity by its ID.""" + for entity_type, symbol in ( + ("State", "x"), + ("Observable", "y"), + ("Expression", "w"), + ("Parameter", "p"), + ("FixedParameter", "k"), + ): + if model: + if entity_id in getattr(model, f"get{entity_type}Ids")(): + return symbol + else: + if entity_id in getattr( + rdata + if isinstance(rdata, amici.ReturnData) + else rdata._swigptr, + f"{entity_type.lower()}_ids", + ): + return symbol + + raise KeyError(f"Unknown symbol {entity_id}.") + + +def evaluate(expr: StrOrExpr, rdata: ReturnDataView) -> np.array: + """Evaluate a symbolic expression based on the given simulation outputs. + + :param expr: + 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. + :param rdata: + The simulation results. + + :return: + The evaluated expression for the simulation output timepoints. + """ + from sympy.utilities.lambdify import lambdify + + if isinstance(expr, str): + expr = sp.sympify(expr) + + arg_names = list(sorted(expr.free_symbols, key=lambda x: x.name)) + func = lambdify(arg_names, expr, "numpy") + args = [rdata.by_id(arg.name) for arg in arg_names] + return func(*args) diff --git a/deps/AMICI/python/sdist/amici/ode_export.py b/deps/AMICI/python/sdist/amici/ode_export.py deleted file mode 120000 index f6a27b74d..000000000 --- a/deps/AMICI/python/sdist/amici/ode_export.py +++ /dev/null @@ -1 +0,0 @@ -../../amici/ode_export.py \ No newline at end of file diff --git a/deps/AMICI/python/sdist/amici/pandas.py b/deps/AMICI/python/sdist/amici/pandas.py deleted file mode 120000 index 6e1bc9cff..000000000 --- a/deps/AMICI/python/sdist/amici/pandas.py +++ /dev/null @@ -1 +0,0 @@ -../../amici/pandas.py \ No newline at end of file diff --git a/deps/AMICI/python/sdist/amici/pandas.py b/deps/AMICI/python/sdist/amici/pandas.py new file mode 100644 index 000000000..8a2eb5049 --- /dev/null +++ b/deps/AMICI/python/sdist/amici/pandas.py @@ -0,0 +1,793 @@ +""" +Pandas Wrappers +--------------- +This module contains convenience wrappers that allow for easy interconversion +between C++ objects from :mod:`amici.amici` and pandas DataFrames +""" + +import copy +import math +from typing import Dict, List, Optional, SupportsFloat, Union + +import amici +import numpy as np +import pandas as pd + +from .numpy import ExpDataView + +__all__ = [ + "get_expressions_as_dataframe", + "getEdataFromDataFrame", + "getDataObservablesAsDataFrame", + "getSimulationObservablesAsDataFrame", + "getSimulationStatesAsDataFrame", + "getResidualsAsDataFrame", +] + +ExpDatas = Union[ + List[amici.amici.ExpData], + List[amici.ExpDataPtr], + amici.amici.ExpData, + amici.ExpDataPtr, +] +ReturnDatas = Union[List[amici.ReturnDataView], amici.ReturnDataView] + +AmiciModel = Union[amici.ModelPtr, amici.Model] + + +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` + + :param edata_list: + list of instances or single instance + + :return: + list of instance(s) + """ + if isinstance(edata_list, (amici.amici.ExpData, amici.ExpDataPtr)): + return [edata_list] + else: + return edata_list + + +def _process_rdata_list(rdata_list: ReturnDatas) -> List[amici.ReturnDataView]: + """ + Maps single instances of :class:`amici.ReturnData` to lists of + :class:`amici.ReturnData` + + :param rdata_list: + list of instances or single instance + + :return: + list of instance(s) + """ + if isinstance(rdata_list, amici.ReturnDataView): + return [rdata_list] + else: + return rdata_list + + +def getDataObservablesAsDataFrame( + model: AmiciModel, edata_list: ExpDatas, by_id: Optional[bool] = False +) -> pd.DataFrame: + """ + Write Observables from experimental data as DataFrame. + + :param model: + Model instance. + + :param edata_list: + list of ExpData instances with experimental data. + May also be a single ExpData instance. + + :param by_id: + If True, uses observable ids as column names in the generated + DataFrame, otherwise the possibly more descriptive observable names + are used. + + :return: + pandas DataFrame with conditions/timepoints as rows and observables as + columns. + """ + edata_list = _process_edata_list(edata_list) + + # list of all column names using either ids or names + cols = _get_extended_observable_cols(model, by_id=by_id) + + # aggregate records + dicts = [] + for edata in edata_list: + npdata = ExpDataView(edata) + for i_time, timepoint in enumerate(edata.getTimepoints()): + datadict = {"time": timepoint, "datatype": "data"} + # add observables and noises + for i_obs, obs in enumerate( + _get_names_or_ids(model, "Observable", by_id=by_id) + ): + datadict[obs] = npdata["observedData"][i_time, i_obs] + datadict[obs + "_std"] = npdata["observedDataStdDev"][ + i_time, i_obs + ] + + # add conditions + _fill_conditions_dict(datadict, model, edata, by_id=by_id) + + dicts.append(datadict) + + return pd.DataFrame.from_records(dicts, columns=cols) + + +def getSimulationObservablesAsDataFrame( + model: amici.Model, + edata_list: ExpDatas, + rdata_list: ReturnDatas, + by_id: Optional[bool] = False, +) -> pd.DataFrame: + """ + Write Observables from simulation results as DataFrame. + + :param model: + Model instance. + + :param edata_list: + list of ExpData instances with experimental data. + May also be a single ExpData instance. + + :param rdata_list: + list of ReturnData instances corresponding to ExpData. + May also be a single ReturnData instance. + + :param by_id: + If True, ids are used as identifiers, otherwise the possibly more + descriptive names. + + :return: + pandas DataFrame with conditions/timepoints as rows and observables as + columns. + """ + edata_list = _process_edata_list(edata_list) + rdata_list = _process_rdata_list(rdata_list) + + # list of all column names using either names or ids + cols = _get_extended_observable_cols(model, by_id=by_id) + + # aggregate records + dicts = [] + for edata, rdata in zip(edata_list, rdata_list): + for i_time, timepoint in enumerate(rdata["t"]): + datadict = { + "time": timepoint, + "datatype": "simulation", + } + # append simulations + for i_obs, obs in enumerate( + _get_names_or_ids(model, "Observable", by_id=by_id) + ): + datadict[obs] = rdata["y"][i_time, i_obs] + datadict[obs + "_std"] = rdata["sigmay"][i_time, i_obs] + + # use edata to fill conditions columns + _fill_conditions_dict(datadict, model, edata, by_id=by_id) + + # append to dataframe + dicts.append(datadict) + + return pd.DataFrame.from_records(dicts, columns=cols) + + +def getSimulationStatesAsDataFrame( + model: amici.Model, + edata_list: ExpDatas, + rdata_list: ReturnDatas, + by_id: Optional[bool] = False, +) -> pd.DataFrame: + """ + Get model state according to lists of ReturnData and ExpData. + + :param model: + Model instance. + + :param edata_list: + list of ExpData instances with experimental data. + May also be a single ExpData instance. + + :param rdata_list: + list of ReturnData instances corresponding to ExpData. + May also be a single ReturnData instance. + + :param by_id: + If True, ids are used as identifiers, otherwise the possibly more + descriptive names. + + :return: pandas DataFrame with conditions/timepoints as rows and + state variables as columns. + """ + edata_list = _process_edata_list(edata_list) + rdata_list = _process_rdata_list(rdata_list) + + # get conditions and state column names by name or id + cols = _get_state_cols(model, by_id=by_id) + + # aggregate records + dicts = [] + for edata, rdata in zip(edata_list, rdata_list): + for i_time, timepoint in enumerate(rdata["t"]): + datadict = { + "time": timepoint, + } + + # append states + for i_state, state in enumerate( + _get_names_or_ids(model, "State", by_id=by_id) + ): + datadict[state] = rdata["x"][i_time, i_state] + + # use data to fill condition columns + _fill_conditions_dict(datadict, model, edata, by_id=by_id) + + # append to dataframe + dicts.append(datadict) + + return pd.DataFrame.from_records(dicts, columns=cols) + + +def get_expressions_as_dataframe( + model: amici.Model, + edata_list: ExpDatas, + rdata_list: ReturnDatas, + by_id: Optional[bool] = False, +) -> pd.DataFrame: + """ + Get values of model expressions from lists of ReturnData as DataFrame. + + :param model: + Model instance. + + :param edata_list: + list of ExpData instances with experimental data. + May also be a single ExpData instance. + + :param rdata_list: + list of ReturnData instances corresponding to ExpData. + May also be a single ReturnData instance. + + :param by_id: + If True, ids are used as identifiers, otherwise the possibly more + descriptive names. + + :return: pandas DataFrame with conditions/timepoints as rows and + model expressions as columns. + """ + edata_list = _process_edata_list(edata_list) + rdata_list = _process_rdata_list(rdata_list) + + # get conditions and state column names by name or id + cols = _get_expression_cols(model, by_id=by_id) + + # aggregate records + dicts = [] + for edata, rdata in zip(edata_list, rdata_list): + for i_time, timepoint in enumerate(rdata["t"]): + datadict = { + "time": timepoint, + } + + # append expressions + for i_expr, expr in enumerate( + _get_names_or_ids(model, "Expression", by_id=by_id) + ): + datadict[expr] = rdata["w"][i_time, i_expr] + + # use data to fill condition columns + _fill_conditions_dict(datadict, model, edata, by_id=by_id) + + # append to dataframe + dicts.append(datadict) + + return pd.DataFrame.from_records(dicts, columns=cols) + + +def getResidualsAsDataFrame( + model: amici.Model, + edata_list: ExpDatas, + rdata_list: ReturnDatas, + by_id: Optional[bool] = False, +) -> pd.DataFrame: + """ + Convert a list of ReturnData and ExpData to pandas DataFrame with + residuals. + + :param model: + Model instance. + + :param edata_list: + list of ExpData instances with experimental data. May also be a + single ExpData instance. + + :param rdata_list: + list of ReturnData instances corresponding to ExpData. May also be a + single ReturnData instance. + + :param by_id: bool, optional (default = False) + If True, ids are used as identifiers, otherwise the possibly more + descriptive names. + + :return: + pandas DataFrame with conditions and residuals. + """ + edata_list = _process_edata_list(edata_list) + rdata_list = _process_rdata_list(rdata_list) + + # create observable and simulation dataframes + df_edata = getDataObservablesAsDataFrame(model, edata_list, by_id=by_id) + df_rdata = getSimulationObservablesAsDataFrame( + model, edata_list, rdata_list, by_id=by_id + ) + + # get all column names using names or ids + cols = _get_observable_cols(model, by_id=by_id) + + # aggregate records + dicts = [] + for row in df_rdata.index: + datadict = { + "time": df_rdata.loc[row]["time"], + "t_presim": df_rdata.loc[row]["t_presim"], + } + + # iterate over observables + for obs in _get_names_or_ids(model, "Observable", by_id=by_id): + # compute residual and append to dict + datadict[obs] = abs( + (df_edata.loc[row][obs] - df_rdata.loc[row][obs]) + / df_rdata.loc[row][obs + "_std"] + ) + + # iterate over fixed parameters + for par in _get_names_or_ids(model, "FixedParameter", by_id=by_id): + # fill in conditions + datadict[par] = df_rdata.loc[row][par] + datadict[par + "_preeq"] = df_rdata.loc[row][par + "_preeq"] + datadict[par + "_presim"] = df_rdata.loc[row][par + "_presim"] + + # append to dataframe + dicts.append(datadict) + + return pd.DataFrame.from_records(dicts, columns=cols) + + +def _fill_conditions_dict( + datadict: Dict[str, float], + model: AmiciModel, + edata: amici.amici.ExpData, + by_id: bool, +) -> Dict[str, float]: + """ + Helper function that fills in condition parameters from model and + edata. + + :param datadict: + dictionary in which condition parameters will be inserted + as key value pairs. + + :param model: + Model instance. + + :param edata: + ExpData instance. + + :param by_id: + If True, ids are used as identifiers, otherwise the possibly more + descriptive names. + + :return: + dictionary with filled condition parameters. + + """ + datadict["condition_id"] = edata.id + datadict["t_presim"] = edata.t_presim + + for i_par, par in enumerate( + _get_names_or_ids(model, "FixedParameter", by_id=by_id) + ): + if len(edata.fixedParameters): + datadict[par] = edata.fixedParameters[i_par] + else: + datadict[par] = model.getFixedParameters()[i_par] + + if len(edata.fixedParametersPreequilibration): + datadict[par + "_preeq"] = edata.fixedParametersPreequilibration[ + i_par + ] + else: + datadict[par + "_preeq"] = np.nan + + if len(edata.fixedParametersPresimulation): + datadict[par + "_presim"] = edata.fixedParametersPresimulation[ + i_par + ] + else: + datadict[par + "_presim"] = np.nan + return datadict + + +def _get_extended_observable_cols(model: AmiciModel, by_id: bool) -> List[str]: + """ + Construction helper for extended observable dataframe headers. + + :param model: + Model instance. + + :param by_id: + If True, ids are used as identifiers, otherwise the possibly more + descriptive names. + + :return: + column names as list. + """ + return ( + ["condition_id", "time", "datatype", "t_presim"] + + _get_names_or_ids(model, "FixedParameter", by_id=by_id) + + [ + name + "_preeq" + for name in _get_names_or_ids(model, "FixedParameter", by_id=by_id) + ] + + [ + name + "_presim" + for name in _get_names_or_ids(model, "FixedParameter", by_id=by_id) + ] + + _get_names_or_ids(model, "Observable", by_id=by_id) + + [ + name + "_std" + for name in _get_names_or_ids(model, "Observable", by_id=by_id) + ] + ) + + +def _get_observable_cols(model: AmiciModel, by_id: bool) -> List[str]: + """ + Construction helper for observable dataframe headers. + + :param model: + Model instance. + + :param by_id: + If True, ids are used as identifiers, otherwise the possibly more + descriptive names. + + :return: + column names as list. + """ + return ( + ["condition_id", "time", "t_presim"] + + _get_names_or_ids(model, "FixedParameter", by_id=by_id) + + [ + name + "_preeq" + for name in _get_names_or_ids(model, "FixedParameter", by_id=by_id) + ] + + [ + name + "_presim" + for name in _get_names_or_ids(model, "FixedParameter", 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]: + """ + Construction helper for state dataframe headers. + + :param model: + Model instance. + + :param by_id: + If True, ids are used as identifiers, otherwise the possibly more + descriptive names. + + :return: + column names as list. + """ + return ( + ["condition_id", "time", "t_presim"] + + _get_names_or_ids(model, "FixedParameter", by_id=by_id) + + [ + name + "_preeq" + for name in _get_names_or_ids(model, "FixedParameter", by_id=by_id) + ] + + [ + name + "_presim" + for name in _get_names_or_ids(model, "FixedParameter", 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]: + """Construction helper for expression dataframe headers. + + :param model: + Model instance. + + :param by_id: + If True, ids are used as identifiers, otherwise the possibly more + descriptive names. + + :return: + column names as list. + """ + return ( + ["condition_id", "time", "t_presim"] + + _get_names_or_ids(model, "FixedParameter", by_id=by_id) + + [ + name + "_preeq" + for name in _get_names_or_ids(model, "FixedParameter", by_id=by_id) + ] + + [ + name + "_presim" + for name in _get_names_or_ids(model, "FixedParameter", 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]: + """ + Obtains a unique list of identifiers for the specified variable. + First tries model.getVariableNames and then uses model.getVariableIds. + + :param model: + Model instance. + + :param variable: + variable name. + + :param by_id: + If True, ids are used as identifiers, otherwise first the possibly + more descriptive names are used. + + :return: + column names as list. + """ + # check whether variable type permitted + variable_options = [ + "Parameter", + "FixedParameter", + "Observable", + "State", + "Expression", + ] + if variable not in variable_options: + raise ValueError("Variable must be in " + str(variable_options)) + + # extract attributes + names = list(getattr(model, f"get{variable}Names")()) + ids = list(getattr(model, f"get{variable}Ids")()) + + # find out if model has names and ids + has_names = getattr(model, f"has{variable}Names")() + has_ids = getattr(model, f"has{variable}Ids")() + + # extract labels + if not by_id and has_names and len(set(names)) == len(names): + # use variable names + return names + elif has_ids: + # use variable ids + return ids + else: + # unable to create unique labels + if by_id: + msg = f"Model {variable} ids are not set." + else: + msg = ( + f"Model {variable} names are not unique and " + f"{variable} ids are not set." + ) + raise ValueError(msg) + + +def _get_specialized_fixed_parameters( + model: AmiciModel, + condition: Union[Dict[str, SupportsFloat], pd.Series], + overwrite: Union[Dict[str, SupportsFloat], pd.Series], + by_id: bool, +) -> List[float]: + """ + Copies values in condition and overwrites them according to key + value pairs specified in overwrite. + + :param model: + Model instance. + :param condition: + fixedParameter values. + :param overwrite: + dict specifying which values in condition are to be replaced. + :param by_id: + bool + If True, ids are used as identifiers, otherwise the possibly more + descriptive names. + + :return: + overwritten FixedParameter as list. + """ + cond = copy.deepcopy(condition) + for field in overwrite: + cond[field] = overwrite[field] + return [ + float(cond[name]) + for name in _get_names_or_ids(model, "FixedParameter", by_id=by_id) + ] + + +def constructEdataFromDataFrame( + df: pd.DataFrame, + model: AmiciModel, + condition: pd.Series, + by_id: Optional[bool] = False, +) -> amici.amici.ExpData: + """ + Constructs an ExpData instance according to the provided Model + and DataFrame. + + :param df: + pd.DataFrame with Observable Names/Ids as columns. + Standard deviations may be specified by appending '_std' as suffix. + + :param model: + Model instance. + + :param condition: + pd.Series with FixedParameter Names/Ids as columns. + Preequilibration conditions may be specified by appending + '_preeq' as suffix. Presimulation conditions may be specified by + appending '_presim' as suffix. + + :param by_id: + Indicate whether in the arguments, column headers are based on ids or + names. This should correspond to the way `df` and `condition` was + created in the first place. + + :return: + ExpData instance. + """ + # initialize edata + edata = amici.ExpData(model.get()) + + # timepoints + df = df.sort_values(by="time", ascending=True) + edata.setTimepoints(df["time"].values.astype(float)) + + # get fixed parameters from condition + overwrite_preeq = {} + overwrite_presim = {} + for par in list(_get_names_or_ids(model, "FixedParameter", by_id=by_id)): + if par + "_preeq" in condition.keys() and not math.isnan( + condition[par + "_preeq"].astype(float) + ): + overwrite_preeq[par] = condition[par + "_preeq"].astype(float) + if par + "_presim" in condition.keys() and not math.isnan( + condition[par + "_presim"].astype(float) + ): + overwrite_presim[par] = condition[par + "_presim"].astype(float) + + # fill in fixed parameters + edata.fixedParameters = ( + condition[_get_names_or_ids(model, "FixedParameter", by_id=by_id)] + .astype(float) + .values + ) + + # fill in preequilibration parameters + if any( + [overwrite_preeq[key] != condition[key] for key in overwrite_preeq] + ): + edata.fixedParametersPreequilibration = ( + _get_specialized_fixed_parameters( + model, condition, overwrite_preeq, by_id=by_id + ) + ) + elif len(overwrite_preeq): + edata.fixedParametersPreequilibration = copy.deepcopy( + edata.fixedParameters + ) + + # fill in presimulation parameters + if any( + [ + overwrite_presim[key] != condition[key] + for key in overwrite_presim.keys() + ] + ): + edata.fixedParametersPresimulation = _get_specialized_fixed_parameters( + model, condition, overwrite_presim, by_id=by_id + ) + elif len(overwrite_presim.keys()): + edata.fixedParametersPresimulation = copy.deepcopy( + edata.fixedParameters + ) + + # fill in presimulation time + if "t_presim" in condition.keys(): + edata.t_presim = float(condition["t_presim"]) + + # fill in data and stds + for obs_index, obs in enumerate( + _get_names_or_ids(model, "Observable", by_id=by_id) + ): + if obs in df.keys(): + edata.setObservedData(df[obs].values.astype(float), obs_index) + if obs + "_std" in df.keys(): + edata.setObservedDataStdDev( + df[obs + "_std"].values.astype(float), obs_index + ) + + return edata + + +def getEdataFromDataFrame( + model: AmiciModel, df: pd.DataFrame, by_id: Optional[bool] = False +) -> List[amici.amici.ExpData]: + """ + Constructs a ExpData instances according to the provided Model and + DataFrame. + + :param df: + dataframe with Observable Names/Ids, FixedParameter Names/Ids + and time as columns. Standard deviations may be specified by + appending '_std' as suffix. Preequilibration fixedParameters may be + specified by appending '_preeq' as suffix. Presimulation + fixedParameters may be specified by appending '_presim' as suffix. + Presimulation time may be specified as 't_presim' column. + + :param model: + Model instance. + + :param by_id: + Whether the column names in `df` are based on ids or names, + corresponding to how the dataframe was created in the first place. + + :return: + list of ExpData instances. + """ + edata_list = [] + + # aggregate features that define a condition + + # fixed parameters + condition_parameters = _get_names_or_ids( + model, "FixedParameter", by_id=by_id + ) + # preeq and presim parameters + for par in _get_names_or_ids(model, "FixedParameter", by_id=by_id): + if par + "_preeq" in df.columns: + condition_parameters.append(par + "_preeq") + if par + "_presim" in df.columns: + condition_parameters.append(par + "_presim") + # presimulation time + if "t_presim" in df.columns: + condition_parameters.append("t_presim") + # drop duplicates to create final conditions + conditions = df[condition_parameters].drop_duplicates() + + # iterate over conditions + for ir, row in conditions.iterrows(): + # subselect rows that match condition + selected = np.ones((len(df),), dtype=bool) + for par_label, par in row.items(): + if math.isnan(par): + selected = selected & np.isnan( + df[par_label].astype(float).values + ) + else: + selected = selected & (df[par_label] == par) + edata_df = df[selected] + + edata_list.append( + constructEdataFromDataFrame(edata_df, model, row, by_id=by_id) + ) + + return edata_list diff --git a/deps/AMICI/python/sdist/amici/parameter_mapping.py b/deps/AMICI/python/sdist/amici/parameter_mapping.py deleted file mode 120000 index 45a20acfc..000000000 --- a/deps/AMICI/python/sdist/amici/parameter_mapping.py +++ /dev/null @@ -1 +0,0 @@ -../../amici/parameter_mapping.py \ No newline at end of file diff --git a/deps/AMICI/python/sdist/amici/parameter_mapping.py b/deps/AMICI/python/sdist/amici/parameter_mapping.py new file mode 100644 index 000000000..f1cf75a15 --- /dev/null +++ b/deps/AMICI/python/sdist/amici/parameter_mapping.py @@ -0,0 +1,457 @@ +""" +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. +""" +from __future__ import annotations + +import numbers +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]) diff --git a/deps/AMICI/python/sdist/amici/petab_import.py b/deps/AMICI/python/sdist/amici/petab_import.py deleted file mode 120000 index d524fe81f..000000000 --- a/deps/AMICI/python/sdist/amici/petab_import.py +++ /dev/null @@ -1 +0,0 @@ -../../amici/petab_import.py \ No newline at end of file diff --git a/deps/AMICI/python/sdist/amici/petab_import.py b/deps/AMICI/python/sdist/amici/petab_import.py new file mode 100644 index 000000000..23fe4394f --- /dev/null +++ b/deps/AMICI/python/sdist/amici/petab_import.py @@ -0,0 +1,1054 @@ +""" +PEtab Import +------------ +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, + ) + + +if __name__ == "__main__": + _main() diff --git a/deps/AMICI/python/sdist/amici/petab_import_pysb.py b/deps/AMICI/python/sdist/amici/petab_import_pysb.py deleted file mode 120000 index 01591fc62..000000000 --- a/deps/AMICI/python/sdist/amici/petab_import_pysb.py +++ /dev/null @@ -1 +0,0 @@ -../../amici/petab_import_pysb.py \ No newline at end of file diff --git a/deps/AMICI/python/sdist/amici/petab_import_pysb.py b/deps/AMICI/python/sdist/amici/petab_import_pysb.py new file mode 100644 index 000000000..8036d1358 --- /dev/null +++ b/deps/AMICI/python/sdist/amici/petab_import_pysb.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 +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) + + 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 + ) + + 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_objective.py b/deps/AMICI/python/sdist/amici/petab_objective.py deleted file mode 120000 index 9d08244e1..000000000 --- a/deps/AMICI/python/sdist/amici/petab_objective.py +++ /dev/null @@ -1 +0,0 @@ -../../amici/petab_objective.py \ No newline at end of file diff --git a/deps/AMICI/python/sdist/amici/petab_objective.py b/deps/AMICI/python/sdist/amici/petab_objective.py new file mode 100644 index 000000000..e3111d3b6 --- /dev/null +++ b/deps/AMICI/python/sdist/amici/petab_objective.py @@ -0,0 +1,1184 @@ +""" +PEtab Objective +--------------- +Functionality related to running simulations or evaluating the objective +function as defined by a PEtab problem +""" + +import copy +import logging +import numbers +import re +from typing import ( + Any, + Collection, + Dict, + Iterator, + List, + Optional, + Sequence, + Tuple, + Union, +) + +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 + +from . import AmiciExpData, AmiciModel +from .logging import get_logger, log_execution_time +from .parameter_mapping import ( + ParameterMapping, + ParameterMappingForCondition, + fill_in_parameters, +) +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. + + :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 diff --git a/deps/AMICI/python/sdist/amici/petab_simulate.py b/deps/AMICI/python/sdist/amici/petab_simulate.py deleted file mode 120000 index 350628c21..000000000 --- a/deps/AMICI/python/sdist/amici/petab_simulate.py +++ /dev/null @@ -1 +0,0 @@ -../../amici/petab_simulate.py \ No newline at end of file diff --git a/deps/AMICI/python/sdist/amici/petab_simulate.py b/deps/AMICI/python/sdist/amici/petab_simulate.py new file mode 100644 index 000000000..32c1ef895 --- /dev/null +++ b/deps/AMICI/python/sdist/amici/petab_simulate.py @@ -0,0 +1,113 @@ +""" +PEtab Simulate +-------------- +Functionality related to the use of AMICI for simulation with PEtab's +Simulator class. + +Use cases: + +- generate data for use with PEtab's plotting methods +- generate synthetic data +""" + +import inspect +import sys +from typing import Callable + +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, +) + +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..9108b108b --- /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 Dict, Tuple, Union + +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 + +# ID of model parameter that is to be added to SBML model to indicate +# preequilibration +PREEQ_INDICATOR_ID = "preequilibration_indicator" + + +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. + + 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 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 diff --git a/deps/AMICI/python/sdist/amici/plotting.py b/deps/AMICI/python/sdist/amici/plotting.py deleted file mode 120000 index 5195d2f4c..000000000 --- a/deps/AMICI/python/sdist/amici/plotting.py +++ /dev/null @@ -1 +0,0 @@ -../../amici/plotting.py \ No newline at end of file diff --git a/deps/AMICI/python/sdist/amici/plotting.py b/deps/AMICI/python/sdist/amici/plotting.py new file mode 100644 index 000000000..bd1f3a8ba --- /dev/null +++ b/deps/AMICI/python/sdist/amici/plotting.py @@ -0,0 +1,141 @@ +""" +Plotting +-------- +Plotting related functions +""" +from typing import Iterable, Optional, Sequence, Union + +import matplotlib.pyplot as plt +import pandas as pd +import seaborn as sns +from matplotlib.axes import Axes + +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, + model: Model = None, + prefer_names: bool = True, +) -> None: + """ + Plot state trajectories + + :param rdata: + AMICI simulation results as returned by + :func:`amici.amici.runAmiciSimulation` + + :param state_indices: + Indices of states for which trajectories are to be plotted + + :param ax: + matplotlib Axes instance to plot into + + :param model: + amici model instance + + :param prefer_names: + Whether state names should be preferred over IDs, if available. + """ + 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") + + +def plot_observable_trajectories( + rdata: ReturnDataView, + observable_indices: Optional[Iterable[int]] = None, + ax: Optional[Axes] = None, + model: Model = None, + prefer_names: bool = True, +) -> None: + """ + Plot observable trajectories + + :param rdata: + AMICI simulation results as returned by + :func:`amici.amici.runAmiciSimulation` + + :param observable_indices: + Indices of observables for which trajectories are to be plotted + + :param ax: + matplotlib Axes instance to plot into + + :param model: + amici model instance + + :param prefer_names: + Whether observables names should be preferred over IDs, if available. + """ + 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") + + +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, + ) + sns.heatmap(df, center=0.0) + plt.title("Jacobian") + + +# backwards compatibility +plotStateTrajectories = plot_state_trajectories +plotObservableTrajectories = plot_observable_trajectories + + +def plot_expressions( + exprs: Union[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. + :param rdata: + The simulation results. + """ + if not isinstance(exprs, Sequence) or isinstance(exprs, str): + exprs = [exprs] + + for expr in exprs: + plt.plot(rdata.t, evaluate(expr, rdata), label=str(expr)) + + plt.legend() + plt.gca().set_xlabel("$t$") diff --git a/deps/AMICI/python/sdist/amici/pysb_import.py b/deps/AMICI/python/sdist/amici/pysb_import.py deleted file mode 120000 index f3ad0bdb5..000000000 --- a/deps/AMICI/python/sdist/amici/pysb_import.py +++ /dev/null @@ -1 +0,0 @@ -../../amici/pysb_import.py \ No newline at end of file diff --git a/deps/AMICI/python/sdist/amici/pysb_import.py b/deps/AMICI/python/sdist/amici/pysb_import.py new file mode 100644 index 000000000..aa1dc7cd9 --- /dev/null +++ b/deps/AMICI/python/sdist/amici/pysb_import.py @@ -0,0 +1,1471 @@ +""" +PySB Import +------------ +This module provides all necessary functionality to import a model specified +in the :class:`pysb.core.Model` format. +""" + +import itertools +import logging +import os +import sys +from pathlib import Path +from typing import ( + Any, + Callable, + Dict, + Iterable, + List, + Optional, + Set, + Tuple, + Union, +) + +import numpy as np +import pysb +import pysb.bng +import pysb.pattern +import sympy as sp + +from .de_export import ( + Constant, + DEExporter, + DEModel, + DifferentialState, + Expression, + LogLikelihoodY, + Observable, + Parameter, + SigmaY, + _default_simplify, +) +from .import_utils import ( + _get_str_symbol_identifiers, + _parse_special_functions, + generate_measurement_symbol, + noise_distribution_to_cost_function, + noise_distribution_to_observable_transformation, +) +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]] + +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, + assume_pow_positivity: bool = False, + compiler: str = None, + compute_conservation_laws: bool = True, + compile: bool = True, + simplify: Callable = _default_simplify, + # Do not enable by default without testing. + # See https://github.com/AMICI-dev/AMICI/pull/1672 + cache_simplify: bool = False, + generate_sensitivity_code: bool = True, + model_name: Optional[str] = None, +): + r""" + Generate AMICI C++ files for the provided model. + + .. warning:: + **PySB models with Compartments** + + When importing a PySB model with ``pysb.Compartment``\ s, BioNetGen + scales reaction fluxes with the compartment size. Instead of using the + respective symbols, the compartment size Parameter or Expression is + evaluated when generating equations. This may lead to unexpected + results if the compartment size parameter is changed for AMICI + simulations. + + :param model: + pysb model, :attr:`pysb.Model.name` will determine the name of the + generated module + + :param output_dir: + see :meth:`amici.de_export.ODEExporter.set_paths` + + :param observables: + list of :class:`pysb.core.Expression` or :class:`pysb.core.Observable` + names in the provided model that should be mapped to observables + + :param sigmas: + dict of :class:`pysb.core.Expression` names that should be mapped to + sigmas + + :param noise_distributions: + dict with names of observable Expressions as keys and a noise type + identifier, or a callable generating a custom noise formula string + (see :py:func:`amici.import_utils.noise_distribution_to_cost_function` + ). If nothing is passed for some observable id, a normal model is + assumed as default. + + :param constant_parameters: + list of :class:`pysb.core.Parameter` names that should be mapped as + fixed parameters + + :param verbose: verbosity level for logging, True/False default to + :attr:`logging.DEBUG`/:attr:`logging.ERROR` + + :param assume_pow_positivity: + if set to ``True``, a special pow function is used to avoid problems + with state variables that may become negative due to numerical + errors + + :param compiler: + Absolute path to the compiler executable to be used to build the Python + extension, e.g. ``/usr/bin/clang``. + + :param compute_conservation_laws: + if set to ``True``, conservation laws are automatically computed and + applied such that the state-jacobian of the ODE right-hand-side has + full rank. This option should be set to ``True`` when using the Newton + algorithm to compute steadystates + + :param compile: + If ``True``, build the python module for the generated model. If false, + just generate the source code. + + :param simplify: + see :attr:`amici.DEModel._simplify` + + :param cache_simplify: + see :func:`amici.DEModel.__init__` + Note that there are possible issues with PySB models: + https://github.com/AMICI-dev/AMICI/pull/1672 + + :param generate_sensitivity_code: + if set to ``False``, code for sensitivity computation will not be + generated + + :param model_name: + Name for the generated model module. If None, :attr:`pysb.Model.name` + will be used. + """ + if observables is None: + observables = [] + if constant_parameters is None: + constant_parameters = [] + + if sigmas is None: + sigmas = {} + + model_name = model_name or model.name + + set_log_level(logger, verbose) + ode_model = ode_model_from_pysb_importer( + model, + constant_parameters=constant_parameters, + observables=observables, + sigmas=sigmas, + noise_distributions=noise_distributions, + compute_conservation_laws=compute_conservation_laws, + simplify=simplify, + cache_simplify=cache_simplify, + verbose=verbose, + ) + exporter = DEExporter( + ode_model, + outdir=output_dir, + model_name=model_name, + verbose=verbose, + assume_pow_positivity=assume_pow_positivity, + compiler=compiler, + generate_sensitivity_code=generate_sensitivity_code, + ) + exporter.generate_model_code() + + if compile: + exporter.compile_model() + + +@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, + 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, +) -> DEModel: + """ + Creates an :class:`amici.DEModel` instance from a :class:`pysb.Model` + instance. + + :param model: + see :func:`amici.pysb_import.pysb2amici` + + :param constant_parameters: + see :func:`amici.pysb_import.pysb2amici` + + :param observables: + see :func:`amici.pysb_import.pysb2amici` + + :param sigmas: + dict with names of observable Expressions as keys and names of sigma + Expressions as value sigma + + :param noise_distributions: + see :func:`amici.pysb_import.pysb2amici` + + :param compute_conservation_laws: + see :func:`amici.pysb_import.pysb2amici` + + :param simplify: + see :attr:`amici.DEModel._simplify` + + :param cache_simplify: + see :func:`amici.DEModel.__init__` + Note that there are possible issues with PySB models: + https://github.com/AMICI-dev/AMICI/pull/1672 + + :param verbose: verbosity level for logging, True/False default to + :attr:`logging.DEBUG`/:attr:`logging.ERROR` + + :return: + New DEModel instance according to pysbModel + """ + + ode = DEModel( + verbose=verbose, + 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 = [] + + if observables is None: + observables = [] + + if sigmas is None: + sigmas = {} + + pysb.bng.generate_equations(model, verbose=verbose) + + _process_pysb_species(model, ode) + _process_pysb_parameters(model, ode, constant_parameters) + if compute_conservation_laws: + _process_pysb_conservation_laws(model, ode) + _process_pysb_observables( + model, ode, observables, sigmas, noise_distributions + ) + _process_pysb_expressions( + model, ode, observables, sigmas, noise_distributions + ) + ode._has_quadratic_nllh = not noise_distributions or all( + noise_distr in ["normal", "lin-normal", "log-normal", "log10-normal"] + for noise_distr in noise_distributions.values() + ) + + _process_stoichiometric_matrix(model, ode, constant_parameters) + + ode.generate_basic_variables() + + return ode + + +@log_execution_time("processing PySB stoich. matrix", logger) +def _process_stoichiometric_matrix( + pysb_model: pysb.Model, ode_model: DEModel, constant_parameters: List[str] +) -> None: + """ + Exploits the PySB stoichiometric matrix to generate xdot derivatives + + :param pysb_model: + pysb model instance + + :param ode_model: + DEModel instance + + :param constant_parameters: + list of constant parameters + """ + + x = ode_model.sym("x") + w = list(ode_model.sym("w")) + p = list(ode_model.sym("p")) + x_rdata = list(ode_model.sym("x_rdata")) + + n_x = len(x) + n_w = len(w) + n_p = len(p) + n_r = len(pysb_model.reactions) + + solver_index = ode_model.get_solver_indices() + dflux_dx_dict = {} + dflux_dw_dict = {} + dflux_dp_dict = {} + + w_idx = dict() + p_idx = dict() + wx_idx = dict() + + def get_cached_index(symbol, sarray, index_cache): + idx = index_cache.get(symbol, None) + if idx is not None: + return idx + idx = sarray.index(symbol) + index_cache[symbol] = idx + return idx + + for ir, rxn in enumerate(pysb_model.reactions): + for ix in np.unique(rxn["reactants"]): + idx = solver_index.get(ix, None) + if idx is not None: + # species + values = dflux_dx_dict + else: + # conservation law + idx = get_cached_index(x_rdata[ix], w, wx_idx) + values = dflux_dw_dict + + values[(ir, idx)] = sp.diff(rxn["rate"], x_rdata[ix]) + + # typically <= 3 free symbols in rate, we already account for + # species above so we only need to account for propensity, which + # can only be a parameter or expression + for fs in rxn["rate"].free_symbols: + # dw + if isinstance(fs, pysb.Expression): + var = w + idx_cache = w_idx + values = dflux_dw_dict + # dp + elif isinstance(fs, pysb.Parameter): + if fs.name in constant_parameters: + continue + var = p + idx_cache = p_idx + values = dflux_dp_dict + else: + continue + + idx = get_cached_index(fs, var, idx_cache) + values[(ir, idx)] = sp.diff(rxn["rate"], fs) + + dflux_dx = sp.ImmutableSparseMatrix(n_r, n_x, dflux_dx_dict) + dflux_dw = sp.ImmutableSparseMatrix(n_r, n_w, dflux_dw_dict) + dflux_dp = sp.ImmutableSparseMatrix(n_r, n_p, dflux_dp_dict) + + # use dok format to convert numeric csc to sparse symbolic + S = sp.ImmutableSparseMatrix( + n_x, + n_r, # don't use shape here as we are eliminating rows + pysb_model.stoichiometry_matrix[ + np.asarray(list(solver_index.keys())), : + ].todok(), + ) + # don't use `.dot` since it's awfully slow + ode_model._eqs["dxdotdx_explicit"] = S * dflux_dx + ode_model._eqs["dxdotdw"] = S * dflux_dw + ode_model._eqs["dxdotdp_explicit"] = S * dflux_dp + + +@log_execution_time("processing PySB species", logger) +def _process_pysb_species(pysb_model: pysb.Model, ode_model: DEModel) -> None: + """ + Converts pysb Species into States and adds them to the DEModel instance + + :param pysb_model: + pysb model instance + + :param ode_model: + DEModel instance + """ + xdot = sp.Matrix(pysb_model.odes) + + for ix, specie in enumerate(pysb_model.species): + init = sp.sympify("0.0") + for ic in pysb_model.odes.model.initials: + if pysb.pattern.match_complex_pattern( + ic.pattern, specie, exact=True + ): + # we don't want to allow expressions in initial conditions + if ic.value in pysb_model.expressions: + init = pysb_model.expressions[ic.value.name].expand_expr() + else: + init = ic.value + + ode_model.add_component( + DifferentialState( + sp.Symbol(f"__s{ix}"), f"{specie}", init, xdot[ix] + ) + ) + logger.debug(f"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] +) -> None: + """ + Converts pysb parameters into Parameters or Constants and adds them to + the DEModel instance + + :param pysb_model: + pysb model + + :param constant_parameters: + list of Parameters that should be constants + + :param ode_model: + DEModel instance + """ + for par in pysb_model.parameters: + if par.name in constant_parameters: + comp = Constant + else: + comp = Parameter + + ode_model.add_component(comp(par, f"{par.name}", par.value)) + + +@log_execution_time("processing PySB expressions", logger) +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, +) -> None: + r""" + Converts pysb expressions/observables into Observables (with + corresponding standard deviation SigmaY and LogLikelihoodY) or + Expressions and adds them to the DEModel instance + + :param pysb_model: + pysb model + + :param observables: + list of names of :class`pysb.Expression`\ s or + :class:`pysb.Observable`\ s that are to be mapped to DEModel + observables + + :param sigmas: + dict with names of observable :class:`pysb.Expression` / + :class:`pysb.Observable` names as keys and names of sigma + :class:`pysb.Expressions` as values + + :param noise_distributions: + see :func:`amici.pysb_import.pysb2amici` + + :param ode_model: + DEModel instance + """ + # we no longer expand expressions here. pysb/bng guarantees that + # they are ordered according to their dependency and we can + # evaluate them sequentially without reordering. Important to make + # sure that observables are processed first though. + + # we use _constant and _dynamic functions to get access to derived + # expressions that are otherwise only accessible as private attribute + for expr in pysb_model.expressions_constant( + include_derived=True + ) | pysb_model.expressions_dynamic(include_derived=True): + if any( + isinstance(symbol, pysb.Tag) + for symbol in expr.expand_expr().free_symbols + ): + # we only need explicit instantiations of expressions with tags, + # which are defined in the derived expressions. The abstract + # expressions are not needed and lead to compilation errors so + # we skip them. + continue + _add_expression( + expr, + expr.name, + expr.expr, + pysb_model, + ode_model, + observables, + sigmas, + noise_distributions, + ) + + +def _add_expression( + sym: sp.Symbol, + name: str, + 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, +): + """ + Adds expressions to the ODE model given and adds observables/sigmas if + appropriate + + :param sym: + symbol how the expression is referenced in the model + + :param name: + name of the expression + + :param expr: + symbolic expression that the symbol refers to + + :param pysb_model: + see :py:func:`_process_pysb_expressions` + + :param observables: + see :py:func:`_process_pysb_expressions` + + :param sigmas: + see :py:func:`_process_pysb_expressions` + + :param noise_distributions: + see :py:func:`amici.pysb_import.pysb2amici` + + :param ode_model: + see :py:func:`_process_pysb_expressions` + """ + ode_model.add_component( + Expression(sym, name, _parse_special_functions(expr)) + ) + + if name in observables: + noise_dist = ( + noise_distributions.get(name, "normal") + if noise_distributions + else "normal" + ) + + y = sp.Symbol(f"{name}") + trafo = noise_distribution_to_observable_transformation(noise_dist) + obs = Observable(y, name, sym, transformation=trafo) + ode_model.add_component(obs) + + sigma_name, sigma_value = _get_sigma_name_and_value( + pysb_model, name, sigmas + ) + + sigma = sp.Symbol(sigma_name) + ode_model.add_component(SigmaY(sigma, f"{sigma_name}", sigma_value)) + + cost_fun_str = noise_distribution_to_cost_function(noise_dist)(name) + my = generate_measurement_symbol(obs.get_id()) + cost_fun_expr = sp.sympify( + cost_fun_str, + locals=dict( + zip(_get_str_symbol_identifiers(name), (y, my, sigma)) + ), + ) + ode_model.add_component( + LogLikelihoodY( + sp.Symbol(f"llh_{name}"), f"llh_{name}", cost_fun_expr + ) + ) + + +def _get_sigma_name_and_value( + 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 + available sets default values + + :param pysb_model: + pysb model + + :param obs_name: + name of the observable + + :param sigmas: + dict of :class:`pysb.core.Expression` names that should be mapped to + sigmas + + :return: + tuple containing symbolic identifier and formula for the specified + observable + """ + if obs_name in sigmas: + sigma_name = sigmas[obs_name] + try: + # find corresponding Expression instance + sigma_expr = next( + x for x in pysb_model.expressions if x.name == sigma_name + ) + except StopIteration: + raise ValueError( + f"value of sigma {obs_name} is not a " f"valid expression." + ) + sigma_value = sigma_expr.expand_expr() + else: + sigma_name = f"sigma_{obs_name}" + sigma_value = sp.sympify(1.0) + + return sigma_name, sigma_value + + +@log_execution_time("processing PySB observables", logger) +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, +) -> None: + """ + Converts :class:`pysb.core.Observable` into + :class:`DEModel.Expressions` and adds them to the DEModel instance + + :param pysb_model: + pysb model + + :param ode_model: + DEModel instance + + :param observables: + list of names of pysb.Expressions or pysb.Observables that are to be + mapped to DEModel observables + + :param sigmas: + dict with names of observable pysb.Expressions/pysb.Observables + names as keys and names of sigma pysb.Expressions as values + + :param noise_distributions: + see :func:`amici.pysb_import.pysb2amici` + """ + # only add those pysb observables that occur in the added + # Observables as expressions + for obs in pysb_model.observables: + _add_expression( + obs, + obs.name, + obs.expand_obs(), + pysb_model, + ode_model, + observables, + sigmas, + noise_distributions, + ) + + +@log_execution_time("computing PySB conservation laws", logger) +def _process_pysb_conservation_laws( + pysb_model: pysb.Model, ode_model: DEModel +) -> None: + """ + Removes species according to conservation laws to ensure that the + jacobian has full rank + + :param pysb_model: + pysb model + + :param ode_model: + DEModel instance + """ + + monomers_without_conservation_law = set() + for rule in pysb_model.rules: + monomers_without_conservation_law |= _get_unconserved_monomers( + rule, pysb_model + ) + + monomers_without_conservation_law |= ( + _compute_monomers_with_fixed_initial_conditions(pysb_model) + ) + + cl_prototypes = _generate_cl_prototypes( + monomers_without_conservation_law, pysb_model, ode_model + ) + conservation_laws = _construct_conservation_from_prototypes( + cl_prototypes, pysb_model + ) + _add_conservation_for_constant_species(ode_model, conservation_laws) + + _flatten_conservation_laws(conservation_laws) + + for cl in conservation_laws: + ode_model.add_conservation_law(**cl) + + +def _compute_monomers_with_fixed_initial_conditions( + pysb_model: pysb.Model, +) -> Set[str]: + """ + Computes the set of monomers in a model with species that have fixed + initial conditions + + :param pysb_model: pysb model + + :return: + set of monomer names with fixed initial conditions + """ + monomers_with_fixed_initial_conditions = set() + + for monomer in pysb_model.monomers: + # check if monomer has an initial condition that is fixed (means + # that corresponding state is constant and all conservation + # laws are broken) + if any( + [ + ic.fixed # true or false + for ic in pysb_model.initials + if monomer.name in extract_monomers(ic.pattern) + ] + ): + monomers_with_fixed_initial_conditions |= {monomer.name} + + return monomers_with_fixed_initial_conditions + + +def _generate_cl_prototypes( + excluded_monomers: Iterable[str], + pysb_model: pysb.Model, + ode_model: DEModel, +) -> CL_Prototype: + """ + Constructs a dict that contains preprocessed information for the + construction of conservation laws + + :param excluded_monomers: + list of monomer names for which no prototypes + should be computed + + :param pysb_model: + pysb model + + :param ode_model: + DEModel instance + + :return: + dict('monomer.name':{'possible_indices': ..., 'target_indices': ...} + """ + cl_prototypes = dict() + + _compute_possible_indices( + cl_prototypes, pysb_model, ode_model, excluded_monomers + ) + _compute_dependency_idx(cl_prototypes) + _compute_target_index(cl_prototypes, ode_model) + + return cl_prototypes + + +def _compute_possible_indices( + cl_prototypes: CL_Prototype, + pysb_model: pysb.Model, + ode_model: DEModel, + excluded_monomers: Iterable[str], +) -> None: + """ + Computes viable choices for target_index, ie species that could be + removed and replaced by an algebraic expression according to the + conservation law + + :param cl_prototypes: + dict in which possible indices will be written + + :param pysb_model: + pysb model + + :param ode_model: + DEModel instance + + :param excluded_monomers: + monomers for which no conservation laws will be + computed + """ + for monomer in pysb_model.monomers: + if monomer.name not in excluded_monomers: + compartments = [ + str(mp.compartment) # string based comparison as + # compartments are not hashable + for cp in pysb_model.species + for mp in cp.monomer_patterns + if mp.monomer.name == monomer.name + ] + + if len(set(compartments)) > 1: + raise ValueError( + "Conservation laws involving species in " + "multiple compartments are currently not " + "supported! Please run pysb2amici with " + "compute_conservation_laws=False" + ) + # TODO: implement this, multiply species by the volume of + # their respective compartment and allow total_cl to depend + # on parameters + constants and update the respective symbolic + # derivative accordingly + + prototype = dict() + prototype["possible_indices"] = [ + ix + for ix, specie in enumerate(pysb_model.species) + if monomer.name in extract_monomers(specie) + and not ode_model.state_is_constant(ix) + ] + + prototype["species_count"] = len(prototype["possible_indices"]) + + if prototype["possible_indices"]: + cl_prototypes[monomer.name] = prototype + + +def _compute_dependency_idx(cl_prototypes: CL_Prototype) -> None: + """ + Compute connecting species, this allows us to efficiently compute + whether the respective conservation law would induce a cyclic dependency. + Adds a 'dependency_idx' field to the prototype dict that + itself is a dict where keys correspond to indexes that, when used as + target index yield dependencies on conservation laws of monomers in + the respective values + + :param cl_prototypes: + dict in which possible indices will be written + """ + # + for monomer_i, prototype_i in cl_prototypes.items(): + if "dependency_idx" not in prototype_i: + prototype_i["dependency_idx"] = dict() + + for monomer_j, prototype_j in cl_prototypes.items(): + if monomer_i == monomer_j: + continue + + if "dependency_idx" not in prototype_j: + prototype_j["dependency_idx"] = dict() + + idx_overlap = set(prototype_i["possible_indices"]).intersection( + set(prototype_j["possible_indices"]) + ) + if len(idx_overlap) == 0: + continue + + for idx in idx_overlap: + if idx not in prototype_i["dependency_idx"]: + prototype_i["dependency_idx"][idx] = set() + + if idx not in prototype_j["dependency_idx"]: + prototype_j["dependency_idx"][idx] = set() + + prototype_i["dependency_idx"][idx] |= {monomer_j} + prototype_j["dependency_idx"][idx] |= {monomer_i} + + +def _compute_target_index( + cl_prototypes: CL_Prototype, ode_model: DEModel +) -> None: + """ + Computes the target index for every monomer + + :param cl_prototypes: + dict that contains possible indices for every monomer + + :param ode_model: + DEModel instance + """ + possible_indices = list( + set( + list( + itertools.chain( + *[ + cl_prototypes[monomer]["possible_indices"] + for monomer in cl_prototypes + ] + ) + ) + ) + ) + + # Note: currently this function is supposed to also count appearances in + # expressions. However, expressions are currently still empty as they + # are also populated from conservation laws. In case there are many + # state heavy expressions in the model (should not be the case for mass + # action kinetics). This may lead to suboptimal results and could improved. + # As this would require substantial code shuffling, this will only be + # fixed if this becomes an actual problem + appearance_counts = ode_model.get_appearance_counts(possible_indices) + + # in this initial guess we ignore the cost of having cyclic dependencies + # between conservation laws + for monomer in cl_prototypes: + prototype = cl_prototypes[monomer] + # extract monomer specific appearance counts + prototype["appearance_counts"] = [ + appearance_counts[possible_indices.index(idx)] + for idx in prototype["possible_indices"] + ] + # select target index as possible index with minimal appearance count + if len(prototype["appearance_counts"]) == 0: + raise RuntimeError( + f"Failed to compute conservation law for " f"monomer {monomer}" + ) + + idx = np.argmin(prototype["appearance_counts"]) + + # remove entries from possible indices and appearance counts so we + # do not consider them again in later iterations + prototype["target_index"] = prototype["possible_indices"].pop(idx) + prototype["appearance_count"] = prototype["appearance_counts"].pop(idx) + + # this is only an approximation as the effective species count + # of other conservation laws may also be affected by the chosen + # target index. As long as the number of unique monomers in + # multimers has a low upper bound and the species count does not + # vary too much across conservation laws, this approximation + # should be fine + prototype["fillin"] = ( + prototype["appearance_count"] * prototype["species_count"] + ) + + # we might end up with the same index for multiple monomers, so loop until + # we have a set of unique target indices + while not _cl_prototypes_are_valid(cl_prototypes): + _greedy_target_index_update(cl_prototypes) + + +def _cl_prototypes_are_valid(cl_prototypes: CL_Prototype) -> bool: + """ + Checks consistency of cl_prototypes by asserting that target indices + are unique and there are no cyclic dependencies + + :param cl_prototypes: + dict that contains dependency and target indexes for + every monomer + """ + # target indices are unique + if len(cl_prototypes) != len(set(_get_target_indices(cl_prototypes))): + return False + # conservation law dependencies are cycle free + if any(_cl_has_cycle(monomer, cl_prototypes) for monomer in cl_prototypes): + return False + + return True + + +def _cl_has_cycle(monomer: str, cl_prototypes: CL_Prototype) -> bool: + """ + Checks whether monomer has a conservation law that is part of a + cyclic dependency + + :param monomer: + name of monomer for which conservation law is to be checked + + :param cl_prototypes: + dict that contains dependency and target indexes for every monomer + + :return: + boolean indicating whether the conservation law is cyclic + """ + + prototype = cl_prototypes[monomer] + + if prototype["target_index"] not in prototype["dependency_idx"]: + return False + + visited = [monomer] + root = monomer + return any( + _is_in_cycle(connecting_monomer, cl_prototypes, visited, root) + for connecting_monomer in prototype["dependency_idx"][ + prototype["target_index"] + ] + ) + + +def _is_in_cycle( + monomer: str, cl_prototypes: CL_Prototype, visited: List[str], root: str +) -> bool: + """ + Recursively checks for cycles in conservation law dependencies via + Depth First Search + + :param monomer: + current location in cl dependency graph + + :param cl_prototypes: + dict that contains dependency and target indexes for + every monomer + + :param visited: + history of visited monomers with conservation laws + + :param root: + monomer at which the cycle search was started + + :return: + boolean indicating whether the specified monomer is part of a cyclic + conservation law + + """ + if monomer == root: + return True # we found a cycle and root is part of it + + if monomer in visited: + return False # we found a cycle but root is not part of it + + visited.append(monomer) + + prototype = cl_prototypes[monomer] + + if prototype["target_index"] not in prototype["dependency_idx"]: + return False + + return any( + _is_in_cycle(connecting_monomer, cl_prototypes, visited, root) + for connecting_monomer in prototype["dependency_idx"][ + prototype["target_index"] + ] + ) + + +def _greedy_target_index_update(cl_prototypes: CL_Prototype) -> None: + """ + Computes unique target indices for conservation laws from possible + indices such that expected fill in in symbolic derivatives is minimized + + :param cl_prototypes: + dict that contains possible indices and non-unique target indices + for every monomer + """ + + target_indices = _get_target_indices(cl_prototypes) + + for monomer, prototype in cl_prototypes.items(): + if target_indices.count( + prototype["target_index"] + ) > 1 or _cl_has_cycle(monomer, cl_prototypes): + # compute how much fillin the next best target_index would yield + + # we exclude already existing target indices to avoid that + # updating the target index removes uniqueness from already unique + # target indices, this may slightly reduce chances of finding a + # solution but prevents infinite loops + for target_index in list(set(target_indices)): + try: + local_idx = prototype["possible_indices"].index( + target_index + ) + except ValueError: + local_idx = None + + if local_idx: + del prototype["possible_indices"][local_idx] + del prototype["appearance_counts"][local_idx] + + if len(prototype["possible_indices"]) == 0: + prototype["diff_fillin"] = -1 + continue + + idx = np.argmin(prototype["appearance_counts"]) + + prototype["local_index"] = idx + prototype["alternate_target_index"] = prototype[ + "possible_indices" + ][idx] + prototype["alternate_appearance_count"] = prototype[ + "appearance_counts" + ][idx] + + prototype["alternate_fillin"] = ( + prototype["alternate_appearance_count"] + * prototype["species_count"] + ) + + prototype["diff_fillin"] = ( + prototype["alternate_fillin"] - prototype["fillin"] + ) + else: + prototype["diff_fillin"] = -1 + + if all( + prototype["diff_fillin"] == -1 for prototype in cl_prototypes.values() + ): + raise RuntimeError( + "Could not compute a valid set of conservation " + "laws for this model!" + ) + + # this puts prototypes with high diff_fillin last + cl_prototypes = sorted( + cl_prototypes.items(), key=lambda kv: kv[1]["diff_fillin"] + ) + cl_prototypes = {proto[0]: proto[1] for proto in cl_prototypes} + + for monomer in cl_prototypes: + prototype = cl_prototypes[monomer] + # we check that we + # A) have an alternative index computed, i.e. that + # that monomer originally had a non-unique target_index + # B) that the target_index still is not unique or part of a cyclic + # dependency. due to the sorting, this will always be the monomer + # with the highest diff_fillin (note that the target index counts + # are recomputed on the fly) + + if prototype["diff_fillin"] > -1 and ( + _get_target_indices(cl_prototypes).count(prototype["target_index"]) + > 1 + or _cl_has_cycle(monomer, cl_prototypes) + ): + prototype["fillin"] = prototype["alternate_fillin"] + prototype["target_index"] = prototype["alternate_target_index"] + prototype["appearance_count"] = prototype[ + "alternate_appearance_count" + ] + + del prototype["possible_indices"][prototype["local_index"]] + del prototype["appearance_counts"][prototype["local_index"]] + + +def _get_target_indices(cl_prototypes: CL_Prototype) -> List[List[int]]: + """ + Computes the list target indices for the current + conservation law prototype + + :param cl_prototypes: + dict that contains target indices for every monomer + + :return: + List of lists of target indices + """ + return [prototype["target_index"] for prototype in cl_prototypes.values()] + + +def _construct_conservation_from_prototypes( + cl_prototypes: CL_Prototype, pysb_model: pysb.Model +) -> List[ConservationLaw]: + """ + Computes the algebraic expression for the total amount of a given + monomer + + :param cl_prototypes: + see return of :func:`_generate_cl_prototypes` + + :param pysb_model: + pysb model + + :return: + list of dicts describing conservation laws + """ + conservation_laws = [] + for monomer_name in cl_prototypes: + target_index = cl_prototypes[monomer_name]["target_index"] + coefficients = dict() + + for ix, specie in enumerate(pysb_model.species): + count = extract_monomers(specie).count(monomer_name) + if count > 0: + coefficients[sp.Symbol(f"__s{ix}")] = count + + conservation_laws.append( + { + "state": sp.Symbol(f"__s{target_index}"), + "total_abundance": sp.Symbol(f"tcl__s{target_index}"), + "coefficients": coefficients, + } + ) + + return conservation_laws + + +def _add_conservation_for_constant_species( + ode_model: DEModel, conservation_laws: List[ConservationLaw] +) -> None: + """ + Computes the algebraic expression for the total amount of a given + monomer + + :param ode_model: + DEModel instance to which the conservation laws will be added + + :param conservation_laws: + see return of :func:`_construct_conservation_from_prototypes` + + """ + + for ix in range(ode_model.num_states_rdata()): + if ode_model.state_is_constant(ix): + conservation_laws.append( + { + "state": sp.Symbol(f"__s{ix}"), + "total_abundance": sp.Symbol(f"tcl__s{ix}"), + "coefficients": {sp.Symbol(f"__s{ix}"): 1.0}, + } + ) + + +def _flatten_conservation_laws( + conservation_laws: List[ConservationLaw], +) -> None: + """ + Flatten the conservation laws such that the state_expr not longer + depend on any states that are replaced by conservation laws + + :param conservation_laws: + see return of :func:`_construct_conservation_from_prototypes` + """ + conservation_law_subs = _get_conservation_law_subs(conservation_laws) + + while conservation_law_subs: + for cl in conservation_laws: + # only update if we changed something + if any( + _apply_conseration_law_sub(cl, sub) + for sub in conservation_law_subs + ): + conservation_law_subs = _get_conservation_law_subs( + conservation_laws + ) + + +def _apply_conseration_law_sub( + cl: ConservationLaw, sub: Tuple[sp.Symbol, ConservationLaw] +) -> bool: + """ + Applies a substitution to a conservation law by replacing the + coefficient of the state of the + + :param cl: + conservation law + + :param sub: + substitution to apply, tuple of (state to be replaced, conservation + law) + + :return: boolean flag indicating whether the substitution was applied + """ + if not _state_in_cl_formula(sub[0], cl): + return False + + coeff = cl["coefficients"].pop(sub[0], 0.0) + # x_j = T/b_j - sum_{i≠j}(x_i * b_i) / b_j + # don't need to account for totals here as we can simply + # absorb that into the new total + for k, v in sub[1].items(): + if k == sub[0]: + continue + update = -coeff * v / sub[1][sub[0]] + + if k in cl["coefficients"]: + cl["coefficients"][k] += update + else: + cl["coefficients"][k] = update + + return True + + +def _state_in_cl_formula(state: sp.Symbol, cl: ConservationLaw) -> bool: + """ + Checks whether state appears in the formula the provided cl + + :param state: + state + + :param cl: + conservation law + + :return: + boolean indicator + """ + if cl["state"] == state: + return False + + return cl["coefficients"].get(state, 0.0) != 0.0 + + +def _get_conservation_law_subs( + 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 + + :param conservation_laws: + see return of :func:`_flatten_conservation_laws` + + :return: + list of tuples containing substitution rules to be used with sympy + subs + """ + return [ + (cl["state"], cl["coefficients"]) + for cl in conservation_laws + if any( + _state_in_cl_formula(cl["state"], other_cl) + for other_cl in conservation_laws + ) + ] + + +def has_fixed_parameter_ic( + specie: pysb.core.ComplexPattern, + pysb_model: pysb.Model, + ode_model: DEModel, +) -> bool: + """ + Wrapper to interface + :meth:`de_export.DEModel.state_has_fixed_parameter_initial_condition` + from a pysb specie/model arguments + + :param specie: + pysb species + + :param pysb_model: + pysb model + + :param ode_model: + ODE model + + :return: + ``False`` if the species does not have an initial condition at all. + Otherwise the return value of + :meth:`de_export.DEModel.state_has_fixed_parameter_initial_condition` + """ + # ComplexPatterns are not hashable, so we have to compare by string + ic_index = next( + ( + ic + for ic, condition in enumerate(pysb_model.initials) + if pysb.pattern.match_complex_pattern( + condition[0], specie, exact=True + ) + ), + None, + ) + if ic_index is None: + return False + else: + return ode_model.state_has_fixed_parameter_initial_condition(ic_index) + + +def extract_monomers( + complex_patterns: Union[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. + + :param complex_patterns: + (list of) complex pattern(s) + + :return: + list of monomer names + """ + if not isinstance(complex_patterns, list): + complex_patterns = [complex_patterns] + return [ + mp.monomer.name + for cp in complex_patterns + if cp is not None + for mp in cp.monomer_patterns + ] + + +def _get_unconserved_monomers( + rule: pysb.Rule, pysb_model: pysb.Model +) -> Set[str]: + """ + Constructs the set of monomer names for which the specified rule changes + the stoichiometry of the monomer in the specified model. + + :param rule: + the pysb rule + + :param pysb_model: + pysb model + + :return: + set of monomer names for which the stoichiometry is not conserved + """ + unconserved_monomers = set() + + if ( + not rule.delete_molecules + and len(rule.product_pattern.complex_patterns) == 0 + ): + # if delete_molecules is not True but we have a degradation rule, + # we have to actually go through the reactions that are created by + # the rule + for reaction in [ + r for r in pysb_model.reactions if rule.name in r["rule"] + ]: + unconserved_monomers |= _get_changed_stoichiometries( + [pysb_model.species[ix] for ix in reaction["reactants"]], + [pysb_model.species[ix] for ix in reaction["products"]], + ) + else: + # otherwise we can simply extract all information for the rule + # itself, which is computationally much more efficient + unconserved_monomers |= _get_changed_stoichiometries( + rule.reactant_pattern.complex_patterns, + rule.product_pattern.complex_patterns, + ) + + return unconserved_monomers + + +def _get_changed_stoichiometries( + reactants: Union[pysb.ComplexPattern, List[pysb.ComplexPattern]], + products: Union[pysb.ComplexPattern, List[pysb.ComplexPattern]], +) -> Set[str]: + """ + Constructs the set of monomer names which have different + stoichiometries in reactants and products. + + :param reactants: + (list of) complex pattern(s) + :param products: + (list of) complex pattern(s) + + :returns: + set of monomer name for which the stoichiometry changed + """ + + changed_stoichiometries = set() + + reactant_monomers = extract_monomers(reactants) + + product_monomers = extract_monomers(products) + + for monomer in set(reactant_monomers + product_monomers): + if reactant_monomers.count(monomer) != product_monomers.count(monomer): + changed_stoichiometries.add(monomer) + + return changed_stoichiometries + + +def pysb_model_from_path(pysb_model_file: Union[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 + :return: The pysb Model instance + """ + + pysb_model_module_name = os.path.splitext( + os.path.split(pysb_model_file)[-1] + )[0] + + import importlib.util + + spec = importlib.util.spec_from_file_location( + pysb_model_module_name, pysb_model_file + ) + module = importlib.util.module_from_spec(spec) + sys.modules[pysb_model_module_name] = module + spec.loader.exec_module(module) + + return module.model diff --git a/deps/AMICI/python/sdist/amici/sbml_import.py b/deps/AMICI/python/sdist/amici/sbml_import.py deleted file mode 120000 index d5ebad6b6..000000000 --- a/deps/AMICI/python/sdist/amici/sbml_import.py +++ /dev/null @@ -1 +0,0 @@ -../../amici/sbml_import.py \ No newline at end of file diff --git a/deps/AMICI/python/sdist/amici/sbml_import.py b/deps/AMICI/python/sdist/amici/sbml_import.py new file mode 100644 index 000000000..dd24b98cf --- /dev/null +++ b/deps/AMICI/python/sdist/amici/sbml_import.py @@ -0,0 +1,2912 @@ +""" +SBML Import +----------- +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 +import math +import os +import re +import warnings +import xml.etree.ElementTree as ET +from pathlib import Path +from typing import ( + Any, + Callable, + Dict, + Iterable, + List, + Optional, + Sequence, + Set, + Tuple, + Union, +) + +import libsbml as sbml +import numpy as np +import sympy as sp +from sympy.logic.boolalg import BooleanFalse, BooleanTrue + +from . import has_clibs +from .constants import SymbolId +from .de_export import ( + DEExporter, + DEModel, + _default_simplify, + smart_is_zero_matrix, + symbol_with_assumptions, +) +from .import_utils import ( + RESERVED_SYMBOLS, + _check_unsupported_functions, + _get_str_symbol_identifiers, + _parse_special_functions, + amici_time_symbol, + annotation_namespace, + generate_measurement_symbol, + generate_regularization_symbol, + noise_distribution_to_cost_function, + noise_distribution_to_observable_transformation, + sbml_time_symbol, + smart_subs, + smart_subs_dict, + toposort_symbols, +) +from .logging import get_logger, log_execution_time, set_log_level +from .sbml_utils import SBMLException, _parse_logical_operators +from .splines import AbstractSpline + +SymbolicFormula = Dict[sp.Symbol, sp.Expr] + + +default_symbols = {symbol: {} for symbol in SymbolId} + +ConservationLaw = Dict[str, Union[str, sp.Expr]] + +logger = get_logger(__name__, logging.ERROR) + + +class SbmlImporter: + """ + Class to generate AMICI C++ files for a model provided in the Systems + Biology Markup Language (SBML). + + :ivar show_sbml_warnings: + indicates whether libSBML warnings should be + displayed + + :ivar symbols: + dict carrying symbolic definitions + + :ivar sbml_reader: + + The libSBML sbml reader + + .. warning:: + Not storing this may result in a segfault. + + :ivar sbml_doc: + document carrying the sbml definition + + .. warning:: + Not storing this may result in a segfault. + + :ivar sbml: + SBML model to import + + :ivar compartments: + dict of compartment ids and compartment volumes + + :ivar stoichiometric_matrix: + stoichiometric matrix of the model + + :ivar flux_vector: + reaction kinetic laws + + :ivar flux_ids: + identifiers for elements of flux_vector + + :ivar _local_symbols: + model symbols for sympy to consider during sympification + see `locals`argument in `sympy.sympify` + + :ivar species_assignment_rules: + Assignment rules for species. + Key is symbolic identifier and value is assignment value + + :ivar compartment_assignment_rules: + Assignment rules for compartments. + Key is symbolic identifier and value is assignment value + + :ivar parameter_assignment_rules: + assignment rules for parameters, these parameters are not permissible + for sensitivity analysis + + :ivar initial_assignments: + initial assignments for parameters, these parameters are not + permissible for sensitivity analysis + + :ivar sbml_parser_settings: + sets behaviour of SBML Formula parsing + """ + + def __init__( + self, + sbml_source: Union[str, Path, sbml.Model], + show_sbml_warnings: bool = False, + from_file: bool = True, + discard_annotations: bool = False, + ) -> None: + """ + Create a new Model instance. + + :param sbml_source: + Either a path to SBML file where the model is specified, + or a model string as created by sbml.sbmlWriter( + ).writeSBMLToString() or an instance of `libsbml.Model`. + + :param show_sbml_warnings: + Indicates whether libSBML warnings should be displayed. + + :param from_file: + Whether `sbml_source` is a file name (True, default), or an SBML + string + + :param discard_annotations: + discard information contained in AMICI SBML annotations (debug). + """ + if isinstance(sbml_source, sbml.Model): + self.sbml_doc: sbml.Document = sbml_source.getSBMLDocument() + else: + self.sbml_reader: sbml.SBMLReader = sbml.SBMLReader() + if from_file: + sbml_doc = self.sbml_reader.readSBMLFromFile(str(sbml_source)) + else: + sbml_doc = self.sbml_reader.readSBMLFromString(sbml_source) + self.sbml_doc = sbml_doc + + self.show_sbml_warnings: bool = show_sbml_warnings + + # process document + self._process_document() + + 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._local_symbols: Dict[str, Union[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._reset_symbols() + + # http://sbml.org/Software/libSBML/5.18.0/docs/python-api/classlibsbml_1_1_l3_parser_settings.html#abcfedd34efd3cae2081ba8f42ea43f52 + # 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._discard_annotations: bool = discard_annotations + + @log_execution_time("loading SBML", logger) + def _process_document(self) -> None: + """ + Validate and simplify document. + """ + # Ensure we got a valid SBML model, otherwise further processing + # might lead to undefined results + log_execution_time("validating SBML", logger)( + self.sbml_doc.validateSBML + )() + _check_lib_sbml_errors(self.sbml_doc, self.show_sbml_warnings) + + # Flatten "comp" model? Do that before any other converters are run + if any( + self.sbml_doc.getPlugin(i_plugin).getPackageName() == "comp" + for i_plugin in range(self.sbml_doc.getNumPlugins()) + ): + # see libsbml CompFlatteningConverter for options + conversion_properties = sbml.ConversionProperties() + conversion_properties.addOption("flatten comp", True) + conversion_properties.addOption("leave_ports", False) + conversion_properties.addOption("performValidation", False) + conversion_properties.addOption("abortIfUnflattenable", "none") + if ( + log_execution_time("flattening hierarchical SBML", logger)( + self.sbml_doc.convert + )(conversion_properties) + != sbml.LIBSBML_OPERATION_SUCCESS + ): + raise SBMLException( + "Required SBML comp extension is currently not supported " + "and flattening the model failed." + ) + # check the flattened model is still valid + log_execution_time("re-validating SBML", logger)( + self.sbml_doc.validateSBML + )() + _check_lib_sbml_errors(self.sbml_doc, self.show_sbml_warnings) + + # apply several model simplifications that make our life substantially + # easier + if self.sbml_doc.getModel().getNumFunctionDefinitions(): + convert_config = ( + sbml.SBMLFunctionDefinitionConverter().getDefaultProperties() + ) + log_execution_time("converting SBML functions", logger)( + self.sbml_doc.convert + )(convert_config) + + convert_config = ( + sbml.SBMLLocalParameterConverter().getDefaultProperties() + ) + log_execution_time("converting SBML local parameters", logger)( + self.sbml_doc.convert + )(convert_config) + + # If any of the above calls produces an error, this will be added to + # the SBMLError log in the sbml document. Thus, it is sufficient to + # check the error log just once after all conversion/validation calls. + _check_lib_sbml_errors(self.sbml_doc, self.show_sbml_warnings) + + # need to reload the converted model + self.sbml = self.sbml_doc.getModel() + + def _reset_symbols(self) -> None: + """ + Reset the symbols attribute to default values + """ + self.symbols = copy.deepcopy(default_symbols) + self._local_symbols = {} + + 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, + 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, + 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, + cache_simplify: bool = False, + log_as_log10: bool = True, + generate_sensitivity_code: bool = True, + hardcode_symbols: Sequence[str] = None, + ) -> None: + """ + Generate and compile AMICI C++ files for the model provided to the + constructor. + + The resulting model can be imported as a regular Python module (if + `compile=True`), or used from Matlab or C++ as described in the + documentation of the respective AMICI interface. + + Note that this generates model ODEs for changes in concentrations, not + amounts unless the `hasOnlySubstanceUnits` attribute has been + defined for a particular species. + + Sensitivity analysis for local parameters is enabled by creating + global parameters ``_{reactionId}_{localParameterName}``. + + :param model_name: + Name of the generated model package. + Note that in a given Python session, only one model with a given + name can be loaded at a time. + The generated Python extensions cannot be unloaded. Therefore, + make sure to choose a unique name for each model. + + :param output_dir: + Directory where the generated model package will be stored. + + :param observables: + Observables to be added to the model: + ``dictionary( observableId:{'name':observableName + (optional), 'formula':formulaString)})``. + + :param event_observables: + Event observables to be added to the model: + ``dictionary( eventObservableId:{'name':eventObservableName + (optional), 'event':eventId, 'formula':formulaString)})`` + + :param constant_parameters: + list of SBML Ids identifying constant parameters + + :param sigmas: + dictionary(observableId: sigma value or (existing) parameter name) + + :param event_sigmas: + dictionary(eventObservableId: sigma value or (existing) parameter + name) + + :param noise_distributions: + dictionary(observableId: noise type). + If nothing is passed for some observable id, a normal model is + assumed as default. Either pass a noise type identifier, or a + callable generating a custom noise string. + For noise identifiers, see + :func:`amici.import_utils.noise_distribution_to_cost_function`. + + :param event_noise_distributions: + dictionary(eventObservableId: noise type). + If nothing is passed for some observable id, a normal model is + assumed as default. Either pass a noise type identifier, or a + callable generating a custom noise string. + For noise identifiers, see + :func:`amici.import_utils.noise_distribution_to_cost_function`. + + :param verbose: + verbosity level for logging, ``True``/``False`` default to + ``logging.Error``/``logging.DEBUG`` + + :param assume_pow_positivity: + if set to ``True``, a special pow function is + used to avoid problems with state variables that may become + negative due to numerical errors + + :param compiler: + Absolute path to the compiler executable to be used to build the Python + extension, e.g. ``/usr/bin/clang``. + + :param allow_reinit_fixpar_initcond: + see :class:`amici.de_export.ODEExporter` + + :param compile: + If ``True``, compile the generated Python package, + if ``False``, just generate code. + + :param compute_conservation_laws: + if set to ``True``, conservation laws are automatically computed + and applied such that the state-jacobian of the ODE + right-hand-side has full rank. This option should be set to + ``True`` when using the Newton algorithm to compute steadystate + sensitivities. + Conservation laws for constant species are enabled by default. + Support for conservation laws for non-constant species is + experimental and may be enabled by setting an environment variable + ``AMICI_EXPERIMENTAL_SBML_NONCONST_CLS`` to either ``demartino`` + to use the algorithm proposed by De Martino et al. (2014) + https://doi.org/10.1371/journal.pone.0100750, or to any other value + to use the deterministic algorithm implemented in + ``conserved_moieties2.py``. In some cases, the ``demartino`` may + run for a very long time. This has been observed for example in the + case of stoichiometric coefficients with many significant digits. + + :param simplify: + see :attr:`amici.ODEModel._simplify` + + :param cache_simplify: + see :meth:`amici.ODEModel.__init__` + + :param log_as_log10: + If ``True``, log in the SBML model will be parsed as ``log10`` + (default), if ``False``, log will be parsed as natural logarithm + ``ln``. + + :param generate_sensitivity_code: + If ``False``, the code required for sensitivity computation will + not be generated. + + :param hardcode_symbols: + List of SBML entity IDs that are to be hardcoded in the generated model. + Their values cannot be changed anymore after model import. + Currently only parameters that are not targets of rules or + initial assignments are supported. + """ + set_log_level(logger, verbose) + + ode_model = self._build_ode_model( + observables=observables, + event_observables=event_observables, + constant_parameters=constant_parameters, + sigmas=sigmas, + event_sigmas=event_sigmas, + noise_distributions=noise_distributions, + event_noise_distributions=event_noise_distributions, + verbose=verbose, + compute_conservation_laws=compute_conservation_laws, + simplify=simplify, + cache_simplify=cache_simplify, + log_as_log10=log_as_log10, + hardcode_symbols=hardcode_symbols, + ) + + exporter = DEExporter( + ode_model, + model_name=model_name, + outdir=output_dir, + verbose=verbose, + assume_pow_positivity=assume_pow_positivity, + compiler=compiler, + allow_reinit_fixpar_initcond=allow_reinit_fixpar_initcond, + generate_sensitivity_code=generate_sensitivity_code, + ) + exporter.generate_model_code() + + if compile: + if not has_clibs: + warnings.warn( + "AMICI C++ extensions have not been built. " + "Generated model code, but unable to compile." + ) + exporter.compile_model() + + def _build_ode_model( + self, + 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, + compute_conservation_laws: bool = True, + simplify: Optional[Callable] = _default_simplify, + cache_simplify: bool = False, + log_as_log10: bool = True, + hardcode_symbols: Sequence[str] = None, + ) -> DEModel: + """Generate an ODEModel from this SBML model. + + See :py:func:`sbml2amici` for parameters. + """ + constant_parameters = ( + list(constant_parameters) if constant_parameters else [] + ) + + hardcode_symbols = set(hardcode_symbols) if hardcode_symbols else {} + if invalid := (set(constant_parameters) & set(hardcode_symbols)): + raise ValueError( + "The following parameters were selected as both constant " + f"and hard-coded which is not allowed: {invalid}" + ) + + if sigmas is None: + sigmas = {} + + if event_sigmas is None: + event_sigmas = {} + + if noise_distributions is None: + noise_distributions = {} + + if event_noise_distributions is None: + event_noise_distributions = {} + + self._reset_symbols() + self.sbml_parser_settings.setParseLog( + sbml.L3P_PARSE_LOG_AS_LOG10 + if log_as_log10 + else sbml.L3P_PARSE_LOG_AS_LN + ) + self._process_sbml( + constant_parameters=constant_parameters, + hardcode_symbols=hardcode_symbols, + ) + + if ( + self.symbols.get(SymbolId.EVENT, False) + or any( + x["value"].has(sp.Heaviside, sp.Piecewise) + for x in self.symbols[SymbolId.EXPRESSION].values() + ) + or self.flux_vector.has(sp.Heaviside, sp.Piecewise) + ): + if compute_conservation_laws: + logger.warning( + "Conservation laws are currently not supported for models " + "with events, piecewise or Heaviside functions, " + "and will be turned off." + ) + compute_conservation_laws = False + + self._process_observables(observables, sigmas, noise_distributions) + self._process_event_observables( + event_observables, event_sigmas, event_noise_distributions + ) + self._replace_compartments_with_volumes() + + self._clean_reserved_symbols() + self._process_time() + + ode_model = DEModel( + verbose=verbose, + simplify=simplify, + cache_simplify=cache_simplify, + ) + ode_model.import_from_sbml_importer( + self, compute_cls=compute_conservation_laws + ) + return ode_model + + @log_execution_time("importing SBML", logger) + def _process_sbml( + self, + constant_parameters: List[str] = None, + hardcode_symbols: Sequence[str] = None, + ) -> None: + """ + Read parameters, species, reactions, and so on from SBML model + + :param constant_parameters: + SBML Ids identifying constant parameters + :param hardcode_parameters: + Parameter IDs to be replaced by their values in the generated model. + """ + if not self._discard_annotations: + self._process_annotations() + self.check_support() + self._gather_locals(hardcode_symbols=hardcode_symbols) + self._process_parameters( + constant_parameters=constant_parameters, + hardcode_symbols=hardcode_symbols, + ) + self._process_compartments() + self._process_species() + self._process_reactions() + self._process_rules() + self._process_events() + self._process_initial_assignments() + self._process_species_references() + + def check_support(self) -> None: + """ + Check whether all required SBML features are supported. + Also ensures that the SBML contains at least one reaction, or rate + rule, or assignment rule, to produce change in the system over time. + """ + + # Check for required but unsupported SBML extensions + if ( + self.sbml_doc.getLevel() != 3 + and hasattr(self.sbml, "all_elements_from_plugins") + and self.sbml.all_elements_from_plugins.getSize() + ): + raise SBMLException("SBML extensions are currently not supported!") + + if self.sbml_doc.getLevel() == 3: + # the "required" attribute is only available in SBML Level 3 + for i_plugin in range(self.sbml.getNumPlugins()): + plugin = self.sbml.getPlugin(i_plugin) + if ( + self.sbml_doc.getPkgRequired(plugin.getPackageName()) + is False + ): + # if not "required", this has no impact on model + # simulation, and we can safely ignore it + + if ( + plugin.getPackageName() == "fbc" + and plugin.getListOfAllElements() + ): + # fbc is labeled not-required, but in fact it is. + # we don't care about the extra attributes of core + # elements, such as fbc:chemicalFormula, but we can't + # do anything meaningful with fbc:objective or + # fbc:fluxBounds + raise SBMLException( + "The following fbc extension elements are " + "currently not supported: " + + ", ".join( + list(map(str, plugin.getListOfAllElements())) + ) + ) + + continue + + # Check if there are extension elements. If not, we can safely + # ignore the enabled package + if plugin.getListOfAllElements(): + raise SBMLException( + f"Required SBML extension {plugin.getPackageName()} " + f"is currently not supported!" + ) + + if any( + rule.isRate() + and not isinstance( + self.sbml.getElementBySId(rule.getVariable()), + (sbml.Compartment, sbml.Species, sbml.Parameter), + ) + for rule in self.sbml.getListOfRules() + ): + raise SBMLException( + "Rate rules are only supported for " + "species, compartments, and parameters." + ) + + if any(r.getFast() for r in self.sbml.getListOfReactions()): + raise SBMLException("Fast reactions are currently not supported!") + + # Check events for unsupported functionality + self.check_event_support() + + def check_event_support(self) -> None: + """ + Check possible events in the model, as AMICI does currently not support + + * delays in events + * priorities of events + * events fired at initial time + + Furthermore, event triggers are optional (e.g., if an event is fired at + initial time, no trigger function is necessary). + In this case, warn that this event will have no effect. + """ + for event in self.sbml.getListOfEvents(): + event_id = event.getId() + # Check for delays in events + delay = event.getDelay() + if delay is not None: + try: + delay_time = float(self._sympy_from_sbml_math(delay)) + if delay_time != 0: + raise ValueError + # `TypeError` would be raised in the above `float(...)` + # if the delay is not a fixed time + except (TypeError, ValueError): + raise SBMLException( + "Events with execution delays are " + "currently not supported in AMICI." + ) + # Check for priorities + if event.getPriority() is not None: + raise SBMLException( + f"Event {event_id} has a priority " + "specified. This is currently not " + "supported in AMICI." + ) + + # check trigger + trigger_sbml = event.getTrigger() + if trigger_sbml is None: + logger.warning( + f"Event {event_id} trigger has no trigger, " + "so will be skipped." + ) + continue + if trigger_sbml.getMath() is None: + logger.warning( + f"Event {event_id} trigger has no trigger " + "expression, so a dummy trigger will be set." + ) + + if not trigger_sbml.getPersistent(): + raise SBMLException( + f"Event {event_id} has a non-persistent trigger." + "This is currently not supported in AMICI." + ) + + @log_execution_time("gathering local SBML symbols", logger) + def _gather_locals(self, hardcode_symbols: Sequence[str] = None) -> None: + """ + Populate self.local_symbols with all model entities. + + This is later used during sympifications to avoid sympy builtins + shadowing model entities as well as to avoid possibly costly + symbolic substitutions + """ + self._gather_base_locals(hardcode_symbols=hardcode_symbols) + self._gather_dependent_locals() + + def _gather_base_locals( + self, hardcode_symbols: Sequence[str] = None + ) -> None: + """ + Populate self.local_symbols with pure symbol definitions that do not + depend on any other symbol. + """ + + special_symbols_and_funs = { + # oo is sympy infinity + "INF": sp.oo, + "NaN": sp.nan, + "rem": sp.Mod, + "time": symbol_with_assumptions("time"), + # SBML L3 explicitly defines this value, which is not equal + # to the most recent SI definition. + "avogadro": sp.Float(6.02214179e23), + "exponentiale": sp.E, + } + for s, v in special_symbols_and_funs.items(): + self.add_local_symbol(s, v) + + for c in itt.chain( + self.sbml.getListOfSpecies(), + self.sbml.getListOfParameters(), + self.sbml.getListOfCompartments(), + ): + if not c.isSetId(): + continue + if c.getId() in hardcode_symbols: + if c.getConstant() is not True: + # disallow anything that can be changed by rules/reaction/events + raise ValueError( + f"Cannot hardcode non-constant symbol `{c.getId()}`." + ) + if self.sbml.getInitialAssignment(c.getId()): + raise NotImplementedError( + f"Cannot hardcode symbol `{c.getId()}` " + "that is an initial assignment target." + ) + self.add_local_symbol(c.getId(), sp.Float(c.getValue())) + else: + self.add_local_symbol(c.getId(), _get_identifier_symbol(c)) + + for x_ref in _get_list_of_species_references(self.sbml): + if not x_ref.isSetId(): + continue + if ( + x_ref.isSetStoichiometry() + and not self.is_assignment_rule_target(x_ref) + ): + value = sp.Float(x_ref.getStoichiometry()) + else: + value = _get_identifier_symbol(x_ref) + + ia_sym = self._get_element_initial_assignment(x_ref.getId()) + if ia_sym is not None: + value = ia_sym + + self.add_local_symbol(x_ref.getId(), value) + + def _gather_dependent_locals(self): + """ + Populate self.local_symbols with symbol definitions that may depend on + other symbol definitions. + """ + for r in self.sbml.getListOfReactions(): + if not r.isSetId(): + continue + self.add_local_symbol( + r.getId(), + self._sympy_from_sbml_math(r.getKineticLaw() or sp.Float(0)), + ) + + def add_local_symbol(self, key: str, value: sp.Expr): + """ + Add local symbols with some sanity checking for duplication which + would indicate redefinition of internals, which SBML permits, + but we don't. + + :param key: + local symbol key + + :param value: + local symbol value + """ + if key in self._local_symbols.keys(): + raise SBMLException( + f"AMICI tried to add a local symbol {key} with value {value}, " + f"but {key} was already instantiated with " + f"{self._local_symbols[key]}. This means that there " + f"are multiple SBML elements with SId {key}, which is " + f"invalid SBML. This can be fixed by renaming " + f"the elements with SId {key}." + ) + if key in {"True", "False", "true", "false", "pi"}: + raise SBMLException( + f"AMICI tried to add a local symbol {key} with value {value}, " + f"but {key} is a reserved symbol in AMICI. This can be fixed " + f"by renaming the element with SId {key}." + ) + self._local_symbols[key] = value + + @log_execution_time("processing SBML compartments", logger) + def _process_compartments(self) -> None: + """ + Get compartment information, stoichiometric matrix and fluxes from + SBML model. + """ + compartments = self.sbml.getListOfCompartments() + self.compartments = {} + for comp in compartments: + init = sp.Float(1.0) + + if comp.isSetVolume(): + init = self._sympy_from_sbml_math(comp.getVolume()) + + ia_sym = self._get_element_initial_assignment(comp.getId()) + if ia_sym is not None: + init = ia_sym + + self.compartments[_get_identifier_symbol(comp)] = init + + @log_execution_time("processing SBML species", logger) + def _process_species(self) -> None: + """ + Get species information from SBML model. + """ + if self.sbml.isSetConversionFactor(): + conversion_factor = symbol_with_assumptions( + self.sbml.getConversionFactor() + ) + else: + conversion_factor = 1 + + for s in self.sbml.getListOfSpecies(): + if self.is_assignment_rule_target(s): + continue + self.symbols[SymbolId.SPECIES][_get_identifier_symbol(s)] = { + "name": s.getName() if s.isSetName() else s.getId(), + "compartment": _get_species_compartment_symbol(s), + "constant": s.getConstant() or s.getBoundaryCondition(), + "amount": s.getHasOnlySubstanceUnits(), + "conversion_factor": symbol_with_assumptions( + s.getConversionFactor() + ) + if s.isSetConversionFactor() + else conversion_factor, + "index": len(self.symbols[SymbolId.SPECIES]), + } + + self._convert_event_assignment_parameter_targets_to_species() + self._process_species_initial() + self._process_rate_rules() + + @log_execution_time("processing SBML species initials", logger) + def _process_species_initial(self): + """ + Extract initial values and initial assignments from species + """ + for species_variable in self.sbml.getListOfSpecies(): + initial = get_species_initial(species_variable) + + species_id = _get_identifier_symbol(species_variable) + # If species_id is a target of an AssignmentRule, species will be + # None, but we don't have to account for the initial definition + # of the species itself and SBML doesn't permit AssignmentRule + # targets to have InitialAssignments. + species = self.symbols[SymbolId.SPECIES].get(species_id, None) + + ia_initial = self._get_element_initial_assignment( + species_variable.getId() + ) + if ia_initial is not None: + initial = ia_initial + if species: + species["init"] = initial + + # hide rateOf-arguments from toposort and the substitution below + all_rateof_dummies = [] + for species in self.symbols[SymbolId.SPECIES].values(): + species["init"], rateof_dummies = _rateof_to_dummy(species["init"]) + all_rateof_dummies.append(rateof_dummies) + + # don't assign this since they need to stay in order + sorted_species = toposort_symbols( + self.symbols[SymbolId.SPECIES], "init" + ) + for species, rateof_dummies in zip( + self.symbols[SymbolId.SPECIES].values(), all_rateof_dummies + ): + species["init"] = _dummy_to_rateof( + smart_subs_dict(species["init"], sorted_species, "init"), + rateof_dummies, + ) + + @log_execution_time("processing SBML rate rules", logger) + def _process_rate_rules(self): + """ + Process rate rules for species, compartments and parameters. + Compartments and parameters with rate rules are implemented as species. + Note that, in the case of species, rate rules may describe the change + in amount, not concentration, of a species. + """ + rules = self.sbml.getListOfRules() + # compartments with rules are replaced with constants in the relevant + # equations during the _replace_in_all_expressions call inside + # _process_rules + for rule in rules: + if rule.getTypeCode() != sbml.SBML_RATE_RULE: + continue + + variable = symbol_with_assumptions(rule.getVariable()) + formula = self._sympy_from_sbml_math(rule) + if formula is None: + continue + + # Species rules are processed first, to avoid processing + # compartments twice (as compartments with rate rules are + # implemented as species). + ia_init = self._get_element_initial_assignment(rule.getVariable()) + if variable in self.symbols[SymbolId.SPECIES]: + init = self.symbols[SymbolId.SPECIES][variable]["init"] + name = None + + if variable in self.compartments: + init = self.compartments[variable] + name = str(variable) + del self.compartments[variable] + + elif variable in self.symbols[SymbolId.PARAMETER]: + init = self._sympy_from_sbml_math( + self.symbols[SymbolId.PARAMETER][variable]["value"], + ) + name = self.symbols[SymbolId.PARAMETER][variable]["name"] + del self.symbols[SymbolId.PARAMETER][variable] + + # parameter with initial assignment, cannot use + # self.initial_assignments as it is not filled at this + # point + elif ia_init is not None: + init = ia_init + par = self.sbml.getElementBySId(rule.getVariable()) + name = par.getName() if par.isSetName() else par.getId() + + self.add_d_dt(formula, variable, init, name) + + def add_d_dt( + self, + d_dt: sp.Expr, + variable: sp.Symbol, + variable0: Union[float, sp.Expr], + name: str, + ) -> None: + """ + Creates or modifies species, to implement rate rules for + compartments and species, respectively. + + :param d_dt: + The rate rule (or, right-hand side of an ODE). + + :param variable: + The subject of the rate rule. + + :param variable0: + The initial value of the variable. + + :param name: + Species name, only applicable if this function generates a new + species + """ + if variable in self.symbols[SymbolId.SPECIES]: + # only update dt if species was already generated + self.symbols[SymbolId.SPECIES][variable]["dt"] = d_dt + else: + # update initial values + for species_id, species in self.symbols[SymbolId.SPECIES].items(): + variable0 = smart_subs(variable0, species_id, species["init"]) + + for species in self.symbols[SymbolId.SPECIES].values(): + species["init"] = smart_subs( + species["init"], variable, variable0 + ) + + # add compartment/parameter species + self.symbols[SymbolId.SPECIES][variable] = { + "name": name, + "init": variable0, + "amount": False, + "conversion_factor": 1.0, + "constant": False, + "index": len(self.symbols[SymbolId.SPECIES]), + "dt": d_dt, + } + + @log_execution_time("processing SBML annotations", logger) + def _process_annotations(self) -> None: + """ + Process annotations that make modifications to the + SBML model and thus have to be run before everything else + """ + # Remove all parameters (and corresponding rules) + # for which amici:discard is set + parameter_ids_to_remove = [] + for p in self.sbml.getListOfParameters(): + annotation = p.getAnnotationString() + assert isinstance(annotation, str) + if len(annotation) != 0: + annotation = ET.fromstring(annotation) + for child in annotation: + if child.tag == f"{{{annotation_namespace}}}discard": + parameter_ids_to_remove.append(p.getIdAttribute()) + for parameter_id in parameter_ids_to_remove: + # Remove corresponding rules + self.sbml.removeRuleByVariable(parameter_id) + # Remove parameter + self.sbml.removeParameter(parameter_id) + + @log_execution_time("processing SBML parameters", logger) + def _process_parameters( + self, + constant_parameters: List[str] = None, + hardcode_symbols: Sequence[str] = None, + ) -> None: + """ + Get parameter information from SBML model. + + :param constant_parameters: + SBML Ids identifying constant parameters + """ + + if constant_parameters is None: + constant_parameters = [] + + # Ensure specified constant parameters exist in the model + for parameter in constant_parameters: + if not self.sbml.getParameter(parameter): + raise KeyError( + "Cannot make %s a constant parameter: " + "Parameter does not exist." % parameter + ) + + fixed_parameters = [ + parameter + for parameter in self.sbml.getListOfParameters() + if parameter.getId() in constant_parameters + ] + for parameter in fixed_parameters: + if ( + self._get_element_initial_assignment(parameter.getId()) + is not None + or self.is_assignment_rule_target(parameter) + or self.is_rate_rule_target(parameter) + ): + raise SBMLException( + f"Cannot turn parameter {parameter.getId()} into a " + "constant/fixed parameter since it either has an " + "initial assignment or is the target of an assignment or " + "rate rule." + ) + + 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 not self.is_assignment_rule_target(parameter) + and parameter.getId() not in hardcode_symbols + ] + + loop_settings = { + SymbolId.PARAMETER: {"var": parameters, "name": "parameter"}, + SymbolId.FIXED_PARAMETER: { + "var": fixed_parameters, + "name": "fixed_parameter", + }, + } + + for partype, settings in loop_settings.items(): + 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()), + } + + # Parameters that need to be turned into expressions + # so far, this concerns parameters with initial assignments containing rateOf(.) + # (those have been skipped above) + for par in self.sbml.getListOfParameters(): + if ( + ia := self._get_element_initial_assignment(par.getId()) + ) is not None and ia.find( + sp.core.function.UndefinedFunction("rateOf") + ): + self.symbols[SymbolId.EXPRESSION][ + _get_identifier_symbol(par) + ] = { + "name": par.getName() if par.isSetName() else par.getId(), + "value": ia, + } + + @log_execution_time("processing SBML reactions", logger) + def _process_reactions(self): + """ + Get reactions from SBML model. + """ + reactions = self.sbml.getListOfReactions() + # nr (number of reactions) should have a minimum length of 1. This is + # to ensure that, if there are no reactions, the stoichiometric matrix + # and flux vector multiply to a zero vector with dimensions (nx, 1). + nr = max(1, len(reactions)) + nx = len(self.symbols[SymbolId.SPECIES]) + # stoichiometric matrix + self.stoichiometric_matrix = sp.SparseMatrix(sp.zeros(nx, nr)) + self.flux_vector = sp.zeros(nr, 1) + # Use reaction IDs as IDs for flux expressions (note that prior to SBML + # level 3 version 2 the ID attribute was not mandatory and may be + # unset) + self.flux_ids = [ + f"flux_{reaction.getId()}" + if reaction.isSetId() + else f"flux_r{reaction_idx}" + for reaction_idx, reaction in enumerate(reactions) + ] or ["flux_r0"] + + reaction_ids = [ + reaction.getId() for reaction in reactions if reaction.isSetId() + ] + + for reaction_index, reaction in enumerate(reactions): + for element_list, sign in [ + (reaction.getListOfReactants(), -1), + (reaction.getListOfProducts(), 1), + ]: + for element in element_list: + stoichiometry = self._get_element_stoichiometry(element) + sbml_species = self.sbml.getSpecies(element.getSpecies()) + if self.is_assignment_rule_target(sbml_species): + continue + species_id = _get_identifier_symbol(sbml_species) + species = self.symbols[SymbolId.SPECIES][species_id] + + if species["constant"]: + continue + + # Division by species compartment size (to find the + # rate of change in species concentration) now occurs + # in the `dx_dt` method in "de_export.py", which also + # accounts for possibly variable compartments. + self.stoichiometric_matrix[ + species["index"], reaction_index + ] += (sign * stoichiometry * species["conversion_factor"]) + if reaction.isSetId(): + sym_math = self._local_symbols[reaction.getId()] + else: + sym_math = self._sympy_from_sbml_math( + reaction.getKineticLaw() or sp.Float(0) + ) + + self.flux_vector[reaction_index] = sym_math.subs( + { + BooleanTrue(): sp.Float(1.0), + BooleanFalse(): sp.Float(0.0), + } + ) + if any( + str(symbol) in reaction_ids + for symbol in self.flux_vector[reaction_index].free_symbols + ): + raise SBMLException( + "Kinetic laws involving reaction ids are currently" + " not supported!" + ) + + @log_execution_time("processing SBML rules", logger) + def _process_rules(self) -> None: + """ + Process Rules defined in the SBML model. + """ + for rule in self.sbml.getListOfRules(): + # rate rules are processed in _process_species + if rule.getTypeCode() == sbml.SBML_RATE_RULE: + continue + + if rule.getTypeCode() == sbml.SBML_ALGEBRAIC_RULE: + if self.sbml_doc.getLevel() < 3: + # not interested in implementing level 2 boundary condition + # shenanigans, see test 01787 in the sbml testsuite + raise SBMLException( + "Algebraic rules are only supported in SBML L3+" + ) + self._process_rule_algebraic(rule) + else: + self._process_rule_assignment(rule) + + self.symbols[SymbolId.EXPRESSION] = toposort_symbols( + self.symbols[SymbolId.EXPRESSION], "value" + ) + + # expressions must not occur in definition of x0 + for species in self.symbols[SymbolId.SPECIES].values(): + species["init"] = self._make_initial( + smart_subs_dict( + species["init"], self.symbols[SymbolId.EXPRESSION], "value" + ) + ) + + def _process_rule_algebraic(self, rule: sbml.AlgebraicRule): + formula = self._sympy_from_sbml_math(rule) + if formula is None: + return + + free_variables = set() + # SBML L3V2 spec, p. 61: + # "Therefore, if an algebraic rule is introduced in a model, + # for at least one of the entities referenced in the rule’s + # math element the value of that entity must not be + # completely determined by other constructs in the model" + # find those elements: + for symbol in formula.free_symbols: + sbml_var = self.sbml.getElementBySId(str(symbol)) + # This means that at least this entity must + # not have the attribute constant=“true” + if sbml_var.isSetConstant() and sbml_var.getConstant(): + continue + # and there must also not be a rate rule or assignment + # rule for it + if self.is_assignment_rule_target( + sbml_var + ) or self.is_rate_rule_target(sbml_var): + continue + # Furthermore, if the entity is a Species object, its value + # must not be determined by reactions, which means that it + # must either have the attribute boundaryCondition=“false” + # or else not be involved in any reaction at all. + is_species = isinstance(sbml_var, sbml.Species) + is_boundary_condition = ( + is_species + and sbml_var.isSetBoundaryCondition() + and sbml_var.getBoundaryCondition() + ) + is_involved_in_reaction = is_species and not smart_is_zero_matrix( + self.stoichiometric_matrix[ + list(self.symbols[SymbolId.SPECIES].keys()).index(symbol), + :, + ] + ) + if ( + is_species + and not is_boundary_condition + and is_involved_in_reaction + ): + continue + free_variables.add(symbol) + + # this should be guaranteed by sbml validation, so better check to make sure + # we don't mess anything up + assert len(free_variables) >= 1 + + self.symbols[SymbolId.ALGEBRAIC_EQUATION][ + f"ae{len(self.symbols[SymbolId.ALGEBRAIC_EQUATION])}" + ] = {"value": formula} + # remove the symbol from the original definition and add to + # algebraic symbols (if not already done) + for var in free_variables: + if var in self.symbols[SymbolId.FIXED_PARAMETER]: + raise SBMLException( + "There are algebraic rules that specify the " + f"value of {var}, which is also marked as " + "fixed parameter. This is currently not supported! " + f"If {var} is supposed to be a fixed parameter, " + "set its SBML attribute `constant` to True." + ) + + if var in self.symbols[SymbolId.ALGEBRAIC_STATE]: + continue + if var in self.compartments: + init = self.compartments[var] + symbol = { + "name": str(var), + "value": init, + } + symbol_id = "compartment" + var_ix = np.nan + del self.compartments[var] + else: + symbol_id, source_symbols = next( + ( + (symbol_id, self.symbols[symbol_id]) + for symbol_id in (SymbolId.PARAMETER, SymbolId.SPECIES) + if var in self.symbols[symbol_id] + ), + ) + var_ix = list(source_symbols.keys()).index(var) + symbol = source_symbols.pop(var) + # update symbol and adapt stoichiometric matrix + if symbol_id != SymbolId.SPECIES: + # parameters have numeric values so we can use Float here + symbol["init"] = sp.Float(symbol.pop("value")) + # if not a species, add a zeros row to the stoichiometric + # matrix + if ( + isinstance(symbol["init"], float) + and np.isnan(symbol["init"]) + ) or ( + isinstance(symbol["init"], sp.Number) + and symbol["init"] == sp.nan + ): + # placeholder, needs to be determined in IC calculation + symbol["init"] = sp.Float(0.0) + self.stoichiometric_matrix = ( + self.stoichiometric_matrix.row_insert( + self.stoichiometric_matrix.shape[0], + sp.SparseMatrix( + [[0] * self.stoichiometric_matrix.shape[1]] + ), + ) + ) + elif var_ix != self.stoichiometric_matrix.shape[0] - 1: + # if not the last col, move it to the end + # as we reorder state variables + state_ordering = list( + range( + len(self.symbols[SymbolId.SPECIES]) + + len(self.symbols[SymbolId.ALGEBRAIC_STATE]) + + 1 + ) + ) + state_ordering.append(state_ordering.pop(var_ix)) + self.stoichiometric_matrix = self.stoichiometric_matrix[ + state_ordering, : + ] + + self.symbols[SymbolId.ALGEBRAIC_STATE][var] = symbol + + def _process_rule_assignment(self, rule: sbml.AssignmentRule): + sbml_var = self.sbml.getElementBySId(rule.getVariable()) + sym_id = symbol_with_assumptions(rule.getVariable()) + + # Check whether this rule is a spline rule. + if not self._discard_annotations: + if rule.getTypeCode() == sbml.SBML_ASSIGNMENT_RULE: + annotation = AbstractSpline.get_annotation(rule) + if annotation is not None: + spline = AbstractSpline.from_annotation( + sym_id, + annotation, + locals_=self._local_symbols, + ) + if ( + spline.evaluate_at != amici_time_symbol + and spline.evaluate_at != sbml_time_symbol + ): + raise NotImplementedError( + "AMICI at the moment does not support splines " + "whose evaluation point is not the model time." + ) + self.splines.append(spline) + return + + formula = self._sympy_from_sbml_math(rule) + if formula is None: + return + + if isinstance(sbml_var, sbml.Species): + self.species_assignment_rules[sym_id] = formula + + elif isinstance(sbml_var, sbml.Compartment): + self.compartment_assignment_rules[sym_id] = formula + self.compartments[sym_id] = formula + + elif isinstance(sbml_var, sbml.Parameter): + self.parameter_assignment_rules[sym_id] = formula + + self.symbols[SymbolId.EXPRESSION][sym_id] = { + "name": str(sym_id), + "value": formula, + } + + def _process_time(self) -> None: + """ + Convert time_symbol into cpp variable. + """ + self._replace_in_all_expressions(sbml_time_symbol, amici_time_symbol) + + def _convert_event_assignment_parameter_targets_to_species(self): + """ + Convert parameters that are targets of event assignments to species. + + This is for the convenience of only implementing event assignments for + "species". + """ + parameter_targets = _collect_event_assignment_parameter_targets( + self.sbml + ) + for parameter_target in parameter_targets: + # Parameter rate rules already exist as species. + if parameter_target in self.symbols[SymbolId.SPECIES]: + continue + if parameter_target in self.parameter_assignment_rules: + raise SBMLException( + "AMICI does not currently support models with SBML events " + "that affect parameters that are also the target of " + "assignment rules." + ) + parameter_def = None + for symbol_id in {SymbolId.PARAMETER, SymbolId.FIXED_PARAMETER}: + if parameter_target in self.symbols[symbol_id]: + # `parameter_target` should only exist in one of the + # `symbol_id` dictionaries. + if parameter_def is not None: + raise AssertionError( + "Unexpected error. The parameter target of an " + "event assignment was processed twice." + ) + parameter_def = self.symbols[symbol_id].pop( + parameter_target + ) + if parameter_def is None: + # this happens for parameters that have initial assignments + # or are assignment rule targets + par = self.sbml.getElementBySId(str(parameter_target)) + ia_init = self._get_element_initial_assignment(par.getId()) + parameter_def = { + "name": par.getName() if par.isSetName() else par.getId(), + "value": sp.Float(par.getValue()) + if ia_init is None + else ia_init, + } + # Fixed parameters are added as species such that they can be + # targets of events. + self.symbols[SymbolId.SPECIES][parameter_target] = { + "name": parameter_def["name"], + "init": parameter_def["value"], + # '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), + } + + @log_execution_time("processing SBML events", logger) + def _process_events(self) -> None: + """Process SBML events.""" + events = self.sbml.getListOfEvents() + + def get_empty_bolus_value() -> sp.Float: + """ + Used in the event update vector for species that are not affected + by the event. + """ + return sp.Symbol("AMICI_EMTPY_BOLUS") + + # Used to update species concentrations when an event affects a + # compartment. + concentration_species_by_compartment = { + symbol_with_assumptions(c.getId()): [] + for c in self.sbml.getListOfCompartments() + } + for species, species_def in self.symbols[SymbolId.SPECIES].items(): + if ( + # Species is a concentration + not species_def.get("amount", True) + and + # Species has a compartment + "compartment" in species_def + ): + concentration_species_by_compartment[ + species_def["compartment"] + ].append(species) + + for ievent, event in enumerate(events): + # get the event id (which is optional unfortunately) + event_id = event.getId() + if event_id is None or event_id == "": + event_id = f"event_{ievent}" + event_sym = sp.Symbol(event_id) + + # get and parse the trigger function + trigger_sbml = event.getTrigger() + 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() + for event_assignment in event_assignments: + variable_sym = symbol_with_assumptions( + event_assignment.getVariable() + ) + if event_assignment.getMath() is None: + # Ignore event assignments with no change in value. + continue + formula = self._sympy_from_sbml_math(event_assignment) + try: + # Try to find the species in the state vector. + index = state_vector.index(variable_sym) + bolus[index] = formula + except ValueError: + raise SBMLException( + "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." + ) + try: + # Try working with the formula now to detect errors + # here instead of at multiple points downstream. + _ = formula - variable_sym + except TypeError: + raise SBMLException( + "Could not process event assignment for " + f"{str(variable_sym)}. AMICI only allows symbolic " + "expressions as event assignments." + ) + if variable_sym in concentration_species_by_compartment: + compartment_event_assignments.add(variable_sym) + + for ( + comp, + assignment, + ) in self.compartment_assignment_rules.items(): + if variable_sym not in assignment.free_symbols: + continue + compartment_event_assignments.add(comp) + + # 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 species_sym in concentration_species_by_compartment[ + compartment_sym + ]: + # If the species was not affected by an event assignment + # then the old value should be updated. + if ( + bolus[state_vector.index(species_sym)] + == get_empty_bolus_value() + ): + species_value = species_sym + # else the species was affected by an event assignment, + # hence the updated value should be updated further. + else: + species_value = bolus[state_vector.index(species_sym)] + # New species value is old amount / new volume. + bolus[state_vector.index(species_sym)] = ( + species_value * compartment_sym / formula + ) + + # Subtract the current species value from each species with an + # update, as the bolus will be added on to the current species + # value during simulation. + for index in range(len(bolus)): + if bolus[index] != get_empty_bolus_value(): + bolus[index] -= state_vector[index] + bolus[index] = bolus[index].subs( + get_empty_bolus_value(), sp.Float(0.0) + ) + + initial_value = ( + trigger_sbml.getInitialValue() + if trigger_sbml is not None + else True + ) + if self.symbols[SymbolId.ALGEBRAIC_EQUATION] and not initial_value: + # in principle this could be implemented, requires running + # IDACalcIc (in solver->setup) before check event initialization + # (in model->initialize), but at the time of writing this sounded + # like something that might break stuff in all kinds of other places + # (it might not, but this could be checked when someone actually + # needs the feature). + raise SBMLException( + "Events with initial values are not supported in models with" + " algebraic rules." + ) + + self.symbols[SymbolId.EVENT][event_sym] = { + "name": event_id, + "value": trigger, + "state_update": sp.MutableDenseMatrix(bolus), + "initial_value": initial_value, + } + + @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], + ) -> None: + """ + Perform symbolic computations required for observable and objective + function evaluation. + + :param observables: + dictionary(observableId: {'name':observableName + (optional), 'formula':formulaString)}) + to be added to the model + + :param sigmas: + dictionary(observableId: sigma value or (existing) + parameter name) + + :param noise_distributions: + dictionary(observableId: noise type) + See :py:func:`sbml2amici`. + """ + + _validate_observables( + observables, sigmas, noise_distributions, events=False + ) + + # add user-provided observables or make all species, and compartments + # with assignment rules, observable + if observables: + # gather local symbols before parsing observable and sigma formulas + for obs in observables.keys(): + self.add_local_symbol(obs, symbol_with_assumptions(obs)) + + self.symbols[SymbolId.OBSERVABLE] = { + symbol_with_assumptions(obs): { + "name": definition.get("name", f"y{iobs}"), + "value": self._sympy_from_sbml_math(definition["formula"]), + "transformation": noise_distribution_to_observable_transformation( + noise_distributions.get(obs, "normal") + ), + } + for iobs, (obs, definition) in enumerate(observables.items()) + } + # check for nesting of observables (unsupported) + observable_syms = set(self.symbols[SymbolId.OBSERVABLE].keys()) + for obs in self.symbols[SymbolId.OBSERVABLE].values(): + if any( + sym in observable_syms for sym in obs["value"].free_symbols + ): + raise ValueError( + "Nested observables are not supported, " + f"but observable `{obs['name']} = {obs['value']}` " + "references another observable." + ) + elif observables is None: + self._generate_default_observables() + + _check_symbol_nesting( + self.symbols[SymbolId.OBSERVABLE], "eventObservable" + ) + + self._process_log_likelihood(sigmas, noise_distributions) + + @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], + ) -> None: + """ + Perform symbolic computations required for observable and objective + function evaluation. + + :param event_observables: + See :py:func:`sbml2amici`. + + :param event_sigmas: + See :py:func:`sbml2amici`. + + :param event_noise_distributions: + See :py:func:`sbml2amici`. + """ + if event_observables is None: + return + + _validate_observables( + event_observables, + event_sigmas, + event_noise_distributions, + events=True, + ) + + # gather local symbols before parsing observable and sigma formulas + for obs, definition in event_observables.items(): + self.add_local_symbol(obs, symbol_with_assumptions(obs)) + # check corresponding event exists + if ( + sp.Symbol(definition["event"]) + not in self.symbols[SymbolId.EVENT] + ): + raise ValueError( + "Could not find an event with the event identifier " + f'{definition["event"]} for the event observable with name' + f'{definition["name"]}.' + ) + + self.symbols[SymbolId.EVENT_OBSERVABLE] = { + symbol_with_assumptions(obs): { + "name": definition.get("name", f"z{iobs}"), + "value": self._sympy_from_sbml_math(definition["formula"]), + "event": sp.Symbol(definition.get("event")), + "transformation": noise_distribution_to_observable_transformation( + event_noise_distributions.get(obs, "normal") + ), + } + for iobs, (obs, definition) in enumerate(event_observables.items()) + } + + wrong_t = sp.Symbol("t") + for eo in self.symbols[SymbolId.EVENT_OBSERVABLE].values(): + if eo["value"].has(wrong_t): + warnings.warn( + 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!" + ) + + # check for nesting of observables (unsupported) + _check_symbol_nesting( + self.symbols[SymbolId.EVENT_OBSERVABLE], "eventObservable" + ) + + self._process_log_likelihood( + event_sigmas, event_noise_distributions, events=True + ) + self._process_log_likelihood( + event_sigmas, + event_noise_distributions, + events=True, + event_reg=True, + ) + + def _generate_default_observables(self): + """ + Generate default observables from species, compartments and + (initial) assignment rules. + """ + self.symbols[SymbolId.OBSERVABLE] = { + symbol_with_assumptions(f"y{state_id}"): { + "name": state["name"], + "value": state_id, + } + for state_id, state in { + **self.symbols[SymbolId.SPECIES], + **self.symbols[SymbolId.ALGEBRAIC_STATE], + }.items() + } + + for variable, formula in itt.chain( + self.parameter_assignment_rules.items(), + self.initial_assignments.items(), + self.compartment_assignment_rules.items(), + self.species_assignment_rules.items(), + self.compartments.items(), + ): + symbol = symbol_with_assumptions(f"y{variable}") + # Assignment rules take precedence over compartment volume + # definitions, so they need to be evaluated first. + # Species assignment rules always overwrite. + if ( + symbol in self.symbols[SymbolId.OBSERVABLE] + and variable not in self.species_assignment_rules + ): + continue + self.symbols[SymbolId.OBSERVABLE][symbol] = { + "name": str(variable), + "value": formula, + } + + def _process_log_likelihood( + self, + sigmas: Dict[str, Union[str, float]], + noise_distributions: Dict[str, str], + events: bool = False, + event_reg: bool = False, + ): + """ + Perform symbolic computations required for objective function + evaluation. + + :param sigmas: + See :py:func:`SBMLImporter._process_observables` + + :param noise_distributions: + See :py:func:`SBMLImporter._process_observables` + + :param events: + indicates whether the passed definitions are for observables + (False) or for event observables (True). + + :param event_reg: + indicates whether log-likelihood definitions should be processed + for event observable regularization (Jrz). If this is activated, + measurements are substituted by 0 and the observable by the + respective regularization symbol. + """ + + if events: + if event_reg: + obs_symbol = SymbolId.EVENT_OBSERVABLE + sigma_symbol = SymbolId.SIGMAZ + llh_symbol = SymbolId.LLHRZ + else: + obs_symbol = SymbolId.EVENT_OBSERVABLE + sigma_symbol = SymbolId.SIGMAZ + llh_symbol = SymbolId.LLHZ + else: + assert not event_reg + obs_symbol = SymbolId.OBSERVABLE + sigma_symbol = SymbolId.SIGMAY + llh_symbol = SymbolId.LLHY + + for obs_id, obs in self.symbols[obs_symbol].items(): + obs["measurement_symbol"] = generate_measurement_symbol(obs_id) + if event_reg: + obs["reg_symbol"] = generate_regularization_symbol(obs_id) + + if not event_reg: + self.symbols[sigma_symbol] = { + symbol_with_assumptions(f"sigma_{obs_id}"): { + "name": f'sigma_{obs["name"]}', + "value": self._sympy_from_sbml_math( + sigmas.get(str(obs_id), "1.0") + ), + } + for obs_id, obs in self.symbols[obs_symbol].items() + } + + self.symbols[llh_symbol] = {} + for (obs_id, obs), (sigma_id, sigma) in zip( + self.symbols[obs_symbol].items(), + self.symbols[sigma_symbol].items(), + ): + symbol = symbol_with_assumptions(f"J{obs_id}") + dist = noise_distributions.get(str(obs_id), "normal") + cost_fun = noise_distribution_to_cost_function(dist)(obs_id) + value = sp.sympify( + cost_fun, + locals=dict( + zip( + _get_str_symbol_identifiers(obs_id), + (obs_id, obs["measurement_symbol"], sigma_id), + ) + ), + ) + if event_reg: + value = value.subs(obs["measurement_symbol"], 0.0) + value = value.subs(obs_id, obs["reg_symbol"]) + self.symbols[llh_symbol][symbol] = { + "name": f'J{obs["name"]}', + "value": value, + "dist": dist, + } + + @log_execution_time("processing SBML initial assignments", logger) + def _process_initial_assignments(self): + """ + Accounts for initial assignments of parameters and species + references. Initial assignments for species and compartments are + processed in :py:func:`amici.SBMLImporter._process_initial_species` and + :py:func:`amici.SBMLImporter._process_compartments` respectively. + """ + for ia in self.sbml.getListOfInitialAssignments(): + identifier = _get_identifier_symbol(ia) + if identifier in itt.chain( + self.symbols[SymbolId.SPECIES], self.compartments + ): + continue + + sym_math = self._get_element_initial_assignment(ia.getId()) + if sym_math is None: + continue + + sym_math = self._make_initial( + smart_subs_dict( + sym_math, self.symbols[SymbolId.EXPRESSION], "value" + ) + ) + self.initial_assignments[_get_identifier_symbol(ia)] = sym_math + + # sort and flatten + self.initial_assignments = toposort_symbols(self.initial_assignments) + for ia_id, ia in self.initial_assignments.items(): + self.initial_assignments[ia_id] = smart_subs_dict( + ia, self.initial_assignments + ) + + for identifier, sym_math in list(self.initial_assignments.items()): + self._replace_in_all_expressions(identifier, sym_math) + + @log_execution_time("processing SBML species references", logger) + def _process_species_references(self): + """ + Replaces species references that define anything but stoichiometries. + + Species references for stoichiometries are processed in + :py:func:`amici.SBMLImporter._process_reactions`. + """ + # doesnt look like there is a better way to get hold of those lists: + species_references = _get_list_of_species_references(self.sbml) + for species_reference in species_references: + if ( + hasattr(species_reference, "getStoichiometryMath") + and species_reference.getStoichiometryMath() is not None + ): + raise SBMLException( + "StoichiometryMath is currently not " + "supported for species references." + ) + if species_reference.getId() == "": + continue + + stoich = self._get_element_stoichiometry(species_reference) + self._replace_in_all_expressions( + _get_identifier_symbol(species_reference), + self._sympy_from_sbml_math(stoich), + ) + + def _make_initial( + self, sym_math: Union[sp.Expr, None, float] + ) -> Union[sp.Expr, None, float]: + """ + Transforms an expression to its value at the initial time point by + replacing species by their initial values. + + :param sym_math: + symbolic expression + :return: + transformed expression + """ + if not isinstance(sym_math, sp.Expr): + return sym_math + + sym_math, rateof_to_dummy = _rateof_to_dummy(sym_math) + + for species_id, species in self.symbols[SymbolId.SPECIES].items(): + 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 = _dummy_to_rateof(sym_math, rateof_to_dummy) + + return sym_math + + def process_conservation_laws(self, ode_model) -> None: + """ + Find conservation laws in reactions and species. + + :param ode_model: + ODEModel object with basic definitions + """ + conservation_laws = [] + + # Create conservation laws for constant species + species_solver = _add_conservation_for_constant_species( + ode_model, conservation_laws + ) + # Non-constant species processed here + if ( + "AMICI_EXPERIMENTAL_SBML_NONCONST_CLS" in os.environ + or "GITHUB_ACTIONS" in os.environ + ): + species_solver = list( + set( + self._add_conservation_for_non_constant_species( + ode_model, conservation_laws + ) + ) + & set(species_solver) + ) + + # add algebraic variables to species_solver as they were ignored above + ndifferential = len(ode_model._differential_states) + nalgebraic = len(ode_model._algebraic_states) + species_solver.extend( + list(range(ndifferential, ndifferential + nalgebraic)) + ) + + # Check, whether species_solver is empty now. As currently, AMICI + # cannot handle ODEs without species, CLs must be switched off in this + # case + if not len(species_solver): + conservation_laws = [] + species_solver = list(range(ode_model.num_states_rdata())) + + # prune out species from stoichiometry and + self.stoichiometric_matrix = self.stoichiometric_matrix[ + species_solver, : + ] + + # add the found CLs to the ode_model + for cl in conservation_laws: + ode_model.add_conservation_law(**cl) + + def _get_conservation_laws_demartino( + self, + ode_model: DEModel, + ) -> List[Tuple[int, List[int], List[float]]]: + """Identify conservation laws based on algorithm by DeMartino et al. + (see conserved_moieties.py). + + :param ode_model: Model for which to compute conserved quantities + :returns: List of one tuple per conservation law, each containing: + (0) the index of the (solver-)species to eliminate, + (1) (solver-)indices of all species engaged in the conserved + quantity (including the eliminated one) + (2) coefficients for the species in (1) + """ + from .conserved_quantities_demartino import ( + compute_moiety_conservation_laws, + ) + + sm = self.stoichiometric_matrix[ + : len(self.symbols[SymbolId.SPECIES]), : + ] + + try: + stoichiometric_list = [float(entry) for entry in sm.T.flat()] + except TypeError: + # Due to the numerical algorithm currently used to identify + # conserved quantities, we can't have symbols in the + # stoichiometric matrix + warnings.warn( + "Conservation laws for non-constant species in " + "combination with parameterized stoichiometric " + "coefficients are not currently supported " + "and will be turned off." + ) + return [] + + if not _non_const_conservation_laws_supported(self.sbml): + return [] + + cls_state_idxs, cls_coefficients = compute_moiety_conservation_laws( + stoichiometric_list, + *sm.shape, + rng_seed=32, + species_names=[ + str(x.get_id()) for x in ode_model._differential_states + ], + ) + + # Sparsify conserved quantities + # ``compute_moiety_conservation_laws`` identifies conserved quantities + # with positive coefficients. The resulting system is, therefore, + # often non-sparse. This leads to circular dependencies in the + # state expressions of eliminated states. The identified conserved + # quantities are linearly independent. We can construct `A` as in + # `A * x0 = total_cl` and bring it to reduced row echelon form. The + # pivot species are the ones to be eliminated. The resulting state + # expressions are sparse and void of any circular dependencies. + A = sp.zeros( + len(cls_coefficients), len(ode_model._differential_states) + ) + for i_cl, (cl, coefficients) in enumerate( + zip(cls_state_idxs, cls_coefficients) + ): + for i, c in zip(cl, coefficients): + A[i_cl, i] = sp.Rational(c) + rref, pivots = A.rref() + + raw_cls = [] + for i_cl, target_state_model_idx in enumerate(pivots): + # collect values for species engaged in the current CL + state_idxs = [i for i, coeff in enumerate(rref[i_cl, :]) if coeff] + coefficients = [coeff for coeff in rref[i_cl, :] if coeff] + raw_cls.append( + (target_state_model_idx, state_idxs, coefficients), + ) + return raw_cls + + def _get_conservation_laws_rref( + self, + ) -> List[Tuple[int, List[int], List[float]]]: + """Identify conservation laws based on left nullspace of the + stoichiometric matrix, computed through (numeric) Gaussian elimination + + :returns: List of one tuple per conservation law, each containing: + (0) the index of the (solver-)species to eliminate, + (1) (solver-)indices of all species engaged in the conserved + quantity (including the eliminated one) + (2) coefficients for the species in (1) + """ + import numpy as np + from numpy.linalg import matrix_rank + + from .conserved_quantities_rref import nullspace_by_rref, rref + + try: + S = np.asarray( + self.stoichiometric_matrix[ + : len(self.symbols[SymbolId.SPECIES]), : + ], + dtype=float, + ) + except TypeError: + # Due to the numerical algorithm currently used to identify + # conserved quantities, we can't have symbols in the + # stoichiometric matrix + warnings.warn( + "Conservation laws for non-constant species in " + "combination with parameterized stoichiometric " + "coefficients are not currently supported " + "and will be turned off." + ) + return [] + + if not _non_const_conservation_laws_supported(self.sbml): + return [] + + # Determine rank via SVD + rank = matrix_rank(S) if S.shape[0] else 0 + if rank == S.shape[0]: + return [] + kernel = nullspace_by_rref(S.T) + # Check dimensions - due to numerical errors, nullspace_by_rref may + # fail in certain situations + if kernel.shape[0] != S.shape[0] - rank: + raise AssertionError( + "Failed to determine all conserved quantities " + f"(found {kernel.shape[0]}, expected {S.shape[0] - rank}). " + "Try another algorithm, disable detection of conservation " + "laws, or submit a bug report along with the model." + ) + kernel = rref(kernel) + raw_cls = [] + for row in kernel: + state_idxs = [i for i, coeff in enumerate(row) if coeff] + coefficients = [coeff for coeff in row if coeff] + raw_cls.append( + (state_idxs[0], state_idxs, coefficients), + ) + + return raw_cls + + def _add_conservation_for_non_constant_species( + self, model: DEModel, conservation_laws: List[ConservationLaw] + ) -> List[int]: + """Add non-constant species to conservation laws + + :param model: + ODEModel object with basic definitions + :param conservation_laws: + List of already known conservation laws + :returns: + List of species indices which later remain in the DE solver + """ + # indices of retained species + species_solver = list(range(len(model._differential_states))) + + algorithm = os.environ.get("AMICI_EXPERIMENTAL_SBML_NONCONST_CLS", "") + if algorithm.lower() == "demartino": + raw_cls = self._get_conservation_laws_demartino(model) + else: + raw_cls = self._get_conservation_laws_rref() + + if not raw_cls: + # no conservation laws identified + return species_solver + + species_to_be_removed = {x[0] for x in raw_cls} + + # keep new conservations laws separate until we know everything worked + new_conservation_laws = [] + # previously removed constant species + eliminated_state_ids = {cl["state"] for cl in conservation_laws} + + all_state_ids = [x.get_id() for x in model.states()] + all_compartment_sizes = [] + for state_id in all_state_ids: + symbol = { + **self.symbols[SymbolId.SPECIES], + **self.symbols[SymbolId.ALGEBRAIC_STATE], + }[state_id] + if "amount" not in symbol: + continue # not a species + if symbol["amount"]: + compartment_size = sp.Integer(1) + else: + compartment_size = self.compartments[symbol["compartment"]] + all_compartment_sizes.append(compartment_size) + + # iterate over list of conservation laws, create symbolic expressions, + for target_state_model_idx, state_idxs, coefficients in raw_cls: + if all_state_ids[target_state_model_idx] in eliminated_state_ids: + # constants state, already eliminated + continue + # collect values for species engaged in the current CL + state_ids = [all_state_ids[i_state] for i_state in state_idxs] + compartment_sizes = [all_compartment_sizes[i] for i in state_idxs] + + target_state_id = all_state_ids[target_state_model_idx] + total_abundance = symbol_with_assumptions(f"tcl_{target_state_id}") + + new_conservation_laws.append( + { + "state": target_state_id, + "total_abundance": total_abundance, + "coefficients": { + state_id: coeff * compartment + for state_id, coeff, compartment in zip( + state_ids, coefficients, compartment_sizes + ) + }, + } + ) + species_to_be_removed.add(target_state_model_idx) + + conservation_laws.extend(new_conservation_laws) + + # list of species that are not determined by conservation laws + return [ix for ix in species_solver if ix not in species_to_be_removed] + + def _replace_compartments_with_volumes(self): + """ + Replaces compartment symbols in expressions with their respective + (possibly variable) volumes. + """ + for comp, vol in self.compartments.items(): + if ( + comp in self.symbols[SymbolId.SPECIES] + or comp in self.symbols[SymbolId.ALGEBRAIC_STATE] + ): + # for comps with rate rules volume is only initial + for state in { + **self.symbols[SymbolId.SPECIES], + **self.symbols[SymbolId.ALGEBRAIC_STATE], + }.values(): + if isinstance(state["init"], sp.Expr): + state["init"] = smart_subs(state["init"], comp, vol) + continue + self._replace_in_all_expressions(comp, vol) + + def _replace_in_all_expressions( + self, old: sp.Symbol, new: sp.Expr, replace_identifiers=False + ) -> None: + """ + Replace 'old' by 'new' in all symbolic expressions. + + :param old: + symbolic variables to be replaced + + :param new: + replacement symbolic variables + """ + fields = [ + "stoichiometric_matrix", + "flux_vector", + ] + for field in fields: + if field in dir(self): + self.__setattr__( + field, smart_subs(self.__getattribute__(field), old, new) + ) + + dictfields = [ + "compartment_assignment_rules", + "parameter_assignment_rules", + "initial_assignments", + ] + for dictfield in dictfields: + d = getattr(self, dictfield) + + # replace identifiers + if old in d and replace_identifiers: + d[new] = d[old] + del d[old] + + if dictfield == "initial_assignments": + tmp_new = self._make_initial(new) + else: + tmp_new = new + + # replace values + for k in d: + d[k] = smart_subs(d[k], old, tmp_new) + + # replace in identifiers + if replace_identifiers: + for symbol in [ + SymbolId.EXPRESSION, + SymbolId.SPECIES, + SymbolId.ALGEBRAIC_STATE, + ]: + # completely recreate the dict to keep ordering consistent + if old not in self.symbols[symbol]: + continue + self.symbols[symbol] = { + smart_subs(k, old, new): v + for k, v in self.symbols[symbol].items() + } + + for symbol in [ + SymbolId.OBSERVABLE, + SymbolId.LLHY, + SymbolId.SIGMAY, + ]: + if old not in self.symbols[symbol]: + continue + self.symbols[symbol][new] = self.symbols[symbol][old] + del self.symbols[symbol][old] + + # replace in values + for symbol in [ + SymbolId.OBSERVABLE, + SymbolId.LLHY, + SymbolId.LLHZ, + SymbolId.SIGMAY, + SymbolId.SIGMAZ, + SymbolId.EXPRESSION, + SymbolId.EVENT, + SymbolId.EVENT_OBSERVABLE, + SymbolId.ALGEBRAIC_EQUATION, + ]: + for element in self.symbols[symbol].values(): + element["value"] = smart_subs(element["value"], old, new) + + # replace in event state updates (boluses) + if self.symbols.get(SymbolId.EVENT, False): + for event in self.symbols[SymbolId.EVENT].values(): + for index in range(len(event["state_update"])): + event["state_update"][index] = smart_subs( + event["state_update"][index], old, new + ) + + for state in { + **self.symbols[SymbolId.SPECIES], + **self.symbols[SymbolId.ALGEBRAIC_STATE], + }.values(): + state["init"] = smart_subs( + state["init"], old, self._make_initial(new) + ) + + if "dt" in state: + state["dt"] = smart_subs(state["dt"], old, new) + + # Initial compartment volume may also be specified with an assignment + # 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)) + for c, v in self.compartments.items() + } + + # Substitute inside spline definitions + for spline in self.splines: + spline._replace_in_all_expressions(old, new) + + def _clean_reserved_symbols(self) -> None: + """ + Remove all reserved symbols from self.symbols + """ + for sym in RESERVED_SYMBOLS: + old_symbol = symbol_with_assumptions(sym) + new_symbol = symbol_with_assumptions(f"amici_{sym}") + self._replace_in_all_expressions( + old_symbol, new_symbol, replace_identifiers=True + ) + for symbols_ids, symbols in self.symbols.items(): + if old_symbol in symbols: + # reconstitute the whole dict in order to keep the ordering + self.symbols[symbols_ids] = { + new_symbol if k is old_symbol else k: v + for k, v in symbols.items() + } + + def _sympy_from_sbml_math( + self, var_or_math: [sbml.SBase, str] + ) -> Union[sp.Expr, float, None]: + """ + Sympify Math of SBML variables with all sanity checks and + transformations + + :param var_or_math: + SBML variable that has a getMath() function or math string + :return: + sympfified symbolic expression + """ + if isinstance(var_or_math, sbml.SBase): + math_string = sbml.formulaToL3StringWithSettings( + var_or_math.getMath(), self.sbml_parser_settings + ) + ele_name = var_or_math.element_name + else: + math_string = var_or_math + ele_name = "string" + math_string = replace_logx(math_string) + try: + try: + formula = sp.sympify( + _parse_logical_operators(math_string), + locals=self._local_symbols, + ) + except TypeError as err: + if str(err) == "BooleanAtom not allowed in this context.": + formula = sp.sympify( + _parse_logical_operators(math_string), + locals={ + "true": sp.Float(1.0), + "false": sp.Float(0.0), + **self._local_symbols, + }, + ) + else: + raise + except (sp.SympifyError, TypeError, ZeroDivisionError) as err: + raise SBMLException( + f'{ele_name} "{math_string}" ' + "contains an unsupported expression: " + f"{err}." + ) + + if isinstance(formula, sp.Expr): + formula = _parse_special_functions_sbml(formula) + _check_unsupported_functions_sbml( + formula, expression_type=ele_name + ) + return formula + + def _get_element_initial_assignment( + self, element_id: str + ) -> Union[sp.Expr, None]: + """ + Extract value of sbml variable according to its initial assignment + + :param element_id: + sbml variable name + :return: + + """ + assignment = self.sbml.getInitialAssignment(element_id) + if assignment is None: + return None + sym = self._sympy_from_sbml_math(assignment) + # this is an initial assignment so we need to use + # initial conditions + sym = self._make_initial(sym) + return sym + + def _get_element_stoichiometry(self, ele: sbml.SBase) -> sp.Expr: + """ + Computes the stoichiometry of a reactant or product of a reaction + + :param ele: + reactant or product + :return: + symbolic variable that defines stoichiometry + """ + if ele.isSetId(): + sym = self._get_element_initial_assignment(ele.getId()) + if sym is not None: + return sym + + if self.is_assignment_rule_target(ele): + return _get_identifier_symbol(ele) + + if ele.isSetStoichiometry(): + stoichiometry: float = ele.getStoichiometry() + return ( + sp.Integer(stoichiometry) + if stoichiometry.is_integer() + else sp.Float(stoichiometry) + ) + + return sp.Integer(1) + + def is_assignment_rule_target(self, element: sbml.SBase) -> bool: + """ + Checks if an element has a valid assignment rule in the specified + model. + + :param element: + SBML variable + + :return: + boolean indicating truth of function name + """ + a = self.sbml.getAssignmentRuleByVariable(element.getId()) + return a is not None and self._sympy_from_sbml_math(a) is not None + + def is_rate_rule_target(self, element: sbml.SBase) -> bool: + """ + Checks if an element has a valid assignment rule in the specified + model. + + :param element: + SBML variable + + :return: + boolean indicating truth of function name + """ + a = self.sbml.getRateRuleByVariable(element.getId()) + return a is not None and self._sympy_from_sbml_math(a) is not None + + +def _check_lib_sbml_errors( + sbml_doc: sbml.SBMLDocument, show_warnings: bool = False +) -> None: + """ + Checks the error log in the current self.sbml_doc. + + :param sbml_doc: + SBML document + + :param show_warnings: + display SBML warnings + """ + num_warning = sbml_doc.getNumErrors(sbml.LIBSBML_SEV_WARNING) + num_error = sbml_doc.getNumErrors(sbml.LIBSBML_SEV_ERROR) + num_fatal = sbml_doc.getNumErrors(sbml.LIBSBML_SEV_FATAL) + + if num_warning + num_error + num_fatal: + for i_error in range(sbml_doc.getNumErrors()): + error = sbml_doc.getError(i_error) + # we ignore any info messages for now + if error.getSeverity() >= sbml.LIBSBML_SEV_ERROR or ( + show_warnings + and error.getSeverity() >= sbml.LIBSBML_SEV_WARNING + ): + logger.error( + f"libSBML {error.getCategoryAsString()} " + f"({error.getSeverityAsString()}):" + f" {error.getMessage()}" + ) + + if num_error + num_fatal: + raise SBMLException( + "SBML Document failed to load (see error messages above)" + ) + + +def _parse_event_trigger(trigger: sp.Expr) -> sp.Expr: + """ + Recursively translates a boolean trigger function into a real valued + root function + + :param trigger: + :return: real valued root function expression + """ + # Events can be defined without trigger, i.e., the event will never fire. + # In this case, set a dummy trigger: + if trigger is None: + return sp.Float(1.0) + if trigger.is_Relational: + root = trigger.args[0] - trigger.args[1] + _check_unsupported_functions_sbml(root, "sympy.Expression") + + # convert relational expressions into trigger functions + if isinstance( + trigger, + (sp.core.relational.LessThan, sp.core.relational.StrictLessThan), + ): + # y < x or y <= x + return -root + if isinstance( + trigger, + ( + sp.core.relational.GreaterThan, + sp.core.relational.StrictGreaterThan, + ), + ): + # y >= x or y > x + return root + + # or(x,y): any of {x,y} is > 0: sp.Max(x, y) + if isinstance(trigger, sp.Or): + return sp.Max(*[_parse_event_trigger(arg) for arg in trigger.args]) + # and(x,y): all out of {x,y} are > 0: sp.Min(x, y) + if isinstance(trigger, sp.And): + return sp.Min(*[_parse_event_trigger(arg) for arg in trigger.args]) + + raise SBMLException( + "AMICI can not parse piecewise/event trigger functions with argument " + f"{trigger}." + ) + + +def assignmentRules2observables( + sbml_model: sbml.Model, filter_function: Callable = lambda *_: True +): + """ + Turn assignment rules into observables. + + :param sbml_model: + Model to operate on + + :param filter_function: + Callback function taking assignment variable as input and returning + ``True``/``False`` to indicate if the respective rule should be + turned into an observable. + + :return: + A dictionary(observableId:{ + 'name': observableName, + 'formula': formulaString + }) + """ + observables = {} + for rule in sbml_model.getListOfRules(): + if rule.getTypeCode() != sbml.SBML_ASSIGNMENT_RULE: + continue + parameter_id = rule.getVariable() + if (p := sbml_model.getParameter(parameter_id)) and filter_function(p): + observables[parameter_id] = { + "name": p.getName() if p.isSetName() else parameter_id, + "formula": sbml_model.getAssignmentRuleByVariable( + parameter_id + ).getFormula(), + } + + for parameter_id in observables: + sbml_model.removeRuleByVariable(parameter_id) + sbml_model.removeParameter(parameter_id) + + return observables + + +def _add_conservation_for_constant_species( + ode_model: DEModel, conservation_laws: List[ConservationLaw] +) -> List[int]: + """ + Adds constant species to conservations laws + + :param ode_model: + ODEModel object with basic definitions + + :param conservation_laws: + List of already known conservation laws + + :returns species_solver: + List of species indices which remain later in the DE solver + """ + + # decide which species to keep in stoichiometry + species_solver = list(range(len(ode_model._differential_states))) + + # iterate over species, find constant ones + for ix in reversed(range(len(ode_model._differential_states))): + if ode_model.state_is_constant(ix): + # dont use sym('x') here since conservation laws need to be + # added before symbols are generated + target_state = ode_model._differential_states[ix].get_id() + total_abundance = symbol_with_assumptions(f"tcl_{target_state}") + conservation_laws.append( + { + "state": target_state, + "total_abundance": total_abundance, + "coefficients": {target_state: 1.0}, + } + ) + # mark species to delete from stoichiometric matrix + species_solver.pop(ix) + + return species_solver + + +def _get_species_compartment_symbol(species: sbml.Species) -> sp.Symbol: + """ + Generate compartment symbol for the compartment of a specific species. + This function will always return the same unique python object for a + given species name. + + :param species: + sbml species + :return: + compartment symbol + """ + return symbol_with_assumptions(species.getCompartment()) + + +def _get_identifier_symbol(var: sbml.SBase) -> sp.Symbol: + """ + Generate identifier symbol for a sbml variable. + This function will always return the same unique python object for a + given entity. + + :param var: + sbml variable + :return: + identifier symbol + """ + return symbol_with_assumptions(var.getId()) + + +def get_species_initial(species: sbml.Species) -> sp.Expr: + """ + Extract the initial concentration from a given species + + :param species: + species index + + :return: + initial species concentration + """ + if species.isSetInitialConcentration(): + conc = species.getInitialConcentration() + if species.getHasOnlySubstanceUnits(): + return sp.Float(conc) * _get_species_compartment_symbol(species) + else: + return sp.Float(conc) + + if species.isSetInitialAmount(): + amt = species.getInitialAmount() + if math.isnan(amt): + return sp.Float(0.0) + + if species.getHasOnlySubstanceUnits(): + return sp.Float(amt) + else: + return sp.Float(amt) / _get_species_compartment_symbol(species) + + return sp.Float(0.0) + + +def _get_list_of_species_references( + sbml_model: sbml.Model, +) -> List[sbml.SpeciesReference]: + """ + Extracts list of species references as SBML doesn't provide a native + function for this. + + :param sbml_model: + SBML model instance + + :return: + ListOfSpeciesReferences + """ + return [ + reference + for reaction in sbml_model.getListOfReactions() + for reference in itt.chain( + reaction.getListOfReactants(), + reaction.getListOfProducts(), + reaction.getListOfModifiers(), + ) + ] + + +def replace_logx(math_str: Union[str, float, None]) -> Union[str, float, None]: + """ + Replace logX(.) by log(., X) since sympy cannot parse the former + + :param math_str: + string for sympification + + :return: + sympifiable string + """ + if not isinstance(math_str, str): + return math_str + + 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() + 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( + _get_identifier_symbol( + sbml_parameters[sbml_parameter_ids.index(target_id)] + ) + ) + return targets + + +def _check_unsupported_functions_sbml( + sym: sp.Expr, expression_type: str, full_sym: Optional[sp.Expr] = None +): + try: + _check_unsupported_functions(sym, expression_type, full_sym) + except RuntimeError as err: + raise SBMLException(str(err)) + + +def _parse_special_functions_sbml( + sym: sp.Expr, toplevel: bool = True +) -> sp.Expr: + try: + return _parse_special_functions(sym, toplevel) + except RuntimeError as err: + raise SBMLException(str(err)) + + +def _validate_observables( + observables: Union[Dict[str, Dict[str, str]], None], + sigmas: Dict[str, Union[str, float]], + noise_distributions: Dict[str, str], + events: bool = False, +) -> None: + if observables is None or not observables: + return + + # Ensure no non-existing observableIds have been specified + # (no problem here, but usually an upstream bug) + unknown_ids = set(sigmas.keys()) - set(observables.keys()) + if unknown_ids: + raise ValueError( + f"Sigma provided for unknown " + f"{'eventO' if events else 'o'}bservableIds: " + f"{unknown_ids}." + ) + + # Ensure no non-existing observableIds have been specified + # (no problem here, but usually an upstream bug) + unknown_ids = set(noise_distributions.keys()) - set(observables.keys()) + if unknown_ids: + raise ValueError( + f"Noise distribution provided for unknown " + f"{'eventO' if events else 'o'}bservableIds: " + f"{unknown_ids}." + ) + + +def _check_symbol_nesting( + symbols: Dict[sp.Symbol, Dict[str, sp.Expr]], symbol_type: str +): + observable_syms = set(symbols.keys()) + for obs in symbols.values(): + if any(sym in observable_syms for sym in obs["value"].free_symbols): + raise ValueError( + "Nested observables are not supported, " + f"but {symbol_type} `{obs['name']} = {obs['value']}` " + "references another observable." + ) + + +def _non_const_conservation_laws_supported(sbml_model: sbml.Model) -> bool: + """Check whether non-constant conservation laws can be handled for the + given model.""" + if any( + rule.getTypeCode() == sbml.SBML_RATE_RULE + for rule in sbml_model.getListOfRules() + ): + # see SBML semantic test suite, case 33 for an example + warnings.warn( + "Conservation laws for non-constant species in " + "models with RateRules are currently not supported " + "and will be turned off." + ) + return False + + if any( + rule.getTypeCode() == sbml.SBML_ASSIGNMENT_RULE + and sbml_model.getSpecies(rule.getVariable()) + for rule in sbml_model.getListOfRules() + ): + warnings.warn( + "Conservation laws for non-constant species in " + "models with Species-AssignmentRules are currently not " + "supported and will be turned off." + ) + return False + + return True + + +def _rateof_to_dummy(sym_math): + """Replace rateOf(...) by dummy variable + + if `rateOf(some_species)` is used in an initial assignment, we don't want to substitute the species argument + by its initial value. + + Usage: + sym_math, rateof_to_dummy = _rateof_to_dummy(sym_math) + [...substitute...] + sym_math = _dummy_to_rateof(sym_math, rateof_to_dummy) + """ + if rate_ofs := sym_math.find(sp.core.function.UndefinedFunction("rateOf")): + # replace by dummies to avoid species substitution + rateof_dummies = { + rate_of: sp.Dummy(f"Dummy_RateOf_{rate_of.args[0].name}") + for rate_of in rate_ofs + } + + return sym_math.subs(rateof_dummies), rateof_dummies + return sym_math, {} + + +def _dummy_to_rateof(sym_math, rateof_dummies): + """Back-substitution of dummies from `_rateof_to_dummy`""" + if rateof_dummies: + return sym_math.subs({v: k for k, v in rateof_dummies.items()}) + return sym_math diff --git a/deps/AMICI/python/sdist/amici/sbml_utils.py b/deps/AMICI/python/sdist/amici/sbml_utils.py new file mode 100644 index 000000000..66c9d01bb --- /dev/null +++ b/deps/AMICI/python/sdist/amici/sbml_utils.py @@ -0,0 +1,541 @@ +""" +SBML Utilities +-------------- +This module provides helper functions for working with SBML. +""" + +from __future__ import annotations + +from typing import TYPE_CHECKING + +import sympy as sp + +if TYPE_CHECKING: + from typing import Any, Dict, Optional, Tuple, Union + + SbmlID = Union[str, sp.Symbol] + +import xml.dom.minidom + +import libsbml +from sympy.core.parameters import evaluate +from sympy.printing.mathml import MathMLContentPrinter + +from .import_utils import ( + SBMLException, + _check_unsupported_functions, + _parse_special_functions, + amici_time_symbol, + sbml_time_symbol, +) + + +class SbmlInvalidIdSyntax(SBMLException): + pass + + +class SbmlDuplicateComponentIdError(SBMLException): + pass + + +class SbmlMissingComponentIdError(SBMLException): + pass + + +class SbmlMathError(SBMLException): + pass + + +class SbmlAnnotationError(SBMLException): + pass + + +def create_sbml_model( + model_id: str, level: int = 2, version: int = 5 +) -> Tuple[libsbml.SBMLDocument, libsbml.Model]: + """Helper for creating an empty SBML model. + + :param model_id: + SBML ID of the new model. + + :param level: + Level of the new SBML document. + + :param version: + Version of the new SBML document. + + :return: + A tuple containing the newly created :py:class:`libsbml.SBMLDocument` + and :py:class:`libsbml.Model`. + """ + doc = libsbml.SBMLDocument(level, version) + model = doc.createModel() + model.setId(model_id) + return doc, model + + +def add_compartment( + model: libsbml.Model, + compartment_id: SbmlID, + *, + size: float = 1.0, +) -> libsbml.Species: + """Helper for adding a compartment to a SBML model. + + :param model: + SBML model to which the compartment is to be added. + + :param compartment_id: + SBML ID of the new compartment. + + :param size: + Size of the new compartment. Defaults to `1.0`. + + :return: + The new compartment as a :py:class:`libsbml.Compartment` object. + """ + compartment_id = str(compartment_id) + + # Check whether a compartment with the same ID already exists + # TODO the resulting SBML may still be invalid + # if other types of objects (e.g., parameter) have the same ID + if model.getCompartment(compartment_id): + raise SbmlDuplicateComponentIdError( + f"A compartment with ID {compartment_id} has already been defined" + ) + + cmp = model.createCompartment() + if cmp.setId(compartment_id) != libsbml.LIBSBML_OPERATION_SUCCESS: + raise SbmlInvalidIdSyntax(f"{compartment_id} is not a valid SBML ID") + cmp.setSize(size) + + return cmp + + +def add_species( + model: libsbml.Model, + species_id: SbmlID, + *, + compartment_id: Optional[str] = None, + name: Union[bool, str] = False, + initial_amount: float = 0.0, + units: Optional[str] = None, +) -> libsbml.Species: + """Helper for adding a species to a SBML model. + + :param model: + SBML model to which the species is to be added. + + :param species_id: + SBML ID of the new species. + + :param compartment_id: + Compartment ID for the new species. + If there is only one compartment it can be auto-selected. + + :param initial_amount: + Initial amount of the new species. + + :param units: + Units attribute for the new species. + + :return: + The new species as a :py:class:`libsbml.Species` object. + """ + species_id = str(species_id) + if name is True: + name = species_id + + # Check whether an element with the same ID already exists + if model.getElementBySId(species_id): + raise SbmlDuplicateComponentIdError( + f"An element with ID {species_id} has already been defined." + ) + + if compartment_id is None: + compartments = model.getListOfCompartments() + if len(compartments) != 1: + raise ValueError( + "Compartment auto-selection is possible " + "only if there is one and only one compartment." + ) + compartment_id = compartments[0].getId() + elif not model.getCompartment(compartment_id): + raise SbmlMissingComponentIdError( + f"No compartment with ID {compartment_id}." + ) + + sp = model.createSpecies() + if sp.setIdAttribute(species_id) != libsbml.LIBSBML_OPERATION_SUCCESS: + raise SbmlInvalidIdSyntax(f"{species_id} is not a valid SBML ID.") + sp.setCompartment(compartment_id) + sp.setInitialAmount(float(initial_amount)) + if units is not None: + sp.setUnits(str(units)) + if isinstance(name, str): + sp.setName(name) + + return sp + + +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, +) -> libsbml.Parameter: + """Helper for adding a parameter to a SBML model. + + :param model: + SBML model to which the parameter is to be added. + + :param parameter_id: + SBML ID of the new parameter. + + :param name: + SBML name of the new parameter. + + :param value: + Value attribute for the new parameter. + + :param units: + Units attribute for the new parameter. + + :param constant: + Constant attribute for the new parameter. + + :return: + The new parameter as a :py:class:`libsbml.Parameter` object. + """ + parameter_id = str(parameter_id) + if name is True: + name = parameter_id + + # Check whether an element with the same ID already exists + if model.getElementBySId(parameter_id): + raise SbmlDuplicateComponentIdError( + f"An element with ID {parameter_id} has already been defined." + ) + + par = model.createParameter() + if par.setIdAttribute(parameter_id) != libsbml.LIBSBML_OPERATION_SUCCESS: + raise SbmlInvalidIdSyntax(f"{parameter_id} is not a valid SBML ID.") + if units is not None: + par.setUnits(str(units)) + if constant is not None: + par.setConstant(bool(constant)) + if value is not None: + par.setValue(float(value)) + if isinstance(name, str): + par.setName(name) + + return par + + +def add_assignment_rule( + model: libsbml.Model, + variable_id: SbmlID, + formula, + rule_id: Optional[str] = None, +) -> libsbml.AssignmentRule: + """Helper for adding an assignment rule to a SBML model. + + :param model: + SBML model to which the assignment rule is to be added. + + :param variable_id: + SBML ID of the quantity for which the assignment rule is to be added. + + :param formula: + Formula for the assignment rule (it will be sympified). + + :param rule_id: + SBML ID of the new assignment rule. + Defaults to `'assignment_' + variableId`. + + :return: + The assignment rule as a :py:class:`libsbml.AssignmentRule` object. + """ + variable_id = str(variable_id) + if rule_id is None: + rule_id = "assignment_" + variable_id + + # Check whether rules exists for this parameter or with the same name + if model.getRuleByVariable(variable_id): + raise SbmlDuplicateComponentIdError( + f"A rule for parameter {variable_id} has already been defined." + ) + if model.getElementBySId(rule_id): + raise SbmlDuplicateComponentIdError( + f"An element with SBML ID {rule_id} has already been defined." + ) + + rule = model.createAssignmentRule() + if rule.setVariable(variable_id) != libsbml.LIBSBML_OPERATION_SUCCESS: + raise SbmlInvalidIdSyntax(f"{variable_id} is not a valid SBML ID.") + if rule.setIdAttribute(rule_id) != libsbml.LIBSBML_OPERATION_SUCCESS: + raise SbmlInvalidIdSyntax(f"{rule_id} is not a valid SBML ID.") + set_sbml_math(rule, formula) + + return rule + + +def add_rate_rule( + model: libsbml.Model, + variable_id: SbmlID, + formula, + rule_id: Optional[str] = None, +) -> libsbml.RateRule: + """ + Helper for adding a rate rule to a SBML model. + + :param model: + SBML model to which the rate rule is to be added. + + :param variable_id: + SBML ID of the quantity for which the rate rule is to be added. + + :param formula: + Formula for the rate rule (it will be sympified). + + :param rule_id: + SBML ID of the new rate rule. + Defaults to `'rate_' + variableId`. + + :return: + The new rate rule as a :py:class:`libsbml.RateRule` object. + """ + variable_id = str(variable_id) + if rule_id is None: + rule_id = "rate_" + variable_id + + # Check whether rules exists for this parameter or with the same name + if model.getRuleByVariable(variable_id): + raise SbmlDuplicateComponentIdError( + f"A rule for parameter {variable_id} has already been defined." + ) + if model.getElementBySId(rule_id): + raise SbmlDuplicateComponentIdError( + f"An element with SBML ID {rule_id} has already been defined." + ) + + rule = model.createRateRule() + if rule.setVariable(variable_id) != libsbml.LIBSBML_OPERATION_SUCCESS: + raise SbmlInvalidIdSyntax(f"{variable_id} is not a valid SBML ID.") + if rule.setIdAttribute(rule_id) != libsbml.LIBSBML_OPERATION_SUCCESS: + raise SbmlInvalidIdSyntax(f"{rule_id} is not a valid SBML ID.") + set_sbml_math(rule, formula) + + return rule + + +def add_inflow( + model: libsbml.Model, + species_id: SbmlID, + rate, + *, + reaction_id: Optional[str] = None, + reversible: bool = False, +) -> libsbml.Reaction: + species_id = str(species_id) + if reaction_id is None: + reaction_id = f"inflow_of_{species_id}" + + if model.getElementBySId(reaction_id): + raise SbmlDuplicateComponentIdError( + f"An element with SBML ID {reaction_id} has already been defined." + ) + + reaction = model.createReaction() + if reaction.setId(reaction_id) != libsbml.LIBSBML_OPERATION_SUCCESS: + raise SbmlInvalidIdSyntax(f"{reaction_id} is not a valid SBML ID.") + reaction.setReversible(reversible) + + spr = reaction.createProduct() + spr.setSpecies(species_id) + + kl = reaction.createKineticLaw() + compartment_id = model.getSpecies(species_id).getCompartment() + set_sbml_math(kl, sp.Symbol(compartment_id) * rate) + + return reaction + + +def get_sbml_units( + model: libsbml.Model, x: Union[SbmlID, sp.Basic] +) -> Union[None, str]: + """Try to get the units for expression `x`. + + :param model: + SBML model. + :param x: + Expression to get the units of. + :return: + A string if the units could be determined, otherwise `None`. + """ + # TODO can the SBML unit inference machinery be used? + x = sp.sympify(x) + if not x.is_Symbol: + return None + if x.name == sbml_time_symbol.name: + if model.isSetTimeUnits(): + return model.getTimeUnits() + return None + par = model.getParameter(x.name) + if par is None: + return None + units = par.getUnits() + if units == "": + return None + return units + + +def pretty_xml(ugly_xml: str) -> str: + "Prettifies an XML document (given as a string)." + dom = xml.dom.minidom.parseString(ugly_xml) + pretty_xml = dom.toprettyxml() + # We must delete the first line (xml header) + return pretty_xml[pretty_xml.index("\n") + 1 :] + + +class MathMLSbmlPrinter(MathMLContentPrinter): + """Prints a SymPy expression to a MathML expression parsable by libSBML. + + Differences from `sympy.MathMLContentPrinter`: + 1. underscores in symbol names are not converted to subscripts + 2. symbols with name 'time' are converted to the SBML time symbol + """ + + def _print_Symbol(self, sym: sp.Symbol) -> xml.dom.minidom.Element: + ci = self.dom.createElement(self.mathml_tag(sym)) + ci.appendChild(self.dom.createTextNode(sym.name)) + return ci + + def doprint(self, expr, *, pretty: bool = False) -> str: + mathml = '' + mathml += super().doprint(expr) + mathml += "" + mathml = mathml.replace( + "time", + ' time ', + ) + return pretty_xml(mathml) if pretty else mathml + + +def sbml_mathml( + expr, *, replace_time: bool = False, pretty: bool = False, **settings +) -> str: + """Prints a SymPy expression to a MathML expression parsable by libSBML. + + :param expr: + expression to be converted to MathML (will be sympified). + + :param replace_time: + replace the AMICI time symbol with the SBML time symbol. + + :param pretty: + prettify the resulting MathML. + """ + with evaluate(False): + expr = sp.sympify(expr) + if replace_time: + expr = expr.subs(amici_time_symbol, sbml_time_symbol) + return MathMLSbmlPrinter(settings).doprint(expr, pretty=pretty) + + +def sbml_math_ast(expr, **kwargs) -> libsbml.ASTNode: + """Convert a SymPy expression to SBML math AST. + + :param expr: + expression to be converted (will be sympified). + + :param kwargs: + extra options for MathML conversion. + """ + mathml = sbml_mathml(expr, **kwargs) + ast = libsbml.readMathMLFromString(mathml) + if ast is None: + raise SbmlMathError( + f"error while converting the following expression to SBML AST.\n" + f"expression:\n{expr}\n" + f"MathML:\n{pretty_xml(mathml)}" + ) + return ast + + +def set_sbml_math(obj: libsbml.SBase, expr, **kwargs) -> None: + """Set the math attribute of a SBML node using a SymPy expression. + + :param obj: + SBML node supporting `setMath` method. + + :param expr: + expression to which the math attribute of `obj` should be se to + (will be sympified). + + :param kwargs: + extra options for MathML conversion. + """ + mathml = sbml_math_ast(expr, **kwargs) + if obj.setMath(mathml) != libsbml.LIBSBML_OPERATION_SUCCESS: + raise SbmlMathError( + f"Could not set math attribute of SBML object {obj}\n" + f"expression:\n{expr}\n" + f"MathML:\n{pretty_xml(mathml)}" + ) + + +def mathml2sympy( + mathml: str, + *, + evaluate: bool = False, + locals: Optional[Dict[str, Any]] = None, + expression_type: str = "mathml2sympy", +) -> sp.Basic: + ast = libsbml.readMathMLFromString(mathml) + if ast is None: + raise ValueError( + f"libSBML could not parse MathML string:\n{pretty_xml(mathml)}" + ) + + formula = _parse_logical_operators(libsbml.formulaToL3String(ast)) + + if evaluate: + expr = sp.sympify(formula, locals=locals) + else: + with sp.core.parameters.evaluate(False): + expr = sp.sympify(formula, locals=locals) + + expr = _parse_special_functions(expr) + + if expression_type is not None: + _check_unsupported_functions(expr, expression_type) + + return expr + + +def _parse_logical_operators( + math_str: Union[str, float, None] +) -> Union[str, float, None]: + """ + Parses a math string in order to replace logical operators by a form + parsable for sympy + + :param math_str: + str with mathematical expression + :param math_str: + parsed math_str + """ + if not isinstance(math_str, str): + return math_str + + if " xor(" in math_str or " Xor(" in math_str: + raise SBMLException( + "Xor is currently not supported as logical " "operation." + ) + + return (math_str.replace("&&", "&")).replace("||", "|") diff --git a/deps/AMICI/python/sdist/amici/setup.template.py b/deps/AMICI/python/sdist/amici/setup.template.py deleted file mode 120000 index 925816025..000000000 --- a/deps/AMICI/python/sdist/amici/setup.template.py +++ /dev/null @@ -1 +0,0 @@ -../../amici/setup.template.py \ No newline at end of file diff --git a/deps/AMICI/python/sdist/amici/setup.template.py b/deps/AMICI/python/sdist/amici/setup.template.py new file mode 100644 index 000000000..e7995e2c5 --- /dev/null +++ b/deps/AMICI/python/sdist/amici/setup.template.py @@ -0,0 +1,77 @@ +"""AMICI model package setup""" +import os +from pathlib import Path + +from amici import _get_amici_path +from amici.custom_commands import AmiciBuildCMakeExtension +from cmake_build_extension import CMakeExtension +from setuptools import find_namespace_packages, setup + + +def get_extension() -> CMakeExtension: + """Get setuptools extension object for this AMICI model package""" + + # Build shared object + prefix_path = Path(_get_amici_path()) + AmiciBuildCMakeExtension.extend_cmake_prefix_path(str(prefix_path)) + + # handle parallel building + # Note: can be empty to use all hardware threads + if (parallel_jobs := os.environ.get("AMICI_PARALLEL_COMPILE")) is not None: + os.environ["CMAKE_BUILD_PARALLEL_LEVEL"] = parallel_jobs + else: + os.environ["CMAKE_BUILD_PARALLEL_LEVEL"] = "1" + + return CMakeExtension( + name="model_ext", + source_dir=os.getcwd(), + install_prefix="TPL_MODELNAME", + cmake_configure_options=[ + "-DCMAKE_VERBOSE_MAKEFILE=ON", + "-DCMAKE_MODULE_PATH=" + f"{prefix_path.as_posix()}/lib/cmake/SuiteSparse;" + f"{prefix_path.as_posix()}/lib64/cmake/SuiteSparse", + f"-DKLU_ROOT={prefix_path.as_posix()}", + "-DAMICI_PYTHON_BUILD_EXT_ONLY=ON", + ], + ) + + +# Change working directory to setup.py location +os.chdir(os.path.dirname(os.path.abspath(__file__))) + +MODEL_EXT = get_extension() + +CLASSIFIERS = [ + "Development Status :: 3 - Alpha", + "Intended Audience :: Science/Research", + "Operating System :: POSIX :: Linux", + "Operating System :: MacOS :: MacOS X", + "Programming Language :: Python", + "Programming Language :: C++", + "Topic :: Scientific/Engineering :: Bio-Informatics", +] + +CMDCLASS = { + # for CMake-based builds + "build_ext": AmiciBuildCMakeExtension, +} + +# Install +setup( + name="TPL_MODELNAME", + cmdclass=CMDCLASS, + version="TPL_PACKAGE_VERSION", + description="AMICI-generated module for model TPL_MODELNAME", + url="https://github.com/AMICI-dev/AMICI", + author="model-author-todo", + author_email="model-author-todo", + ext_modules=[MODEL_EXT], + packages=find_namespace_packages(), + install_requires=["amici==TPL_AMICI_VERSION"], + extras_require={"wurlitzer": ["wurlitzer"]}, + python_requires=">=3.9", + package_data={}, + zip_safe=False, + classifiers=CLASSIFIERS, +) diff --git a/deps/AMICI/python/sdist/amici/setuptools.py b/deps/AMICI/python/sdist/amici/setuptools.py deleted file mode 120000 index 9fc45c94b..000000000 --- a/deps/AMICI/python/sdist/amici/setuptools.py +++ /dev/null @@ -1 +0,0 @@ -../../amici/setuptools.py \ No newline at end of file diff --git a/deps/AMICI/python/sdist/amici/splines.py b/deps/AMICI/python/sdist/amici/splines.py new file mode 100644 index 000000000..fdb091204 --- /dev/null +++ b/deps/AMICI/python/sdist/amici/splines.py @@ -0,0 +1,1856 @@ +""" +Splines +------- +This module provides helper functions for reading/writing splines with AMICI +annotations from/to SBML files and for adding such splines to the AMICI C++ +code. +""" +from __future__ import annotations + +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from numbers import Real + from typing import ( + Any, + Callable, + Dict, + List, + Optional, + Sequence, + Set, + Tuple, + Union, + ) + + from . import sbml_import + + BClike = Union[None, str, Tuple[Union[None, str], Union[None, str]]] + + NormalizedBC = Tuple[Union[None, str], Union[None, str]] + +import collections.abc +import logging +import xml.etree.ElementTree as ET +from abc import ABC, abstractmethod +from itertools import count +from numbers import Integral + +import libsbml +import numpy as np +import sympy as sp +from sympy.core.parameters import evaluate + +from .import_utils import ( + amici_time_symbol, + annotation_namespace, + sbml_time_symbol, + symbol_with_assumptions, +) +from .logging import get_logger +from .sbml_utils import ( + SbmlAnnotationError, + add_assignment_rule, + add_parameter, + get_sbml_units, + mathml2sympy, + pretty_xml, + sbml_mathml, +) + +logger = get_logger(__name__, logging.WARNING) + + +def sympify_noeval(x): + with evaluate(False): + return sp.sympify(x) + + +############################################################################### + + +class UniformGrid(collections.abc.Sequence): + """ + A grid of uniformly-spaced real points, computed with rational arithmetic. + + Implements the :py:class:`collections.abc.Sequence` interface and can be + converted to a :py:class:`numpy.ndarray` (conversion to float can be + specified with ``dtype=float``). + + :ivar start: first point. + + :ivar stop: last point. + + :ivar step: distance between consecutive points. + + :ivar number_of_nodes: number of grid nodes. + """ + + def __init__( + self, + start: Union[Real, sp.Basic], + stop: Union[Real, sp.Basic], + step: Optional[Union[Real, sp.Basic]] = None, + *, + number_of_nodes: Optional[Integral] = None, + always_include_stop: bool = True, + ): + """Create a new ``UniformGrid``. + + Note: A ``UniformGrid`` with a single node cannot be created. + + :param start: + First point in the grid + :param stop: + Last point in the grid (some caveats apply, see ``always_include_stop``) + :param step: + Desired step size of the grid. Mutually exclusive with ``number_of_nodes``. + :param number_of_nodes: + Number of grid nodes, i.e., the length of the grid. + It must be greater than or equal to 2. + Mutually exclusive with ``step``. + :param always_include_stop: + Controls the behaviour when ``step`` is not ``None``. + If ``True`` (default), the endpoint is the smallest + ``start + k * step``, with ``k`` integer, which is + greater than or equal to ``stop``. + Otherwise, the endpoint is the largest + ``start + k * step``, with ``k`` integer, which is + smaller than or equal to ``stop``. + """ + start = sp.nsimplify(sp.sympify(start)) + stop = sp.nsimplify(sp.sympify(stop)) + if step is None: + if number_of_nodes is None: + raise ValueError( + "One of step/number_of_nodes must be specified!" + ) + if not isinstance(number_of_nodes, Integral): + raise TypeError("Length must be an integer!") + if number_of_nodes < 2: + raise ValueError("Length must be at least 2!") + step = (stop - start) / (number_of_nodes - 1) + elif number_of_nodes is not None: + raise ValueError( + "Only one of step/number_of_nodes can be specified!" + ) + else: + step = sp.nsimplify(sp.sympify(step)) + + if start > stop: + raise ValueError( + f"Start point {start} greater than stop point {stop}!" + ) + + if step <= 0: + raise ValueError(f"Step size {step} must be strictly positive!") + + xx = [] + for i in count(): + x = start + i * step + if not always_include_stop and x > stop: + break + xx.append(x) + if always_include_stop and x >= stop: + break + + if len(xx) == 1: + raise ValueError( + f"Step size {step} is less than (stop - start) = {stop - start} " + "and always_include_stop is set to False, " + "leading to a UniformGrid with a single node, " + "which is unsupported!" + ) + + self._xx = np.asarray(xx) + + @property + def start(self) -> sp.Basic: + """First point.""" + return self._xx[0] + + @property + def stop(self) -> sp.Basic: + """Last point.""" + return self._xx[-1] + + @property + def step(self) -> sp.Basic: + """Distance between consecutive points.""" + return self._xx[1] - self._xx[0] + + @property + def number_of_nodes(self) -> sp.Basic: + """Number of grid nodes.""" + return len(self._xx) + + def __getitem__(self, i: Integral) -> sp.Basic: + return self._xx[i] + + def __len__(self) -> int: + return len(self._xx) + + def __array__(self, dtype=None) -> np.ndarray: + if dtype is None: + return self._xx + return np.array(self._xx, dtype=dtype) + + def __repr__(self) -> str: + return ( + f"UniformGrid(start={self.start}, stop={self.stop}, " + f"step={self.step})" + ) + + +############################################################################### + + +class AbstractSpline(ABC): + """ + Base class for spline functions which can be computed efficiently + thanks to tailored C++ implementations in AMICI. + Inside an SBML file, such splines are implemented with + an assignment rule containing both a symbolic piecewise formula + for the spline (allowing compatibility with any SBML-aware software) + and annotations which encode the necessary information for AMICI to + recreate the spline object (allowing for fast computations when the SBML + file is used together with AMICI). + """ + + def __init__( + self, + sbml_id: Union[str, sp.Symbol], + nodes: Sequence, + values_at_nodes: Sequence, + *, + evaluate_at: Optional[Union[str, sp.Basic]] = None, + bc: BClike = None, + extrapolate: BClike = None, + logarithmic_parametrization: bool = False, + ): + """Base constructor for ``AbstractSpline`` objects. + + :param sbml_id: + The SBML ID of the parameter associated to the spline + as a string or a SymPy symbol. + + :param nodes: + The points at which the spline values are known. + Currently, they must be numeric or only depend on constant parameters. + These points should be strictly increasing. + This argument will be sympified. + + :param values_at_nodes: + The spline values at each of the points in ``nodes``. + They must not depend on model species. + This argument will be sympified. + + :param evaluate_at: + The point at which the spline is evaluated. + It will be sympified. + Defaults to model time. + + :param bc: + Tuple of applied boundary conditions, one for each side of the + spline domain. If a single boundary condition is given it will be + applied to both sides. + Possible boundary conditions + (allowed values depend on the ``AbstractSpline`` subclass): + + `None` or `'no_bc'`: + Boundary conditions are not needed for this spline object; + `'zeroderivative'`: + first derivative set to zero; + `'natural'`: + second derivative set to zero; + `'zeroderivative+natural'`: + first and second derivatives set to zero; + `'periodic'`: + periodic bc. + + :param extrapolate: + Whether to extrapolate the spline outside the base interval + defined by ``(nodes[0], nodes[-1])``. + It is a tuple of extrapolation methods, one for each side of the + base interval. + If it is not a tuple, then the same extrapolation will be applied + on both sides. + Extrapolation methods supported: + + `None` or `'no_extrapolation'`: + no extrapolation should be performed. An exception will be + raised in the C++ code if the spline is evaluated outside the + base interval. In the fallback SBML symbolic expression + `'polynomial'` extrapolation will be used. + `'polynomial'`: + the cubic polynomial used in the nearest spline segment will be + used. + `'constant'`: + constant extrapolation will be used. + Requires `'zeroderivative'` boundary condition. + For splines which are continuous up to the second derivative, + it requires the stricter `'zeroderivative+natural'` + boundary condition. + `'linear'`: + linear extrapolation will be used. + For splines which are continuous up to the second derivative, + this requires the `'natural'` boundary condition. + `'periodic'`: + Periodic extrapolation. Requires `'periodic'` boundary + conditions. + + :param logarithmic_parametrization: + Whether interpolation should be done in log-scale. + """ + + if isinstance(sbml_id, str): + sbml_id = symbol_with_assumptions(sbml_id) + elif not isinstance(sbml_id, sp.Symbol): + raise TypeError( + "sbml_id must be either a string or a SymPy symbol, " + f"got {sbml_id} of type {type(sbml_id)} instead!" + ) + + if evaluate_at is None: + evaluate_at = amici_time_symbol + else: + evaluate_at = sympify_noeval(evaluate_at) + if not isinstance(evaluate_at, sp.Basic): + # It may still be e.g. a list! + raise ValueError(f"Invalid evaluate_at = {evaluate_at}!") + if ( + evaluate_at != amici_time_symbol + and evaluate_at != sbml_time_symbol + ): + logger.warning( + "At the moment AMICI only supports evaluate_at = (model time). " + "Annotations with correct piecewise MathML formulas " + "can still be created and used in other tools, " + "but they will raise an error when imported by AMICI." + ) + + if not isinstance(nodes, UniformGrid): + nodes = np.asarray([sympify_noeval(x) for x in nodes]) + values_at_nodes = np.asarray( + [sympify_noeval(y) for y in values_at_nodes] + ) + + if len(nodes) != len(values_at_nodes): + raise ValueError( + "Length of nodes and values_at_nodes must be the same " + f"(instead len(nodes) = {len(nodes)} and len(values_at_nodes) = {len(values_at_nodes)})!" + ) + + if all(x.is_Number for x in nodes) and not np.all(np.diff(nodes) >= 0): + raise ValueError("nodes should be strictly increasing!") + + if ( + logarithmic_parametrization + and all(y.is_Number for y in values_at_nodes) + and any(y <= 0 for y in values_at_nodes) + ): + raise ValueError( + "When interpolation is done in log-scale, " + "values_at_nodes should be strictly positive!" + ) + + bc, extrapolate = self._normalize_bc_and_extrapolate(bc, extrapolate) + if ( + bc == ("periodic", "periodic") + and values_at_nodes[0] != values_at_nodes[-1] + ): + raise ValueError( + "If the spline is to be periodic, " + "the first and last elements of values_at_nodes must be equal!" + ) + + self._sbml_id: sp.Symbol = sbml_id + self._evaluate_at = evaluate_at + self._nodes = nodes + self._values_at_nodes = values_at_nodes + self._bc = bc + self._extrapolate = extrapolate + self._logarithmic_parametrization = logarithmic_parametrization + self._formula_cache = {} + + def _normalize_bc_and_extrapolate(self, bc: BClike, extrapolate: BClike): + bc = AbstractSpline._normalize_bc(bc) + return self._normalize_extrapolate(bc, extrapolate) + + @staticmethod + def _normalize_bc(bc: BClike) -> NormalizedBC: + """ + Preprocess the boundary condition `bc` to a standard form. + """ + if not isinstance(bc, tuple): + bc = (bc, bc) + elif len(bc) != 2: + raise TypeError(f"bc should be a 2-tuple, got {bc} instead!") + + bc = list(bc) + + valid_bc = ( + "periodic", + "zeroderivative", + "zeroderivative+natural", + "natural", + "no_bc", + "auto", + None, + ) + + for i in (0, 1): + if bc[i] not in valid_bc: + raise ValueError( + f"Unsupported bc = {bc[i]}! " + f"The currently supported bc methods are: {valid_bc}" + ) + elif bc[i] == "no_bc": + bc[i] = None + + if (bc[0] == "periodic" or bc[1] == "periodic") and bc[0] != bc[1]: + raise ValueError( + "If the bc on one side is periodic, " + "then the bc on the other side must be periodic too!" + ) + + return bc[0], bc[1] + + def _normalize_extrapolate( + self, bc: NormalizedBC, extrapolate: BClike + ) -> Tuple[NormalizedBC, NormalizedBC]: + """ + Preprocess `extrapolate` to a standard form + and perform consistency checks + """ + if not isinstance(extrapolate, tuple): + extrapolate = (extrapolate, extrapolate) + elif len(extrapolate) != 2: + raise TypeError( + f"extrapolate should be a 2-tuple, got {extrapolate} instead!" + ) + extrapolate = list(extrapolate) + + if not isinstance(bc, tuple) or len(bc) != 2: + raise TypeError(f"bc should be a 2-tuple, got {bc} instead!") + bc = list(bc) + + valid_extrapolate = ( + "no_extrapolation", + "constant", + "linear", + "polynomial", + "periodic", + None, + ) + + for i in (0, 1): + if extrapolate[i] not in valid_extrapolate: + raise ValueError( + f"Unsupported extrapolate= {extrapolate[i]}!" + + " The currently supported extrapolation methods are: " + + str(valid_extrapolate) + ) + + if extrapolate[i] == "no_extrapolation": + extrapolate[i] = None + + if extrapolate[i] == "periodic": + if bc[0] == "auto": + bc[0] = "periodic" + if bc[1] == "auto": + bc[1] = "periodic" + if not (bc[0] == bc[1] == "periodic"): + raise ValueError( + "The spline must satisfy periodic boundary conditions " + "on both sides of the base interval " + "in order for periodic extrapolation to be used!" + ) + + elif extrapolate[i] == "constant": + assert self.smoothness > 0 + if self.smoothness == 1: + if bc[i] == "auto": + bc[i] = "zeroderivative" + elif bc[i] != "zeroderivative": + raise ValueError( + "The spline must satisfy zero-derivative bc " + "in order for constant extrapolation to be used!" + ) + elif bc[i] == "auto": + bc[i] = "zeroderivative+natural" + elif bc[i] != "zeroderivative+natural": + raise ValueError( + "The spline must satisfy zero-derivative and natural" + " bc in order for constant extrapolation to be used!" + ) + + elif extrapolate[i] == "linear": + assert self.smoothness > 0 + if self.smoothness > 1: + if bc[i] == "auto": + bc[i] = "natural" + elif bc[i] != "natural": + raise ValueError( + "The spline must satisfy natural bc " + "in order for linear extrapolation to be used!" + ) + elif bc[i] == "auto": + bc[i] = None + + elif bc[i] == "auto": + bc[i] = None + + if ( + (extrapolate[0] == "periodic" or extrapolate[1] == "periodic") + and extrapolate[0] != extrapolate[1] + and extrapolate[0] is not None + and extrapolate[1] is not None + ): + raise NotImplementedError( + "At the moment if periodic extrapolation is applied " + "to one side, the extrapolation at the other side " + "must either be periodic or not be applied " + "(in which case it will be periodic anyway)." + ) + + return (bc[0], bc[1]), (extrapolate[0], extrapolate[1]) + + @property + def sbml_id(self) -> sp.Symbol: + """SBML ID of the spline parameter.""" + return self._sbml_id + + @property + def evaluate_at(self) -> sp.Basic: + """The symbolic argument at which the spline is evaluated.""" + return self._evaluate_at + + @property + def nodes(self) -> np.ndarray: + """The points at which the spline values are known.""" + return self._nodes + + @property + def values_at_nodes(self) -> np.ndarray: + """The spline values at each of the points in ``nodes``.""" + return self._values_at_nodes + + @property + def bc(self) -> NormalizedBC: + """Boundary conditions applied to this spline.""" + return self._bc + + @property + def extrapolate(self) -> NormalizedBC: + """Whether to extrapolate the spline outside the base interval.""" + return self._extrapolate + + @property + def logarithmic_parametrization(self) -> bool: + """Whether interpolation is done in log-scale.""" + return self._logarithmic_parametrization + + @property + @abstractmethod + def smoothness(self) -> int: + """Smoothness of this spline.""" + raise NotImplementedError() + + @property + @abstractmethod + def method(self) -> str: + """Spline method.""" + raise NotImplementedError() + + def check_if_valid(self, importer: sbml_import.SbmlImporter) -> None: + """ + Check if the spline described by this object can be correctly + be implemented by AMICI. E.g., check whether the formulas + for spline grid points, values, ... contain species symbols. + """ + # At the moment only basic checks are done. + # There may still be some edge cases that break + # the AMICI spline implementation. + # If found, they should be checked for here + # until (if at all) they are accounted for. + from .de_export import SymbolId + + fixed_parameters: List[sp.Symbol] = list( + importer.symbols[SymbolId.FIXED_PARAMETER].keys() + ) + species: List[sp.Symbol] = list( + importer.symbols[SymbolId.SPECIES].keys() + ) + + for x in self.nodes: + if not x.free_symbols.issubset(fixed_parameters): + raise ValueError( + "nodes should only depend on constant parameters!" + ) + + for y in self.values_at_nodes: + if y.free_symbols.intersection(species): + raise ValueError( + "values_at_nodes should not depend on model species!" + ) + + fixed_parameters_values = [ + importer.symbols[SymbolId.FIXED_PARAMETER][fp]["value"] + for fp in fixed_parameters + ] + subs = dict(zip(fixed_parameters, fixed_parameters_values)) + 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: + """ + Get the polynomial interpolant on the ``(nodes[i], nodes[i+1])`` interval. + The polynomial is written in Horner form with respect to the scaled + variable ``poly_variable(x, i)``. + If no variable ``x`` is provided, it will default to the one given at + initialization time. + """ + if i < 0: + i += len(self.nodes) - 1 + + if not 0 <= i < len(self.nodes) - 1: + raise ValueError(f"Interval index {i} is out of bounds!") + + if x is None: + x = self.evaluate_at + + # Compute polynomial in Horner form for the scaled variable + t = sp.Dummy("t") + poly = self._poly(t, i).expand().as_poly(wrt=t, domain=sp.RR) + + # Rewrite in Horner form + # NB any coefficient containing functions must be rewritten for some reason + subs = {} + reverse_subs = {} + for s in poly.args[2:]: + if not s.is_Symbol: + wild = sp.Dummy() + subs[s] = wild + reverse_subs[wild] = s + poly = sp.horner(poly.subs(subs)).subs(reverse_subs) + + # Replace scaled variable with its value, + # without changing the expression form + t_value = self._poly_variable(x, i) + with evaluate(False): + return poly.subs(t, t_value) + + def poly_variable(self, x: Union[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. + """ + if not 0 <= i < len(self.nodes) - 1: + raise ValueError(f"Interval index {i} is out of bounds!") + return self._poly_variable(x, i) + + @abstractmethod + def _poly_variable( + self, x: Union[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: + """ + Return the symbolic expression for the spline restricted to the `i`-th + interval as a polynomial in the scaled variable `t`. + """ + raise NotImplementedError() + + def segment_formula( + self, i: Integral, *, x: Union[Real, sp.Basic] = None + ) -> sp.Basic: + """ + Return the formula for the actual value of the spline expression + on the ``(nodes[i], nodes[i+1])`` interval. + Unless logarithmic parametrization is used, + this is equal to the interpolating polynomial. + """ + if x is None: + x = self.evaluate_at + poly = self.poly(i, x=x) + if self.logarithmic_parametrization: + return sp.exp(poly) + return poly + + def y_scaled(self, i: Integral): + """ + Return the values which should be interpolated by a polynomial. + Unless logarithmic parametrization is used, + they are equal to the values given at initialization time. + """ + if self.logarithmic_parametrization: + return sp.log(self.values_at_nodes[i]) + return self.values_at_nodes[i] + + @property + def extrapolation_formulas( + self, + ) -> Tuple[Union[None, sp.Basic], Union[None, sp.Basic]]: + """ + Returns the extrapolation formulas on the left and right side + of the interval ``(nodes[0], nodes[-1])``. + A value of ``None`` means that no extrapolation is required. + """ + return self._extrapolation_formulas(self.evaluate_at) + + def _extrapolation_formulas( + self, + x: Union[Real, sp.Basic], + extrapolate: Optional[NormalizedBC] = None, + ) -> Tuple[Union[None, sp.Expr], Union[None, sp.Expr]]: + if extrapolate is None: + extr_left, extr_right = self.extrapolate + else: + extr_left, extr_right = extrapolate + + if extr_left == "constant": + extr_left = self.values_at_nodes[0] + elif extr_left == "linear": + dx = x - self.nodes[0] + dydx = self.derivative(self.nodes[0], extrapolate=None) + extr_left = self.values_at_nodes[0] + dydx * dx + elif extr_left == "polynomial": + extr_left = None + else: + assert extr_left is None + + if extr_right == "constant": + extr_right = self.values_at_nodes[-1] + elif extr_right == "linear": + dx = x - self.nodes[-1] + dydx = self.derivative(self.nodes[-1], extrapolate=None) + extr_right = self.values_at_nodes[-1] + dydx * dx + elif extr_right == "polynomial": + extr_right = None + else: + assert extr_right is None + + return extr_left, extr_right + + @property + def formula(self) -> sp.Piecewise: + """ + Compute a symbolic piecewise formula for the spline. + """ + return self._formula(sbml_syms=False, sbml_ops=False) + + @property + def sbml_formula(self) -> sp.Piecewise: + """ + Compute a symbolic piecewise formula for the spline, + using SBML symbol naming + (the AMICI time symbol will be replaced with its SBML counterpart). + """ + return self._formula(sbml_syms=True, sbml_ops=False) + + @property + def mathml_formula(self) -> sp.Piecewise: + """ + Compute a symbolic piecewise formula for the spline for use inside + a SBML assignment rule: SBML symbol naming will be used + and operations not supported by SBML MathML will be avoided. + """ + return self._formula(sbml_syms=True, sbml_ops=True) + + def _formula( + self, + *, + x: Union[Real, sp.Basic] = None, + sbml_syms: bool = False, + sbml_ops: bool = False, + cache: bool = True, + **kwargs, + ) -> sp.Piecewise: + # Cache formulas in the case they are reused + if cache: + if "extrapolate" in kwargs.keys(): + key = (x, sbml_syms, sbml_ops, kwargs["extrapolate"]) + else: + key = (x, sbml_syms, sbml_ops) + if key in self._formula_cache.keys(): + return self._formula_cache[key] + + if x is None: + x = self.evaluate_at + if "extrapolate" in kwargs.keys(): + _bc, extrapolate = self._normalize_extrapolate( + self.bc, kwargs["extrapolate"] + ) + assert self.bc == _bc + else: + extrapolate = self.extrapolate + + pieces = [] + + if extrapolate[0] == "periodic" or extrapolate[1] == "periodic": + if sbml_ops: + # NB mod is not supported in SBML + x = symbol_with_assumptions( + self.sbml_id.name + "_x_in_fundamental_period" + ) + # NB we will do the parameter substitution in SBML + # because the formula for x will be a piecewise + # and sympy handles Piecewises inside other Piecewises + # really badly. + else: + x = self._to_base_interval(x) + extr_left, extr_right = None, None + else: + extr_left, extr_right = self._extrapolation_formulas( + x, extrapolate + ) + + if extr_left is not None: + pieces.append((extr_left, x < self.nodes[0])) + + for i in range(len(self.nodes) - 2): + pieces.append( + (self.segment_formula(i, x=x), x < self.nodes[i + 1]) + ) + + if extr_right is not None: + pieces.append((self.segment_formula(-1, x=x), x < self.nodes[-1])) + pieces.append((extr_right, sp.sympify(True))) + else: + pieces.append((self.segment_formula(-1, x=x), sp.sympify(True))) + + with evaluate(False): + if sbml_syms: + pieces = [ + ( + p.subs(amici_time_symbol, sbml_time_symbol), + c.subs(amici_time_symbol, sbml_time_symbol), + ) + for (p, c) in pieces + ] + formula = sp.Piecewise(*pieces) + + if cache: + self._formula_cache[key] = formula + return formula + + @property + def period(self) -> Union[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]]: + """For periodic splines, maps the real point `x` to the reference + period.""" + if self.bc != ("periodic", "periodic"): + raise ValueError( + "_to_base_interval makes no sense with non-periodic bc" + ) + + xA = self.nodes[0] + xB = self.nodes[-1] + T = self.period + z = xA + sp.Mod(x - xA, T) + assert not z.is_Number or xA <= z < xB + + if with_interval_number: + k = sp.floor((x - xA) / T) + assert isinstance(k, sp.core.numbers.Integer) + assert x == z + k * T + return k, z + return z + + def evaluate(self, x: Union[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: + """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: + """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) + + def squared_L2_norm_of_curvature(self) -> sp.Basic: + """ + Return the squared L2 norm of the spline's curvature + (commonly used as a regularizer). + This is always computed in the spline native scale + (i.e., in log-scale for positivity enforcing splines). + """ + x = sp.Dummy("x") + integral = sp.sympify(0) + for i in range(len(self.nodes) - 1): + formula = self.poly(i, x=x).diff(x, 2) ** 2 + integral += sp.integrate( + formula, (x, self.nodes[i], self.nodes[i + 1]) + ) + return sp.simplify(integral) + + def integrate( + self, x0: Union[Real, sp.Basic], x1: Union[Real, sp.Basic] + ) -> sp.Basic: + """Integrate the spline between the points `x0` and `x1`.""" + x = sp.Dummy("x") + x0, x1 = sp.sympify((x0, x1)) + + if x0 > x1: + raise ValueError("x0 > x1") + + if x0 == x1: + return sp.sympify(0) + + if self.extrapolate != ("periodic", "periodic"): + return self._formula(x=x, cache=False).integrate((x, x0, x1)) + + formula = self._formula(x=x, cache=False, extrapolate=None) + + xA, xB = self.nodes[0], self.nodes[-1] + k0, z0 = self._to_base_interval(x0, with_interval_number=True) + k1, z1 = self._to_base_interval(x1, with_interval_number=True) + + assert k0 <= k1 + + if k0 == k1: + return formula.integrate((x, z0, z1)) + + if k0 + 1 == k1: + return formula.integrate((x, z0, xB)) + formula.integrate( + (x, xA, z1) + ) + + return ( + formula.integrate((x, z0, xB)) + + (k1 - k0 - 1) * formula.integrate((x, xA, xB)) + + formula.integrate((x, xA, z1)) + ) + + @property + def amici_annotation(self) -> str: + """An SBML annotation describing the spline.""" + annotation = f'" + for gc in grandchildren: + annotation += gc + annotation += f"" + + annotation += "" + + # Check XML and prettify + return pretty_xml(annotation) + + def _annotation_attributes(self) -> Dict[str, Any]: + attributes = {"spline_method": self.method} + + if self.bc[0] == self.bc[1]: + if self.bc[0] is not None: + attributes["spline_bc"] = self.bc[0] + else: + bc1, bc2 = self.bc + bc1 = "no_bc" if bc1 is None else bc1 + bc2 = "no_bc" if bc2 is None else bc2 + attributes["spline_bc"] = f"({bc1}, {bc2})" + + if self.extrapolate[0] == self.extrapolate[1]: + extr = None if self.extrapolate is None else self.extrapolate[0] + else: + extr1, extr2 = self.extrapolate + extr1 = "no_extrapolation" if extr1 is None else extr1 + extr2 = "no_extrapolation" if extr2 is None else extr2 + extr = f"({extr1}, {extr2})" + if extr is not None: + attributes["spline_extrapolate"] = extr + + if self.logarithmic_parametrization: + attributes["spline_logarithmic_parametrization"] = True + + return attributes + + def _annotation_children(self) -> Dict[str, Union[str, List[str]]]: + children = {} + + with evaluate(False): + x = self.evaluate_at.subs(amici_time_symbol, sbml_time_symbol) + children["spline_evaluation_point"] = sbml_mathml(x) + + if isinstance(self.nodes, UniformGrid): + children["spline_uniform_grid"] = [ + sbml_mathml(self.nodes.start), + sbml_mathml(self.nodes.stop), + sbml_mathml(self.nodes.step), + ] + else: + for x in self.nodes: + assert amici_time_symbol not in x.free_symbols + children["spline_grid"] = [sbml_mathml(x) for x in self.nodes] + + children["spline_values"] = [ + sbml_mathml(y) for y in self.values_at_nodes + ] + + return children + + 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, + ) -> None: + """ + Function to add the spline to an SBML model using an assignment rule + with AMICI-specific annotations. + + :param model: + A :py:class:`libsbml.Model` to which the spline is to be added. + + :param auto_add: + Automatically add missing parameters to the SBML model + (defaults to `False`). + Only used for expressions consisting in a single symbol. + If equal to `'spline'`, + only the parameter representing the spline will be added. + + :param x_nominal: + Nominal values used when auto-adding parameters for `nodes`. + + :param y_nominal: + Nominal values used when auto-adding parameters for `values_at_nodes`. + + :param x_units: + Units used when auto-adding parameters for `nodes`. + + :param y_units: + Units used when auto-adding parameters for `values_at_nodes`. + + :param y_constant: + Constant flags used when auto-adding parameters for `values_at_nodes`. + """ + # Convert time from AMICI to SBML naming + with evaluate(False): + x = self.evaluate_at.subs(amici_time_symbol, sbml_time_symbol) + + # Try to auto-determine units + if x_units is None: + x_units = get_sbml_units(model, x) + for _x in self.nodes: + if x_units is not None: + break + x_units = get_sbml_units(model, _x) + if y_units is None: + for _y in self.values_at_nodes: + y_units = get_sbml_units(model, _y) + if y_units is not None: + break + + # Autoadd parameters + if auto_add is True or auto_add == "spline": + if not model.getParameter( + str(self.sbml_id) + ) and not model.getSpecies(str(self.sbml_id)): + add_parameter( + model, self.sbml_id, constant=False, units=y_units + ) + + if auto_add is True: + if isinstance(x_nominal, collections.abc.Sequence): + if len(x_nominal) != len(self.nodes): + raise ValueError( + "If x_nominal is a list, then it must have " + "the same length as the spline grid!" + ) + for i in range(len(x_nominal) - 1): + if x[i] >= x[i + 1]: + raise ValueError( + "x_nominal must be strictly increasing!" + ) + elif x_nominal is None: + x_nominal = len(self.nodes) * [None] + else: + # 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): + if _x.is_Symbol and not model.getParameter(_x.name): + add_parameter( + model, _x.name, value=_val, units=x_units + ) + + if isinstance(y_nominal, collections.abc.Sequence): + if len(y_nominal) != len(self.values_at_nodes): + raise ValueError( + "If y_nominal is a list, then it must have " + "the same length as the spline values!" + ) + else: + y_nominal = len(self.values_at_nodes) * [y_nominal] + if isinstance(y_constant, collections.abc.Sequence): + if len(y_constant) != len(self.values_at_nodes): + raise ValueError( + "If y_constant is a list, then it must have " + "the same length as the spline values!" + ) + else: + y_constant = len(self.values_at_nodes) * [y_constant] + for _y, _val, _const in zip( + self.values_at_nodes, y_nominal, y_constant + ): + if _y.is_Symbol and not model.getParameter(_y.name): + add_parameter( + model, + _y.name, + value=_val, + constant=_const, + units=y_units, + ) + + elif auto_add is not False: + raise ValueError(f"Invalid value {auto_add} for auto_add!") + + # Create assignment rule for spline + rule = add_assignment_rule(model, self.sbml_id, self.mathml_formula) + + # Add annotation specifying spline method + retcode = rule.setAnnotation(self.amici_annotation) + if retcode != libsbml.LIBSBML_OPERATION_SUCCESS: + raise SbmlAnnotationError("Could not set SBML annotation!") + + # Create additional assignment rule for periodic extrapolation + # TODO is supported in SBML Level 3 (but not in Level 2). + # Consider simplifying the formulas using it + # (after checking it actually works as expected), + # checking what level the input SBML model is. + if any(extr == "periodic" for extr in self.extrapolate): + parameter_id = self.sbml_id.name + "_x_in_fundamental_period" + T = self.nodes[-1] - self.nodes[0] + x0 = self.nodes[0] + s = 2 * sp.pi * ((x - x0) / T - sp.sympify(1) / 4) + k = sp.Piecewise((3, sp.cos(s) < 0), (1, True)) + formula = x0 + T * (sp.atan(sp.tan(s)) / (2 * sp.pi) + k / 4) + assert amici_time_symbol not in formula.free_symbols + par = add_parameter( + model, parameter_id, constant=False, units=x_units + ) + retcode = par.setAnnotation( + f'' + ) + if retcode != libsbml.LIBSBML_OPERATION_SUCCESS: + raise SbmlAnnotationError("Could not set SBML annotation!") + add_assignment_rule(model, parameter_id, formula) + + def _replace_in_all_expressions( + self, old: sp.Symbol, new: sp.Symbol + ) -> None: + if self.sbml_id == old: + self._sbml_id = new + self._x = self.evaluate_at.subs(old, new) + if not isinstance(self.nodes, UniformGrid): + self._nodes = [x.subs(old, new) for x in self.nodes] + self._values_at_nodes = [ + y.subs(old, new) for y in self.values_at_nodes + ] + + @staticmethod + def is_spline(rule: libsbml.AssignmentRule) -> bool: + """ + Determine if an SBML assignment rule (given as a + :py:class:`libsbml.AssignmentRule` object) is an AMICI-annotated + spline formula. + """ + return AbstractSpline.get_annotation(rule) is not None + + @staticmethod + def get_annotation( + rule: libsbml.AssignmentRule, + ) -> Union[ET.Element, None]: + """ + Extract AMICI spline annotation from an SBML assignment rule + (given as a :py:class:`libsbml.AssignmentRule` object). + Return ``None`` if any such annotation could not be found. + """ + if not isinstance(rule, libsbml.AssignmentRule): + raise TypeError("Rule must be an AssignmentRule!") + if rule.isSetAnnotation(): + annotation = ET.fromstring(rule.getAnnotationString()) + for child in annotation: + if child.tag == f"{{{annotation_namespace}}}spline": + return child + return None + + @staticmethod + def from_annotation( + sbml_id: sp.Symbol, + annotation: ET.Element, + *, + locals_: Dict[str, Any], + ) -> AbstractSpline: + """Create a spline object from a SBML annotation. + + This function extracts annotation and children from the XML annotation + and gives them to the ``_fromAnnotation`` function for parsing. + Subclass behaviour should be implemented by extending + ``_fromAnnotation``. + However, the mapping between method strings and subclasses + must be hard-coded into this function here (at the moment). + """ + if annotation.tag != f"{{{annotation_namespace}}}spline": + raise ValueError( + "The given annotation is not an AMICI spline annotation!" + ) + + attributes = {} + for key, value in annotation.items(): + if not key.startswith(f"{{{annotation_namespace}}}"): + raise ValueError( + f"Unexpected attribute {key} inside spline annotation!" + ) + key = key[len(annotation_namespace) + 2 :] + if value == "true": + value = True + elif value == "false": + value = False + attributes[key] = value + + children = {} + for child in annotation: + if not child.tag.startswith(f"{{{annotation_namespace}}}"): + raise ValueError( + f"Unexpected node {child.tag} inside spline annotation!" + ) + key = child.tag[len(annotation_namespace) + 2 :] + value = [ + mathml2sympy( + ET.tostring(gc).decode(), + evaluate=False, + locals=locals_, + expression_type="Rule", + ) + for gc in child + ] + children[key] = value + + if attributes["spline_method"] == "cubic_hermite": + cls = CubicHermiteSpline + else: + raise ValueError( + f"Unknown spline method {attributes['spline_method']}!" + ) + + del attributes["spline_method"] + kwargs = cls._from_annotation(attributes, children) + + if attributes: + raise ValueError( + "Unprocessed attributes in spline annotation!\n" + + str(attributes) + ) + + if children: + raise ValueError( + "Unprocessed children in spline annotation!\n" + str(children) + ) + + return cls(sbml_id, **kwargs) + + @classmethod + def _from_annotation( + cls, + 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 + to the spline object ``__init__`` function. + """ + kwargs = {} + + bc = attributes.pop("spline_bc", None) + if isinstance(bc, str) and bc.startswith("("): + if not bc.endswith(")"): + raise ValueError(f"Ill-formatted bc {bc}!") + bc_cmps = bc[1:-1].split(",") + if len(bc_cmps) != 2: + raise ValueError(f"Ill-formatted bc {bc}!") + bc = (bc_cmps[0].strip(), bc_cmps[1].strip()) + kwargs["bc"] = bc + + extr = attributes.pop("spline_extrapolate", None) + if isinstance(extr, str) and extr.startswith("("): + if not extr.endswith(")"): + raise ValueError(f"Ill-formatted extrapolation {extr}!") + extr_cmps = extr[1:-1].split(",") + if len(extr_cmps) != 2: + raise ValueError(f"Ill-formatted extrapolation {extr}!") + extr = (extr_cmps[0].strip(), extr_cmps[1].strip()) + kwargs["extrapolate"] = extr + + kwargs["logarithmic_parametrization"] = attributes.pop( + "spline_logarithmic_parametrization", False + ) + + if "spline_evaluation_point" not in children.keys(): + raise ValueError( + "Required spline annotation 'spline_evaluation_point' missing!" + ) + x = children.pop("spline_evaluation_point") + if len(x) != 1: + raise ValueError( + "Ill-formatted spline annotation 'spline_evaluation_point' " + "(more than one children is present)!" + ) + kwargs["evaluate_at"] = x[0] + + if "spline_uniform_grid" in children: + start, stop, step = children.pop("spline_uniform_grid") + kwargs["nodes"] = UniformGrid(start, stop, step) + elif "spline_grid" in children: + kwargs["nodes"] = children.pop("spline_grid") + else: + raise ValueError( + "Spline annotation requires either " + "'spline_grid' or 'spline_uniform_grid' to be specified!" + ) + + if "spline_values" not in children: + raise ValueError( + "Required spline annotation 'spline_values' missing!" + ) + kwargs["values_at_nodes"] = children.pop("spline_values") + + return kwargs + + 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]: + parameters = set() + for y in self.values_at_nodes: + parameters.update(y.free_symbols) + return parameters + + def ode_model_symbol( + self, importer: sbml_import.SbmlImporter + ) -> sp.Function: + """ + Returns the `sympy` object to be used by + :py:class:`amici.de_export.ODEModel`. + This expression can be differentiated and easily mapped to the C++ + code. + """ + parameters = list(self.parameters(importer)) + + class AmiciSpline(sp.Function): + # AmiciSpline(splineId, x, *parameters) + nargs = (len(parameters) + 2,) + + @classmethod + def eval(cls, *args): + return None # means leave unevaluated + + def fdiff(self, argindex=1): + if argindex == 1: + # Derivative with respect to the spline SBML ID + # Since the SBML ID is not a real function parameter + # (more like a subscript), the derivative will be zero + return sp.Integer(0) + + if argindex == 2: + + class AmiciSplineDerivative(sp.Function): + # Spline derivative + # AmiciSplineDerivative(splineId, x, *parameters) + nargs = (len(parameters) + 2,) + + @classmethod + def eval(cls, *args): + return None # means leave unevaluated + + def fdiff(self, argindex=1): + return NotImplementedError( + "Second order derivatives for spline " + "are not implemented yet." + ) + + def _eval_is_real(self): + return True + + return AmiciSplineDerivative(*self.args) + + pindex = argindex - 3 + assert 0 <= pindex < len(parameters) + + class AmiciSplineSensitivity(sp.Function): + # Derivative with respect to a parameter paramId + # AmiciSplineSensitivity(splineId, x, paramId, *parameters) + nargs = (len(parameters) + 3,) + + @classmethod + def eval(cls, *args): + return None # means leave unevaluated + + def fdiff(self, argindex=1): + return NotImplementedError( + "Second order derivatives for spline " + "are not implemented yet." + ) + + def _eval_is_real(self): + return True + + return AmiciSplineSensitivity( + self.args[0], + self.args[1], + parameters[pindex], + *self.args[2:], + ) + + def _eval_is_real(self): + return True + + return AmiciSpline(self.sbml_id, self.evaluate_at, *parameters) + + def plot( + self, + parameters: Optional[Dict] = None, + *, + xlim: Optional[Tuple[float, float]] = None, + npoints: int = 100, + xlabel: Optional[str] = None, + ylabel: Union[str, None] = "spline value", + ax=None, + ): + "Plots the spline, highlighting the nodes positions." + if parameters is None: + parameters = {} + if ax is None: + from matplotlib import pyplot as plt + + fig, ax = plt.subplots() + if xlim is None: + nodes = np.asarray(self.nodes) + xlim = (float(nodes[0]), float(nodes[-1])) + nodes = np.linspace(*xlim, npoints) + ax.plot( + nodes, [float(self.evaluate(x).subs(parameters)) for x in nodes] + ) + ax.plot( + self.nodes, + [float(y.subs(parameters)) for y in self.values_at_nodes], + "o", + ) + if xlabel is not None: + ax.set_xlabel(xlabel) + if ylabel is not None: + ax.set_ylabel(ylabel) + return ax + + +def spline_user_functions( + 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. + """ + spline_ids = [spline.sbml_id.name for spline in splines] + return { + "AmiciSpline": [ + ( + lambda *args: True, + lambda spline_id, x, *p: f"spl_{spline_ids.index(spline_id)}", + ) + ], + "AmiciSplineDerivative": [ + ( + lambda *args: True, + lambda spline_id, x, *p: f"dspl_{spline_ids.index(spline_id)}", + ) + ], + "AmiciSplineSensitivity": [ + ( + lambda *args: True, + lambda spline_id, x, param_id, *p: f"sspl_{spline_ids.index(spline_id)}_{p_index[param_id]}", + ) + ], + } + + +class CubicHermiteSpline(AbstractSpline): + def __init__( + self, + sbml_id: Union[str, sp.Symbol], + nodes: Sequence, + values_at_nodes: Sequence, + derivatives_at_nodes: Sequence = None, + *, + evaluate_at: Optional[Union[str, sp.Basic]] = None, + bc: BClike = "auto", + extrapolate: BClike = None, + logarithmic_parametrization: bool = False, + ): + """ + Constructor for `CubicHermiteSpline` objects. + + :param sbml_id: + The SBML ID of the parameter associated to the spline + as a string or a SymPy symbol. + + :param x: + The point at which the spline is evaluated. + It will be sympified. + + :param nodes: + The points at which the spline values are known. + Currently, they must be numeric or only depend on constant parameters. + These points should be strictly increasing. + This argument will be sympified. + + :param values_at_nodes: + The spline values at each of the points in `nodes`. + They must not depend on model species. + This argument will be sympified. + + :param derivatives_at_nodes: + The spline derivatives at each of the points in `nodes`. + They must not depend on model species. + This argument will be sympified. + If not specified, it will be computed by finite differences. + + :param evaluate_at: + The point at which the spline is evaluated. + It will be sympified. + Defaults to model time. + + :param bc: + Applied boundary conditions + (see `AbstractSpline` documentation). + If `'auto'` (the default), the boundary conditions will be + automatically set depending on the extrapolation methods. + + :param extrapolate: + Extrapolation method (see `AbstractSpline` documentation). + + :param logarithmic_parametrization: + Whether interpolation should be done in log-scale. + """ + + if not isinstance(nodes, UniformGrid): + nodes = np.asarray([sympify_noeval(x) for x in nodes]) + values_at_nodes = np.asarray( + [sympify_noeval(y) for y in values_at_nodes] + ) + + if len(nodes) != len(values_at_nodes): + # NB this would be checked in AbstractSpline.__init__() + # however, we check it now so that an informative message + # can be printed (otherwise finite difference computation fails) + raise ValueError( + "Length of nodes and values_at_nodes must be the same " + f"(instead len(nodes) = {len(nodes)} and len(values_at_nodes) = {len(values_at_nodes)})!" + ) + + bc, extrapolate = self._normalize_bc_and_extrapolate(bc, extrapolate) + if ( + bc[0] == "zeroderivative+natural" + or bc[1] == "zeroderivative+natural" + ): + raise ValueError( + "zeroderivative+natural bc not supported by " + "CubicHermiteSplines!" + ) + + if derivatives_at_nodes is None: + derivatives_at_nodes = _finite_differences( + nodes, values_at_nodes, bc + ) + self._derivatives_by_fd = True + else: + derivatives_at_nodes = np.asarray( + [sympify_noeval(d) for d in derivatives_at_nodes] + ) + self._derivatives_by_fd = False + + if len(nodes) != len(derivatives_at_nodes): + raise ValueError( + "Length of nodes and derivatives_at_nodes must be the same " + f"(instead len(nodes) = {len(nodes)} and len(derivatives_at_nodes) = {len(derivatives_at_nodes)})!" + ) + + if bc == ("periodic", "periodic") and ( + values_at_nodes[0] != values_at_nodes[-1] + or derivatives_at_nodes[0] != derivatives_at_nodes[-1] + ): + raise ValueError( + "bc=periodic but given values_at_nodes and derivatives_at_nodes do not satisfy " + "periodic boundary conditions!" + ) + + super().__init__( + sbml_id, + nodes, + values_at_nodes, + evaluate_at=evaluate_at, + bc=bc, + extrapolate=extrapolate, + logarithmic_parametrization=logarithmic_parametrization, + ) + + self._derivatives_at_nodes = derivatives_at_nodes + + @property + def derivatives_at_nodes(self) -> np.ndarray: + """The spline derivatives at each of the points in `nodes`.""" + return self._derivatives_at_nodes + + @property + def smoothness(self) -> int: + """ + Smoothness of this spline (equal to 1 for cubic Hermite splines + since they are continuous up to the first derivative). + """ + return 1 + + @property + def method(self) -> str: + """Spline method (cubic Hermite spline)""" + return "cubic_hermite" + + @property + def derivatives_by_fd(self) -> bool: + return self._derivatives_by_fd + + def check_if_valid(self, importer: sbml_import.SbmlImporter) -> None: + """ + Check if the spline described by this object can be correctly + be implemented by AMICI. E.g., check whether the formulas + for spline grid points, values, ... contain species symbols. + """ + # TODO this is very much a draft + from .de_export import SymbolId + + species: List[sp.Symbol] = list(importer.symbols[SymbolId.SPECIES]) + for d in self.derivatives_at_nodes: + if len(d.free_symbols.intersection(species)) != 0: + raise ValueError( + "derivatives_at_nodes should not depend on model species" + ) + + super().check_if_valid(importer) + + def d_scaled(self, i: Integral) -> sp.Expr: + """ + Return the derivative of the polynomial interpolant at the `i`-th + point. Unless logarithmic parametrization is used, it is equal to the + derivative of the spline expression. + """ + if self.logarithmic_parametrization: + 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: + 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: + """ + Return the symbolic expression for the spline restricted to the `i`-th + interval as polynomial in the scaled variable `t`. + """ + assert 0 <= i < len(self.nodes) - 1 + + dx = self.nodes[i + 1] - self.nodes[i] + + h00 = 2 * t**3 - 3 * t**2 + 1 + h10 = t**3 - 2 * t**2 + t + h01 = -2 * t**3 + 3 * t**2 + h11 = t**3 - t**2 + + y0 = self.y_scaled(i) + y1 = self.y_scaled(i + 1) + dy0 = self.d_scaled(i) + dy1 = self.d_scaled(i + 1) + + with evaluate(False): + return h00 * y0 + h10 * dx * dy0 + h01 * y1 + h11 * dx * dy1 + + def _annotation_children(self) -> Dict[str, Union[str, List[str]]]: + children = super()._annotation_children() + if not self._derivatives_by_fd: + children["spline_derivatives"] = [ + sbml_mathml(d) for d in self.derivatives_at_nodes + ] + return children + + def _parameters(self) -> Set[sp.Symbol]: + parameters = super()._parameters() + for d in self.derivatives_at_nodes: + parameters.update(d.free_symbols) + return parameters + + def _replace_in_all_expressions( + self, old: sp.Symbol, new: sp.Symbol + ) -> None: + super()._replace_in_all_expressions(old, new) + self._derivatives_at_nodes = [ + d.subs(old, new) for d in self.derivatives_at_nodes + ] + + @classmethod + def _from_annotation(cls, attributes, children) -> Dict[str, Any]: + kwargs = super()._from_annotation(attributes, children) + + if "spline_derivatives" in children.keys(): + kwargs["derivatives_at_nodes"] = children.pop("spline_derivatives") + + return kwargs + + def __str__(self) -> str: + s = ( + "HermiteCubicSpline " + + f"on ({self.nodes[0]}, {self.nodes[-1]}) with {len(self.nodes)} points" + ) + cmps = [] + if self.bc != (None, None): + if self.bc == ("periodic", "periodic"): + cmps.append("periodic") + else: + cmps.append(f"bc = {self.bc}") + if self.derivatives_by_fd: + cmps.append("finite differences") + if self.extrapolate != (None, None): + cmps.append(f"extrapolate = {self.extrapolate}") + if not cmps: + return s + return s + " [" + ", ".join(cmps) + "]" + + +def _finite_differences( + xx: np.ndarray, yy: np.ndarray, bc: NormalizedBC +) -> np.ndarray: + dd = [] + + if bc[0] == "periodic": + fd = _centered_fd(yy[-2], yy[0], yy[1], xx[-1] - xx[-2], xx[1] - xx[0]) + elif bc[0] == "zeroderivative": + fd = sp.Integer(0) + elif bc[0] == "natural": + if len(xx) < 3: + raise ValueError( + "At least 3 nodes are needed " + "for computing finite differences with natural bc!" + ) + fd = _natural_fd(yy[0], xx[1] - xx[0], yy[1], xx[2] - xx[1], yy[2]) + else: + fd = _onesided_fd(yy[0], yy[1], xx[1] - xx[0]) + dd.append(fd) + + for i in range(1, len(xx) - 1): + dd.append( + _centered_fd( + yy[i - 1], + yy[i], + yy[i + 1], + xx[i] - xx[i - 1], + xx[i + 1] - xx[i], + ) + ) + + if bc[1] == "periodic": + fd = dd[0] + elif bc[1] == "zeroderivative": + fd = sp.Integer(0) + elif bc[1] == "natural": + if len(xx) < 3: + raise ValueError( + "At least 3 nodes are needed " + "for computing finite differences with natural bc!" + ) + fd = _natural_fd( + yy[-1], xx[-2] - xx[-1], yy[-2], xx[-3] - xx[-2], yy[-3] + ) + else: + fd = _onesided_fd(yy[-2], yy[-1], xx[-1] - xx[-2]) + dd.append(fd) + + return np.asarray(dd) + + +def _onesided_fd(y0: sp.Expr, y1: sp.Expr, h: sp.Expr) -> sp.Basic: + return sp.Mul(1 / h, y1 - y0, evaluate=False) + + +def _centered_fd( + ym1: sp.Expr, + y0: sp.Expr, + yp1: sp.Expr, + hm: sp.Expr, + hp: sp.Expr, +) -> sp.Expr: + if hm == hp: + return sp.Mul(1 / (2 * hm), yp1 - ym1, evaluate=False) + else: + return ((yp1 - y0) / hp + (y0 - ym1) / hm) / 2 + + +def _natural_fd( + y0: sp.Expr, + dx1: sp.Expr, + y1: sp.Expr, + dx2: sp.Expr, + y2: sp.Expr, +) -> sp.Expr: + if dx1 == dx2: + den = 4 * dx1 + with evaluate(False): + return (6 * y1 - 5 * y0 - y2) / den + else: + with evaluate(False): + return ((y1 - y2) / dx2 - 5 * (y0 - y1) / dx1) / 4 + # Another formula, which depends on + # y0, dx1 = x1 - x0, y1 and dy1 (derivative at x1) + # (-dx1*dy1 - 3*y0 + 3*y1)/(2*dx1) diff --git a/deps/AMICI/python/sdist/amici/swig.py b/deps/AMICI/python/sdist/amici/swig.py deleted file mode 120000 index 638f8c3b3..000000000 --- a/deps/AMICI/python/sdist/amici/swig.py +++ /dev/null @@ -1 +0,0 @@ -../../amici/swig.py \ No newline at end of file diff --git a/deps/AMICI/python/sdist/amici/swig.py b/deps/AMICI/python/sdist/amici/swig.py new file mode 100644 index 000000000..ef7564638 --- /dev/null +++ b/deps/AMICI/python/sdist/amici/swig.py @@ -0,0 +1,124 @@ +"""Functions related to SWIG or SWIG-generated code""" +import ast +import contextlib +import re + + +class TypeHintFixer(ast.NodeTransformer): + """Replaces SWIG-generated C++ typehints by corresponding Python types""" + + mapping = { + "void": None, + "double": ast.Name("float"), + "int": ast.Name("int"), + "long": ast.Name("int"), + "ptrdiff_t": ast.Name("int"), + "size_t": ast.Name("int"), + "bool": ast.Name("bool"), + "std::unique_ptr< amici::Solver >": ast.Constant("Solver"), + "amici::InternalSensitivityMethod": ast.Constant( + "InternalSensitivityMethod" + ), + "amici::InterpolationType": ast.Constant("InterpolationType"), + "amici::LinearMultistepMethod": ast.Constant("LinearMultistepMethod"), + "amici::LinearSolver": ast.Constant("LinearSolver"), + "amici::Model *": ast.Constant("Model"), + "amici::Model const *": ast.Constant("Model"), + "amici::NewtonDampingFactorMode": ast.Constant( + "NewtonDampingFactorMode" + ), + "amici::NonlinearSolverIteration": ast.Constant( + "NonlinearSolverIteration" + ), + "amici::ObservableScaling": ast.Constant("ObservableScaling"), + "amici::ParameterScaling": ast.Constant("ParameterScaling"), + "amici::RDataReporting": ast.Constant("RDataReporting"), + "amici::SensitivityMethod": ast.Constant("SensitivityMethod"), + "amici::SensitivityOrder": ast.Constant("SensitivityOrder"), + "amici::Solver *": ast.Constant("Solver"), + "amici::SteadyStateSensitivityMode": ast.Constant( + "SteadyStateSensitivityMode" + ), + "amici::realtype": ast.Name("float"), + "DoubleVector": ast.Constant("Sequence[float]"), + "IntVector": ast.Name("Sequence[int]"), + "std::string": ast.Name("str"), + "std::string const &": ast.Name("str"), + "std::unique_ptr< amici::ExpData >": ast.Constant("ExpData"), + "std::unique_ptr< amici::ReturnData >": ast.Constant("ReturnData"), + "std::vector< amici::ParameterScaling," + "std::allocator< amici::ParameterScaling > > const &": ast.Constant( + "ParameterScalingVector" + ), + } + + def visit_FunctionDef(self, node): + # Has a return type annotation? + if node.returns: + node.returns = self._new_annot(node.returns.value) + + # Has arguments? + if node.args.args: + for arg in node.args.args: + if not arg.annotation: + continue + if isinstance(arg.annotation, ast.Name): + # there is already proper annotation + continue + + arg.annotation = self._new_annot(arg.annotation.value) + return node + + def _new_annot(self, old_annot: str): + with contextlib.suppress(KeyError): + return self.mapping[old_annot] + + # std::vector size type + if re.match(r"std::vector< .* >::(?:size|difference)_type", old_annot): + return ast.Name("int") + + # std::vector value type + if ( + value_type := re.sub( + r"std::vector< (.*) >::value_type(?: const &)?", + r"\1", + old_annot, + ) + ) in self.mapping: + return self.mapping[value_type] + + # std::vector + if ( + value_type := re.sub( + r"std::vector< (.*),std::allocator< \1 > >(?: const &)?", + r"\1", + old_annot, + ) + ) 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}']") + if isinstance(value_type_annot, ast.Name): + return ast.Name(f"Tuple[{value_type_annot.id}]") + + return ast.Constant(old_annot) + + +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: + source = f.read() + parsed_source = ast.parse(source) + + # Change AST + fixer = TypeHintFixer() + parsed_source = fixer.visit(parsed_source) + + # AST -> file + with open(outfilename, "w") as f: + f.write(ast.unparse(parsed_source)) diff --git a/deps/AMICI/python/sdist/amici/swig_wrappers.py b/deps/AMICI/python/sdist/amici/swig_wrappers.py new file mode 100644 index 000000000..f56f3bd5d --- /dev/null +++ b/deps/AMICI/python/sdist/amici/swig_wrappers.py @@ -0,0 +1,335 @@ +"""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 + +import amici +import amici.amici as amici_swig + +from . import numpy +from .logging import get_logger + +logger = get_logger(__name__, log_level=logging.DEBUG) + + +__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, +) -> "numpy.ReturnDataView": + """ + Convenience wrapper around :py:func:`amici.amici.runAmiciSimulation` + (generated by swig) + + :param model: + Model instance + + :param solver: + Solver instance, must be generated from + :py:meth:`amici.amici.Model.getSolver` + + :param edata: + ExpData instance (optional) + + :returns: + ReturnData object with simulation results + """ + if ( + model.ne > 0 + and solver.getSensitivityMethod() + == amici_swig.SensitivityMethod.adjoint + and solver.getSensitivityOrder() == amici_swig.SensitivityOrder.first + ): + 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." + ) + + with _capture_cstdout(): + 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"]: + """ + Convenience wrapper for loops of amici.runAmiciSimulation + + :param model: Model instance + :param solver: Solver instance, must be generated from Model.getSolver() + :param edata_list: list of ExpData instances + :param failfast: returns as soon as an integration failure is encountered + :param num_threads: number of threads to use (only used if compiled + with openmp) + + :returns: list of simulation results + """ + if ( + model.ne > 0 + and solver.getSensitivityMethod() + == amici_swig.SensitivityMethod.adjoint + and solver.getSensitivityOrder() == amici_swig.SensitivityOrder.first + ): + 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." + ) + + 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, + ) + for rdata in rdata_ptr_list: + _log_simulation(rdata) + if solver.getReturnDataReportingMode() == amici.RDataReporting.full: + _ids_and_names_to_rdata(rdata, model) + + return [numpy.ReturnDataView(r) for r in rdata_ptr_list] + + +def readSolverSettingsFromHDF5( + file: str, solver: AmiciSolver, location: Optional[str] = "solverSettings" +) -> None: + """ + Convenience wrapper for :py:func:`amici.readSolverSettingsFromHDF5` + + :param file: hdf5 filename + :param solver: Solver instance to which settings will be transferred + :param location: location of solver settings in hdf5 file + """ + amici_swig.readSolverSettingsFromHDF5(file, _get_ptr(solver), location) + + +def writeSolverSettingsToHDF5( + solver: AmiciSolver, + file: Union[str, object], + location: Optional[str] = "solverSettings", +) -> None: + """ + Convenience wrapper for :py:func:`amici.amici.writeSolverSettingsToHDF5` + + :param file: hdf5 filename, can also be an object created by + :py:func:`amici.amici.createOrOpenForWriting` + :param solver: Solver instance from which settings will be stored + :param location: location of solver settings in hdf5 file + """ + amici_swig.writeSolverSettingsToHDF5(_get_ptr(solver), file, location) + + +# Values are suffixes of `get[...]` and `set[...]` `amici.Model` methods. +# If either the getter or setter is not named with this pattern, then the value +# is a tuple where the first and second elements are the getter and setter +# methods, respectively. +model_instance_settings = [ + # `setParameter{List,Scale}` will clear initial state sensitivities, so + # `setParameter{List,Scale}` has to be called first. + "ParameterList", + "ParameterScale", # getter returns a SWIG object + "AddSigmaResiduals", + "AlwaysCheckFinite", + "FixedParameters", + "InitialStates", + ("getInitialStateSensitivities", "setUnscaledInitialStateSensitivities"), + "MinimumSigmaResiduals", + ("nMaxEvent", "setNMaxEvent"), + "Parameters", + "ReinitializationStateIdxs", + "ReinitializeFixedParameterInitialStates", + "StateIsNonNegative", + "SteadyStateComputationMode", + "SteadyStateSensitivityMode", + ("t0", "setT0"), + "Timepoints", +] + + +def get_model_settings( + model: AmiciModel, +) -> Dict[str, Any]: + """Get model settings that are set independently of the compiled model. + + :param model: The AMICI model instance. + + :returns: Keys are AMICI model attributes, values are attribute values. + """ + settings = {} + for setting in model_instance_settings: + getter = setting[0] if isinstance(setting, tuple) else f"get{setting}" + + if getter == "getInitialStates" and not model.hasCustomInitialStates(): + settings[setting] = [] + continue + if ( + getter == "getInitialStateSensitivities" + and not model.hasCustomInitialStateSensitivities() + ): + settings[setting] = [] + continue + + settings[setting] = getattr(model, getter)() + # TODO `amici.Model.getParameterScale` returns a SWIG object instead + # of a Python list/tuple. + if setting == "ParameterScale": + settings[setting] = tuple(settings[setting]) + return settings + + +def set_model_settings( + model: AmiciModel, + settings: Dict[str, Any], +) -> None: + """Set model settings. + + :param model: The AMICI model instance. + :param settings: Keys are callable attributes (setters) of an AMICI model, + values are provided to the setters. + """ + for setting, value in settings.items(): + setter = setting[1] if isinstance(setting, tuple) else f"set{setting}" + getattr(model, setter)(value) + + +def _log_simulation(rdata: amici_swig.ReturnData): + """Extension warnings to Python logging.""" + amici_severity_to_logging = { + amici_swig.LogSeverity_debug: logging.DEBUG, + amici_swig.LogSeverity_warning: logging.WARNING, + amici_swig.LogSeverity_error: logging.ERROR, + } + for msg in rdata.messages: + condition = f"[{rdata.id}]" if rdata.id else "" + logger.log( + amici_severity_to_logging[msg.severity], + f"{condition}[{msg.identifier}] {msg.message}", + ) + + +def _ids_and_names_to_rdata( + rdata: amici_swig.ReturnData, model: amici_swig.Model +): + """Copy entity IDs and names from a Model to ReturnData.""" + for entity_type in ( + "State", + "Observable", + "Expression", + "Parameter", + "FixedParameter", + ): + for name_or_id in ("Ids", "Names"): + names_or_ids = getattr(model, f"get{entity_type}{name_or_id}")() + setattr( + rdata, + f"{entity_type.lower()}_{name_or_id.lower()}", + names_or_ids, + ) + rdata.state_ids_solver = model.getStateIdsSolver() + rdata.state_names_solver = model.getStateNamesSolver() diff --git a/deps/AMICI/python/sdist/amici/testing.py b/deps/AMICI/python/sdist/amici/testing.py deleted file mode 120000 index 3c889afbf..000000000 --- a/deps/AMICI/python/sdist/amici/testing.py +++ /dev/null @@ -1 +0,0 @@ -../../amici/testing.py \ No newline at end of file diff --git a/deps/AMICI/python/sdist/amici/testing.py b/deps/AMICI/python/sdist/amici/testing.py new file mode 100644 index 000000000..cdee80b1f --- /dev/null +++ b/deps/AMICI/python/sdist/amici/testing.py @@ -0,0 +1,45 @@ +"""Test support functions""" +import os +import sys +from tempfile import TemporaryDirectory + +import pytest + +# Indicates whether we are currently running under valgrind +# see also https://stackoverflow.com/a/62364698 +ON_VALGRIND = any( + needle in haystack + for needle in ("valgrind", "vgpreload") + for haystack in ( + os.getenv("LD_PRELOAD", ""), + os.getenv("DYLD_INSERT_LIBRARIES", ""), + ) +) + +# Decorator to skip certain tests when we are under valgrind +# (those that are independent of the AMICI C++ parts, or that take too long, +# or that test performance) +skip_on_valgrind = pytest.mark.skipif( + ON_VALGRIND, reason="Takes too long or is meaningless under valgrind" +) + + +class TemporaryDirectoryWinSafe(TemporaryDirectory): + """TemporaryDirectory that will not raise if cleanup fails. + + If any extension was loaded from the temporary directory, cleanup would + otherwise fail on Windows with a ``PermissionError``. This class ignores + such failures. + """ + + def cleanup(self): + try: + super().cleanup() + except PermissionError as e: + if sys.platform not in {"win32", "cygwin"}: + raise e + except NotADirectoryError: + # Ignore exception on Windows for pyd files: + # NotADirectoryError: [WinError 267] The directory name is + # invalid: '....pyd' + pass diff --git a/deps/AMICI/python/sdist/pyproject.toml b/deps/AMICI/python/sdist/pyproject.toml index 31c8ed75a..011064fbd 100644 --- a/deps/AMICI/python/sdist/pyproject.toml +++ b/deps/AMICI/python/sdist/pyproject.toml @@ -2,19 +2,16 @@ requires = [ "setuptools>=40.6.3", "wheel", - # We pin numpy here to the lowest supported version to have - # ABI-compatibility with the numpy version in the runtime environment. - # There seems to be no easy way to require the numpy version from the - # runtime environment for the build requirement here. The only alternative - # would be pinning the setup.py numpy requirement to the same version as - # here, which we want to avoid. + # oldest-supported-numpy helps us to pin numpy here to the lowest supported + # version to have ABI-compatibility with the numpy version in the runtime + # environment. The undesirable alternative would be pinning the setup.py + # numpy requirement to the same version as here, which we want to avoid. # cf. discussion at https://github.com/numpy/numpy/issues/5888 - # See also: - # https://github.com/scipy/oldest-supported-numpy/blob/master/setup.cfg - # https://github.com/h5py/h5py/blob/master/setup.py - "numpy==1.14.5; python_version=='3.7'", - "numpy==1.17.5; python_version=='3.8'", - "numpy==1.19.3; python_version=='3.9'", - "numpy==1.21.4; python_version>='3.10'", + # (https://github.com/scipy/oldest-supported-numpy/) + "oldest-supported-numpy", + "cmake-build-extension==0.5.1", ] build-backend = "setuptools.build_meta" + +[tool.black] +line-length = 79 diff --git a/deps/AMICI/python/sdist/setup.cfg b/deps/AMICI/python/sdist/setup.cfg index c34c60774..f41957875 100644 --- a/deps/AMICI/python/sdist/setup.cfg +++ b/deps/AMICI/python/sdist/setup.cfg @@ -22,29 +22,40 @@ classifiers = Topic :: Scientific/Engineering :: Bio-Informatics [options] -packages = find: +packages = find_namespace: package_dir = amici = amici -python_requires = >=3.7 +python_requires = >=3.9 install_requires = + cmake-build-extension==0.5.1 sympy>=1.9 - numpy>=1.14.5, <1.22.0; python_version=='3.7' - numpy>=1.17.5, <1.22.0; python_version=='3.8' - numpy>=1.19.3, <1.22.0; python_version=='3.9' - numpy>=1.21.4, <1.22.0; python_version>='3.10' + 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 - pkgconfig + pandas>=2.0.2 wurlitzer toposort setuptools>=48 + mpmath include_package_data = True zip_safe = False [options.extras_require] -petab = petab>=0.1.17 +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 = @@ -63,5 +74,5 @@ amici = ; 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 + 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 2b97a5d7d..4d65634cc 100755 --- a/deps/AMICI/python/sdist/setup.py +++ b/deps/AMICI/python/sdist/setup.py @@ -6,143 +6,175 @@ This file expects to be run from within its directory. Non-python-package requirements: -- swig3.0 +- swig>=3.0 - Optional: hdf5 libraries and headers """ - import os import sys +from pathlib import Path + +from cmake_build_extension import CMakeExtension +from setuptools import setup # Add containing directory to path, as we need some modules from the AMICI # package already for installation sys.path.insert(0, os.path.dirname(__file__)) -import numpy as np -import setup_clibs # Must run from within containing directory -from setuptools import find_packages, setup, Extension - from amici.custom_commands import ( - AmiciInstall, AmiciBuildCLib, AmiciDevelop, - AmiciInstallLib, AmiciBuildExt, AmiciSDist) -from amici.setuptools import ( - get_blas_config, - get_hdf5_config, - add_coverage_flags_if_required, - add_debug_flags_if_required, - add_openmp_flags, + AmiciBuildCMakeExtension, + AmiciBuildPy, + AmiciDevelop, + AmiciInstall, + AmiciInstallLib, + AmiciSDist, ) -def main(): - # Extra compiler flags - cxx_flags = [] - amici_module_linker_flags = [] - define_macros = [] - add_openmp_flags(cxx_flags=cxx_flags, ldflags=amici_module_linker_flags) +def get_extensions(): + """Get required C(++) extensions for build_ext""" + # CMake prefix path for finding FindXXX.cmake to find SuiteSparse + # components + install_dir = (Path(__file__).parent / "amici").absolute() + prefix_path = install_dir + AmiciBuildCMakeExtension.extend_cmake_prefix_path(str(prefix_path)) - blaspkgcfg = get_blas_config() - amici_module_linker_flags.extend(blaspkgcfg['extra_link_args']) - amici_module_linker_flags.extend( - f'-l{lib}' for lib in blaspkgcfg['libraries']) - define_macros.extend(blaspkgcfg['define_macros']) - - extension_sources = [ - 'amici/amici_wrap.cxx', # swig interface + # Used by all extensions + global_cmake_configure_options = [ + "-DCMAKE_VERBOSE_MAKEFILE=ON", ] - h5pkgcfg = get_hdf5_config() - - if h5pkgcfg['found']: - # Manually add linker flags. The libraries passed to Extension will - # end up in front of the clibs in the linker line and not after, where - # they are required. - print("HDF5 library found. Building AMICI with HDF5 support.") - amici_module_linker_flags.extend( - [f'-l{lib}' for lib in - ['hdf5_hl_cpp', 'hdf5_hl', 'hdf5_cpp', 'hdf5']]) - define_macros.extend(h5pkgcfg['define_macros']) - else: - print("HDF5 library NOT found. Building AMICI WITHOUT HDF5 support.") - define_macros.append(('AMICI_SWIG_WITHOUT_HDF5', None)) - - add_coverage_flags_if_required( - cxx_flags, - amici_module_linker_flags, + # SuiteSparse Config + suitesparse_config = CMakeExtension( + name="SuiteSparse_config", + install_prefix="amici", + source_dir="amici/ThirdParty/SuiteSparse/SuiteSparse_config", + cmake_configure_options=[ + *global_cmake_configure_options, + "-DBLA_VENDOR=All", + "-DENABLE_CUDA=FALSE", + "-DNFORTRAN=TRUE", + ], ) - - add_debug_flags_if_required( - cxx_flags, - amici_module_linker_flags, + # SuiteSparse AMD + amd = CMakeExtension( + name="amd", + install_prefix="amici", + source_dir="amici/ThirdParty/SuiteSparse/AMD", + cmake_configure_options=[ + *global_cmake_configure_options, + "-DNFORTRAN=TRUE", + ], ) - - # compiler and linker flags for libamici - if 'AMICI_CXXFLAGS' in os.environ: - cxx_flags.extend(os.environ['AMICI_CXXFLAGS'].split(' ')) - if 'AMICI_LDFLAGS' in os.environ: - amici_module_linker_flags.extend( - os.environ['AMICI_LDFLAGS'].split(' ')) - - libamici = setup_clibs.get_lib_amici( - h5pkgcfg=h5pkgcfg, blaspkgcfg=blaspkgcfg, - extra_compiler_flags=cxx_flags) - libsundials = setup_clibs.get_lib_sundials(extra_compiler_flags=cxx_flags) - libsuitesparse = setup_clibs.get_lib_suite_sparse( - extra_compiler_flags=cxx_flags + ['-DDLONG'] + # SuiteSparse BTF + btf = CMakeExtension( + name="btf", + install_prefix="amici", + source_dir="amici/ThirdParty/SuiteSparse/BTF", + cmake_configure_options=[ + *global_cmake_configure_options, + "-DNFORTRAN=TRUE", + ], + ) + # SuiteSparse COLAMD + colamd = CMakeExtension( + name="colamd", + install_prefix="amici", + source_dir="amici/ThirdParty/SuiteSparse/COLAMD", + cmake_configure_options=[ + *global_cmake_configure_options, + "-DNFORTRAN=TRUE", + ], + ) + # SuiteSparse KLU + klu = CMakeExtension( + name="klu", + install_prefix="amici", + source_dir="amici/ThirdParty/SuiteSparse/KLU", + cmake_configure_options=[ + *global_cmake_configure_options, + "-DNCHOLMOD=ON", + "-DENABLE_CUDA=FALSE", + "-DNFORTRAN=TRUE", + ], + ) + # SUNDIALS + sundials = CMakeExtension( + name="sundials", + install_prefix="amici", + source_dir="amici/ThirdParty/sundials", + cmake_configure_options=[ + *global_cmake_configure_options, + "-DBUILD_ARKODE=OFF", + "-DBUILD_CVODE=OFF", + "-DBUILD_IDA=OFF", + "-DBUILD_KINSOL=OFF", + "-DBUILD_SHARED_LIBS=OFF", + "-DBUILD_STATIC_LIBS=ON", + "-DBUILD_NVECTOR_MANYVECTOR=OFF", + "-DBUILD_SUNNONLINSOL_PETSCSNES=OFF", + "-DEXAMPLES_ENABLE_C=OFF", + "-DEXAMPLES_INSTALL=OFF", + "-DENABLE_KLU=ON", + # We need the potentially temporary and unpredictable build path + # to use artifacts from other extensions here. `${build_dir}` will + # 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'", + ], + ) + # AMICI + amici_ext = CMakeExtension( + name="amici", + install_prefix="amici", + source_dir="amici", + cmake_configure_options=[ + *global_cmake_configure_options, + "-Werror=dev" + if "GITHUB_ACTIONS" in os.environ + else "-Wno-error=dev", + "-DAMICI_PYTHON_BUILD_EXT_ONLY=ON", + f"-DPython3_EXECUTABLE={Path(sys.executable).as_posix()}", + ], ) + # 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: + with open( + os.path.join(os.path.dirname(__file__), "README.md"), + "r", + encoding="utf-8", + ) as fh: long_description = fh.read() - # Build shared object - amici_module = Extension( - name='amici._amici', - sources=extension_sources, - include_dirs=['amici/include', - 'amici/ThirdParty/gsl/', - *libsundials[1]['include_dirs'], - *libsuitesparse[1]['include_dirs'], - *h5pkgcfg['include_dirs'], - *blaspkgcfg['include_dirs'], - np.get_include() - ], - # Cannot use here, see above - # libraries=[ - # 'hdf5_hl_cpp', 'hdf5_hl', 'hdf5_cpp', 'hdf5' - # ], - define_macros=define_macros, - library_dirs=[ - *h5pkgcfg['library_dirs'], - *blaspkgcfg['library_dirs'], - 'amici/libs', # clib target directory - ], - extra_compile_args=cxx_flags, - extra_link_args=amici_module_linker_flags - ) - # Monkey-patch extension (see - # `custom_commands.set_compiler_specific_extension_options`) - amici_module.extra_compile_args_mingw32 = ['-std=c++14'] - amici_module.extra_compile_args_unix = ['-std=c++14'] - amici_module.extra_compile_args_msvc = ['/std:c++14'] + ext_modules = get_extensions() + + # handle parallel building + # Note: can be empty to use all hardware threads + if (parallel_jobs := os.environ.get("AMICI_PARALLEL_COMPILE")) is not None: + os.environ["CMAKE_BUILD_PARALLEL_LEVEL"] = parallel_jobs + else: + os.environ["CMAKE_BUILD_PARALLEL_LEVEL"] = "1" # Install setup( cmdclass={ - 'install': AmiciInstall, - 'sdist': AmiciSDist, - 'build_ext': AmiciBuildExt, - 'build_clib': AmiciBuildCLib, - 'install_lib': AmiciInstallLib, - 'develop': AmiciDevelop, + "install": AmiciInstall, + "sdist": AmiciSDist, + "build_ext": AmiciBuildCMakeExtension, + "install_lib": AmiciInstallLib, + "develop": AmiciDevelop, + "build_py": AmiciBuildPy, }, long_description=long_description, long_description_content_type="text/markdown", - libraries=[libamici, libsundials, libsuitesparse], - ext_modules=[amici_module], + ext_modules=ext_modules, ) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/deps/AMICI/python/sdist/setup_clibs.py b/deps/AMICI/python/sdist/setup_clibs.py deleted file mode 100644 index 1b68320f8..000000000 --- a/deps/AMICI/python/sdist/setup_clibs.py +++ /dev/null @@ -1,261 +0,0 @@ -""" -Provides setuptools clibs for AMICI core, Sundials and SuiteSparse - -We could compile all source together into the AMICI base Python module, -however we want to keep the static libs to avoid recompilation for -AMICI-generated models. -""" - -import os -import glob -import re - -from typing import Dict, List, Union, Tuple, Any, Optional - -PackageInfo = Dict[str, List[Union[str, Tuple[str, Any]]]] -Library = Tuple[str, PackageInfo] - -# suite sparse include directories -suite_sparse_include_dirs = [ - 'amici/ThirdParty/SuiteSparse/KLU/Include/', - 'amici/ThirdParty/SuiteSparse/AMD/Include/', - 'amici/ThirdParty/SuiteSparse/COLAMD/Include/', - 'amici/ThirdParty/SuiteSparse/BTF/Include/', - 'amici/ThirdParty/SuiteSparse/SuiteSparse_config', - 'amici/ThirdParty/SuiteSparse/include' -] - -# sundials include directories -sundials_include_dirs = [ - 'amici/ThirdParty/sundials/include', - 'amici/ThirdParty/sundials/src', -] - - -def get_sundials_sources() -> List[str]: - """Get list of Sundials source files""" - srcs = [ - os.path.join('src', 'sunmatrix', 'dense', 'sunmatrix_dense.c'), - os.path.join('src', 'sunmatrix', 'band', 'sunmatrix_band.c'), - os.path.join('src', 'sunmatrix', 'sparse', 'sunmatrix_sparse.c'), - os.path.join('src', 'sunlinsol', 'spgmr', 'sunlinsol_spgmr.c'), - os.path.join('src', 'sunlinsol', 'sptfqmr', 'sunlinsol_sptfqmr.c'), - os.path.join('src', 'sunlinsol', 'klu', 'sunlinsol_klu.c'), - os.path.join('src', 'sunlinsol', 'dense', 'sunlinsol_dense.c'), - os.path.join('src', 'sunlinsol', 'spfgmr', 'sunlinsol_spfgmr.c'), - os.path.join('src', 'sunlinsol', 'pcg', 'sunlinsol_pcg.c'), - os.path.join('src', 'sunlinsol', 'spbcgs', 'sunlinsol_spbcgs.c'), - os.path.join('src', 'sunlinsol', 'band', 'sunlinsol_band.c'), - os.path.join('src', 'idas', 'idas_direct.c'), - os.path.join('src', 'idas', 'idaa.c'), - os.path.join('src', 'idas', 'idas_ic.c'), - os.path.join('src', 'idas', 'idas_nls_stg.c'), - os.path.join('src', 'idas', 'idas.c'), - os.path.join('src', 'idas', 'idas_bbdpre.c'), - os.path.join('src', 'idas', 'idas_spils.c'), - os.path.join('src', 'idas', 'idas_nls.c'), - os.path.join('src', 'idas', 'idas_ls.c'), - os.path.join('src', 'idas', 'idas_io.c'), - os.path.join('src', 'idas', 'idas_nls_sim.c'), - os.path.join('src', 'idas', 'idaa_io.c'), - os.path.join('src', 'sundials', 'sundials_math.c'), - os.path.join('src', 'sundials', 'sundials_matrix.c'), - os.path.join('src', 'sundials', 'sundials_direct.c'), - os.path.join('src', 'sundials', 'sundials_nvector_senswrapper.c'), - os.path.join('src', 'sundials', 'sundials_dense.c'), - os.path.join('src', 'sundials', 'sundials_nvector.c'), - os.path.join('src', 'sundials', 'sundials_version.c'), - os.path.join('src', 'sundials', 'sundials_iterative.c'), - os.path.join('src', 'sundials', 'sundials_nonlinearsolver.c'), - os.path.join('src', 'sundials', 'sundials_linearsolver.c'), - os.path.join('src', 'sundials', 'sundials_band.c'), - os.path.join('src', 'sundials', 'sundials_futils.c'), - os.path.join('src', 'sunnonlinsol', 'newton', 'sunnonlinsol_newton.c'), - os.path.join('src', 'sunnonlinsol', 'fixedpoint', - 'sunnonlinsol_fixedpoint.c'), - os.path.join('src', 'nvector', 'serial', 'nvector_serial.c'), - os.path.join('src', 'cvodes', 'cvodes_spils.c'), - os.path.join('src', 'cvodes', 'cvodes_nls_stg.c'), - os.path.join('src', 'cvodes', 'cvodes_ls.c'), - os.path.join('src', 'cvodes', 'cvodes_nls_stg1.c'), - os.path.join('src', 'cvodes', 'cvodes_bbdpre.c'), - os.path.join('src', 'cvodes', 'cvodes.c'), - os.path.join('src', 'cvodes', 'cvodes_bandpre.c'), - os.path.join('src', 'cvodes', 'cvodea.c'), - os.path.join('src', 'cvodes', 'cvodes_nls_sim.c'), - os.path.join('src', 'cvodes', 'cvodea_io.c'), - os.path.join('src', 'cvodes', 'cvodes_nls.c'), - os.path.join('src', 'cvodes', 'cvodes_diag.c'), - os.path.join('src', 'cvodes', 'cvodes_io.c'), - os.path.join('src', 'cvodes', 'cvodes_direct.c') - ] - return [os.path.join('amici', 'ThirdParty', 'sundials', src) - for src in srcs] - - -def get_suite_sparse_sources() -> List[str]: - """Get list of SuiteSparse source files""" - srcs = [ - os.path.join('KLU', 'Source', 'klu_analyze_given.c'), - os.path.join('KLU', 'Source', 'klu_analyze.c'), - os.path.join('KLU', 'Source', 'klu_defaults.c'), - os.path.join('KLU', 'Source', 'klu_diagnostics.c'), - os.path.join('KLU', 'Source', 'klu_dump.c'), - os.path.join('KLU', 'Source', 'klu_extract.c'), - os.path.join('KLU', 'Source', 'klu_factor.c'), - os.path.join('KLU', 'Source', 'klu_free_numeric.c'), - os.path.join('KLU', 'Source', 'klu_free_symbolic.c'), - os.path.join('KLU', 'Source', 'klu_kernel.c'), - os.path.join('KLU', 'Source', 'klu_memory.c'), - os.path.join('KLU', 'Source', 'klu_refactor.c'), - os.path.join('KLU', 'Source', 'klu_scale.c'), - os.path.join('KLU', 'Source', 'klu_sort.c'), - os.path.join('KLU', 'Source', 'klu_solve.c'), - os.path.join('KLU', 'Source', 'klu_tsolve.c'), - os.path.join('KLU', 'Source', 'klu.c'), - os.path.join('AMD', 'Source', 'amd_1.c'), - os.path.join('AMD', 'Source', 'amd_2.c'), - os.path.join('AMD', 'Source', 'amd_aat.c'), - os.path.join('AMD', 'Source', 'amd_control.c'), - os.path.join('AMD', 'Source', 'amd_defaults.c'), - os.path.join('AMD', 'Source', 'amd_dump.c'), - os.path.join('AMD', 'Source', 'amd_global.c'), - os.path.join('AMD', 'Source', 'amd_info.c'), - os.path.join('AMD', 'Source', 'amd_order.c'), - os.path.join('AMD', 'Source', 'amd_post_tree.c'), - os.path.join('AMD', 'Source', 'amd_postorder.c'), - os.path.join('AMD', 'Source', 'amd_preprocess.c'), - os.path.join('AMD', 'Source', 'amd_valid.c'), - os.path.join('COLAMD', 'Source', 'colamd.c'), - os.path.join('BTF', 'Source', 'btf_maxtrans.c'), - os.path.join('BTF', 'Source', 'btf_order.c'), - os.path.join('BTF', 'Source', 'btf_strongcomp.c'), - os.path.join('SuiteSparse_config', 'SuiteSparse_config.c'), - ] - return [os.path.join('amici', 'ThirdParty', 'SuiteSparse', src) - for src in srcs] - - -def get_amici_base_sources(with_hdf5: bool = True) -> List[str]: - """Get list of source files for the amici base library - - Expects that we are inside $AMICI_ROOT/python/sdist - - Arguments: - with_hdf5: compile with HDF5 support - """ - - amici_base_sources = glob.glob(os.path.join('amici', 'src', '*.cpp')) - amici_base_sources = [src for src in amici_base_sources - if not re.search(r'(matlab)|(\.(ODE_)?template\.)', src)] - - if not with_hdf5: - hdf5_cpp = os.path.join('amici', 'src', 'hdf5.cpp') - try: - # sometimes this fails for unknwon reasons... - amici_base_sources.remove(hdf5_cpp) - except ValueError: - print(f'Warning: could not find {hdf5_cpp} in ' - f'{amici_base_sources}') - - return amici_base_sources - - -def get_lib_sundials(extra_compiler_flags: Optional[List[str]] = None) -> \ - Library: - """ - Get sundials library build info for setuptools - - :param extra_compiler_flags: - Extra compiler flags - """ - if extra_compiler_flags is None: - extra_compiler_flags = [] - - libsundials = ('sundials', { - 'sources': get_sundials_sources(), - 'include_dirs': [*sundials_include_dirs, - *suite_sparse_include_dirs, - ], - 'cflags': [*extra_compiler_flags], - 'cflags_mingw32': ['-Wno-misleading-indentation'], - 'cflags_unix': ['-Wno-misleading-indentation'], - }) - return libsundials - - -def get_lib_suite_sparse(extra_compiler_flags: Optional[List[str]] = None) -> \ - Library: - """ - Get SuiteSparse library build info for setuptools - - :param extra_compiler_flags: - Extra compiler flags - """ - if extra_compiler_flags is None: - extra_compiler_flags = [] - - libsuitesparse = ('suitesparse', { - 'sources': get_suite_sparse_sources(), - 'include_dirs': suite_sparse_include_dirs, - 'cflags': [*extra_compiler_flags], - 'cflags_mingw32': ['-Wno-unused-but-set-variable'], - 'cflags_unix': ['-Wno-unused-but-set-variable'] - }) - return libsuitesparse - - -def get_lib_amici(extra_compiler_flags: List[str] = None, - h5pkgcfg: Optional[PackageInfo] = None, - blaspkgcfg: Optional[PackageInfo] = None) -> Library: - """ - Get AMICI core library build info for setuptools - - :param extra_compiler_flags: - Extra compiler flags - - :param h5pkgcfg: - hdf5 package info - - :param blaspkgcfg: - blas package info - - """ - - if extra_compiler_flags is None: - extra_compiler_flags = [] - - libamici = ('amici', { - 'sources': get_amici_base_sources( - with_hdf5=(h5pkgcfg - and 'include_dirs' in h5pkgcfg - and len(h5pkgcfg['include_dirs'])) - ), - 'include_dirs': ['amici/include', - *suite_sparse_include_dirs, - *sundials_include_dirs, - 'amici/ThirdParty/gsl/', - ], - 'cflags': [*extra_compiler_flags], - 'cflags_mingw32': ['-std=c++14'], - 'cflags_unix': ['-std=c++14'], - 'cflags_msvc': ['/std:c++14'], - 'macros': [], - }) - - if h5pkgcfg and 'include_dirs' in h5pkgcfg: - libamici[1]['include_dirs'].extend(h5pkgcfg['include_dirs']) - - if h5pkgcfg and 'define_macros' in h5pkgcfg: - libamici[1]['macros'].extend(h5pkgcfg['define_macros']) - - if blaspkgcfg and 'include_dirs' in blaspkgcfg: - libamici[1]['include_dirs'].extend(blaspkgcfg['include_dirs']) - - if blaspkgcfg and 'define_macros' in blaspkgcfg: - libamici[1]['macros'].extend(blaspkgcfg['define_macros']) - - if blaspkgcfg and 'extra_compile_args' in blaspkgcfg: - libamici[1]['cflags'].extend(blaspkgcfg['extra_compile_args']) - - return libamici diff --git a/deps/AMICI/python/tests/conftest.py b/deps/AMICI/python/tests/conftest.py index d5a9dd643..9ab64b91d 100644 --- a/deps/AMICI/python/tests/conftest.py +++ b/deps/AMICI/python/tests/conftest.py @@ -7,74 +7,77 @@ import amici import pytest +from amici.testing import TemporaryDirectoryWinSafe @pytest.fixture(scope="session") def sbml_example_presimulation_module(): """SBML example_presimulation model module fixture""" - sbml_file = os.path.join(os.path.dirname(__file__), '..', - 'examples', 'example_presimulation', - 'model_presimulation.xml') + sbml_file = os.path.join( + os.path.dirname(__file__), + "..", + "examples", + "example_presimulation", + "model_presimulation.xml", + ) sbml_importer = amici.SbmlImporter(sbml_file) - constant_parameters = ['DRUG_0', 'KIN_0'] + constant_parameters = ["DRUG_0", "KIN_0"] observables = amici.assignmentRules2observables( sbml_importer.sbml, # the libsbml model object - filter_function=lambda variable: variable.getName() == 'pPROT_obs' + filter_function=lambda variable: variable.getName() == "pPROT_obs", ) - outdir = 'test_model_presimulation' - module_name = 'test_model_presimulation' - sbml_importer.sbml2amici( - model_name=module_name, - output_dir=outdir, - verbose=False, - observables=observables, - constant_parameters=constant_parameters) + module_name = "test_model_presimulation" - model_module = amici.import_model_module(module_name=module_name, - module_path=outdir) - yield model_module + with TemporaryDirectoryWinSafe(prefix=module_name) as outdir: + sbml_importer.sbml2amici( + model_name=module_name, + output_dir=outdir, + verbose=False, + observables=observables, + constant_parameters=constant_parameters, + ) - shutil.rmtree(outdir, ignore_errors=True) + yield amici.import_model_module( + module_name=module_name, module_path=outdir + ) @pytest.fixture(scope="session") def pysb_example_presimulation_module(): """PySB example_presimulation model module fixture""" - pysb = pytest.importorskip("pysb") + from amici.pysb_import import pysb2amici - constant_parameters = ['DRUG_0', 'KIN_0'] + constant_parameters = ["DRUG_0", "KIN_0"] pysb.SelfExporter.cleanup() # reset pysb pysb.SelfExporter.do_export = True - model_path = os.path.join(os.path.dirname(__file__), '..', - 'examples', 'example_presimulation') + model_path = os.path.join( + os.path.dirname(__file__), "..", "examples", "example_presimulation" + ) with amici.add_path(model_path): - if 'createModelPresimulation' in sys.modules: - importlib.reload(sys.modules['createModelPresimulation']) - model_module = sys.modules['createModelPresimulation'] + if "createModelPresimulation" in sys.modules: + importlib.reload(sys.modules["createModelPresimulation"]) + model_module = sys.modules["createModelPresimulation"] else: - model_module = importlib.import_module('createModelPresimulation') + model_module = importlib.import_module("createModelPresimulation") model = copy.deepcopy(model_module.model) - model.name = 'test_model_presimulation_pysb' - outdir = model.name - - from amici.pysb_import import pysb2amici - - pysb2amici(model, outdir, verbose=True, - observables=['pPROT_obs'], - constant_parameters=constant_parameters) - - with amici.add_path(outdir): - model_module_pysb = importlib.import_module(outdir) + model.name = "test_model_presimulation_pysb" - yield model_module_pysb + with TemporaryDirectoryWinSafe(prefix=model.name) as outdir: + pysb2amici( + model, + outdir, + verbose=True, + observables=["pPROT_obs"], + constant_parameters=constant_parameters, + ) - shutil.rmtree(outdir, ignore_errors=True) + yield amici.import_model_module(model.name, outdir) diff --git a/deps/AMICI/python/tests/petab_test_problems/lotka_volterra/README.md b/deps/AMICI/python/tests/petab_test_problems/lotka_volterra/README.md new file mode 100644 index 000000000..25fbc0cc1 --- /dev/null +++ b/deps/AMICI/python/tests/petab_test_problems/lotka_volterra/README.md @@ -0,0 +1,5 @@ +A Lotka-Volterra model, based on the model provided as an example in the `yaml2sbml` package: https://github.com/yaml2sbml-dev/yaml2sbml + +The PEtab problem can be created by running `bash model/convert_to_petab.sh` (test on Ubuntu 20.04) inside the `model` directory, in a Python 3 environment with `yaml2sbml`: https://pypi.org/project/yaml2sbml/ + +The model is augmented with new parameters `departure_prey` and `arrival_predator`, to allow for a steady-state under certain conditions. This results in a model that can pre-equilibrate, then switch to the expected oscillations of a Lotka-Volterra model. diff --git a/deps/AMICI/python/tests/petab_test_problems/lotka_volterra/model/convert_to_petab.sh b/deps/AMICI/python/tests/petab_test_problems/lotka_volterra/model/convert_to_petab.sh new file mode 100644 index 000000000..7d1b4894f --- /dev/null +++ b/deps/AMICI/python/tests/petab_test_problems/lotka_volterra/model/convert_to_petab.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +set -eou pipefail +script_dir=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) +petab_path=$script_dir/../petab + +mkdir -p "${petab_path}" + +# Validate input yaml2sbml model +yaml2sbml_validate lotka_volterra.yaml + +# Copy measurements to PEtab directory +cp "${script_dir}/measurements.tsv" "$petab_path/measurements.tsv" + +# Write the PEtab problem +python "${script_dir}/writer.py" + +# Remove condition parameters from PEtab parameters table +for condition_parameter in beta delta departure_prey arrival_predator +do + sed -i "/^${condition_parameter}/d" "${petab_path}/parameter*" +done + +# Validate the PEtab problem +petablint -vy "${petab_path}/problem.yaml" diff --git a/deps/AMICI/python/tests/petab_test_problems/lotka_volterra/model/lotka_volterra.yaml b/deps/AMICI/python/tests/petab_test_problems/lotka_volterra/model/lotka_volterra.yaml new file mode 100644 index 000000000..b6f756cb3 --- /dev/null +++ b/deps/AMICI/python/tests/petab_test_problems/lotka_volterra/model/lotka_volterra.yaml @@ -0,0 +1,69 @@ +odes: + - stateId: prey + rightHandSide: alpha * prey - beta * prey * predator - departure_prey * prey * prey + initialValue: 2 + + - stateId: predator + rightHandSide: delta * prey * predator - gamma * predator + arrival_predator * prey + initialValue: 2 + +parameters: + - parameterId: alpha + nominalValue: 2 + parameterScale: log10 + lowerBound: 0.1 + upperBound: 10 + estimate: 1 + + - parameterId: beta + nominalValue: 4 + parameterScale: log10 + estimate: 0 + + - parameterId: gamma + nominalValue: 3 + parameterScale: log10 + lowerBound: 0.1 + upperBound: 10 + estimate: 1 + + - parameterId: delta + nominalValue: 3 + parameterScale: log10 + estimate: 0 + + - parameterId: departure_prey + nominalValue: 3 + parameterScale: log10 + estimate: 0 + + - parameterId: arrival_predator + nominalValue: 3 + parameterScale: log10 + estimate: 0 + +observables: + - observableId: observable_prey + observableFormula: log10(prey) + observableTransformation: lin + noiseFormula: noiseParameter1_observable_prey + noiseDistribution: normal + +conditions: + - conditionId: weak_predator + beta: 2 + delta: 3 + departure_prey: 0 + arrival_predator: 0 + + - conditionId: strong_predator + beta: 4 + delta: 3 + departure_prey: 0 + arrival_predator: 0 + + - conditionId: no_interaction + beta: 0 + delta: 0 + departure_prey: 3 + arrival_predator: 3 diff --git a/deps/AMICI/python/tests/petab_test_problems/lotka_volterra/model/measurements.tsv b/deps/AMICI/python/tests/petab_test_problems/lotka_volterra/model/measurements.tsv new file mode 100644 index 000000000..2875e3fe3 --- /dev/null +++ b/deps/AMICI/python/tests/petab_test_problems/lotka_volterra/model/measurements.tsv @@ -0,0 +1,43 @@ +observableId preequilibrationConditionId simulationConditionId measurement time noiseParameters +observable_prey no_interaction weak_predator 1 0 1.1 +observable_prey no_interaction weak_predator 1 0.1 1.1 +observable_prey no_interaction weak_predator 1 0.2 1.1 +observable_prey no_interaction weak_predator 1 0.3 1.1 +observable_prey no_interaction weak_predator 1 0.4 1.1 +observable_prey no_interaction weak_predator 1 0.5 1.2 +observable_prey no_interaction weak_predator 1 0.6 1.2 +observable_prey no_interaction weak_predator 1 0.7 1.2 +observable_prey no_interaction weak_predator 1 0.8 1.2 +observable_prey no_interaction weak_predator 1 0.9 1.2 +observable_prey no_interaction weak_predator 1 1.0 1.2 +observable_prey no_interaction weak_predator 1 1.1 1.2 +observable_prey no_interaction weak_predator 1 1.2 1.2 +observable_prey no_interaction weak_predator 1 1.3 1.2 +observable_prey no_interaction weak_predator 1 1.4 1.1 +observable_prey no_interaction weak_predator 1 1.5 1.1 +observable_prey no_interaction weak_predator 1 1.6 1.1 +observable_prey no_interaction weak_predator 1 1.7 1.1 +observable_prey no_interaction weak_predator 1 1.8 1.1 +observable_prey no_interaction weak_predator 1 1.9 1.1 +observable_prey no_interaction weak_predator 1 2.0 1.1 +observable_prey no_interaction strong_predator 0.5 0 1.1 +observable_prey no_interaction strong_predator 0.5 0.1 1.1 +observable_prey no_interaction strong_predator 0.5 0.2 1.1 +observable_prey no_interaction strong_predator 0.5 0.3 1.1 +observable_prey no_interaction strong_predator 0.5 0.4 1.1 +observable_prey no_interaction strong_predator 0.5 0.5 1.2 +observable_prey no_interaction strong_predator 0.5 0.6 1.2 +observable_prey no_interaction strong_predator 0.5 0.7 1.2 +observable_prey no_interaction strong_predator 0.5 0.8 1.2 +observable_prey no_interaction strong_predator 0.5 0.9 1.2 +observable_prey no_interaction strong_predator 0.5 1.0 1.2 +observable_prey no_interaction strong_predator 0.5 1.1 1.2 +observable_prey no_interaction strong_predator 0.5 1.2 1.2 +observable_prey no_interaction strong_predator 0.5 1.3 1.2 +observable_prey no_interaction strong_predator 0.5 1.4 1.1 +observable_prey no_interaction strong_predator 0.5 1.5 1.1 +observable_prey no_interaction strong_predator 0.5 1.6 1.1 +observable_prey no_interaction strong_predator 0.5 1.7 1.1 +observable_prey no_interaction strong_predator 0.5 1.8 1.1 +observable_prey no_interaction strong_predator 0.5 1.9 1.1 +observable_prey no_interaction strong_predator 0.5 2.0 1.1 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 new file mode 100644 index 000000000..76b98c3de --- /dev/null +++ b/deps/AMICI/python/tests/petab_test_problems/lotka_volterra/model/writer.py @@ -0,0 +1,18 @@ +from pathlib import Path + +import petab +import yaml2sbml + +yaml2sbml_yaml = "lotka_volterra.yaml" +petab_path = Path(__file__).parent.parent / "petab" +petab_yaml = "problem.yaml" +measurements_tsv = "measurements.tsv" +model_name = "lotka_volterra" + +yaml2sbml.yaml2petab( + yaml_dir=yaml2sbml_yaml, + output_dir=str(petab_path), + sbml_name=model_name, + petab_yaml_name=petab_yaml, + measurement_table_name=measurements_tsv, +) diff --git a/deps/AMICI/python/tests/petab_test_problems/lotka_volterra/petab/experimental_conditions_lotka_volterra.tsv b/deps/AMICI/python/tests/petab_test_problems/lotka_volterra/petab/experimental_conditions_lotka_volterra.tsv new file mode 100644 index 000000000..827bc0b49 --- /dev/null +++ b/deps/AMICI/python/tests/petab_test_problems/lotka_volterra/petab/experimental_conditions_lotka_volterra.tsv @@ -0,0 +1,4 @@ +conditionId beta delta departure_prey arrival_predator +weak_predator 2.0 3.0 0.0 0.0 +strong_predator 4.0 3.0 0.0 0.0 +no_interaction 0.0 0.0 3.0 3.0 diff --git a/deps/AMICI/python/tests/petab_test_problems/lotka_volterra/petab/lotka_volterra.xml b/deps/AMICI/python/tests/petab_test_problems/lotka_volterra/petab/lotka_volterra.xml new file mode 100644 index 000000000..3553564e1 --- /dev/null +++ b/deps/AMICI/python/tests/petab_test_problems/lotka_volterra/petab/lotka_volterra.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + alpha + prey + + + + beta + prey + predator + + + + + departure_prey + prey + prey + + + + + + + + + + + + + delta + prey + predator + + + + gamma + predator + + + + + arrival_predator + prey + + + + + + + diff --git a/deps/AMICI/python/tests/petab_test_problems/lotka_volterra/petab/measurements.tsv b/deps/AMICI/python/tests/petab_test_problems/lotka_volterra/petab/measurements.tsv new file mode 100644 index 000000000..2875e3fe3 --- /dev/null +++ b/deps/AMICI/python/tests/petab_test_problems/lotka_volterra/petab/measurements.tsv @@ -0,0 +1,43 @@ +observableId preequilibrationConditionId simulationConditionId measurement time noiseParameters +observable_prey no_interaction weak_predator 1 0 1.1 +observable_prey no_interaction weak_predator 1 0.1 1.1 +observable_prey no_interaction weak_predator 1 0.2 1.1 +observable_prey no_interaction weak_predator 1 0.3 1.1 +observable_prey no_interaction weak_predator 1 0.4 1.1 +observable_prey no_interaction weak_predator 1 0.5 1.2 +observable_prey no_interaction weak_predator 1 0.6 1.2 +observable_prey no_interaction weak_predator 1 0.7 1.2 +observable_prey no_interaction weak_predator 1 0.8 1.2 +observable_prey no_interaction weak_predator 1 0.9 1.2 +observable_prey no_interaction weak_predator 1 1.0 1.2 +observable_prey no_interaction weak_predator 1 1.1 1.2 +observable_prey no_interaction weak_predator 1 1.2 1.2 +observable_prey no_interaction weak_predator 1 1.3 1.2 +observable_prey no_interaction weak_predator 1 1.4 1.1 +observable_prey no_interaction weak_predator 1 1.5 1.1 +observable_prey no_interaction weak_predator 1 1.6 1.1 +observable_prey no_interaction weak_predator 1 1.7 1.1 +observable_prey no_interaction weak_predator 1 1.8 1.1 +observable_prey no_interaction weak_predator 1 1.9 1.1 +observable_prey no_interaction weak_predator 1 2.0 1.1 +observable_prey no_interaction strong_predator 0.5 0 1.1 +observable_prey no_interaction strong_predator 0.5 0.1 1.1 +observable_prey no_interaction strong_predator 0.5 0.2 1.1 +observable_prey no_interaction strong_predator 0.5 0.3 1.1 +observable_prey no_interaction strong_predator 0.5 0.4 1.1 +observable_prey no_interaction strong_predator 0.5 0.5 1.2 +observable_prey no_interaction strong_predator 0.5 0.6 1.2 +observable_prey no_interaction strong_predator 0.5 0.7 1.2 +observable_prey no_interaction strong_predator 0.5 0.8 1.2 +observable_prey no_interaction strong_predator 0.5 0.9 1.2 +observable_prey no_interaction strong_predator 0.5 1.0 1.2 +observable_prey no_interaction strong_predator 0.5 1.1 1.2 +observable_prey no_interaction strong_predator 0.5 1.2 1.2 +observable_prey no_interaction strong_predator 0.5 1.3 1.2 +observable_prey no_interaction strong_predator 0.5 1.4 1.1 +observable_prey no_interaction strong_predator 0.5 1.5 1.1 +observable_prey no_interaction strong_predator 0.5 1.6 1.1 +observable_prey no_interaction strong_predator 0.5 1.7 1.1 +observable_prey no_interaction strong_predator 0.5 1.8 1.1 +observable_prey no_interaction strong_predator 0.5 1.9 1.1 +observable_prey no_interaction strong_predator 0.5 2.0 1.1 diff --git a/deps/AMICI/python/tests/petab_test_problems/lotka_volterra/petab/observables_lotka_volterra.tsv b/deps/AMICI/python/tests/petab_test_problems/lotka_volterra/petab/observables_lotka_volterra.tsv new file mode 100644 index 000000000..10cbcf279 --- /dev/null +++ b/deps/AMICI/python/tests/petab_test_problems/lotka_volterra/petab/observables_lotka_volterra.tsv @@ -0,0 +1,2 @@ +observableId observableFormula noiseFormula observableTransformation noiseDistribution +observable_prey log10(prey) noiseParameter1_observable_prey lin normal diff --git a/deps/AMICI/python/tests/petab_test_problems/lotka_volterra/petab/parameters_lotka_volterra.tsv b/deps/AMICI/python/tests/petab_test_problems/lotka_volterra/petab/parameters_lotka_volterra.tsv new file mode 100644 index 000000000..e5e65b225 --- /dev/null +++ b/deps/AMICI/python/tests/petab_test_problems/lotka_volterra/petab/parameters_lotka_volterra.tsv @@ -0,0 +1,3 @@ +parameterId parameterScale lowerBound upperBound estimate nominalValue +alpha log10 0.1 10.0 1.0 2.0 +gamma log10 0.1 10.0 1.0 3.0 diff --git a/deps/AMICI/python/tests/petab_test_problems/lotka_volterra/petab/problem.yaml b/deps/AMICI/python/tests/petab_test_problems/lotka_volterra/petab/problem.yaml new file mode 100644 index 000000000..d98098488 --- /dev/null +++ b/deps/AMICI/python/tests/petab_test_problems/lotka_volterra/petab/problem.yaml @@ -0,0 +1,11 @@ +format_version: 1 +parameter_file: parameters_lotka_volterra.tsv +problems: +- condition_files: + - experimental_conditions_lotka_volterra.tsv + measurement_files: + - measurements.tsv + observable_files: + - observables_lotka_volterra.tsv + sbml_files: + - lotka_volterra.xml 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 aa6f409c0..767c239c5 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 @@ -4,98 +4,115 @@ """ from __future__ import print_function + from pysb import * Model() -Parameter('NA', 6.02e23) # Avogadro's number (molecules/mol) -Parameter('f', 1) # Fraction of the cell to simulate -Expression('Vo', f*1.0e-10) # Extracellular volume=1/cell_density (L) -Expression('V', f*3.0e-12) # Cytoplasmic volume (L) +Parameter("NA", 6.02e23) # Avogadro's number (molecules/mol) +Parameter("f", 1) # Fraction of the cell to simulate +Expression("Vo", f * 1.0e-10) # Extracellular volume=1/cell_density (L) +Expression("V", f * 3.0e-12) # Cytoplasmic volume (L) # Initial amount of ligand (20 nM) converted to copies per cell -Expression('EGF_init', 20*1e-9*NA*Vo) +Expression("EGF_init", 20 * 1e-9 * NA * Vo) # Initial amounts of cellular components (copies per cell) -Expression('EGFR_init', f*1.8e5) -Expression('Grb2_init', f*1.5e5) -Expression('Sos1_init', f*6.2e4) +Expression("EGFR_init", f * 1.8e5) +Expression("Grb2_init", f * 1.5e5) +Expression("Sos1_init", f * 6.2e4) # Rate constants # Divide by NA*V to convert bimolecular rate constants from /M/sec to # /(molecule/cell)/sec -Expression('kp1', 9.0e7/(NA*Vo)) # ligand-monomer binding -Parameter('km1', 0.06) # ligand-monomer dissociation -Expression('kp2', 1.0e7/(NA*V)) # aggregation of bound monomers -Parameter('km2', 0.1) # dissociation of bound monomers -Parameter('kp3', 0.5) # dimer transphosphorylation -Parameter('km3', 4.505) # dimer dephosphorylation -Expression('kp4', 1.5e6/(NA*V)) # binding of Grb2 to receptor -Parameter('km4', 0.05) # dissociation of Grb2 from receptor -Expression('kp5', 1.0e7/(NA*V)) # binding of Grb2 to Sos1 -Parameter('km5', 0.06) # dissociation of Grb2 from Sos1 -Parameter('deg', 0.01) # degradation of receptor dimers +Expression("kp1", 9.0e7 / (NA * Vo)) # ligand-monomer binding +Parameter("km1", 0.06) # ligand-monomer dissociation +Expression("kp2", 1.0e7 / (NA * V)) # aggregation of bound monomers +Parameter("km2", 0.1) # dissociation of bound monomers +Parameter("kp3", 0.5) # dimer transphosphorylation +Parameter("km3", 4.505) # dimer dephosphorylation +Expression("kp4", 1.5e6 / (NA * V)) # binding of Grb2 to receptor +Parameter("km4", 0.05) # dissociation of Grb2 from receptor +Expression("kp5", 1.0e7 / (NA * V)) # binding of Grb2 to Sos1 +Parameter("km5", 0.06) # dissociation of Grb2 from Sos1 +Parameter("deg", 0.01) # degradation of receptor dimers -Monomer('EGF', ['R']) -Monomer('EGFR', ['L','CR1','Y1068'], {'Y1068':('U','P')}) -Monomer('Grb2', ['SH2','SH3']) -Monomer('Sos1', ['PxxP']) +Monomer("EGF", ["R"]) +Monomer("EGFR", ["L", "CR1", "Y1068"], {"Y1068": ("U", "P")}) +Monomer("Grb2", ["SH2", "SH3"]) +Monomer("Sos1", ["PxxP"]) Initial(EGF(R=None), EGF_init) -Initial(EGFR(L=None, CR1=None, Y1068='U'), EGFR_init) +Initial(EGFR(L=None, CR1=None, Y1068="U"), EGFR_init) Initial(Grb2(SH2=None, SH3=None), Grb2_init) Initial(Sos1(PxxP=None), Sos1_init) -Observable('EGFR_tot', EGFR()) -Observable('Lig_free', EGF(R=None)) -Observable('Dim', EGFR(CR1=ANY), match='species') -Observable('RP', EGFR(Y1068=('P',WILD))) -Observable('Grb2Sos1', Grb2(SH2=None, SH3=1) % Sos1(PxxP=1)) -Observable('Sos1_act', EGFR(Y1068=1) % Grb2(SH2=1, SH3=2) % Sos1(PxxP=2)) +Observable("EGFR_tot", EGFR()) +Observable("Lig_free", EGF(R=None)) +Observable("Dim", EGFR(CR1=ANY), match="species") +Observable("RP", EGFR(Y1068=("P", WILD))) +Observable("Grb2Sos1", Grb2(SH2=None, SH3=1) % Sos1(PxxP=1)) +Observable("Sos1_act", EGFR(Y1068=1) % Grb2(SH2=1, SH3=2) % Sos1(PxxP=2)) # Ligand-receptor binding -Rule('egf_bind_egfr', - EGFR(L=None, CR1=None) + EGF(R=None) | EGFR(L=1, CR1=None) % EGF(R=1), - kp1, km1) +Rule( + "egf_bind_egfr", + EGFR(L=None, CR1=None) + EGF(R=None) | EGFR(L=1, CR1=None) % EGF(R=1), + kp1, + km1, +) # Receptor-aggregation -Rule('egfr_dimerize', - EGFR(L=ANY, CR1=None) + EGFR(L=ANY, CR1=None) | - EGFR(L=ANY, CR1=1) % EGFR(L=ANY, CR1=1), - kp2, km2) +Rule( + "egfr_dimerize", + EGFR(L=ANY, CR1=None) + EGFR(L=ANY, CR1=None) + | EGFR(L=ANY, CR1=1) % EGFR(L=ANY, CR1=1), + kp2, + km2, +) # Transphosphorylation of EGFR by RTK -Rule('egfr_transphos', - EGFR(CR1=ANY, Y1068='U') >> EGFR(CR1=ANY, Y1068='P'), kp3) +Rule( + "egfr_transphos", EGFR(CR1=ANY, Y1068="U") >> EGFR(CR1=ANY, Y1068="P"), kp3 +) # Dephosphorylation -Rule('egfr_dephos', - EGFR(Y1068='P') >> EGFR(Y1068='U'), km3) +Rule("egfr_dephos", EGFR(Y1068="P") >> EGFR(Y1068="U"), km3) # Grb2 binding to pY1068 -Rule('grb2_bind_egfr', - EGFR(Y1068='P') + Grb2(SH2=None) | EGFR(Y1068=('P',1)) % Grb2(SH2=1), - kp4, km4) +Rule( + "grb2_bind_egfr", + EGFR(Y1068="P") + Grb2(SH2=None) | EGFR(Y1068=("P", 1)) % Grb2(SH2=1), + kp4, + km4, +) # Grb2 binding to Sos1 -Rule('sos1_bind_grb2', - Grb2(SH3=None) + Sos1(PxxP=None) | Grb2(SH3=1) % Sos1(PxxP=1), - kp5, km5) +Rule( + "sos1_bind_grb2", + Grb2(SH3=None) + Sos1(PxxP=None) | Grb2(SH3=1) % Sos1(PxxP=1), + kp5, + km5, +) # Receptor dimer internalization/degradation -Rule('egfr_dimer_degrade', - EGF(R=1) % EGF(R=2) % EGFR(L=1, CR1=3) % EGFR(L=2, CR1=3) >> None, - deg, - delete_molecules=True) +Rule( + "egfr_dimer_degrade", + EGF(R=1) % EGF(R=2) % EGFR(L=1, CR1=3) % EGFR(L=2, CR1=3) >> None, + deg, + delete_molecules=True, +) -if __name__ == '__main__': +if __name__ == "__main__": print(__doc__, "\n", model) - print(""" + print( + """ NOTE: This model code is designed to be imported and programatically manipulated, not executed directly. The above output is merely a -diagnostic aid.""") \ No newline at end of file +diagnostic aid.""" + ) diff --git a/deps/AMICI/python/tests/splines_utils.py b/deps/AMICI/python/tests/splines_utils.py new file mode 100644 index 000000000..0746207dd --- /dev/null +++ b/deps/AMICI/python/tests/splines_utils.py @@ -0,0 +1,971 @@ +""" +Utilities for creating test SBML models containing splines, +for running them and for comparing them to a symbolically +computed ground truth. +""" + +import math +import os +import uuid +from tempfile import mkdtemp +from typing import Any, Dict, List, Optional, Sequence, Union + +import amici +import numpy as np +import pandas as pd +import 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.sbml_utils import ( + add_compartment, + add_inflow, + add_parameter, + add_rate_rule, + add_species, + amici_time_symbol, + create_sbml_model, +) +from amici.splines import AbstractSpline, CubicHermiteSpline, UniformGrid +from amici.testing import TemporaryDirectoryWinSafe as TemporaryDirectory +from petab.models.sbml_model import SbmlModel + + +def evaluate_spline( + spline: AbstractSpline, params: dict, tt: Sequence[float], **kwargs +): + """ + Evaluate the `AbstractSpline` `spline` at timepoints `tt` + for the parameters given in the dictionary `params`. + """ + return np.asarray([spline.evaluate(t).subs(params) for t in tt], **kwargs) + + +def integrate_spline( + spline: AbstractSpline, + params: Union[Dict, None], + tt: Sequence[float], + initial_value: float = 0, +): + """ + Integrate the `AbstractSpline` `spline` at timepoints `tt` + for the parameters given in the dictionary `params`. + """ + ispline = [initial_value + spline.integrate(0, t) for t in tt] + if params is not None: + ispline = [x.subs(params) for x in ispline] + return ispline + + +def create_condition_table() -> pd.DataFrame: + """Create a PEtab condition table.""" + condition_df = pd.DataFrame({"conditionId": ["condition1"]}) + condition_df.set_index(["conditionId"], inplace=True) + return condition_df + + +def create_parameter_table(**columns) -> pd.DataFrame: + """Create a PEtab parameter table.""" + if isinstance(columns["parameterId"], str): + columns["parameterId"] = [columns["parameterId"]] + columns.setdefault("parameterScale", "lin") + columns.setdefault("estimate", 1) + parameter_df = pd.DataFrame(columns) + parameter_df.set_index(["parameterId"], inplace=True) + return parameter_df + + +def create_observable_table(**columns) -> pd.DataFrame: + """Create a PEtab observable table.""" + if isinstance(columns["observableId"], str): + columns["observableId"] = [columns["observableId"]] + columns.setdefault("observableTransformation", "lin") + columns.setdefault("noiseDistribution", "normal") + observable_df = pd.DataFrame(columns) + observable_df.set_index(["observableId"], inplace=True) + return observable_df + + +def create_measurement_table(**columns) -> pd.DataFrame: + """Create a PEtab measurement table.""" + if isinstance(columns["observableId"], str): + columns["observableId"] = [columns["observableId"]] + columns.setdefault("simulationConditionId", "condition1") + return pd.DataFrame(columns) + + +def species(i) -> str: + """Name to use for the `i`-th species.""" + return f"z{i}" + + +def observable(i) -> str: + """ + Name to use for the `i`-th observable, + i.e., the observable associated to the + `i`-th species. + """ + return f"{species(i)}_obs" + + +def species_to_index(name) -> int: + """Get the species index from a species name.""" + assert name[0] == "z" + return int(name[1:]) + + +def create_petab_problem( + splines: List[AbstractSpline], + params_true: Dict, + initial_values: Optional[np.ndarray] = None, + use_reactions: bool = False, + measure_upsample: int = 6, + sigma: float = 1.0, + t_extrapolate: float = 0.25, + folder: Optional[str] = None, + model_name: str = "test_splines", +): + """ + Given a list of `AbstractSplines`, create a PEtab problem for the system of + ODEs given by `z_i(t)' = spline[i](t)`. + + :param params_true: + parameter values used to compute the analytical solution of the ODE + system in order to fill the PEtab measurement table + + :param initial_values: + initial values of the state variables + + :param use_reactions: + whether the ODEs are encoded in the SBML model as reactions (inflows) + or rate rules + + :param measure_upsample: + controls the number of time points at which synthetic measurements are + taken. The interval between subsequent time points is equal to the + smallest interval between subsequent spline nodes divided by + `measure_upsample` + + :param sigma: + standard deviation for additive Normal noise used to corrupt synthetic + measurements + + :param t_extrapolate: + factor controlling how long after the final spline node the simulation + should continue in order to test extrapolation methods. + + :param folder: + if not `None`, save the PEtab problem to this folder + + :param model_name: + name of the SBML model to be created + """ + + for spline in splines: + if spline.evaluate_at != amici_time_symbol: + raise ValueError( + "the given splines must be evaluated at the simulation time" + ) + + if initial_values is None: + initial_values = np.zeros(len(splines)) + + # Create SBML document + doc, model = create_sbml_model(model_name) + add_compartment(model, "compartment") + for i, spline in enumerate(splines): + spline.add_to_sbml_model(model) + add_species(model, species(i), initial_amount=initial_values[i]) + if use_reactions: + add_inflow(model, species(i), splines[i].sbml_id) + else: + add_rate_rule(model, species(i), splines[i].sbml_id) + for parId, value in params_true.items(): + add_parameter(model, parId, value=value, constant=True) + for spline in splines: + add_parameter(model, spline.sbml_id, constant=False) + + # Compute simulation time + # Must cover all the intervals of definition for the splines, + # plus something extra for extrapolated or periodic splines + T = 0 + for spline in splines: + if spline.extrapolate[0] is None and spline.nodes[0] > 0: + raise ValueError( + "if no left-extrapolation is defined for a spline, " + "its interval of definition should contain zero" + ) + if spline.extrapolate[1] is not None: + f = ( + t_extrapolate + if spline.extrapolate[1] != "periodic" + else 1 + t_extrapolate + ) + DT = f * (spline.nodes[-1] - spline.nodes[0]) + else: + DT = 0 + T = max(T, spline.nodes[-1] + DT) + + # Compute synthetic measurements + dt = min(np.diff(spline.nodes).min() for spline in splines) + dt /= measure_upsample + n_obs = math.ceil(T / dt) + 1 + tt_obs = np.linspace(0, float(T), n_obs) + zz_true = np.array( + [ + integrate_spline(spline, params_true, tt_obs, iv) + for (spline, iv) in zip(splines, initial_values) + ], + dtype=float, + ) + zz_obs = [zz + sigma * np.random.randn(len(zz)) for zz in zz_true] + + # Create PEtab tables + condition_df = create_condition_table() + # ensure that same parameter order is used for all columns + _params = list(params_true.items()) + parameter_df = create_parameter_table( + parameterId=[p.name for (p, v) in _params], + lowerBound=min(v for (p, v) in _params) if _params else [], + upperBound=max(v for (p, v) in _params) if _params else [], + nominalValue=[v for (p, v) in _params], + estimate=1, + ) + observable_df = create_observable_table( + observableId=[observable(i) for i in range(len(splines))], + observableFormula=[species(i) for i in range(len(splines))], + noiseFormula=sigma if sigma > 0 else 1.0, + ) + measurement_df = create_measurement_table( + observableId=np.concatenate( + [len(tt_obs) * [observable(i)] for i in range(len(splines))] + ), + time=len(splines) * list(tt_obs), + measurement=np.concatenate(zz_obs), + ) + + # Create and validate PEtab problem + problem = petab.Problem( + model=SbmlModel( + sbml_document=doc, + sbml_model=model, + ), + condition_df=condition_df, + measurement_df=measurement_df, + parameter_df=parameter_df, + observable_df=observable_df, + ) + if petab.lint_problem(problem): + raise RuntimeError("PEtab lint failed") + + # Write PEtab problem to disk + if folder is None: + return problem, initial_values, T + folder = os.path.abspath(folder) + os.makedirs(folder, exist_ok=True) + problem.to_files( + model_file=os.path.join(folder, f"{model_name}_model.xml"), + condition_file=os.path.join(folder, f"{model_name}_conditions.tsv"), + measurement_file=os.path.join( + folder, f"{model_name}_measurements.tsv" + ), + parameter_file=os.path.join(folder, f"{model_name}_parameters.tsv"), + observable_file=os.path.join(folder, f"{model_name}_observables.tsv"), + yaml_file=os.path.join(folder, f"{model_name}.yaml"), + ) + return os.path.join(folder, f"{model_name}.yaml"), initial_values, T + + +def simulate_splines( + splines, + params_true, + initial_values=None, + *, + folder: Optional[str] = None, + keep_temporary: bool = False, + benchmark: Union[bool, int] = False, + rtol: float = 1e-12, + atol: float = 1e-12, + maxsteps: int = 500_000, + discard_annotations: bool = False, + use_adjoint: bool = False, + skip_sensitivity: bool = False, + petab_problem=None, + amici_model=None, + **kwargs, +): + """ + Create a PEtab problem using `create_petab_problem` and simulate it with + AMICI. + + :param splines: + passed to `create_petab_problem` + + :param params_true: + passed to `create_petab_problem` + + :param initial_values: + passed to `create_petab_problem` + + :param folder: + working directory (a temporary one is used if not specified) + + :param keep_temporary: + whether to keep or delete temporary working directories on exit + + :param benchmark: + instead of returning the simulation data, run the simulation + `benchmark` times (defaults to `50` if `benchmark` is `True`) + and return execution times + + :param rtol: + relative tolerance for AMICI solver + + :param atol: + absolute tolerance for AMICI solver + + :param maxsteps: + maximum number of steps for AMICI solver + + :param discard_annotations: + whether to discard spline annotations, + forcing AMICI to read the spline as a piecewise assignment rule + + :param use_adjoint: + whether to use adjoint sensitivity computation + + :param skip_sensitivity: + whether to skip sensitivity computation + + :param petab_problem: + PEtab problem (if already created) + + :param amici_model: + AMICI model (if already created) + + :param kwargs: + passed to `create_petab_problem` + """ + # If no working directory is given, create a temporary one + if folder is None: + if keep_temporary: + folder = mkdtemp() + print(f"temporary folder is {folder}") + else: + with TemporaryDirectory() as folder: + return simulate_splines( + splines, + params_true, + initial_values, + folder=folder, + benchmark=benchmark, + rtol=rtol, + atol=atol, + maxsteps=maxsteps, + discard_annotations=discard_annotations, + use_adjoint=use_adjoint, + skip_sensitivity=skip_sensitivity, + petab_problem=petab_problem, + amici_model=amici_model, + **kwargs, + ) + + if petab_problem is None and amici_model is not None: + raise ValueError( + "if amici_model is given, petab_problem must be given too" + ) + + if petab_problem is not None and initial_values is None: + raise ValueError( + "if petab_problem is given, initial_values must be given too" + ) + + if petab_problem is None: + # Create PEtab problem + path, initial_values, T = create_petab_problem( + splines, + params_true, + initial_values, + sigma=0.0, + folder=folder, + **kwargs, + ) + petab_problem = petab.Problem.from_yaml(path) + + if amici_model is None: + # Create and compile AMICI model + model_id = uuid.uuid4().hex[-5:] # to prevent folder/module collisions + amici_model = import_petab_problem( + petab_problem, + discard_sbml_annotations=discard_annotations, + model_output_dir=os.path.join(folder, f"amici_models_{model_id}"), + model_name=f"splinetest_{model_id}", + ) + + # Set solver options + solver = amici_model.getSolver() + solver.setRelativeTolerance(rtol) + solver.setAbsoluteTolerance(atol) + solver.setMaxSteps(maxsteps) + if not skip_sensitivity: + solver.setSensitivityOrder(amici.SensitivityOrder.first) + if use_adjoint: + solver.setSensitivityMethod(amici.SensitivityMethod.adjoint) + else: + solver.setSensitivityMethod(amici.SensitivityMethod.forward) + + # Compute and set timepoints + # NB not working, will always be equal to the observation times + # n = max(len(spline.nodes) for spline in splines) * simulate_upsample + # tt = np.linspace(0, float(T), n) + # model.setTimepoints(tt) + + # Create dictionary for parameter values + params_str = {p.name: v for (p, v) in params_true.items()} + + if benchmark is False: + # Simulate PEtab problem + res = simulate_petab(petab_problem, amici_model, solver, params_str) + llh, rdatas, edatas = res[LLH], res[RDATAS], res[EDATAS] + assert len(rdatas) == 1 + llh = float(llh) + rdata = rdatas[0] + assert SLLH in rdata.keys() + sllh = rdata[SLLH] + assert len(edatas) == 1 + edata = edatas[0] + + # Return state/parameter ordering + state_ids = amici_model.getStateIds() + param_ids = amici_model.getParameterIds() + + return ( + initial_values, + petab_problem, + amici_model, + solver, + llh, + sllh, + rdata, + edata, + state_ids, + param_ids, + ) + + if benchmark is True: + benchmark = 50 + import time + + runtimes = [] + for _ in range(int(benchmark)): + t0 = time.perf_counter() + simulate_petab(petab_problem, amici_model, solver, params_str) + t_elapsed = time.perf_counter() - t0 + runtimes.append(t_elapsed) + + return dict( + runtimes=runtimes, + mean=np.mean(runtimes), + median=np.median(runtimes), + min=min(runtimes), + max=max(runtimes), + ) + + +def compute_ground_truth( + splines, initial_values, times, params_true, params_sorted +): + x_true_sym = sp.Matrix( + [ + integrate_spline(spline, None, times, iv) + for (spline, iv) in zip(splines, initial_values) + ] + ).transpose() + groundtruth = { + "x_true": np.asarray(x_true_sym.subs(params_true), dtype=float) + } + sx_by_state = [ + x_true_sym[:, i].jacobian(params_sorted).subs(params_true) + for i in range(x_true_sym.shape[1]) + ] + sx_by_state = [np.asarray(sx, dtype=float) for sx in sx_by_state] + groundtruth["sx_true"] = np.concatenate( + [sx[:, :, np.newaxis] for sx in sx_by_state], axis=2 + ) + return groundtruth + + +def check_splines( + splines, + params_true, + initial_values=None, + *, + discard_annotations: bool = False, + use_adjoint: bool = False, + skip_sensitivity: bool = False, + debug: Union[bool, str] = False, + parameter_lists: Optional[Sequence[Sequence[int]]] = None, + llh_rtol: float = 1e-8, + sllh_atol: float = 1e-8, + x_rtol: float = 1e-11, + x_atol: float = 1e-11, + w_rtol: float = 1e-11, + w_atol: float = 1e-11, + sx_rtol: float = 1e-10, + sx_atol: float = 1e-10, + groundtruth: Optional[Union[str, Dict[str, Any]]] = None, + **kwargs, +): + """ + Create a PEtab problem using `create_petab_problem`, + simulate it with `simulate_splines` + and check it against the analytical solution. + + :param splines: + passed to `simulate_splines` + + :param params_true: + passed to `simulate_splines` + + :param initial_values: + passed to `simulate_splines` + + :param discard_annotations: + whether to discard spline annotations, + forcing AMICI to read the spline as a piecewise assignment rule + + :param use_adjoint: + whether to use adjoint sensitivity computation + + :param skip_sensitivity: + whether to skip sensitivity computation + + :param debug: + if not `False`, do not check and return results and ground truth + instead. + If equal to `'print'`, in addition to the above print error values. + + :param parameter_lists: + Set AMICI parameter list to these values, + in order to check that partial sensitivity computation works. + + :param kwargs: + passed to `simulate_splines` + """ + if isinstance(splines, AbstractSpline): + splines = [splines] + + # Simulate PEtab problem + ( + initial_values, + petab_problem, + amici_model, + amici_solver, + llh, + sllh, + rdata, + edata, + state_ids, + param_ids, + ) = simulate_splines( + splines, + params_true, + initial_values, + discard_annotations=discard_annotations, + skip_sensitivity=skip_sensitivity, + use_adjoint=use_adjoint, + **kwargs, + ) + + tt = rdata["ts"] + + # Sort splines/ics/parameters as in the AMICI model + splines = [splines[species_to_index(name)] for name in state_ids] + initial_values = [ + initial_values[species_to_index(name)] for name in state_ids + ] + + def param_by_name(id): + for p in params_true.keys(): + if p.name == id: + return p + assert False + + params_sorted = [param_by_name(id) for id in param_ids] + + # Check states + if groundtruth == "compute": + groundtruth = compute_ground_truth( + splines, initial_values, tt, params_true, params_sorted + ) + if groundtruth is None: + x_true_sym = sp.Matrix( + [ + integrate_spline(spline, None, tt, iv) + for (spline, iv) in zip(splines, initial_values) + ] + ).transpose() + x_true = np.asarray(x_true_sym.subs(params_true), dtype=float) + else: + x_true = groundtruth["x_true"] + if not debug: + assert rdata.x.shape == x_true.shape + _check_results(rdata, "x", x_true, atol=x_atol, rtol=x_rtol) + elif debug == "print": + x_err_abs = abs(rdata["x"] - x_true) + x_err_rel = np.where(x_err_abs == 0, 0, x_err_abs / abs(x_true)) + print(f"x_atol={x_atol} x_rtol={x_rtol}") + print("x_err_abs:") + print(np.squeeze(x_err_abs)) + print("x_err_abs (maximum):") + print(x_err_abs.max()) + print("x_err_rel:") + print(np.squeeze(x_err_rel)) + print("x_err_rel (maximum):") + print(x_err_rel.max()) + + # Check spline evaluations + # TODO can we know how the splines are ordered inside w? + if False and discard_annotations and len(splines) == 1: + assert rdata["w"].shape[1] == 1 + w_true = np.column_stack( + [ + evaluate_spline(spline, params_true, tt, dtype=float) + for spline in splines + ] + ) + if not debug: + _check_results( + rdata, + "w", + w_true, + atol=w_atol, + rtol=w_rtol, + ) + elif debug == "print": + w_err_abs = abs(rdata["w"] - w_true) + w_err_rel = np.where(w_err_abs == 0, 0, w_err_abs / abs(w_true)) + print(f"w_atol={w_atol} w_rtol={w_rtol}") + print("w_err_abs:") + print(np.squeeze(w_err_abs)) + print("w_err_abs (maximum):") + print(w_err_abs.max()) + print("w_err_rel:") + print(np.squeeze(w_err_rel)) + print("w_err_rel (maximum):") + print(w_err_rel.max()) + else: + w_true = None + + # Check sensitivities + if params_sorted and not use_adjoint: + if skip_sensitivity: + pass + if groundtruth is None: + sx_by_state = [ + x_true_sym[:, i].jacobian(params_sorted).subs(params_true) + for i in range(x_true_sym.shape[1]) + ] + sx_by_state = [np.asarray(sx, dtype=float) for sx in sx_by_state] + sx_true = np.concatenate( + [sx[:, :, np.newaxis] for sx in sx_by_state], axis=2 + ) + else: + sx_true = groundtruth["sx_true"] + if not debug: + assert rdata.sx.shape == sx_true.shape + _check_results( + rdata, + "sx", + sx_true, + atol=sx_atol, + rtol=sx_rtol, + ) + elif debug == "print": + sx_err_abs = abs(rdata["sx"] - sx_true) + sx_err_rel = np.where( + sx_err_abs == 0, 0, sx_err_abs / abs(sx_true) + ) + print(f"sx_atol={sx_atol} sx_rtol={sx_rtol}") + print("sx_err_abs:") + print(np.squeeze(sx_err_abs)) + print("sx_err_abs (maximum):") + print(sx_err_abs.max()) + print("sx_err_rel:") + print(np.squeeze(sx_err_rel)) + print("sx_err_rel (maximum):") + print(sx_err_rel.max()) + else: + assert rdata["sx"] is None + + # Check log-likelihood + llh_true = -0.5 * rdata["y"].size * np.log(2 * np.pi) + llh_error_rel = abs(llh - llh_true) / abs(llh_true) + if (llh_error_rel > llh_rtol and debug is not True) or debug == "print": + print(f"{llh_rtol=}") + print(f"{llh_error_rel=}") + if not debug: + assert llh_error_rel <= llh_rtol + + # Check log-likelihood sensitivities + # (should be all zero, since we simulated with the true parameters) + if params_sorted: + if not skip_sensitivity: + 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 + else: + assert sllh is None + + # Try different parameter lists + if ( + not skip_sensitivity + and (not use_adjoint) + and parameter_lists is not None + ): + for plist in parameter_lists: + amici_model.setParameterList(plist) + amici_model.setTimepoints(rdata.t) + rdata_partial = amici.runAmiciSimulation(amici_model, amici_solver) + assert rdata.sx[:, plist, :].shape == rdata_partial.sx.shape + assert np.allclose(rdata.sx[:, plist, :], rdata_partial.sx) + + if debug: + return dict( + splines=splines, + initial_values=initial_values, + petab_problem=petab_problem, + amici_model=amici_model, + groundtruth=groundtruth, + rdata=rdata, + params_true=params_true, + params_sorted=params_sorted, + x_true=x_true, + w_true=w_true, + sx_true=sx_true, + llh_true=llh_true, + ) + else: + return dict( + initial_values=initial_values, + petab_problem=petab_problem, + amici_model=amici_model, + groundtruth=groundtruth, + ) + + +def check_splines_full( + splines, + params, + tols, + *args, + check_piecewise: bool = True, + check_forward: bool = True, + check_adjoint: bool = True, + folder: Optional[str] = None, + groundtruth: Optional[Union[dict, str]] = "compute", + return_groundtruth: bool = False, + **kwargs, +): + """ + Check example PEtab problem with `check_splines` + both using adjoint and forward sensitivities + and also in the case in which the splines are read as piecewise functions. + """ + if folder is None: + with TemporaryDirectory() as folder: + return check_splines_full( + splines, + params, + tols, + *args, + check_piecewise=check_piecewise, + check_forward=check_forward, + check_adjoint=check_adjoint, + folder=folder, + groundtruth=groundtruth, + return_groundtruth=return_groundtruth, + **kwargs, + ) + + if isinstance(tols, dict): + tols1 = tols2 = tols3 = tols + else: + tols1, tols2, tols3 = tols + + if isinstance(splines, AbstractSpline): + splines = [splines] + + contains_periodic = any( + spline.extrapolate == ("periodic", "periodic") for spline in splines + ) + + # Amortize creation of PEtab and AMICI objects + results = None + initial_values = None + petab_problem = None + amici_model = None + + if check_piecewise and not contains_periodic: + results = check_splines( + splines, + params, + *args, + **kwargs, + **tols1, + folder=folder, + discard_annotations=True, + use_adjoint=False, + groundtruth=groundtruth, + ) + initial_values = results["initial_values"] + petab_problem = results["petab_problem"] + groundtruth = results["groundtruth"] + + if check_forward: + results = check_splines( + splines, + params, + *args, + **kwargs, + **tols2, + initial_values=initial_values, + folder=folder, + petab_problem=petab_problem, + use_adjoint=False, + groundtruth=groundtruth, + ) + initial_values = results["initial_values"] + petab_problem = results["petab_problem"] + amici_model = results["amici_model"] + groundtruth = results["groundtruth"] + + if check_adjoint: + results = check_splines( + splines, + params, + *args, + **kwargs, + **tols3, + initial_values=initial_values, + folder=folder, + petab_problem=petab_problem, + amici_model=amici_model, + use_adjoint=True, + groundtruth=( + None if groundtruth == "compute" else groundtruth + ), # do not compute sensitivities if possible + ) + + if return_groundtruth: + if groundtruth is not None and not isinstance(groundtruth, str): + return groundtruth + elif results is None: + return None + else: + return results["groundtruth"] + + +def example_spline_1( + idx: int = 0, + offset: float = 0, + scale: float = 1, + num_nodes: int = 9, + fixed_values=None, # a list of indices or 'all' + extrapolate=None, +): + """A simple spline with no extrapolation.""" + + yy_true = np.asarray( + [0.0, 2.0, 5.0, 6.0, 5.0, 4.0, 2.0, 3.0, 4.0, 6.0, 7.0, 7.5, 6.5, 4.0] + ) + if num_nodes is not None: + assert 1 < num_nodes <= len(yy_true) + yy_true = yy_true[:num_nodes] + yy_true = scale * yy_true + offset + xx = UniformGrid(0, 25, number_of_nodes=len(yy_true)) + yy = list(sp.symbols(f"y{idx}_0:{len(yy_true)}")) + + if fixed_values is None: + params = dict(zip(yy, yy_true)) + elif fixed_values == "all": + params = {} + for i in range(len(yy_true)): + yy[i] = yy_true[i] + else: + params = {} + for i in range(len(yy_true)): + if i in fixed_values: + yy[i] = yy_true[i] + else: + params[yy[i]] = yy_true[i] + + spline = CubicHermiteSpline( + f"y{idx}", + nodes=xx, + values_at_nodes=yy, + bc=None, + extrapolate=extrapolate, + ) + + if os.name == "nt": + 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), + ) + else: + tols = ( + dict(llh_rtol=1e-15), + dict(llh_rtol=1e-15), + dict(llh_rtol=1e-15, sllh_atol=5e-8), + ) + + return spline, params, tols + + +def example_spline_2(idx: int = 0): + """A simple periodic spline.""" + yy_true = [0.0, 2.0, 3.0, 4.0, 1.0, -0.5, -1, -1.5, 0.5, 0.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)) + spline = CubicHermiteSpline( + f"y{idx}", + nodes=xx, + values_at_nodes=yy, + bc="periodic", + extrapolate="periodic", + ) + tols = ( + dict(llh_rtol=1e-15), + dict(llh_rtol=1e-15), + dict(llh_rtol=1e-15, sllh_atol=5e-8, x_rtol=1e-10, x_atol=5e-10), + ) + return spline, params, tols + + +def example_spline_3(idx: int = 0): + """A simple spline with extrapolation on the right side.""" + 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)) + spline = CubicHermiteSpline( + f"y{idx}", + nodes=xx, + values_at_nodes=yy, + bc=(None, "zeroderivative"), + extrapolate=(None, "constant"), + ) + tols = {} + return spline, params, tols diff --git a/deps/AMICI/python/tests/test_antimony_import.py b/deps/AMICI/python/tests/test_antimony_import.py new file mode 100644 index 000000000..41af014aa --- /dev/null +++ b/deps/AMICI/python/tests/test_antimony_import.py @@ -0,0 +1,42 @@ +import amici +import numpy as np +from amici.antimony_import import antimony2amici +from amici.testing import TemporaryDirectoryWinSafe as TemporaryDirectory + + +def test_antimony_example(): + """If this example requires changes, please also update documentation/python_interface.rst.""" + ant_model = """ + model lotka_volterra + # see https://en.wikipedia.org/wiki/Lotka%E2%80%93Volterra_equations + + # initial conditions + prey_density = 10; + predator_density = 10; + + # parameters + prey_growth_rate = 1.1; + predator_effect_on_prey = 0.4; + predator_death_rate = 0.4; + prey_effect_on_predator = 0.1; + + # dx/dt + prey_density' = prey_growth_rate * prey_density - predator_effect_on_prey * prey_density * predator_density; + predator_density' = prey_effect_on_predator * prey_density * predator_density - predator_death_rate * predator_density; + end + """ + module_name = "test_antimony_example_lv" + with TemporaryDirectory(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() + 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 diff --git a/deps/AMICI/python/tests/test_bngl.py b/deps/AMICI/python/tests/test_bngl.py new file mode 100644 index 000000000..42926e379 --- /dev/null +++ b/deps/AMICI/python/tests/test_bngl.py @@ -0,0 +1,100 @@ +import os + +import amici +import numpy as np +import pytest + +pysb = pytest.importorskip("pysb") + +from amici.bngl_import import bngl2amici +from amici.testing import TemporaryDirectoryWinSafe, skip_on_valgrind +from pysb.importers.bngl import model_from_bngl +from pysb.simulator import ScipyOdeSimulator + +tests = [ + "CaOscillate_Func", + "deleteMolecules", + "empty_compartments_block", + "gene_expr", + "gene_expr_func", + "gene_expr_simple", + "isomerization", + "Motivating_example_cBNGL", + "motor", + "simple_system", + "test_compartment_XML", + "test_setconc", + "test_synthesis_cBNGL_simple", + "test_synthesis_complex", + "test_synthesis_complex_0_cBNGL", + "test_synthesis_complex_source_cBNGL", + "test_synthesis_simple", + "univ_synth", + "Repressilator", + "test_paramname", + "tlmr", +] + + +@skip_on_valgrind +@pytest.mark.parametrize("example", tests) +def test_compare_to_pysb_simulation(example): + atol = 1e-8 + rtol = 1e-8 + + model_file = os.path.join( + os.path.dirname(__file__), + "..", + "..", + "ThirdParty", + "BioNetGen-2.7.0", + "Validate", + f"{example}.bngl", + ) + + pysb_model = model_from_bngl(model_file) + + # pysb part + tspan = np.linspace(0, 100, 101) + sim = ScipyOdeSimulator( + pysb_model, + tspan=tspan, + integrator_options={"rtol": rtol, "atol": atol}, + compiler="python", + ) + pysb_simres = sim.run() + + # amici part + cl = example not in ["Motivating_example_cBNGL", "univ_synth"] + + kwargs = { + "compute_conservation_laws": cl, + "observables": list(pysb_model.observables.keys()), + } + + with TemporaryDirectoryWinSafe(prefix=pysb_model.name) as outdir: + if not cl: + with pytest.raises(ValueError, match="Conservation laws"): + bngl2amici(model_file, outdir, compute_conservation_laws=True) + + if example in ["empty_compartments_block", "motor"]: + with pytest.raises(ValueError, match="Cannot add"): + bngl2amici(model_file, outdir, **kwargs) + return + else: + bngl2amici(model_file, outdir, **kwargs) + + amici_model_module = amici.import_model_module(pysb_model.name, outdir) + + model_amici = amici_model_module.getModel() + + model_amici.setTimepoints(tspan) + + solver = model_amici.getSolver() + solver.setMaxSteps(10**6) + solver.setAbsoluteTolerance(atol) + solver.setRelativeTolerance(rtol) + rdata = amici.runAmiciSimulation(model_amici, solver) + + # check agreement of species simulation + assert np.isclose(rdata.x, pysb_simres.species, 1e-4, 1e-4).all() 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 53f14a1bc..4d6a453b5 100644 --- a/deps/AMICI/python/tests/test_compare_conservation_laws_sbml.py +++ b/deps/AMICI/python/tests/test_compare_conservation_laws_sbml.py @@ -1,75 +1,93 @@ -import amici import os -import sys -import pytest -import numpy as np import warnings +import amici +import numpy as np +import pytest +from numpy.testing import assert_allclose, assert_array_equal + @pytest.fixture def edata_fixture(): """edata is generated to test pre- and postequilibration""" - edata_pre = amici.ExpData(2, 0, 0, - np.array([0., 0.1, 0.2, 0.5, 1., 2., 5., 10.])) + edata_pre = amici.ExpData( + 2, 0, 0, np.array([0.0, 0.1, 0.2, 0.5, 1.0, 2.0, 5.0, 10.0]) + ) edata_pre.setObservedData([1.5] * 16) - edata_pre.fixedParameters = np.array([5., 20.]) - edata_pre.fixedParametersPreequilibration = np.array([0., 10.]) + edata_pre.fixedParameters = np.array([5.0, 20.0]) + edata_pre.fixedParametersPreequilibration = np.array([0.0, 10.0]) edata_pre.reinitializeFixedParameterInitialStates = True # edata for postequilibration - edata_post = amici.ExpData(2, 0, 0, - np.array([float('inf')] * 3)) + edata_post = amici.ExpData(2, 0, 0, np.array([float("inf")] * 3)) edata_post.setObservedData([0.75] * 6) - edata_post.fixedParameters = np.array([7.5, 30.]) + edata_post.fixedParameters = np.array([7.5, 30.0]) # edata with both equilibrations - edata_full = amici.ExpData(2, 0, 0, - np.array([0., 0., 0., 1., 2., 2., 4., float('inf'), float('inf')])) + edata_full = amici.ExpData( + 2, + 0, + 0, + np.array( + [0.0, 0.0, 0.0, 1.0, 2.0, 2.0, 4.0, float("inf"), float("inf")] + ), + ) edata_full.setObservedData([3.14] * 18) - edata_full.fixedParameters = np.array([1., 2.]) - edata_full.fixedParametersPreequilibration = np.array([3., 4.]) + edata_full.fixedParameters = np.array([1.0, 2.0]) + edata_full.fixedParametersPreequilibration = np.array([3.0, 4.0]) edata_full.reinitializeFixedParameterInitialStates = True return edata_pre, edata_post, edata_full -def generate_models(): +@pytest.fixture(scope="session") +def models(): # SBML model we want to import - sbml_file = os.path.join(os.path.dirname(__file__), '..', - 'examples', 'example_constant_species', - 'model_constant_species.xml') + sbml_file = os.path.join( + os.path.dirname(__file__), + "..", + "examples", + "example_constant_species", + "model_constant_species.xml", + ) sbml_importer = amici.SbmlImporter(sbml_file) # Name of the model that will also be the name of the python module - model_name = model_output_dir ='model_constant_species' - model_name_cl = model_output_dir_cl = 'model_constant_species_cl' + model_name = model_output_dir = "model_constant_species" + model_name_cl = model_output_dir_cl = "model_constant_species_cl" # Define constants, observables, sigmas - constant_parameters = ['synthesis_substrate', 'init_enzyme'] + constant_parameters = ["synthesis_substrate", "init_enzyme"] observables = { - 'observable_product': {'name': '', 'formula': 'product'}, - 'observable_substrate': {'name': '', 'formula': 'substrate'}, + "observable_product": {"name": "", "formula": "product"}, + "observable_substrate": {"name": "", "formula": "substrate"}, } - sigmas = {'observable_product': 1.0, 'observable_substrate': 1.0} + sigmas = {"observable_product": 1.0, "observable_substrate": 1.0} # wrap models with and without conservations laws - sbml_importer.sbml2amici(model_name_cl, - model_output_dir_cl, - observables=observables, - constant_parameters=constant_parameters, - sigmas=sigmas) - sbml_importer.sbml2amici(model_name, - model_output_dir, - observables=observables, - constant_parameters=constant_parameters, - sigmas=sigmas, - compute_conservation_laws=False) + sbml_importer.sbml2amici( + model_name_cl, + model_output_dir_cl, + observables=observables, + constant_parameters=constant_parameters, + sigmas=sigmas, + ) + sbml_importer.sbml2amici( + model_name, + model_output_dir, + observables=observables, + constant_parameters=constant_parameters, + sigmas=sigmas, + compute_conservation_laws=False, + ) # load both models - model_without_cl_module = amici.import_model_module(model_name, - module_path=os.path.abspath(model_name)) - model_with_cl_module = amici.import_model_module(model_name_cl, - module_path=os.path.abspath(model_name_cl)) + model_without_cl_module = amici.import_model_module( + model_name, module_path=os.path.abspath(model_name) + ) + model_with_cl_module = amici.import_model_module( + model_name_cl, module_path=os.path.abspath(model_name_cl) + ) # get the models and return model_without_cl = model_without_cl_module.getModel() @@ -77,11 +95,15 @@ def generate_models(): return model_with_cl, model_without_cl -def get_results(model, edata=None, sensi_order=0, - sensi_meth=amici.SensitivityMethod.forward, - sensi_meth_preeq=amici.SensitivityMethod.forward, - reinitialize_states=False): - +def get_results( + model, + edata=None, + sensi_order=0, + sensi_meth=amici.SensitivityMethod.forward, + sensi_meth_preeq=amici.SensitivityMethod.forward, + stst_sensi_mode=amici.SteadyStateSensitivityMode.newtonOnly, + reinitialize_states=False, +): # set model and data properties model.setReinitializeFixedParameterInitialStates(reinitialize_states) @@ -91,6 +113,7 @@ def get_results(model, edata=None, sensi_order=0, solver.setSensitivityOrder(sensi_order) solver.setSensitivityMethodPreequilibration(sensi_meth_preeq) solver.setSensitivityMethod(sensi_meth) + model.setSteadyStateSensitivityMode(stst_sensi_mode) if edata is None: model.setTimepoints(np.linspace(0, 5, 101)) else: @@ -100,113 +123,174 @@ def get_results(model, edata=None, sensi_order=0, return amici.runAmiciSimulation(model, solver, edata) -def test_compare_conservation_laws_sbml(edata_fixture): - # first, create the modek - model_with_cl, model_without_cl = generate_models() +def test_compare_conservation_laws_sbml(models, edata_fixture): + # first, create the model + model_with_cl, model_without_cl = models + + assert model_with_cl.ncl() > 0 + assert model_without_cl.nx_rdata == model_with_cl.nx_rdata + assert model_with_cl.nx_solver < model_without_cl.nx_solver + assert len(model_with_cl.getStateIdsSolver()) == model_with_cl.nx_solver + assert ( + len(model_without_cl.getStateIdsSolver()) == model_without_cl.nx_solver + ) # ----- compare simulations wo edata, sensi = 0, states ------------------ # run simulations rdata_cl = get_results(model_with_cl) - assert rdata_cl['status'] == amici.AMICI_SUCCESS + assert rdata_cl["status"] == amici.AMICI_SUCCESS rdata = get_results(model_without_cl) - assert rdata['status'] == amici.AMICI_SUCCESS + assert rdata["status"] == amici.AMICI_SUCCESS # compare state trajectories - assert np.isclose(rdata['x'], rdata_cl['x']).all() + assert_allclose( + rdata["x"], + rdata_cl["x"], + rtol=1.0e-5, + atol=1.0e-8, + err_msg="rdata.x mismatch", + ) # ----- compare simulations wo edata, sensi = 1, states and sensis ------- # run simulations rdata_cl = get_results(model_with_cl, sensi_order=1) - assert rdata_cl['status'] == amici.AMICI_SUCCESS + assert rdata_cl["status"] == amici.AMICI_SUCCESS rdata = get_results(model_without_cl, sensi_order=1) - assert rdata['status'] == amici.AMICI_SUCCESS + assert rdata["status"] == amici.AMICI_SUCCESS # compare state trajectories - for field in ['x', 'sx']: - assert np.isclose(rdata[field], rdata_cl[field]).all(), field + for field in ["x", "sx"]: + assert_allclose( + rdata[field], + rdata_cl[field], + rtol=1.0e-5, + atol=1.0e-8, + err_msg=f"rdata.{field} mismatch", + ) # ----- compare simulations wo edata, sensi = 0, states and sensis ------- - model_without_cl.setSteadyStateSensitivityMode( - amici.SteadyStateSensitivityMode.simulationFSA - ) # run simulations edata, _, _ = edata_fixture rdata_cl = get_results(model_with_cl, edata=edata) - assert rdata_cl['status'] == amici.AMICI_SUCCESS + assert rdata_cl["status"] == amici.AMICI_SUCCESS rdata = get_results(model_without_cl, edata=edata) - assert rdata['status'] == amici.AMICI_SUCCESS + assert rdata["status"] == amici.AMICI_SUCCESS # compare preequilibrated states - for field in ['x', 'x_ss', 'llh']: - assert np.isclose(rdata[field], rdata_cl[field]).all(), field + for field in ["x", "x_ss", "llh"]: + assert_allclose( + rdata[field], + rdata_cl[field], + rtol=1.0e-5, + atol=1.0e-8, + err_msg=f"rdata.{field} mismatch", + ) # ----- compare simulations wo edata, sensi = 1, states and sensis ------- # run simulations rdata_cl = get_results(model_with_cl, edata=edata, sensi_order=1) - assert rdata_cl['status'] == amici.AMICI_SUCCESS - rdata = get_results(model_without_cl, edata=edata, sensi_order=1) - assert rdata['status'] == amici.AMICI_SUCCESS + assert rdata_cl["status"] == amici.AMICI_SUCCESS + rdata = get_results( + model_without_cl, + edata=edata, + sensi_order=1, + stst_sensi_mode=amici.SteadyStateSensitivityMode.integrateIfNewtonFails, + ) + assert rdata["status"] == amici.AMICI_SUCCESS # check that steady state computation succeeded only by sim in full model - assert (rdata['preeq_status'] == np.array([-3, 1, 0])).all() + assert_array_equal(rdata["preeq_status"], np.array([[-3, 1, 0]])) # check that steady state computation succeeded by Newton in reduced model - assert (rdata_cl['preeq_status'] == np.array([1, 0, 0])).all() + assert_array_equal(rdata_cl["preeq_status"], np.array([[1, 0, 0]])) # compare state sensitivities with edata and preequilibration - for field in ['x', 'x_ss', 'sx', 'llh', 'sllh']: - assert np.isclose(rdata[field], rdata_cl[field]).all(), field + for field in ["x", "x_ss", "sx", "llh", "sllh"]: + assert_allclose( + rdata[field], + rdata_cl[field], + rtol=1.0e-5, + atol=1.0e-8, + err_msg=f"rdata.{field} mismatch", + ) # ----- check failure st.st. sensi computation if run wo CLs ------------- - # check failure of steady state senistivity computation if run wo CLs + # check failure of steady state sensitivity computation if run wo CLs model_without_cl.setSteadyStateSensitivityMode( amici.SteadyStateSensitivityMode.newtonOnly ) with warnings.catch_warnings(): warnings.filterwarnings("ignore") rdata = get_results(model_without_cl, edata=edata, sensi_order=1) - assert rdata['status'] == amici.AMICI_ERROR + assert rdata["status"] == amici.AMICI_ERROR -def test_adjoint_pre_and_post_equilibration(edata_fixture): +def test_adjoint_pre_and_post_equilibration(models, edata_fixture): # get both models - model_module = amici.import_model_module('model_constant_species', - module_path=os.path.abspath('model_constant_species')) - model = model_module.getModel() - model_module_cl = amici.import_model_module('model_constant_species_cl', - module_path=os.path.abspath('model_constant_species_cl')) - model_cl = model_module_cl.getModel() + model_cl, model = models # check gradient with and without state reinitialization for edata in edata_fixture: for reinit in [False, True]: - # --- compare different ways of preequilibration, full rank Jacobian --------- + # compare different ways of preequilibration, full rank Jacobian # forward preequilibration, forward simulation - rff_cl = get_results(model_cl, edata=edata, sensi_order=1, - sensi_meth=amici.SensitivityMethod.forward, - sensi_meth_preeq=amici.SensitivityMethod.forward, - reinitialize_states=reinit) + rff_cl = get_results( + model_cl, + edata=edata, + sensi_order=1, + sensi_meth=amici.SensitivityMethod.forward, + sensi_meth_preeq=amici.SensitivityMethod.forward, + reinitialize_states=reinit, + ) # forward preequilibration, adjoint simulation - rfa_cl = get_results(model_cl, edata=edata, sensi_order=1, - sensi_meth=amici.SensitivityMethod.adjoint, - sensi_meth_preeq=amici.SensitivityMethod.forward, - reinitialize_states=reinit) + rfa_cl = get_results( + model_cl, + edata=edata, + sensi_order=1, + sensi_meth=amici.SensitivityMethod.adjoint, + sensi_meth_preeq=amici.SensitivityMethod.forward, + reinitialize_states=reinit, + ) # adjoint preequilibration, adjoint simulation - raa_cl = get_results(model_cl, edata=edata, sensi_order=1, - sensi_meth=amici.SensitivityMethod.adjoint, - sensi_meth_preeq=amici.SensitivityMethod.adjoint, - reinitialize_states=reinit) + raa_cl = get_results( + model_cl, + edata=edata, + sensi_order=1, + sensi_meth=amici.SensitivityMethod.adjoint, + sensi_meth_preeq=amici.SensitivityMethod.adjoint, + reinitialize_states=reinit, + ) # assert all are close - assert np.isclose(rff_cl['sllh'], rfa_cl['sllh']).all() - assert np.isclose(rfa_cl['sllh'], raa_cl['sllh']).all() - assert np.isclose(raa_cl['sllh'], rff_cl['sllh']).all() - - # --- compare fully adjoint approach to simulation with singular Jacobian ---- - raa = get_results(model, edata=edata, sensi_order=1, - sensi_meth=amici.SensitivityMethod.adjoint, - sensi_meth_preeq=amici.SensitivityMethod.adjoint, - reinitialize_states=reinit) + assert_allclose( + rff_cl["sllh"], rfa_cl["sllh"], rtol=1.0e-5, atol=1.0e-8 + ) + assert_allclose( + rfa_cl["sllh"], raa_cl["sllh"], rtol=1.0e-5, atol=1.0e-8 + ) + assert_allclose( + raa_cl["sllh"], rff_cl["sllh"], rtol=1.0e-5, atol=1.0e-8 + ) + + # compare fully adjoint approach to simulation with singular + # Jacobian + raa = get_results( + model, + edata=edata, + sensi_order=1, + sensi_meth=amici.SensitivityMethod.adjoint, + sensi_meth_preeq=amici.SensitivityMethod.adjoint, + stst_sensi_mode=amici.SteadyStateSensitivityMode.integrateIfNewtonFails, + reinitialize_states=reinit, + ) # assert gradients are close (quadrature tolerances are laxer) - assert np.isclose(raa_cl['sllh'], raa['sllh'], 1e-5, 1e-5).all() + assert_allclose(raa_cl["sllh"], raa["sllh"], 1e-5, 1e-5) + + +def test_get_set_model_settings(models): + """test amici.(get|set)_model_settings cycles for models with and without + conservation laws""" + + for model in models: + amici.set_model_settings(model, amici.get_model_settings(model)) diff --git a/deps/AMICI/python/tests/test_conserved_quantities_demartino.py b/deps/AMICI/python/tests/test_conserved_quantities_demartino.py new file mode 100644 index 000000000..339743cc4 --- /dev/null +++ b/deps/AMICI/python/tests/test_conserved_quantities_demartino.py @@ -0,0 +1,870 @@ +"""Tests for conservation laws / conserved moieties""" +import os +from time import perf_counter + +import numpy as np +import pytest +import sympy as sp +from amici.conserved_quantities_demartino import _fill, _kernel +from amici.conserved_quantities_demartino import _output as output +from amici.conserved_quantities_demartino import ( + compute_moiety_conservation_laws, +) +from amici.logging import get_logger, log_execution_time +from amici.testing import skip_on_valgrind + +logger = get_logger(__name__) + +# reference data for `engaged_species` after kernel() +demartino2014_kernel_engaged_species = [ + 179, + 181, + 185, + 186, + 187, + 190, + 191, + 194, + 195, + 197, + 198, + 200, + 208, + 209, + 210, + 211, + 214, + 215, + 218, + 219, + 221, + 222, + 224, + 277, + 292, + 340, + 422, + 467, + 468, + 490, + 491, + 598, + 613, + 966, + 968, + 1074, + 1171, + 1221, + 1223, + 1234, + 1266, + 1478, + 1479, + 1480, + 1481, + 1496, + 1497, + 1498, + 1501, + 1526, + 1527, + 1528, + 1529, + 394, + 1066, + 398, + 465, + 466, + 594, + 671, + 429, + 990, + 652, + 655, + 662, + 663, + 664, + 665, + 666, + 667, + 668, + 669, + 759, + 760, + 920, + 921, + 569, + 1491, + 1055, + 1546, + 276, + 1333, + 1421, + 1429, + 1430, + 1438, + 1551, + 1428, + 1439, + 1552, + 1513, + 1553, + 1520, + 1523, + 1530, + 1531, + 384, + 1536, + 440, + 1537, + 447, + 1538, + 456, + 1539, + 582, + 1540, + 876, + 1541, + 885, + 1542, + 911, + 1543, + 978, + 1544, + 1010, + 1545, + 1070, + 1547, + 761, + 1127, + 1548, + 1324, + 1549, + 1370, + 1550, + 1554, + 1560, + 1555, + 1580, + 1556, + 1644, +] + + +@pytest.fixture(scope="session") +def data_demartino2014(): + """Get tests from DeMartino2014 Suppl. Material""" + import gzip + import io + import urllib.request + + # 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(fileobj=io.BytesIO(response.read())) + S = [ + int(item) + for sl in [ + entry.decode("ascii").strip().split("\t") + for entry in data.readlines() + ] + for item in sl + ] + + # 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()) + ] + + return S, row_names + + +@skip_on_valgrind +def test_kernel_demartino2014(data_demartino2014, quiet=True): + """Invoke test case and benchmarking for De Martino's published results + for E. coli network. Kernel-only.""" + stoichiometric_list, row_names = data_demartino2014 + num_species = 1668 + num_reactions = 2381 + assert ( + len(stoichiometric_list) == num_species * num_reactions + ), "Unexpected dimension of stoichiometric matrix" + + # Expected number of metabolites per conservation law found after kernel() + expected_num_species = ( + [53] + [2] * 11 + [6] + [3] * 2 + [2] * 15 + [3] + [2] * 5 + ) + + ( + kernel_dim, + engaged_species, + int_kernel_dim, + conserved_moieties, + cls_species_idxs, + cls_coefficients, + ) = _kernel(stoichiometric_list, num_species, num_reactions) + + if not quiet: + output( + int_kernel_dim, + kernel_dim, + engaged_species, + cls_species_idxs, + cls_coefficients, + row_names, + ) + + # There are 38 conservation laws, engaging 131 metabolites + # 36 are integers (conserved moieties), engaging 128 metabolites (from C++) + assert kernel_dim == 38, "Not all conservation laws found" + assert int_kernel_dim == 36, "Not all conserved moiety laws found" + assert ( + engaged_species == demartino2014_kernel_engaged_species + ), "Wrong engaged metabolites reported" + assert ( + len(conserved_moieties) == 128 + ), "Wrong number of conserved moieties reported" + + # Assert that each conserved moiety has the correct number of metabolites + for i in range(int_kernel_dim - 2): + assert ( + len(cls_species_idxs[i]) == expected_num_species[i] + ), f"Moiety #{i + 1} failed for test case (De Martino et al.)" + + +@skip_on_valgrind +def test_fill_demartino2014(data_demartino2014): + """Test creation of interaction matrix""" + stoichiometric_list, row_names = data_demartino2014 + num_species = 1668 + J, J2, fields = _fill( + stoichiometric_list, demartino2014_kernel_engaged_species, num_species + ) + ref_for_J = [ + [25, 27], + [12, 42], + [13, 43], + [14, 44], + [15, 41], + [16, 45], + [17, 47], + [18, 48], + [19, 23, 49], + [20, 50], + [21, 51], + [22, 52], + [1, 23, 30, 35], + [2, 23, 29, 35], + [3, 23, 35, 46], + [4, 23, 33, 35], + [5, 23, 31, 35], + [6, 23, 35, 37], + [7, 23, 28, 35], + [8, 23, 32, 35], + [9, 23, 34, 35], + [10, 23, 35, 40], + [11, 23, 35, 36], + [ + 8, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 24, + 25, + 26, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 46, + ], + [23, 25], + [0, 23, 24, 35], + [23], + [0, 28], + [18, 23, 27, 35], + [13, 23, 35, 42], + [12, 23, 35, 47], + [16, 23, 35, 47], + [19, 23, 35, 45], + [15, 23, 35, 44], + [20, 23, 35, 48], + [ + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 25, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 36, + 37, + 40, + 46, + ], + [22, 23, 35, 49], + [17, 23, 35, 50], + [23, 51], + [23, 41], + [21, 23, 35, 52], + [4, 39], + [1, 29], + [2, 46], + [3, 33], + [5, 32], + [14, 23, 35, 43], + [6, 30, 31], + [7, 34], + [8, 36], + [9, 37], + [10, 38], + [11, 40], + [54], + [53], + [58, 80], + [57, 59, 82], + [56], + [55, 59, 80], + [56, 58, 82], + [61], + [60], + [63], + [62], + [65], + [64], + [67, 68, 69], + [66, 68, 69], + [66, 67, 69, 70, 71, 94, 95], + [66, 67, 68, 70, 71, 94, 95], + [68, 69, 71], + [68, 69, 70], + [73], + [72], + [75], + [74], + [77], + [76], + [79], + [78], + [55, 58, 81], + [80], + [56, 59], + [84], + [83, 85, 87], + [84, 86, 87], + [85], + [84, 85], + [89], + [88], + [91], + [90], + [93], + [92], + [68, 69, 95], + [68, 69, 94], + [97], + [96], + [99], + [98], + [101], + [100], + [103], + [102], + [105], + [104], + [107], + [106], + [109], + [108], + [111], + [110], + [113], + [112], + [115], + [114], + [117], + [116], + [119], + [118, 120], + [119], + [122], + [121], + [124], + [123], + [126], + [125], + [128], + [127], + [130], + [129], + ] + ref_for_J2 = [ + [-1, -1], + [-1, -1], + [-1, -1], + [-1, -1], + [-1, -1], + [-1, -1], + [-1, -1], + [-1, -1], + [-1, -2, -1], + [-1, -1], + [-1, -1], + [-1, -1], + [-1, 1, -1, -1], + [-1, 1, -1, -1], + [-1, 1, -1, -1], + [-1, 1, -1, -1], + [-1, 1, -1, -1], + [-1, 1, -1, -1], + [-1, 1, -1, -1], + [-1, 1, -1, -1], + [-1, 1, -1, -1], + [-1, 1, -1, -1], + [-1, 1, -1, -1], + [ + -2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + -2, + 1, + -1, + -1, + -1, + -1, + -3, + -6, + -6, + -1, + -13, + -7, + -3, + -3, + -3, + -5, + -5, + ], + [-2, -1], + [-1, 1, -1, -2], + [-1], + [-1, -2], + [-1, -1, -2, 1], + [-1, -1, 1, -2], + [-1, -1, 1, -1], + [-1, -3, 1, -2], + [-1, -6, 1, -2], + [-1, -6, 1, -2], + [-1, -1, 1, -2], + [ + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -13, + -2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + ], + [-1, -7, 1, -2], + [-1, -3, 1, -2], + [-3, -2], + [-3, -2], + [-1, -5, 1, -2], + [-1, -2], + [-1, -2], + [-1, -2], + [-1, -2], + [-1, -2], + [-1, -5, 1, -2], + [-1, -1, -2], + [-1, -2], + [-1, -2], + [-1, -2], + [-1, -2], + [-1, -2], + [-2], + [-2], + [1, -1], + [-2, -1, -1], + [-2], + [1, -1, -1], + [-1, -1, 1], + [-1], + [-1], + [-2], + [-2], + [-2], + [-2], + [-2, -1, 1], + [-2, 1, -1], + [-1, 1, -3, -1, 1, -1, 1], + [1, -1, -3, 1, -1, 1, -1], + [-1, 1, -2], + [1, -1, -2], + [-5], + [-5], + [-6], + [-6], + [-2], + [-2], + [-1], + [-1], + [-1, -1, -1], + [-1], + [-1, 1], + [-1], + [-1, 1, -1], + [1, -1, -1], + [-1], + [-1, -1], + [-1], + [-1], + [-1], + [-1], + [-2], + [-2], + [-1, 1, -10], + [1, -1, -10], + [-1], + [-1], + [-1], + [-1], + [-1], + [-1], + [-1], + [-1], + [-1], + [-1], + [-1], + [-1], + [-2], + [-2], + [-1], + [-1], + [-1], + [-1], + [-1], + [-1], + [-1], + [-1], + [-1], + [-1, -1], + [-1], + [-1], + [-1], + [-1], + [-1], + [-1], + [-1], + [-1], + [-1], + [-1], + [-1], + ] + ref_for_fields = [ + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 51, + 3, + 3, + 1, + 3, + 3, + 3, + 2, + 5, + 8, + 8, + 3, + 15, + 9, + 5, + 5, + 5, + 7, + 3, + 3, + 3, + 3, + 3, + 7, + 4, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 1, + 3, + 2, + 2, + 2, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 2, + 2, + 5, + 5, + 6, + 6, + 2, + 2, + 1, + 1, + 2, + 1, + 1, + 1, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 10, + 10, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + ] + # compare J from Python with reference from C++ + for i in range(len(ref_for_J)): + assert ( + J[i] == ref_for_J[i] + ), f"J_{i} ({J[i]}) does not match J_{i}_ref ({ref_for_J[i]})" + assert not any(J[len(ref_for_J) :]) + + # compare J2 from Python with reference from C++ + for i in range(len(ref_for_J2)): + assert ( + J2[i] == ref_for_J2[i] + ), f"J_{i} ({J2[i]}) does not match J_{i}_ref ({ref_for_J2[i]})" + assert not any(J2[len(ref_for_J2) :]) + + # compare fields from Python with reference from C++ + for i in range(len(ref_for_fields)): + assert ( + fields[i] == ref_for_fields[i] + ), f"J_{i} ({fields[i]}) does not match J_{i}_ref ({ref_for_fields[i]})" + assert not any(fields[len(ref_for_fields) :]) + + +def compute_moiety_conservation_laws_demartino2014( + data_demartino2014, quiet=False +): + """Compute conserved quantities for De Martino's published results + for E. coli network""" + stoichiometric_list, row_names = data_demartino2014 + + num_species = 1668 + num_reactions = 2381 + assert ( + len(stoichiometric_list) == num_species * num_reactions + ), "Unexpected dimension of stoichiometric matrix" + + start = perf_counter() + cls_state_idxs, cls_coefficients = compute_moiety_conservation_laws( + stoichiometric_list, + num_species=num_species, + num_reactions=num_reactions, + ) + runtime = perf_counter() - start + if not quiet: + print(f"Execution time: {runtime} [s]") + + assert len(cls_state_idxs) == len(cls_coefficients) == 38 + return runtime + + +@skip_on_valgrind +def test_compute_moiety_conservation_laws_demartino2014(data_demartino2014): + """Invoke test case and benchmarking for De Martino's published results + for E. coli network""" + compute_moiety_conservation_laws_demartino2014( + data_demartino2014, quiet=False + ) + + +@skip_on_valgrind +@log_execution_time("Detecting moiety conservation laws", logger) +def test_cl_detect_execution_time(data_demartino2014): + """Test execution time stays within a certain predefined bound. + As the algorithm is non-deterministic, allow for some retries. + Only one has to succeed.""" + max_tries = 5 + # <5s on modern hardware, but leave some slack + max_time_seconds = 40 if "GITHUB_ACTIONS" in os.environ else 10 + + runtime = np.Inf + + for _ in range(max_tries): + runtime = compute_moiety_conservation_laws_demartino2014( + data_demartino2014, quiet=True + ) + if runtime < max_time_seconds: + break + assert runtime < max_time_seconds, "Took too long" + + +@skip_on_valgrind +def test_compute_moiety_conservation_laws_simple(): + """Test a simple example, ensure the conservation laws are identified + reliably. Requires the Monte Carlo to identify all.""" + stoichiometric_matrix = sp.Matrix( + [[-1.0, 1.0], [-1.0, 1.0], [1.0, -1.0], [1.0, -1.0]] + ) + stoichiometric_list = [ + float(entry) for entry in stoichiometric_matrix.T.flat() + ] + + num_tries = 1000 + found_all_n_times = 0 + for _ in range(num_tries): + cls_state_idxs, cls_coefficients = compute_moiety_conservation_laws( + stoichiometric_list, *stoichiometric_matrix.shape + ) + + assert cls_state_idxs in ( + [[0, 3], [1, 2], [1, 3]], + [[0, 3], [1, 2], [0, 2]], + # should happen rarely + [[0, 3], [1, 2]], + ) + assert cls_coefficients in ( + [[1.0, 1.0], [1.0, 1.0], [1.0, 1.0]], + [[1.0, 1.0], [1.0, 1.0]], + ) + + num_cls_found = len(cls_state_idxs) + if num_cls_found == 3: + found_all_n_times += 1 + # sometimes we don't find all conservation laws, but this should be rare + assert found_all_n_times / num_tries >= 0.995 diff --git a/deps/AMICI/python/tests/test_conserved_quantities_rref.py b/deps/AMICI/python/tests/test_conserved_quantities_rref.py new file mode 100644 index 000000000..ada4b4672 --- /dev/null +++ b/deps/AMICI/python/tests/test_conserved_quantities_rref.py @@ -0,0 +1,47 @@ +import os + +import numpy as np +import pytest +import sympy as sp +from amici.conserved_quantities_rref import nullspace_by_rref, pivots, rref +from amici.testing import skip_on_valgrind + + +def random_matrix_generator(min_dim, max_dim, count): + """Generate random 2D square matrix""" + rng = np.random.default_rng(12345) + for rows, cols in rng.integers(min_dim, max_dim, [count, 2]): + yield np.random.rand(rows, cols) + + +@skip_on_valgrind +@pytest.mark.parametrize("mat", random_matrix_generator(0, 10, 200)) +def test_rref(mat): + """Create some random matrices and compare output of ``rref`` and + ``pivots`` to that of sympy""" + actual_rref = rref(mat) + expected_rref, expected_pivots = sp.Matrix(mat).rref() + expected_rref = np.asarray(expected_rref, dtype=float) + + assert list(expected_pivots) == pivots(actual_rref) + assert np.allclose(expected_rref, actual_rref) + + +@skip_on_valgrind +@pytest.mark.parametrize("mat", random_matrix_generator(0, 50, 50)) +def test_nullspace_by_rref(mat): + """Test ``nullspace_by_rref`` on a number of random matrices and compare + to sympy results""" + actual = nullspace_by_rref(mat) + + if len(actual): + assert np.allclose(mat.dot(actual.T), 0) + + expected = sp.Matrix(mat).nullspace() + expected = ( + np.hstack(np.asarray(expected, dtype=float)).T + if len(expected) + else np.array([]) + ) + + assert np.allclose(actual, expected, rtol=1e-8) diff --git a/deps/AMICI/python/tests/test_cxxcodeprinter.py b/deps/AMICI/python/tests/test_cxxcodeprinter.py new file mode 100644 index 000000000..384b8ad9a --- /dev/null +++ b/deps/AMICI/python/tests/test_cxxcodeprinter.py @@ -0,0 +1,14 @@ +import sympy as sp +from amici.cxxcodeprinter import AmiciCxxCodePrinter +from sympy.codegen.rewriting import optims_c99 + + +def test_optimizations(): + """Check that AmiciCxxCodePrinter handles optimizations correctly.""" + try: + old_optim = AmiciCxxCodePrinter.optimizations + AmiciCxxCodePrinter.optimizations = optims_c99 + cp = AmiciCxxCodePrinter() + assert "expm1" in cp.doprint(sp.sympify("exp(x) - 1")) + finally: + AmiciCxxCodePrinter.optimizations = old_optim diff --git a/deps/AMICI/python/tests/test_edata.py b/deps/AMICI/python/tests/test_edata.py new file mode 100644 index 000000000..9c4d9b9ed --- /dev/null +++ b/deps/AMICI/python/tests/test_edata.py @@ -0,0 +1,49 @@ +"""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 + + +@skip_on_valgrind +def test_edata_sensi_unscaling(model_units_module): + """ + ExpData parameters should be used for unscaling initial state + sensitivities. + """ + parameters0 = (5, 5) + parameters1 = (2, 2) + + sx0 = (3, 3, 3, 3) + + parameter_scales_log10 = [amici.ParameterScaling.log10.value] * len( + parameters0 + ) + amici_parameter_scales_log10 = amici.parameterScalingFromIntVector( + parameter_scales_log10 + ) + + model = model_units_module.getModel() + model.setTimepoints(np.linspace(0, 1, 3)) + model.setParameterScale(parameter_scales_log10) + model.setParameters(parameters0) + + solver = model.getSolver() + solver.setSensitivityOrder(amici.SensitivityOrder.first) + + edata0 = amici.ExpData(model) + edata0.pscale = amici_parameter_scales_log10 + edata0.parameters = parameters0 + edata0.sx0 = sx0 + + edata1 = amici.ExpData(model) + edata1.pscale = amici_parameter_scales_log10 + edata1.parameters = parameters1 + edata1.sx0 = sx0 + + rdata0 = amici.runAmiciSimulation(model, solver, edata0) + rdata1 = amici.runAmiciSimulation(model, solver, edata1) + + # The initial state sensitivities are as specified. + assert np.isclose(rdata0.sx0.flatten(), sx0).all() + assert np.isclose(rdata1.sx0.flatten(), sx0).all() diff --git a/deps/AMICI/python/tests/test_events.py b/deps/AMICI/python/tests/test_events.py index 8bb34ecf8..d2a177bde 100644 --- a/deps/AMICI/python/tests/test_events.py +++ b/deps/AMICI/python/tests/test_events.py @@ -1,21 +1,34 @@ """Tests for SBML events, including piecewise expressions.""" -import numpy as np -import pytest -import os -from scipy.linalg import expm from copy import deepcopy +import numpy as np +import pytest +from amici.testing import skip_on_valgrind from util import ( - create_sbml_model, - create_amici_model, - check_trajectories_without_sensitivities, check_trajectories_with_forward_sensitivities, + check_trajectories_without_sensitivities, + create_amici_model, + create_sbml_model, ) -@pytest.fixture(params=[ - 'events_plus_heavisides', - 'nested_events', -]) + +@pytest.fixture( + params=[ + pytest.param("events_plus_heavisides", marks=skip_on_valgrind), + pytest.param( + "piecewise_plus_event_simple_case", marks=skip_on_valgrind + ), + pytest.param( + "piecewise_plus_event_semi_complicated", marks=skip_on_valgrind + ), + pytest.param( + "piecewise_plus_event_trigger_depends_on_state", + marks=skip_on_valgrind, + ), + pytest.param("nested_events", marks=skip_on_valgrind), + pytest.param("event_state_dep_ddeltax_dtpx", marks=skip_on_valgrind), + ] +) def model(request): """Returns the requested AMICI model and analytical expressions.""" ( @@ -25,8 +38,8 @@ def model(request): species, events, timepoints, - x_pected, - sx_pected + x_expected, + sx_expected, ) = get_model_definition(request.param) # SBML model @@ -47,22 +60,30 @@ def model(request): ) amici_model.setTimepoints(timepoints) - return amici_model, parameters, timepoints, x_pected, sx_pected + return amici_model, parameters, timepoints, x_expected, sx_expected def get_model_definition(model_name): - if model_name == 'events_plus_heavisides': - return model_definition_events_plus_heavisides() - if model_name == 'nested_events': + if model_name == "piecewise_plus_event_simple_case": + return model_definition_piecewise_plus_event_simple_case() + if model_name == "piecewise_plus_event_semi_complicated": + return model_definition_piecewise_plus_event_semi_complicated() + if model_name == "piecewise_plus_event_trigger_depends_on_state": + return model_definition_piecewise_plus_event_trigger_depends_on_state() + if model_name == "events_plus_heavisides": + return model_definition_events_plus_heavisides() + if model_name == "nested_events": return model_definition_nested_events() - else: - raise NotImplementedError( - f'Model with name {model_name} is not implemented.' - ) + if model_name == "event_state_dep_ddeltax_dtpx": + return model_definition_event_state_dep_ddeltax_dtpx() + + raise NotImplementedError( + f"Model with name {model_name} is not implemented." + ) def model_definition_events_plus_heavisides(): - """Test model for state- and parameter-dependent heavisides. + """Test model for state- and parameter-dependent Heavisides. ODEs ---- @@ -89,44 +110,44 @@ def model_definition_events_plus_heavisides(): [ zeta / 3]] """ # Model components - species = ['x_1', 'x_2', 'x_3'] + species = ["x_1", "x_2", "x_3"] initial_assignments = { - 'x_1': 'k1', - 'x_2': 'k2', - 'x_3': 'k3', + "x_1": "k1", + "x_2": "k2", + "x_3": "k3", } rate_rules = { - 'x_1': 'piecewise( -alpha * x_1, time >= delta, 0)', - 'x_2': 'beta * x_1 - gamma * x_2', - 'x_3': '-eta * x_3 + piecewise( 1, time >= zeta, 0)', + "x_1": "piecewise( -alpha * x_1, time >= delta, 0)", + "x_2": "beta * x_1 - gamma * x_2", + "x_3": "-eta * x_3 + piecewise( 1, time >= zeta, 0)", } parameters = { - 'k1': 2, - 'k2': 0.01, - 'k3': 5, - 'alpha': 2, - 'beta': 3, - 'gamma': 2, - 'delta': 3, - 'eta': 1, - 'zeta': 5, + "k1": 2, + "k2": 0.01, + "k3": 5, + "alpha": 2, + "beta": 3, + "gamma": 2, + "delta": 3, + "eta": 1, + "zeta": 5, } events = { - 'event_1': { - 'trigger': 'x_3 < k1', - 'target': 'x_1', - 'assignment': 'x_1 - x_3 / 2' + "event_1": { + "trigger": "x_3 < k1", + "target": "x_1", + "assignment": "x_1 - x_3 / 2", + }, + "event_2": { + "trigger": "time >= zeta", + "target": "x_3", + "assignment": "x_3 + zeta / 3", }, - 'event_2': { - 'trigger': 'time >= zeta', - 'target': 'x_3', - 'assignment': 'x_3 + zeta / 3' - } } timepoints = np.linspace(0, 8, 400) # Analytical solution - def x_pected(t, k1, k2, k3, alpha, beta, gamma, delta, eta, zeta): + def x_expected(t, k1, k2, k3, alpha, beta, gamma, delta, eta, zeta): # The system reads dx/dt = Ax + b # x0 = (k1, k2, k3) x0 = np.array([[k1], [k2], [k3]]) @@ -140,21 +161,17 @@ def get_early_x(t): # compute dynamics if t < event_1_time: # Define A - A = np.array([[0, 0, 0], - [beta, -gamma, 0], - [0, 0, -eta]]) + A = np.array([[0, 0, 0], [beta, -gamma, 0], [0, 0, -eta]]) tmp_x = expm(t * A) return np.matmul(tmp_x, x0) elif t <= event_2_time: # "simulate" until first event - A = np.array([[0, 0, 0], - [beta, -gamma, 0], - [0, 0, -eta]]) + A = np.array([[0, 0, 0], [beta, -gamma, 0], [0, 0, -eta]]) tmp_x = expm(event_1_time * A) x1 = np.matmul(tmp_x, x0) # apply bolus - delta_x = np.array([[float(-x1[2, :] / 2)], [0], [0]]) + delta_x = np.array([[float(-x1[2, 0] / 2)], [0], [0]]) x1 += delta_x # "simulate" on tmp_x = expm((t - event_1_time) * A) @@ -165,42 +182,40 @@ def get_early_x(t): elif t < event_3_time: x2 = get_early_x(event_2_time) - A = np.array([[-alpha, 0, 0], - [beta, -gamma, 0], - [0, 0, -eta]]) + A = np.array([[-alpha, 0, 0], [beta, -gamma, 0], [0, 0, -eta]]) tmp_x = expm((t - event_2_time) * A) x = np.matmul(tmp_x, x2).flatten() else: x2 = get_early_x(event_2_time) - A = np.array([[-alpha, 0, 0], - [beta, -gamma, 0], - [0, 0, -eta]]) + A = np.array([[-alpha, 0, 0], [beta, -gamma, 0], [0, 0, -eta]]) tmp_x = expm((event_3_time - event_2_time) * A) x3 = np.matmul(tmp_x, x2) # apply bolus x3 += np.array([[0], [0], [zeta / 3]]) hom_x = np.matmul(expm((t - event_3_time) * A), x3) - inhom_x = [[0], [0], - [-np.exp(-eta * (t - event_3_time)) / (eta) - + 1 / (eta)]] + inhom_x = [ + [0], + [0], + [-np.exp(-eta * (t - event_3_time)) / eta + 1 / eta], + ] x = (hom_x + inhom_x).flatten() return np.array(x) - def sx_pected(t, parameters): - # get sx, w.r.t. parameters, via finite differences + def sx_expected(t, parameters): + """get sx, w.r.t. parameters, via finite differences""" sx = [] + eps = 1e-6 for ip in parameters: - eps = 1e-6 perturbed_params = deepcopy(parameters) perturbed_params[ip] += eps - sx_p = x_pected(t, **perturbed_params) - perturbed_params[ip] -= 2*eps - sx_m = x_pected(t, **perturbed_params) + sx_p = x_expected(t, **perturbed_params) + perturbed_params[ip] -= 2 * eps + sx_m = x_expected(t, **perturbed_params) sx.append((sx_p - sx_m) / (2 * eps)) return np.array(sx) @@ -212,12 +227,11 @@ def sx_pected(t, parameters): species, events, timepoints, - x_pected, - sx_pected + x_expected, + sx_expected, ) - def model_definition_nested_events(): """Test model for state- and parameter-dependent heavisides. @@ -240,49 +254,51 @@ def model_definition_nested_events(): [ bolus]] """ # Model components - species = ['x_1', 'x_2'] + species = ["x_1", "x_2"] initial_assignments = { - 'x_1': 'k1', - 'x_2': 'k2', + "x_1": "k1", + "x_2": "k2", } rate_rules = { - 'x_1': 'inflow_1 - decay_1 * x_1', - 'x_2': '- decay_2 * x_2', + "x_1": "inflow_1 - decay_1 * x_1", + "x_2": "- decay_2 * x_2", } parameters = { - 'k1': 0, - 'k2': 0, - 'inflow_1': 4, - 'decay_1': 2, - 'decay_2': 5, - 'bolus': 0, # for bolus != 0, nested event sensitivities are off! + "k1": 0, + "k2": 0, + "inflow_1": 4, + "decay_1": 2, + "decay_2": 5, + "bolus": 0, # for bolus != 0, nested event sensitivities are off! } events = { - 'event_1': { - 'trigger': 'x_1 > inflow_1 / decay_2', - 'target': 'x_2', - 'assignment': 'x_2 - 1 / time' + "event_1": { + "trigger": "x_1 > inflow_1 / decay_2", + "target": "x_2", + "assignment": "x_2 - 1 / time", + }, + "event_2": { + "trigger": "x_2 < - 0.5", + "target": ["x_1", "x_2"], + "assignment": ["x_1 + bolus", "x_2 + bolus"], }, - 'event_2': { - 'trigger': 'x_2 < - 0.5', - 'target': ['x_1', 'x_2'], - 'assignment': ['x_1 + bolus', 'x_2 + bolus'], - } } timepoints = np.linspace(0, 1, 101) # Analytical solution - def x_pected(t, k1, k2, inflow_1, decay_1, decay_2, bolus): + def x_expected(t, k1, k2, inflow_1, decay_1, decay_2, bolus): # gather temporary variables # event_time = x_1 > inflow_1 / decay_2 equil = inflow_1 / decay_1 tmp1 = inflow_1 / decay_2 - inflow_1 / decay_1 tmp2 = k1 - inflow_1 / decay_1 - event_time = (- 1 / decay_1) * np.log( tmp1 / tmp2) + event_time = (-1 / decay_1) * np.log(tmp1 / tmp2) def get_early_x(t): # compute dynamics before event - x_1 = equil * (1 - np.exp(-decay_1 * t)) + k1*np.exp(-decay_1 * t) + x_1 = equil * (1 - np.exp(-decay_1 * t)) + k1 * np.exp( + -decay_1 * t + ) x_2 = k2 * np.exp(-decay_2 * t) return np.array([[x_1], [x_2]]) @@ -296,25 +312,100 @@ def get_early_x(t): # compute dynamics after event inhom = np.exp(decay_1 * event_time) * tau_x1 - x_1 = equil * (1 - np.exp(decay_1 * (event_time - t))) + \ - inhom * np.exp(- decay_1 * t) + x_1 = equil * ( + 1 - np.exp(decay_1 * (event_time - t)) + ) + inhom * np.exp(-decay_1 * t) x_2 = tau_x2 * np.exp(decay_2 * event_time) * np.exp(-decay_2 * t) x = np.array([[x_1], [x_2]]) return x.flatten() - def sx_pected(t, parameters): - # get sx, w.r.t. parameters, via finite differences + def sx_expected(t, parameters): + """get sx, w.r.t. parameters, via finite differences""" + sx = [] + eps = 1e-6 + + for ip in parameters: + perturbed_params = deepcopy(parameters) + perturbed_params[ip] += eps + sx_p = x_expected(t, **perturbed_params) + perturbed_params[ip] -= 2 * eps + sx_m = x_expected(t, **perturbed_params) + sx.append((sx_p - sx_m) / (2 * eps)) + + return np.array(sx) + + return ( + initial_assignments, + parameters, + rate_rules, + species, + events, + timepoints, + x_expected, + sx_expected, + ) + + +def model_definition_piecewise_plus_event_simple_case(): + """Test model for boolean operations in a piecewise condition. + + ODEs + ---- + d/dt x_1: + - { 1, (alpha <= t and t < beta) + - { 0, otherwise + """ + # Model components + species = ["x_1"] + initial_assignments = {"x_1": "x_1_0"} + rate_rules = {"x_1": "piecewise(1, (alpha < time && time < beta), 0)"} + parameters = { + "alpha": 2, + "beta": 3, + "gamma": 4.5, + "x_1_0": 1, + } + timepoints = np.linspace(0.0, 5.0, 100) # np.array((0.0, 4.0,)) + events = { + "event_1": { + "trigger": "time > alpha", + "target": "x_1", + "assignment": "gamma", + }, + "event_2": { + "trigger": "time > beta", + "target": "x_1", + "assignment": "x_1 + 2.5", + }, + } + + # Analytical solution + def x_expected(t, x_1_0, alpha, beta, gamma): + t_event_1 = alpha + t_event_2 = beta + + if t < t_event_1: + x = x_1_0 + elif t < t_event_2: + x = gamma + t - t_event_1 + else: + x = gamma + t_event_2 - t_event_1 + 2.5 + + return np.array((x,)) + + def sx_expected(t, parameters): + """get sx, w.r.t. parameters, via finite differences""" sx = [] + eps = 1e-6 for ip in parameters: - eps = 1e-6 perturbed_params = deepcopy(parameters) perturbed_params[ip] += eps - sx_p = x_pected(t, **perturbed_params) - perturbed_params[ip] -= 2*eps - sx_m = x_pected(t, **perturbed_params) + sx_p = np.array(x_expected(t, **perturbed_params)) + perturbed_params[ip] -= 2 * eps + sx_m = np.array(x_expected(t, **perturbed_params)) sx.append((sx_p - sx_m) / (2 * eps)) return np.array(sx) @@ -326,26 +417,290 @@ def sx_pected(t, parameters): species, events, timepoints, - x_pected, - sx_pected + x_expected, + sx_expected, + ) + + +def model_definition_event_state_dep_ddeltax_dtpx(): + """Test model with state-dependent partial derivatives of update functions wrt parameters, time, and states.""" + # Model components + species = ["x_1"] + initial_assignments = {"x_1": "x_1_0"} + rate_rules = {"x_1": "1"} + parameters = { + "alpha": 1.5, + "beta": 2.5, + "gamma": 3.5, + "delta": 5.5, + "x_1_0": 1, + } + timepoints = np.linspace(0.0, 5.0, 100) + events = { + # state-dependent ddeltaxdt + "event_1": { + "trigger": "time > alpha", + "target": "x_1", + "assignment": "x_1 * time", + }, + # state-dependent ddeltaxdp + "event_2": { + "trigger": "time > beta", + "target": "x_1", + "assignment": "x_1 * delta", + }, + # state-dependent ddeltaxdx + "event_3": { + "trigger": "time > gamma", + "target": "x_1", + "assignment": "2 * x_1 * x_1", + }, + } + + # Analytical solution + def x_expected(t, x_1_0, alpha, beta, gamma, delta): + if t < alpha: + # before first event triggered + x = x_1_0 + t + elif t < beta: + # after first event triggered + x = (x_1_0 + alpha) * alpha + (t - alpha) + elif t < gamma: + # after second event triggered + x = ((x_1_0 + alpha) * alpha + (beta - alpha)) * delta + (t - beta) + else: + # after third event triggered + x = ( + ((x_1_0 + alpha) * alpha + (beta - alpha)) * delta + + (gamma - beta) + ) ** 2 * 2 + (t - gamma) + + return np.array((x,)) + + def sx_expected(t, parameters): + """get sx, w.r.t. parameters, via finite differences""" + sx = [] + eps = 1e-6 + + for ip in parameters: + perturbed_params = deepcopy(parameters) + perturbed_params[ip] += eps + sx_p = np.array(x_expected(t, **perturbed_params)) + perturbed_params[ip] -= 2 * eps + sx_m = np.array(x_expected(t, **perturbed_params)) + sx.append((sx_p - sx_m) / (2 * eps)) + + return np.array(sx) + + return ( + initial_assignments, + parameters, + rate_rules, + species, + events, + timepoints, + x_expected, + sx_expected, + ) + + +def model_definition_piecewise_plus_event_semi_complicated(): + """Test model for boolean operations in a piecewise condition, discrete + events and a non-vanishing quadrature for the adjoint state. + """ + # Model components + species = ["x_1", "x_2"] + initial_assignments = {"x_1": "x_1_0", "x_2": "x_2_0"} + rate_rules = { + "x_1": "piecewise(delta * x_1, (alpha < time && time < beta), - x_1)", + "x_2": "- eta * x_2", + } + parameters = { + "alpha": 2, + "beta": 3, + "gamma": 4.5, + "x_1_0": 1, + "x_2_0": 5, + "delta": 2.5, + "eta": 1.4, + } + timepoints = np.linspace(0.0, 5.0, 100) + events = { + "event_1": { + "trigger": "time > alpha / 2", + "target": "x_1", + "assignment": "gamma", + }, + "event_2": { + "trigger": "time > beta", + "target": "x_1", + "assignment": "x_1 + x_2", + }, + } + + # Analytical solution + def x_expected(t, x_1_0, x_2_0, alpha, beta, gamma, delta, eta): + t_event_1 = alpha / 2 + t_event_2 = beta + heaviside_1 = alpha + + x_2 = x_2_0 * np.exp(-eta * t) + + if t < t_event_1: + x_1 = x_1_0 * np.exp(-t) + elif t < heaviside_1: + x_1 = gamma * np.exp(-(t - t_event_1)) + elif t < t_event_2: + x_1_heaviside_1 = gamma * np.exp(-(heaviside_1 - t_event_1)) + x_1 = x_1_heaviside_1 * np.exp(delta * (t - heaviside_1)) + else: + x_1_heaviside_1 = gamma * np.exp(-(heaviside_1 - t_event_1)) + x_1_at_event_2 = x_1_heaviside_1 * np.exp( + delta * (t_event_2 - heaviside_1) + ) + x_2_at_event_2 = x_2_0 * np.exp(-eta * t_event_2) + x1_after_event_2 = x_1_at_event_2 + x_2_at_event_2 + x_1 = x1_after_event_2 * np.exp(-(t - t_event_2)) + + return np.array((x_1, x_2)) + + def sx_expected(t, parameters): + """get sx, w.r.t. parameters, via finite differences""" + sx = [] + eps = 1e-6 + + for ip in parameters: + perturbed_params = deepcopy(parameters) + perturbed_params[ip] += eps + sx_p = np.array(x_expected(t, **perturbed_params)) + perturbed_params[ip] -= 2 * eps + sx_m = np.array(x_expected(t, **perturbed_params)) + sx.append((sx_p - sx_m) / (2 * eps)) + + return np.array(sx) + + return ( + initial_assignments, + parameters, + rate_rules, + species, + events, + timepoints, + x_expected, + sx_expected, + ) + + +def model_definition_piecewise_plus_event_trigger_depends_on_state(): + """Test model for boolean operations in a piecewise condition. + + ODEs + ---- + d/dt x_1: + - { 1, (alpha <= t and t < beta) + - { 0, otherwise + """ + # Model components + species = ["x_1", "x_2"] + initial_assignments = {"x_1": "x_1_0", "x_2": "x_2_0"} + rate_rules = { + "x_1": "piecewise(1, (alpha < time && time < beta), 0)", + "x_2": "- x_2", + } + parameters = { + "alpha": 2, + "beta": 3, + "gamma": 4.5, + "x_1_0": 1, + "x_2_0": 5, + } + timepoints = np.linspace(0.0, 5.0, 100) + events = { + "event_1": { + "trigger": "x_1 > 1.4", + "target": "x_1", + "assignment": "x_1 + gamma", + }, + "event_2": { + "trigger": "time > beta", + "target": "x_1", + "assignment": "x_1 + x_2", + }, + } + + # Analytical solution + def x_expected(t, x_1_0, x_2_0, alpha, beta, gamma): + heaviside_1 = alpha + t_event_1 = alpha + 1.4 - x_1_0 + t_event_2 = beta + # This should hold in order that the analytical solution is correct + assert heaviside_1 < t_event_1 + + # x_2 never gets perturbed + x_2 = x_2_0 * np.exp(-t) + + if t < heaviside_1: + x_1 = x_1_0 + elif t < t_event_1: + x_1 = (t - heaviside_1) + x_1_0 + elif t < t_event_2: + x_1 = gamma + (t - heaviside_1) + x_1_0 + else: + x_2_at_event_2 = x_2_0 * np.exp(-t_event_2) + x_1_at_event_2 = gamma + (t_event_2 - heaviside_1) + x_1_0 + x_1 = x_1_at_event_2 + x_2_at_event_2 + + return np.array((x_1, x_2)) + + def sx_expected(t, parameters): + """get sx, w.r.t. parameters, via finite differences""" + sx = [] + eps = 1e-6 + + for ip in parameters: + perturbed_params = deepcopy(parameters) + perturbed_params[ip] += eps + sx_p = np.array(x_expected(t, **perturbed_params)) + perturbed_params[ip] -= 2 * eps + sx_m = np.array(x_expected(t, **perturbed_params)) + sx.append((sx_p - sx_m) / (2 * eps)) + + return np.array(sx) + + return ( + initial_assignments, + parameters, + rate_rules, + species, + events, + timepoints, + x_expected, + sx_expected, ) def test_models(model): - amici_model, parameters, timepoints, x_pected, sx_pected = model + amici_model, parameters, timepoints, x_expected, sx_expected = model - result_expected_x = np.array([ - x_pected(t, **parameters) - for t in timepoints - ]) - result_expected_sx = np.array([ - sx_pected(t, parameters) - for t in timepoints - ]) + result_expected_x = np.array( + [x_expected(t, **parameters) for t in timepoints] + ) + result_expected_sx = np.array( + [sx_expected(t, parameters) for t in timepoints] + ) # assert correctness of trajectories - check_trajectories_without_sensitivities(amici_model, - result_expected_x) - check_trajectories_with_forward_sensitivities(amici_model, - result_expected_x, - result_expected_sx) + check_trajectories_without_sensitivities(amici_model, result_expected_x) + check_trajectories_with_forward_sensitivities( + amici_model, result_expected_x, result_expected_sx + ) + + +def expm(x): + """``expm`` wrapper + + Uses ``expm`` from ``mpmath``. *Something* changed in scipy's ``expm`` in + version 1.9.0 breaking these tests""" + from mpmath import expm + + return np.array(expm(x).tolist()).astype(float) diff --git a/deps/AMICI/python/tests/test_hdf5.py b/deps/AMICI/python/tests/test_hdf5.py index 40098f921..232f22be8 100644 --- a/deps/AMICI/python/tests/test_hdf5.py +++ b/deps/AMICI/python/tests/test_hdf5.py @@ -10,18 +10,18 @@ def _modify_solver_attrs(solver): # change to non-default values for attr in dir(solver): - if not attr.startswith('set'): + if not attr.startswith("set"): continue - val = getattr(solver, attr.replace('set', 'get'))() + val = getattr(solver, attr.replace("set", "get"))() if isinstance(val, bool): cval = not val - elif attr == 'setStabilityLimitFlag': + elif attr == "setStabilityLimitFlag": cval = 0 - elif attr == 'setReturnDataReportingMode': + elif attr == "setReturnDataReportingMode": cval = amici.RDataReporting.likelihood - elif attr == 'setMaxTime': + elif attr == "setMaxTime": # default value is the maximum, must not add to that cval = random.random() elif isinstance(val, int): @@ -32,8 +32,9 @@ def _modify_solver_attrs(solver): getattr(solver, attr)(cval) -@pytest.mark.skipif(not amici.hdf5_enabled, - reason='AMICI was compiled without HDF5') +@pytest.mark.skipif( + not amici.hdf5_enabled, reason="AMICI was compiled without HDF5" +) def test_solver_hdf5_roundtrip(sbml_example_presimulation_module): """TestCase class for AMICI HDF5 I/O""" @@ -41,29 +42,31 @@ def test_solver_hdf5_roundtrip(sbml_example_presimulation_module): solver = model.getSolver() _modify_solver_attrs(solver) - hdf5file = 'solverSettings.hdf5' + hdf5file = "solverSettings.hdf5" - amici.writeSolverSettingsToHDF5(solver, hdf5file, 'ssettings') + amici.writeSolverSettingsToHDF5(solver, hdf5file, "ssettings") new_solver = model.getSolver() # check that we changed everything for attr in dir(solver): - if not attr.startswith('set'): + if not attr.startswith("set"): continue - assert getattr(solver, attr.replace('set', 'get'))() \ - != getattr(new_solver, attr.replace('set', 'get'))(), attr + assert ( + getattr(solver, attr.replace("set", "get"))() + != getattr(new_solver, attr.replace("set", "get"))() + ), attr - amici.readSolverSettingsFromHDF5(hdf5file, new_solver, 'ssettings') + amici.readSolverSettingsFromHDF5(hdf5file, new_solver, "ssettings") # check that reading in settings worked for attr in dir(solver): - if not attr.startswith('set'): + if not attr.startswith("set"): continue - assert getattr(solver, attr.replace('set', 'get'))() \ - == pytest.approx( - getattr(new_solver, attr.replace('set', 'get'))()), attr + assert getattr(solver, attr.replace("set", "get"))() == pytest.approx( + getattr(new_solver, attr.replace("set", "get"))() + ), attr os.remove(hdf5file) diff --git a/deps/AMICI/python/tests/test_heavisides.py b/deps/AMICI/python/tests/test_heavisides.py index 1c84ae0ac..c3bea26a0 100644 --- a/deps/AMICI/python/tests/test_heavisides.py +++ b/deps/AMICI/python/tests/test_heavisides.py @@ -1,20 +1,21 @@ """Tests for SBML events, including piecewise expressions.""" import numpy as np import pytest - - from util import ( - create_sbml_model, - create_amici_model, - check_trajectories_without_sensitivities, check_trajectories_with_forward_sensitivities, + check_trajectories_without_sensitivities, + create_amici_model, + create_sbml_model, ) -@pytest.fixture(params=[ - 'state_and_parameter_dependent_heavisides', - 'piecewise_with_boolean_operations', - 'piecewise_many_conditions', -]) + +@pytest.fixture( + params=[ + "state_and_param_dep_heavisides", + "piecewise_with_boolean_operations", + "piecewise_many_conditions", + ] +) def model(request): """Returns the requested AMICI model and analytical expressions.""" ( @@ -24,8 +25,8 @@ def model(request): species, events, timepoints, - x_pected, - sx_pected + x_expected, + sx_expected, ) = get_model_definition(request.param) # SBML model @@ -46,39 +47,36 @@ def model(request): ) amici_model.setTimepoints(timepoints) - return amici_model, parameters, timepoints, x_pected, sx_pected + return amici_model, parameters, timepoints, x_expected, sx_expected def test_models(model): - amici_model, parameters, timepoints, x_pected, sx_pected = model + amici_model, parameters, timepoints, x_expected, sx_expected = model - result_expected_x = np.array([ - x_pected(t, **parameters) - for t in timepoints - ]) - result_expected_sx = np.array([ - sx_pected(t, **parameters) - for t in timepoints - ]) + result_expected_x = np.array( + [x_expected(t, **parameters) for t in timepoints] + ) + result_expected_sx = np.array( + [sx_expected(t, **parameters) for t in timepoints] + ) # Does the AMICI simulation match the analytical solution? - check_trajectories_without_sensitivities(amici_model, - result_expected_x) - check_trajectories_with_forward_sensitivities(amici_model, - result_expected_x, - result_expected_sx) + check_trajectories_without_sensitivities(amici_model, result_expected_x) + check_trajectories_with_forward_sensitivities( + amici_model, result_expected_x, result_expected_sx + ) def get_model_definition(model_name): - if model_name == 'state_and_parameter_dependent_heavisides': + if model_name == "state_and_param_dep_heavisides": return model_definition_state_and_parameter_dependent_heavisides() - elif model_name == 'piecewise_with_boolean_operations': + elif model_name == "piecewise_with_boolean_operations": return model_definition_piecewise_with_boolean_operations() - elif model_name == 'piecewise_many_conditions': + elif model_name == "piecewise_many_conditions": return model_definition_piecewise_many_conditions() else: raise NotImplementedError( - f'Model with name {model_name} is not implemented.' + f"Model with name {model_name} is not implemented." ) @@ -95,44 +93,44 @@ def model_definition_state_and_parameter_dependent_heavisides(): - { eta, t >= delta """ # Model components - species = ['x_1', 'x_2'] + species = ["x_1", "x_2"] initial_assignments = { - 'x_1': 'zeta', + "x_1": "zeta", } rate_rules = { - 'x_1': 'piecewise( alpha * x_1, time < x_2, -beta * x_1 )', - 'x_2': 'piecewise( gamma * x_2, time < delta, eta )', + "x_1": "piecewise( alpha * x_1, time < x_2, -beta * x_1 )", + "x_2": "piecewise( gamma * x_2, time < delta, eta )", } parameters = { - 'alpha': float(np.log(2)), - 'beta': float(np.log(4)), - 'gamma': float(np.log(3)), - 'delta': 1, - 'eta': 0.5, - 'zeta': 0.25, + "alpha": float(np.log(2)), + "beta": float(np.log(4)), + "gamma": float(np.log(3)), + "delta": 1, + "eta": 0.5, + "zeta": 0.25, } timepoints = np.linspace(0, 10, 100) events = {} # Analytical solution - def x_pected(t, alpha, beta, gamma, delta, eta, zeta): + def x_expected(t, alpha, beta, gamma, delta, eta, zeta): # get x_1 tau_1 = (np.exp(gamma * delta) - delta * eta) / (1 - eta) if t < tau_1: x_1 = zeta * np.exp(alpha * t) else: - x_1 = zeta * np.exp(alpha * tau_1 - beta*(t - tau_1)) + x_1 = zeta * np.exp(alpha * tau_1 - beta * (t - tau_1)) # get x_2 tau_2 = delta if t < tau_2: - x_2 = np.exp(gamma*t) + x_2 = np.exp(gamma * t) else: - x_2 = np.exp(gamma*delta) + eta*(t-delta) + x_2 = np.exp(gamma * delta) + eta * (t - delta) - return (x_1, x_2) + return x_1, x_2 - def sx_pected(t, alpha, beta, gamma, delta, eta, zeta): + def sx_expected(t, alpha, beta, gamma, delta, eta, zeta): # get sx_1, w.r.t. parameters tau_1 = (np.exp(gamma * delta) - delta * eta) / (1 - eta) if t < tau_1: @@ -145,52 +143,65 @@ def sx_pected(t, alpha, beta, gamma, delta, eta, zeta): else: # Never trust Wolfram Alpha... sx_1_alpha = ( - zeta * tau_1 * np.exp(alpha * tau_1 - beta*(t - tau_1)) + zeta * tau_1 * np.exp(alpha * tau_1 - beta * (t - tau_1)) ) sx_1_beta = ( - zeta * (tau_1 - t) - * np.exp(alpha * tau_1 - beta*(t - tau_1)) + zeta * (tau_1 - t) * np.exp(alpha * tau_1 - beta * (t - tau_1)) ) sx_1_gamma = ( - zeta * (alpha + beta) * delta * np.exp(gamma * delta) + zeta + * (alpha + beta) + * delta + * np.exp(gamma * delta) / (1 - eta) - * np.exp(alpha * tau_1 - beta*(t - tau_1)) + * np.exp(alpha * tau_1 - beta * (t - tau_1)) ) sx_1_delta = ( - zeta * (alpha + beta) - * np.exp(alpha * tau_1 - beta*(t - tau_1)) + zeta + * (alpha + beta) + * np.exp(alpha * tau_1 - beta * (t - tau_1)) * (gamma * np.exp(gamma * delta) - eta) / (1 - eta) ) sx_1_eta = ( - zeta * (alpha + beta) - * (-delta * (1-eta) + np.exp(gamma * delta) - delta * eta) - / (1 - eta)**2 - * np.exp(alpha * tau_1 - beta*(t - tau_1)) + zeta + * (alpha + beta) + * (-delta * (1 - eta) + np.exp(gamma * delta) - delta * eta) + / (1 - eta) ** 2 + * np.exp(alpha * tau_1 - beta * (t - tau_1)) ) - sx_1_zeta = np.exp(alpha * tau_1 - beta*(t - tau_1)) + sx_1_zeta = np.exp(alpha * tau_1 - beta * (t - tau_1)) # get sx_2, w.r.t. parameters tau_2 = delta + sx_2_alpha = 0 + sx_2_beta = 0 + sx_2_zeta = 0 if t < tau_2: - sx_2_alpha = 0 - sx_2_beta = 0 - sx_2_gamma = t * np.exp(gamma*t) + sx_2_gamma = t * np.exp(gamma * t) sx_2_delta = 0 sx_2_eta = 0 - sx_2_zeta = 0 else: - sx_2_alpha = 0 - sx_2_beta = 0 - sx_2_gamma = delta * np.exp(gamma*delta) - sx_2_delta = gamma*np.exp(gamma*delta) - eta + sx_2_gamma = delta * np.exp(gamma * delta) + sx_2_delta = gamma * np.exp(gamma * delta) - eta sx_2_eta = t - delta - sx_2_zeta = 0 - sx_1 = (sx_1_alpha, sx_1_beta, sx_1_gamma, - sx_1_delta, sx_1_eta, sx_1_zeta) - sx_2 = (sx_2_alpha, sx_2_beta, sx_2_gamma, - sx_2_delta, sx_2_eta, sx_2_zeta) + sx_1 = ( + sx_1_alpha, + sx_1_beta, + sx_1_gamma, + sx_1_delta, + sx_1_eta, + sx_1_zeta, + ) + sx_2 = ( + sx_2_alpha, + sx_2_beta, + sx_2_gamma, + sx_2_delta, + sx_2_eta, + sx_2_zeta, + ) return np.array((sx_1, sx_2)).transpose() @@ -201,8 +212,8 @@ def sx_pected(t, alpha, beta, gamma, delta, eta, zeta): species, events, timepoints, - x_pected, - sx_pected + x_expected, + sx_expected, ) @@ -216,30 +227,30 @@ def model_definition_piecewise_with_boolean_operations(): - { 0, otherwise """ # Model components - species = ['x_1'] - initial_assignments = {'x_1': 'x_1_0'} + species = ["x_1"] + initial_assignments = {"x_1": "x_1_0"} rate_rules = { - 'x_1': ( - 'piecewise(' - '1, ' # noqa - '(alpha <= time && time < beta) || ' # noqa - '(gamma <= time && time < delta), ' - '0' - ')' + "x_1": ( + "piecewise(" + "1, " # noqa + "(alpha <= time && time < beta) || " # noqa + "(gamma <= time && time < delta), " + "0" + ")" ), } parameters = { - 'alpha': 1, - 'beta': 2, - 'gamma': 3, - 'delta': 4, - 'x_1_0': 1, + "alpha": 1, + "beta": 2, + "gamma": 3, + "delta": 4, + "x_1_0": 1, } timepoints = np.linspace(0, 5, 100) events = {} # Analytical solution - def x_pected(t, x_1_0, alpha, beta, gamma, delta): + def x_expected(t, x_1_0, alpha, beta, gamma, delta): if t < alpha: return (x_1_0,) elif alpha <= t < beta: @@ -249,27 +260,16 @@ def x_pected(t, x_1_0, alpha, beta, gamma, delta): elif gamma <= t < delta: return (x_1_0 + (beta - alpha) + (t - gamma),) else: - return (x_1_0 + (beta - alpha) + (delta - gamma), ) + return (x_1_0 + (beta - alpha) + (delta - gamma),) - def sx_pected(t, x_1_0, alpha, beta, gamma, delta): + def sx_expected(t, x_1_0, alpha, beta, gamma, delta): # x0 is very simple... sx_x0 = 1 - sx_alpha = 0 - sx_beta = 0 - sx_gamma = 0 - sx_delta = 0 - - if t >= alpha: - sx_alpha = -1 - if t >= beta: - sx_beta = 1 - if t >= gamma: - sx_gamma = -1 - if t >= delta: - sx_delta = 1 - + sx_alpha = -1 if t >= alpha else 0 + sx_beta = 1 if t >= beta else 0 + sx_gamma = -1 if t >= gamma else 0 + sx_delta = 1 if t >= delta else 0 sx = (sx_alpha, sx_beta, sx_gamma, sx_delta, sx_x0) - return np.array((sx,)).transpose() return ( @@ -279,8 +279,8 @@ def sx_pected(t, x_1_0, alpha, beta, gamma, delta): species, events, timepoints, - x_pected, - sx_pected + x_expected, + sx_expected, ) @@ -294,36 +294,44 @@ def model_definition_piecewise_many_conditions(): - { 0, otherwise """ # Model components - species = ['x_1'] - initial_assignments = {'x_1': 'x_1_0'} + species = ["x_1"] + initial_assignments = {"x_1": "x_1_0"} t_final = 5 - pieces = 'piecewise(' + pieces = "piecewise(" for t in range(t_final): if t > 0: - pieces += ', ' + pieces += ", " if t % 2 == 1: - pieces += f'1, time < {t + 1}' + pieces += f"1, time < {t + 1}" else: - pieces += f'0, time < {t + 1}' - pieces += ', 0)' - rate_rules = {'x_1': pieces, } + pieces += f"0, time < {t + 1}" + pieces += ", 0)" + rate_rules = { + "x_1": pieces, + } parameters = { - 'x_1_0': 1, + "x_1_0": 1, } timepoints = np.linspace(0, t_final, 100) events = {} # Analytical solution - def x_pected(t, x_1_0): + def x_expected(t, x_1_0): if np.floor(t) % 2 == 1: - return (x_1_0 + (np.floor(t)-1)/2 + (t-np.floor(t)), ) + return (x_1_0 + (np.floor(t) - 1) / 2 + (t - np.floor(t)),) else: - return (x_1_0 + np.floor(t)/2, ) - - def sx_pected(t, x_1_0): - return np.array([[1, ], ]) + return (x_1_0 + np.floor(t) / 2,) + + def sx_expected(t, x_1_0): + return np.array( + [ + [ + 1, + ], + ] + ) return ( initial_assignments, @@ -332,6 +340,6 @@ def sx_pected(t, x_1_0): species, events, timepoints, - x_pected, - sx_pected + x_expected, + sx_expected, ) diff --git a/deps/AMICI/python/tests/test_misc.py b/deps/AMICI/python/tests/test_misc.py index 6445b5d51..5a88fda6f 100644 --- a/deps/AMICI/python/tests/test_misc.py +++ b/deps/AMICI/python/tests/test_misc.py @@ -2,14 +2,17 @@ import os import subprocess -from tempfile import TemporaryDirectory +from pathlib import Path import amici -from amici.ode_export import smart_subs_dict -import libsbml import pytest import sympy as sp -from amici.ode_export import _monkeypatched, _custom_pow_eval_derivative +from amici.de_export import ( + _custom_pow_eval_derivative, + _monkeypatched, + smart_subs_dict, +) +from amici.testing import skip_on_valgrind def test_parameter_scaling_from_int_vector(): @@ -19,84 +22,80 @@ def test_parameter_scaling_from_int_vector(): [ amici.ParameterScaling.log10, amici.ParameterScaling.ln, - amici.ParameterScaling.none - ]) + amici.ParameterScaling.none, + ] + ) assert scale_vector[0] == amici.ParameterScaling.log10 assert scale_vector[1] == amici.ParameterScaling.ln assert scale_vector[2] == amici.ParameterScaling.none -def test_sbml2amici_no_observables(): - """Test model generation works for model without observables""" - - # test model - document = libsbml.SBMLDocument(3, 1) - model = document.createModel() - model.setTimeUnits("second") - model.setExtentUnits("mole") - model.setSubstanceUnits('mole') - c1 = model.createCompartment() - c1.setId('C1') - model.addCompartment(c1) - s1 = model.createSpecies() - s1.setId('S1') - s1.setCompartment('C1') - model.addSpecies(s1) - - sbml_importer = amici.sbml_import.SbmlImporter(sbml_source=model, - from_file=False) - tmpdir = TemporaryDirectory() - sbml_importer.sbml2amici(modelName="test", - output_dir=tmpdir.name, - observables=None, - compute_conservation_laws=False) - - +@skip_on_valgrind def test_hill_function_dwdx(): """Kinetic laws with Hill functions, may lead to NaNs in the Jacobian if involved states are zero if not properly arranged symbolically. Test that what we are applying the right sympy simplification.""" - w = sp.Matrix([[sp.sympify('Pow(x1, p1) / (Pow(x1, p1) + a)')]]) - dwdx = w.diff(sp.Symbol('x1')) + w = sp.Matrix([[sp.sympify("Pow(x1, p1) / (Pow(x1, p1) + a)")]]) + dwdx = w.diff(sp.Symbol("x1")) # Verify that without simplification we fail with pytest.raises(ZeroDivisionError): with sp.evaluate(False): - res = dwdx.subs({'x1': 0.0}) + res = dwdx.subs({"x1": 0.0}) _ = str(res) # Test that powsimp does the job dwdx = dwdx.applyfunc(lambda x: sp.powsimp(x, deep=True)) with sp.evaluate(False): - res = dwdx.subs({'x1': 0.0}) + res = dwdx.subs({"x1": 0.0}) _ = str(res) -@pytest.mark.skipif(os.environ.get('AMICI_SKIP_CMAKE_TESTS', '') == 'TRUE', - reason='skipping cmake based test') +@skip_on_valgrind +@pytest.mark.skipif( + os.environ.get("AMICI_SKIP_CMAKE_TESTS", "") == "TRUE", + reason="skipping cmake based test", +) def test_cmake_compilation(sbml_example_presimulation_module): """Check that CMake build succeeds for one of the models generated during Python tests""" source_dir = os.path.dirname(sbml_example_presimulation_module.__path__[0]) - - cmd = f"set -e; cd {source_dir}; mkdir -p build; cd build; "\ - "cmake ..; make" - - subprocess.run(cmd, shell=True, check=True, - stdout=subprocess.PIPE, stderr=subprocess.PIPE) - - + build_dir = f"{source_dir}/build" + # path hint for amici base installation, in case CMake configuration has + # not been exported + 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}'" + ) + + try: + subprocess.run( + cmd, + shell=True, + check=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + except subprocess.CalledProcessError as e: + print(e.stdout.decode()) + print(e.stderr.decode()) + raise + + +@skip_on_valgrind def test_smart_subs_dict(): - expr_str = 'c + d' + expr_str = "c + d" subs_dict = { - 'c': 'a + b', - 'd': 'c + a', + "c": "a + b", + "d": "c + a", } - expected_default_str = '3*a + 2*b' - expected_reverse_str = '2*a + b + c' + expected_default_str = "3*a + 2*b" + expected_reverse_str = "2*a + b + c" expr_sym = sp.sympify(expr_str) subs_sym = {sp.sympify(k): sp.sympify(v) for k, v in subs_dict.items()} @@ -109,20 +108,35 @@ def test_smart_subs_dict(): assert sp.simplify(result_default - expected_default).is_zero 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)] + 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 + 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 + assert (t**n).diff(t).subs(vals) is sp.nan + + +@skip_on_valgrind +def test_get_default_argument(): + # no default + with pytest.raises(ValueError): + amici._get_default_argument(lambda x: x, "x") + + # non-existant parameter + with pytest.raises(KeyError): + amici._get_default_argument(lambda x: x, "y") + + # okay + assert amici._get_default_argument(lambda x=1: x, "x") == 1 diff --git a/deps/AMICI/python/tests/test_observable_events.py b/deps/AMICI/python/tests/test_observable_events.py new file mode 100644 index 000000000..2887308ff --- /dev/null +++ b/deps/AMICI/python/tests/test_observable_events.py @@ -0,0 +1,231 @@ +import os + +import amici +import pytest +from test_pregenerated_models import ( + expected_results, + expected_results_file, + options_file, + verify_simulation_results, +) +from util import create_amici_model, create_sbml_model + + +def model_neuron_def(): + """Python implementation of the neuron model (Hodgkin-Huxley). + + ODEs + ---- + d/dt v: + - 0.04*v^2 + 5*v + 140 - u + I + d/dt u: + - a*(b*v - u); + + Events: + ------- + event_1: + trigger: v - 30 + bolus: [[ -c - v ], + [ 0]] + observable: t + """ + # Model components + species = ["v", "u"] + initial_assignments = { + "v": "v0", + "u": "b*v0", + } + rate_rules = { + "v": "0.04*v^2 + 5*v + 140 - u + I0", + "u": "a*(b*v - u)", + } + parameters = { + "a": 0.02, + "b": 0.3, + "c": 65, + "d": 0.9, + "v0": -60, + "I0": 10, + } + events = { + "event_1": { + "trigger": "v > 30", + "target": ["v", "u"], + "assignment": ["-c", "d+u"], + }, + } + + observables = { + "y1": { + "name": "v", + "formula": "v", + } + } + + event_observables = { + "z1": {"name": "z1", "event": "event_1", "formula": "time"} + } + return ( + initial_assignments, + parameters, + rate_rules, + species, + events, + observables, + event_observables, + ) + + +def model_events_def(): + """Python implementation of the events model. + + ODEs + ---- + d/dt x1: + - -p1*heaviside(t-p4)*x1 + d/dt x2: + - p2*x1*exp(-0.1*t)-p3*x2 + d/dt x3: + - -x3+heaviside(t-4) + + Events: + ------- + event_1: + trigger: x2 > x3 + bolus: 0 + observable: t + event_2: + trigger: x1 > x3 + bolus: 0 + observable: t + """ + # Model components + species = ["x1", "x2", "x3"] + initial_assignments = { + "x1": "k1", + "x2": "k2", + "x3": "k3", + } + rate_rules = { + "x1": "-p1*piecewise(1.0, time>p4, 0.0)*x1", + "x2": "p2*x1*exp(-0.1*time)-p3*x2", + "x3": "-x3+piecewise(1.0, time>4, 0.0)", + } + parameters = { + "p1": 0.5, + "p2": 2, + "p3": 0.5, + "p4": 0.5, + "k1": 4, + "k2": 8, + "k3": 10, + "k4": 4, + } + events = { + "event_1": {"trigger": "x2 > x3", "target": [], "assignment": []}, + "event_2": {"trigger": "x1 > x3", "target": [], "assignment": []}, + } + + observables = { + "y1": { + "name": "y1", + "formula": "p4*(x1+x2+x3)", + } + } + + event_observables = { + "z1": {"name": "z1", "event": "event_1", "formula": "time"}, + "z2": {"name": "z2", "event": "event_2", "formula": "time"}, + } + return ( + initial_assignments, + parameters, + rate_rules, + species, + events, + observables, + event_observables, + ) + + +models = [ + (model_neuron_def, "model_neuron", ["v0", "I0"]), + (model_events_def, "model_events", ["k1", "k2", "k3", "k4"]), +] + + +@pytest.mark.skipif( + os.environ.get("AMICI_SKIP_CMAKE_TESTS", "") == "TRUE", + reason="skipping cmake based test", +) +@pytest.mark.parametrize("model_def,model_name,constants", models) +def test_models(model_def, model_name, constants): + ( + initial_assignments, + parameters, + rate_rules, + species, + events, + observables, + event_observables, + ) = model_def() + + sbml_document, sbml_model = create_sbml_model( + initial_assignments=initial_assignments, + parameters=parameters, + rate_rules=rate_rules, + species=species, + events=events, + # uncomment `to_file` to save SBML model to file for inspection + # to_file=sbml_test_models / (model_name + '.sbml'), + ) + + model = create_amici_model( + sbml_model, + model_name=model_name, + observables=observables, + constant_parameters=constants, + event_observables=event_observables, + ) + + run_test_cases(model) + + return + + +def run_test_cases(model): + solver = model.getSolver() + + model_name = model.getName() + + for case in list(expected_results[model_name].keys()): + if case.startswith("sensi2"): + continue + + amici.readModelDataFromHDF5( + options_file, model.get(), f"/{model_name}/{case}/options" + ) + amici.readSolverSettingsFromHDF5( + options_file, solver.get(), f"/{model_name}/{case}/options" + ) + + edata = None + if "data" in expected_results[model.getName()][case].keys(): + edata = amici.readSimulationExpData( + str(expected_results_file), + f"/{model_name}/{case}/data", + model.get(), + ) + rdata = amici.runAmiciSimulation(model, solver, edata) + + verify_simulation_opts = dict() + + if model_name.startswith("model_neuron"): + verify_simulation_opts["atol"] = 1e-5 + verify_simulation_opts["rtol"] = 1e-2 + + verify_simulation_results( + rdata, + expected_results[model.getName()][case]["results"], + **verify_simulation_opts, + ) diff --git a/deps/AMICI/python/tests/test_ode_export.py b/deps/AMICI/python/tests/test_ode_export.py index 83843348e..b30d451a4 100644 --- a/deps/AMICI/python/tests/test_ode_export.py +++ b/deps/AMICI/python/tests/test_ode_export.py @@ -2,62 +2,107 @@ import sympy as sp from amici.cxxcodeprinter import AmiciCxxCodePrinter +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, symbol_row_vals, sparse_list, symbol_list, sparse_matrix \ - = printer.csc_matrix(matrix, rownames=['a1', 'a2'], - colnames=['b1', 'b2']) + ( + symbol_col_ptrs, + symbol_row_vals, + sparse_list, + symbol_list, + sparse_matrix, + ) = printer.csc_matrix( + matrix, + rownames=[sp.Symbol("a1"), sp.Symbol("a2")], + colnames=[sp.Symbol("b1"), sp.Symbol("b2")], + ) assert symbol_col_ptrs == [0, 2, 3] assert symbol_row_vals == [0, 1, 1] assert sparse_list == sp.Matrix([[1], [2], [3]]) - assert symbol_list == ['da1_db1', 'da2_db1', 'da2_db2'] - assert str(sparse_matrix) == 'Matrix([[da1_db1, 0], [da2_db1, da2_db2]])' + assert symbol_list == ["da1_db1", "da2_db1", "da2_db2"] + assert str(sparse_matrix) == "Matrix([[da1_db1, 0], [da2_db1, da2_db2]])" +@skip_on_valgrind def test_csc_matrix_empty(): """Test sparse CSC matrix creation for empty matrix""" printer = AmiciCxxCodePrinter() matrix = sp.Matrix() - symbol_col_ptrs, symbol_row_vals, sparse_list, symbol_list, sparse_matrix \ - = printer.csc_matrix(matrix, rownames=[], colnames=[]) + ( + symbol_col_ptrs, + symbol_row_vals, + sparse_list, + symbol_list, + sparse_matrix, + ) = printer.csc_matrix(matrix, rownames=[], colnames=[]) assert symbol_col_ptrs == [] assert symbol_row_vals == [] assert sparse_list == sp.Matrix(0, 0, []) assert symbol_list == [] - assert str(sparse_matrix) == 'Matrix(0, 0, [])' + assert str(sparse_matrix) == "Matrix(0, 0, [])" +@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, symbol_row_vals, sparse_list, symbol_list, sparse_matrix \ - = printer.csc_matrix( - matrix[:, 0], colnames=[sp.Symbol('b')], - rownames=[sp.Symbol('a1'), sp.Symbol('a2')] - ) + ( + symbol_col_ptrs, + symbol_row_vals, + sparse_list, + symbol_list, + sparse_matrix, + ) = printer.csc_matrix( + matrix[:, 0], + colnames=[sp.Symbol("b")], + rownames=[sp.Symbol("a1"), sp.Symbol("a2")], + ) assert symbol_col_ptrs == [0, 2] assert symbol_row_vals == [0, 1] assert sparse_list == sp.Matrix([[1], [2]]) - assert symbol_list == ['da1_db', 'da2_db'] - assert str(sparse_matrix) == 'Matrix([[da1_db], [da2_db]])' + assert symbol_list == ["da1_db", "da2_db"] + assert str(sparse_matrix) == "Matrix([[da1_db], [da2_db]])" # Test continuation of numbering of symbols - symbol_col_ptrs, symbol_row_vals, sparse_list, symbol_list, sparse_matrix \ - = printer.csc_matrix( - matrix[:, 1], colnames=[sp.Symbol('b')], - rownames=[sp.Symbol('a1'), sp.Symbol('a2')], identifier=1 - ) + ( + symbol_col_ptrs, + symbol_row_vals, + sparse_list, + symbol_list, + sparse_matrix, + ) = printer.csc_matrix( + matrix[:, 1], + colnames=[sp.Symbol("b")], + rownames=[sp.Symbol("a1"), sp.Symbol("a2")], + identifier=1, + ) assert symbol_col_ptrs == [0, 1] assert symbol_row_vals == [1] assert sparse_list == sp.Matrix([[3]]) - assert symbol_list == ['da2_db_1'] - assert str(sparse_matrix) == 'Matrix([[0], [da2_db_1]])' + assert symbol_list == ["da2_db_1"] + assert str(sparse_matrix) == "Matrix([[0], [da2_db_1]])" + + +def test_match_deriv(): + from amici.de_export import DERIVATIVE_PATTERN as pat + + def check(str, out1, out2): + match = pat.match(str) + assert match[1] == out1, (str, match[1], match[2]) + assert match[2] == out2, (str, match[1], match[2]) + + check("dwdx", "w", "x") + check("dx_rdatadtotal_cl", "x_rdata", "total_cl") + check("dtotal_cldx_rdata", "total_cl", "x_rdata") + check("dxdotdw", "xdot", "w") + check("dxdotdx_explicit", "xdot", "x") diff --git a/deps/AMICI/python/tests/test_pandas.py b/deps/AMICI/python/tests/test_pandas.py index 80ee45735..21c58bcaf 100644 --- a/deps/AMICI/python/tests/test_pandas.py +++ b/deps/AMICI/python/tests/test_pandas.py @@ -6,20 +6,19 @@ import numpy as np import pytest - # test parameters for test_pandas_import_export -combos = itertools.product( - [(10, 5), (5, 10), ()], - repeat=3 -) -cases = [{ - 'fixedParameters': combo[0], - 'fixedParametersPreequilibration': combo[1], - 'fixedParametersPresimulation': combo[2], -} for combo in combos] - - -@pytest.mark.parametrize('case', cases) +combos = itertools.product([(10, 5), (5, 10), ()], repeat=3) +cases = [ + { + "fixedParameters": combo[0], + "fixedParametersPreequilibration": combo[1], + "fixedParametersPresimulation": combo[2], + } + for combo in combos +] + + +@pytest.mark.parametrize("case", cases) def test_pandas_import_export(sbml_example_presimulation_module, case): """TestCase class for testing csv import using pandas""" @@ -39,19 +38,23 @@ def test_pandas_import_export(sbml_example_presimulation_module, case): df_edata = amici.getDataObservablesAsDataFrame(model, edata) edata_reconstructed = amici.getEdataFromDataFrame(model, df_edata) - for fp in ['fixedParameters', 'fixedParametersPreequilibration', - 'fixedParametersPresimulation']: - - if fp != 'fixedParameters' or case[fp] != (): + for fp in [ + "fixedParameters", + "fixedParametersPreequilibration", + "fixedParametersPresimulation", + ]: + if fp != "fixedParameters" or case[fp] != (): assert getattr(edata[0], fp) == getattr(edata_reconstructed[0], fp) assert case[fp] == getattr(edata_reconstructed[0], fp) else: - assert model.getFixedParameters() \ - == getattr(edata_reconstructed[0], fp) + assert model.getFixedParameters() == getattr( + edata_reconstructed[0], fp + ) - assert model.getFixedParameters() == \ - getattr(edata_reconstructed[0], fp) + assert model.getFixedParameters() == getattr( + edata_reconstructed[0], fp + ) assert getattr(edata[0], fp) == case[fp] diff --git a/deps/AMICI/python/tests/test_parameter_mapping.py b/deps/AMICI/python/tests/test_parameter_mapping.py index 65f48d146..ae66e23f5 100644 --- a/deps/AMICI/python/tests/test_parameter_mapping.py +++ b/deps/AMICI/python/tests/test_parameter_mapping.py @@ -1,50 +1,73 @@ """Test for ``amici.parameter_mapping``""" +import os +import pytest from amici.parameter_mapping import ( - ParameterMappingForCondition, ParameterMapping) + ParameterMapping, + ParameterMappingForCondition, +) +from amici.testing import skip_on_valgrind +@skip_on_valgrind def test_parameter_mapping_for_condition_default_args(): """Check we can initialize the mapping with default arguments.""" par_map_for_condition = ParameterMappingForCondition() for attr in [ - 'map_sim_var', 'scale_map_sim_var', 'map_preeq_fix', - 'scale_map_preeq_fix', 'map_sim_fix', 'scale_map_sim_fix']: + "map_sim_var", + "scale_map_sim_var", + "map_preeq_fix", + "scale_map_preeq_fix", + "map_sim_fix", + "scale_map_sim_fix", + ]: assert not getattr(par_map_for_condition, attr) - map_sim_var = {'sim_par0': 8, 'sim_par1': 'opt_par0'} - map_preeq_fix = {'sim_par2': 'opt_par1'} - map_sim_fix = {'sim_par2': 'opt_par2'} + map_sim_var = {"sim_par0": 8, "sim_par1": "opt_par0"} + map_preeq_fix = {"sim_par2": "opt_par1"} + map_sim_fix = {"sim_par2": "opt_par2"} par_map_for_condition = ParameterMappingForCondition( - map_sim_var=map_sim_var, map_preeq_fix=map_preeq_fix, - map_sim_fix=map_sim_fix) + map_sim_var=map_sim_var, + map_preeq_fix=map_preeq_fix, + map_sim_fix=map_sim_fix, + ) - expected_scale_map_sim_var = {'sim_par0': 'lin', 'sim_par1': 'lin'} - expected_scale_map_preeq_fix = {'sim_par2': 'lin'} - expected_scale_map_sim_fix = {'sim_par2': 'lin'} + expected_scale_map_sim_var = {"sim_par0": "lin", "sim_par1": "lin"} + expected_scale_map_preeq_fix = {"sim_par2": "lin"} + expected_scale_map_sim_fix = {"sim_par2": "lin"} - assert par_map_for_condition.scale_map_sim_var == \ - expected_scale_map_sim_var - assert par_map_for_condition.scale_map_preeq_fix == \ - expected_scale_map_preeq_fix - assert par_map_for_condition.scale_map_sim_fix == \ - expected_scale_map_sim_fix + assert ( + par_map_for_condition.scale_map_sim_var == expected_scale_map_sim_var + ) + assert ( + par_map_for_condition.scale_map_preeq_fix + == expected_scale_map_preeq_fix + ) + assert ( + par_map_for_condition.scale_map_sim_fix == expected_scale_map_sim_fix + ) +@skip_on_valgrind def test_parameter_mapping(): """Test :class:``amici.parameter_mapping.ParameterMapping``.""" parameter_mapping = ParameterMapping() assert len(parameter_mapping) == 0 - map_sim_var = {'sim_par0': 8, 'sim_par1': 'opt_par0'} - map_preeq_fix = {'sim_par2': 'opt_par1'} - map_sim_fix = {'sim_par2': 'opt_par2'} + map_sim_var = {"sim_par0": 8, "sim_par1": "opt_par0"} + map_preeq_fix = {"sim_par2": "opt_par1"} + map_sim_fix = {"sim_par2": "opt_par2"} par_map_for_condition = ParameterMappingForCondition( - map_sim_var=map_sim_var, map_preeq_fix=map_preeq_fix, - map_sim_fix=map_sim_fix) + map_sim_var=map_sim_var, + map_preeq_fix=map_preeq_fix, + map_sim_fix=map_sim_fix, + ) parameter_mapping.append(par_map_for_condition) assert len(parameter_mapping) == 1 + + assert isinstance(parameter_mapping[0], ParameterMappingForCondition) + assert isinstance(parameter_mapping[:], ParameterMapping) diff --git a/deps/AMICI/python/tests/test_petab_import.py b/deps/AMICI/python/tests/test_petab_import.py index 5de3f182d..fc978b76e 100644 --- a/deps/AMICI/python/tests/test_petab_import.py +++ b/deps/AMICI/python/tests/test_petab_import.py @@ -1,56 +1,138 @@ """Tests related to amici.petab_import""" import libsbml +import pandas as pd 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") @pytest.fixture def simple_sbml_model(): - """Create a basic SBML model for test_constant_species_to_parameters""" + """Create a basic SBML model for test_get_fixed_parameters""" document = libsbml.SBMLDocument(3, 1) model = document.createModel() + model.setId("simple_sbml_model") model.setTimeUnits("second") model.setExtentUnits("mole") - model.setSubstanceUnits('mole') + model.setSubstanceUnits("mole") + + for par_idx in range(1, 6): + p = model.createParameter() + p.setId(f"p{par_idx}") + p.setValue(par_idx) + + c = model.createCompartment() + c.setId("c1") s = model.createSpecies() - s.setId('x1') + s.setId("x1") s.setConstant(True) s.setInitialConcentration(1.0) - - # Add reaction to ensure species is replaced in reactions - r = model.createReaction() - r.setId('r1') - # Add multiple instances to ensure all are removed - for (coeff, name) in [(1, 'x1'), (1, 'x1')]: - species_ref = r.createReactant() - species_ref.setSpecies(name) - species_ref.setStoichiometry(coeff) - for (coeff, name) in [(1, 'x1'), (1, 'x1')]: - species_ref = r.createProduct() - species_ref.setSpecies(name) - species_ref.setStoichiometry(coeff) - for name in ['x1', 'x1']: - species_ref = r.createModifier() - species_ref.setSpecies(name) + s.setCompartment(c.getId()) return document, model -def test_constant_species_to_parameters(simple_sbml_model): - """Test conversion from species to constant parameters""" +@skip_on_valgrind +def test_get_fixed_parameters(simple_sbml_model): + """Check for correct identification of fixed parameters: + + p1: fixed (via condition table) + p2: (so far) not fixed (parametric override in condition table) + p3: fixed (via parameter table `estimate=0`) + p4: not fixed (via parameter table `estimate=1`) + p5: fixed (implicitly, because not listed as estimated) + """ + from petab.models.sbml_model import SbmlModel + + sbml_doc, sbml_model = simple_sbml_model + condition_df = petab.get_condition_df( + pd.DataFrame( + { + petab.CONDITION_ID: ["condition0"], + "p1": [1.0], + "p2": ["p1"], + } + ) + ) + parameter_df = petab.get_parameter_df( + pd.DataFrame( + {petab.PARAMETER_ID: ["p3", "p4"], petab.ESTIMATE: [0, 1]} + ) + ) + print(condition_df) + print(parameter_df) + petab_problem = petab.Problem( + model=SbmlModel(sbml_model), + parameter_df=parameter_df, + condition_df=condition_df, + ) + assert set(amici_petab_import.get_fixed_parameters(petab_problem)) == { + "p1", + "p3", + "p5", + } + + assert set( + amici_petab_import.get_fixed_parameters( + petab_problem, non_estimated_parameters_as_constants=False + ) + ) == {"p1", "p5"} + - document, model = simple_sbml_model +@skip_on_valgrind +def test_default_output_parameters(simple_sbml_model): + from petab.models.sbml_model import SbmlModel - amici_petab_import.constant_species_to_parameters(model) + sbml_doc, sbml_model = simple_sbml_model + condition_df = petab.get_condition_df( + pd.DataFrame( + { + petab.CONDITION_ID: ["condition0"], + } + ) + ) + parameter_df = petab.get_parameter_df( + pd.DataFrame({petab.PARAMETER_ID: [], petab.ESTIMATE: []}) + ) + observable_df = petab.get_observable_df( + pd.DataFrame( + { + petab.OBSERVABLE_ID: ["obs1"], + petab.OBSERVABLE_FORMULA: ["observableParameter1_obs1"], + petab.NOISE_FORMULA: [1], + } + ) + ) + petab_problem = petab.Problem( + model=SbmlModel(sbml_model), + parameter_df=parameter_df, + condition_df=condition_df, + observable_df=observable_df, + ) - assert len(list(model.getListOfParameters())) == 1 - assert len(list(model.getListOfSpecies())) == 0 + with TemporaryDirectoryWinSafe() as outdir: + sbml_importer = amici_petab_import.import_model( + petab_problem=petab_problem, + output_parameter_defaults={"observableParameter1_obs1": 1.0}, + compile=False, + model_output_dir=outdir, + ) + assert ( + 1.0 + == sbml_importer.sbml.getParameter( + "observableParameter1_obs1" + ).getValue() + ) - r = model.getReaction(0) - assert len(list(r.getListOfReactants())) == 0 - assert len(list(r.getListOfProducts())) == 0 - assert len(list(r.getListOfModifiers())) == 0 + with pytest.raises(ValueError): + amici_petab_import.import_model( + petab_problem=petab_problem, + output_parameter_defaults={"nonExistentParameter": 1.0}, + compile=False, + model_output_dir=outdir, + ) diff --git a/deps/AMICI/python/tests/test_petab_objective.py b/deps/AMICI/python/tests/test_petab_objective.py new file mode 100755 index 000000000..e31e693d1 --- /dev/null +++ b/deps/AMICI/python/tests/test_petab_objective.py @@ -0,0 +1,95 @@ +"""Tests for petab_objective.py.""" + +from functools import partial +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 pytest +from amici.petab_objective import SLLH + +# Absolute and relative tolerances for finite difference gradient checks. +ATOL: float = 1e-3 +RTOL: float = 1e-3 + + +@pytest.fixture +def lotka_volterra() -> petab.Problem: + return petab.Problem.from_yaml( + str( + Path(__file__).parent + / "petab_test_problems" + / "lotka_volterra" + / "petab" + / "problem.yaml" + ) + ) + + +def test_simulate_petab_sensitivities(lotka_volterra): + petab_problem = lotka_volterra + amici_model = amici.petab_import.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, + ) + ) + + results = {} + for scaled_parameters in [True, False]: + for scaled_gradients in [True, False]: + _problem_parameters = problem_parameters.copy() + if scaled_parameters: + _problem_parameters = petab_problem.scale_parameters( + problem_parameters + ) + results[(scaled_parameters, scaled_gradients)] = pd.Series( + amici.petab_objective.simulate_petab( + petab_problem=petab_problem, + amici_model=amici_model, + solver=amici_solver, + problem_parameters=_problem_parameters, + scaled_parameters=scaled_parameters, + scaled_gradients=scaled_gradients, + )[SLLH] + ) + + # Computed previously, is the same as a central difference gradient + # check, to >4 s.f. + expected_results_scaled = pd.Series( + { + "alpha": -2.112626, + "gamma": 21.388535, + } + ) + expected_results_unscaled = pd.Series( + { + "alpha": -0.458800, + "gamma": 3.096308, + } + ) + + assert_equal = partial(pd.testing.assert_series_equal, rtol=1e-3) + + # `scaled_gradients` affects gradients, `scaled_parameters` does not. + assert_equal(results[(True, True)], expected_results_scaled) + assert_equal(results[(False, True)], expected_results_scaled) + + assert_equal(results[(True, False)], expected_results_unscaled) + assert_equal(results[(False, False)], expected_results_unscaled) + + # The test gradients are scaled correctly. + assert_equal( + results[(True, True)], + results[(True, False)] * pd.Series(problem_parameters) * np.log(10), + ) diff --git a/deps/AMICI/python/tests/test_petab_simulate.py b/deps/AMICI/python/tests/test_petab_simulate.py index ecb0a2b16..febea5fd5 100644 --- a/deps/AMICI/python/tests/test_petab_simulate.py +++ b/deps/AMICI/python/tests/test_petab_simulate.py @@ -1,23 +1,26 @@ """Tests for petab_simulate.py.""" - -from pathlib import Path -import pytest import tempfile +from pathlib import Path -from amici.petab_simulate import PetabSimulator import petab import petabtests +import pytest +from amici.petab_simulate import PetabSimulator +from amici.testing import skip_on_valgrind @pytest.fixture def petab_problem() -> petab.Problem: """Create a PEtab problem for use in tests.""" - test_case = '0001' - test_case_dir = Path(petabtests.SBML_DIR) / petabtests.CASES_LIST[0] + test_case = "0001" + test_case_dir = petabtests.get_case_dir( + id_=test_case, format_="sbml", version="v1.0.0" + ) petab_yaml_path = test_case_dir / petabtests.problem_yaml_name(test_case) return petab.Problem.from_yaml(str(petab_yaml_path)) +@skip_on_valgrind def test_simulate_without_noise(petab_problem): """Test the reproducibility of simulation without noise.""" simulator = PetabSimulator(petab_problem) @@ -37,6 +40,7 @@ def test_simulate_without_noise(petab_problem): assert synthetic_data_df_c.equals(synthetic_data_df_a) +@skip_on_valgrind def test_subset_call(petab_problem): """ Test the ability to customize AMICI methods, specifically: @@ -44,18 +48,19 @@ def test_subset_call(petab_problem): `model_output_dir`, import is skipped if `amici_model` is specified), and :py:func:`amici.petab_objective.simulate_petab` (`amici_model`, `solver`). """ - model_name = 'model_name_dummy' + model_name = "model_name_dummy" model_output_dir = tempfile.mkdtemp() simulator0 = PetabSimulator(petab_problem) - assert not (Path(model_output_dir)/model_name).is_dir() - simulator0.simulate(model_name=model_name, - model_output_dir=model_output_dir) + assert not (Path(model_output_dir) / model_name).is_dir() + simulator0.simulate( + model_name=model_name, model_output_dir=model_output_dir + ) # Model name is handled correctly assert simulator0.amici_model.getName() == model_name # Check model output directory is created, by # :py:func:`amici.petab_import.import_petab_problem` - assert (Path(model_output_dir)/model_name).is_dir() + assert (Path(model_output_dir) / model_name).is_dir() simulator = PetabSimulator(petab_problem) simulator.simulate(amici_model=simulator0.amici_model) diff --git a/deps/AMICI/python/tests/test_preequilibration.py b/deps/AMICI/python/tests/test_preequilibration.py index 2af6da888..a42bc6354 100644 --- a/deps/AMICI/python/tests/test_preequilibration.py +++ b/deps/AMICI/python/tests/test_preequilibration.py @@ -1,15 +1,16 @@ -"""Tests for preequilibration""" +"""Tests for pre- and post-equilibration""" import itertools import amici import numpy as np import pytest +from numpy.testing import assert_allclose from test_pysb import get_data + @pytest.fixture def preeq_fixture(pysb_example_presimulation_module): - model = pysb_example_presimulation_module.getModel() model.setReinitializeFixedParameterInitialStates(True) @@ -27,56 +28,77 @@ def preeq_fixture(pysb_example_presimulation_module): edata_preeq = amici.ExpData(edata) edata_preeq.t_presim = 0 edata_preeq.setTimepoints([np.infty]) - edata_preeq.fixedParameters = \ - edata.fixedParametersPreequilibration + edata_preeq.fixedParameters = edata.fixedParametersPreequilibration edata_preeq.fixedParametersPresimulation = () edata_preeq.fixedParametersPreequilibration = () edata_presim = amici.ExpData(edata) edata_presim.t_presim = 0 edata_presim.setTimepoints([edata.t_presim]) - edata_presim.fixedParameters = \ - edata.fixedParametersPresimulation + edata_presim.fixedParameters = edata.fixedParametersPresimulation edata_presim.fixedParametersPresimulation = () edata_presim.fixedParametersPreequilibration = () edata_sim = amici.ExpData(edata) edata_sim.t_presim = 0 edata_sim.setTimepoints(edata.getTimepoints()) - edata_sim.fixedParameters = \ - edata.fixedParameters + edata_sim.fixedParameters = edata.fixedParameters edata_sim.fixedParametersPresimulation = () edata_sim.fixedParametersPreequilibration = () pscales = [ - amici.ParameterScaling.log10, amici.ParameterScaling.ln, + amici.ParameterScaling.log10, + amici.ParameterScaling.ln, amici.ParameterScaling.none, - amici.parameterScalingFromIntVector([ - amici.ParameterScaling.log10, amici.ParameterScaling.ln, - amici.ParameterScaling.none, amici.ParameterScaling.log10, - amici.ParameterScaling.ln, amici.ParameterScaling.none - ]) + amici.parameterScalingFromIntVector( + [ + amici.ParameterScaling.log10, + amici.ParameterScaling.ln, + amici.ParameterScaling.none, + amici.ParameterScaling.log10, + amici.ParameterScaling.ln, + amici.ParameterScaling.none, + ] + ), ] plists = [ - [3, 1, 2, 4], [0, 1, 2, 3, 4, 5], [5, 3, 2, 0, 4, 1], - [1, 2, 3, 4, 5], [1, 1, 1], + [3, 1, 2, 4], + [0, 1, 2, 3, 4, 5], + [5, 3, 2, 0, 4, 1], + [1, 2, 3, 4, 5], + [1, 1, 1], ] - return (model, solver, edata, edata_preeq, - edata_presim, edata_sim, pscales, plists) + return ( + model, + solver, + edata, + edata_preeq, + edata_presim, + edata_sim, + pscales, + plists, + ) def test_manual_preequilibration(preeq_fixture): """Manual preequilibration""" - model, solver, edata, edata_preeq, \ - edata_presim, edata_sim, pscales, plists = preeq_fixture + ( + model, + solver, + edata, + edata_preeq, + edata_presim, + edata_sim, + pscales, + plists, + ) = preeq_fixture settings = itertools.product(pscales, plists) for pscale, plist in settings: - model.setInitialStates([]) model.setInitialStateSensitivities([]) model.setParameterList(plist) @@ -91,10 +113,10 @@ def test_manual_preequilibration(preeq_fixture): assert rdata_preeq.status == amici.AMICI_SUCCESS # manual reinitialization + presimulation - x0 = rdata_preeq['x'][0, :] + x0 = rdata_preeq["x"][0, :] x0[1] = edata_presim.fixedParameters[0] x0[2] = edata_presim.fixedParameters[1] - sx0 = rdata_preeq['sx'][0, :, :] + sx0 = rdata_preeq["sx"][0, :, :] sx0[:, 1] = 0 sx0[:, 2] = 0 model.setInitialStates(x0) @@ -103,10 +125,10 @@ def test_manual_preequilibration(preeq_fixture): assert rdata_presim.status == amici.AMICI_SUCCESS # manual reinitialization + simulation - x0 = rdata_presim['x'][0, :] + x0 = rdata_presim["x"][0, :] x0[1] = edata_sim.fixedParameters[0] x0[2] = edata_sim.fixedParameters[1] - sx0 = rdata_presim['sx'][0, :, :] + sx0 = rdata_presim["sx"][0, :, :] sx0[:, 1] = 0 sx0[:, 2] = 0 model.setInitialStates(x0) @@ -114,19 +136,31 @@ def test_manual_preequilibration(preeq_fixture): rdata_sim = amici.runAmiciSimulation(model, solver, edata_sim) assert rdata_sim.status == amici.AMICI_SUCCESS - for variable in ['x', 'sx']: - assert np.isclose( + for variable in ["x", "sx"]: + assert_allclose( rdata_auto[variable], rdata_sim[variable], - 1e-6, 1e-6 - ).all(), dict(pscale=pscale, plist=plist, variable=variable) + atol=1e-6, + rtol=1e-6, + err_msg=str( + dict(pscale=pscale, plist=plist, variable=variable) + ), + ) def test_parameter_reordering(preeq_fixture): """Test parameter reordering""" - model, solver, edata, edata_preeq, \ - edata_presim, edata_sim, pscales, plists = preeq_fixture + ( + model, + solver, + edata, + edata_preeq, + edata_presim, + edata_sim, + pscales, + plists, + ) = preeq_fixture rdata_ordered = amici.runAmiciSimulation(model, solver, edata) @@ -135,18 +169,28 @@ def test_parameter_reordering(preeq_fixture): rdata_reordered = amici.runAmiciSimulation(model, solver, edata) for ip, p_index in enumerate(plist): - assert np.isclose( - rdata_ordered['sx'][:, p_index, :], - rdata_reordered['sx'][:, ip, :], - 1e-6, 1e-6 - ).all(), plist + assert_allclose( + rdata_ordered["sx"][:, p_index, :], + rdata_reordered["sx"][:, ip, :], + atol=1e-6, + rtol=1e-6, + err_msg=str(dict(variable="sx", plist=plist, p_index=p_index)), + ) def test_data_replicates(preeq_fixture): """Test data replicates""" - model, solver, edata, edata_preeq, \ - edata_presim, edata_sim, pscales, plists = preeq_fixture + ( + model, + solver, + edata, + edata_preeq, + edata_presim, + edata_sim, + pscales, + plists, + ) = preeq_fixture sensi_meth = amici.SensitivityMethod.forward solver.setSensitivityMethod(sensi_meth) @@ -171,19 +215,29 @@ def test_data_replicates(preeq_fixture): rdata_double = amici.runAmiciSimulation(model, solver, edata) - for variable in ['llh', 'sllh']: - assert np.isclose( - 2*rdata_single[variable], + for variable in ["llh", "sllh"]: + assert_allclose( + 2 * rdata_single[variable], rdata_double[variable], - 1e-6, 1e-6 - ).all(), dict(variable=variable, sensi_meth=sensi_meth) + atol=1e-6, + rtol=1e-6, + err_msg=str(dict(variable=variable, sensi_meth=sensi_meth)), + ) def test_parameter_in_expdata(preeq_fixture): """Test parameter in ExpData""" - model, solver, edata, edata_preeq, edata_presim, \ - edata_sim, pscales, plists = preeq_fixture + ( + model, + solver, + edata, + edata_preeq, + edata_presim, + edata_sim, + pscales, + plists, + ) = preeq_fixture rdata = amici.runAmiciSimulation(model, solver, edata) @@ -196,57 +250,65 @@ def test_parameter_in_expdata(preeq_fixture): edata.sx0 = model.getInitialStateSensitivities() # perturb model initial states - model.setInitialStates(rdata['x_ss'] * 4) - model.setInitialStateSensitivities(rdata['sx_ss'].flatten() / 2) + model.setInitialStates(rdata["x_ss"] * 4) + model.setInitialStateSensitivities(rdata["sx_ss"].flatten() / 2) # set ExpData plist edata.plist = model.getParameterList() # perturb model parameter list - model.setParameterList([ - i for i in reversed(model.getParameterList()) - ]) + model.setParameterList([i for i in reversed(model.getParameterList())]) # set ExpData parameters edata.parameters = model.getParameters() # perturb model parameters - model.setParameters(tuple( - p * 2 for p in model.getParameters() - )) + model.setParameters(tuple(p * 2 for p in model.getParameters())) # set ExpData pscale edata.pscale = model.getParameterScale() # perturb model pscale, needs to be done after getting parameters, # otherwise we will mess up parameter value - model.setParameterScale(amici.parameterScalingFromIntVector([ - amici.ParameterScaling.log10 - if scaling == amici.ParameterScaling.none - else amici.ParameterScaling.none - for scaling in model.getParameterScale() - ])) - - rdata_edata = amici.runAmiciSimulation( - model, solver, edata + model.setParameterScale( + amici.parameterScalingFromIntVector( + [ + amici.ParameterScaling.log10 + if scaling == amici.ParameterScaling.none + else amici.ParameterScaling.none + for scaling in model.getParameterScale() + ] + ) ) - for variable in ['x', 'sx']: - assert np.isclose( + + rdata_edata = amici.runAmiciSimulation(model, solver, edata) + for variable in ["x", "sx"]: + assert_allclose( rdata[variable][0, :], rdata_edata[variable][0, :], - 1e-6, 1e-6 - ).all(), variable + atol=1e-6, + rtol=1e-6, + err_msg=str(dict(variable=variable)), + ) def test_raise_presimulation_with_adjoints(preeq_fixture): """Test simulation failures with adjoin+presimulation""" - model, solver, edata, edata_preeq, \ - edata_presim, edata_sim, pscales, plists = preeq_fixture + ( + model, + solver, + edata, + edata_preeq, + edata_presim, + edata_sim, + pscales, + plists, + ) = preeq_fixture # preequilibration and presimulation with adjoints: # this needs to fail unless we remove presimulation solver.setSensitivityMethod(amici.SensitivityMethod.adjoint) rdata = amici.runAmiciSimulation(model, solver, edata) - assert rdata['status'] == amici.AMICI_ERROR + assert rdata["status"] == amici.AMICI_ERROR # add postequilibration y = edata.getObservedData() @@ -262,15 +324,23 @@ def test_raise_presimulation_with_adjoints(preeq_fixture): # no presim any more, this should work rdata = amici.runAmiciSimulation(model, solver, edata) - assert rdata['status'] == amici.AMICI_SUCCESS + assert rdata["status"] == amici.AMICI_SUCCESS def test_equilibration_methods_with_adjoints(preeq_fixture): """Test different combinations of equilibration and simulation sensitivity methods""" - model, solver, edata, edata_preeq, \ - edata_presim, edata_sim, pscales, plists = preeq_fixture + ( + model, + solver, + edata, + edata_preeq, + edata_presim, + edata_sim, + pscales, + plists, + ) = preeq_fixture # we don't want presim edata.t_presim = 0.0 @@ -285,10 +355,15 @@ def test_equilibration_methods_with_adjoints(preeq_fixture): edata.setObservedDataStdDev(np.hstack([stdy, stdy[0]])) rdatas = {} - equil_meths = [amici.SteadyStateSensitivityMode.newtonOnly, - amici.SteadyStateSensitivityMode.simulationFSA] - sensi_meths = [amici.SensitivityMethod.forward, - amici.SensitivityMethod.adjoint] + equil_meths = [ + amici.SteadyStateSensitivityMode.newtonOnly, + amici.SteadyStateSensitivityMode.integrationOnly, + amici.SteadyStateSensitivityMode.integrateIfNewtonFails, + ] + sensi_meths = [ + amici.SensitivityMethod.forward, + amici.SensitivityMethod.adjoint, + ] settings = itertools.product(equil_meths, sensi_meths) for setting in settings: @@ -302,23 +377,37 @@ def test_equilibration_methods_with_adjoints(preeq_fixture): rdatas[setting] = amici.runAmiciSimulation(model, solver, edata) # assert successful simulation - assert rdatas[setting]['status'] == amici.AMICI_SUCCESS + assert rdatas[setting]["status"] == amici.AMICI_SUCCESS for setting1, setting2 in itertools.product(settings, settings): # assert correctness of result - for variable in ['llh', 'sllh']: - assert np.isclose( + for variable in ["llh", "sllh"]: + assert_allclose( rdatas[setting1][variable], rdatas[setting2][variable], - 1e-6, 1e-6 - ).all(), variable + atol=1e-6, + rtol=1e-6, + err_msg=str( + dict( + variable=variable, setting1=setting1, setting2=setting2 + ) + ), + ) def test_newton_solver_equilibration(preeq_fixture): - """Test data replicates""" - - model, solver, edata, edata_preeq, \ - edata_presim, edata_sim, pscales, plists = preeq_fixture + """Test newton solver for equilibration""" + + ( + model, + solver, + edata, + edata_preeq, + edata_presim, + edata_sim, + pscales, + plists, + ) = preeq_fixture # we don't want presim edata.t_presim = 0.0 @@ -333,8 +422,13 @@ def test_newton_solver_equilibration(preeq_fixture): edata.setObservedDataStdDev(np.hstack([stdy, stdy[0]])) rdatas = {} - settings = [amici.SteadyStateSensitivityMode.simulationFSA, - amici.SteadyStateSensitivityMode.newtonOnly] + settings = [ + amici.SteadyStateSensitivityMode.integrationOnly, + amici.SteadyStateSensitivityMode.newtonOnly, + ] + + solver.setNewtonStepSteadyStateCheck(True) + solver.setRelativeToleranceSteadyState(1e-12) for equil_meth in settings: # set sensi method @@ -343,35 +437,199 @@ def test_newton_solver_equilibration(preeq_fixture): model.setSteadyStateSensitivityMode(equil_meth) if equil_meth == amici.SteadyStateSensitivityMode.newtonOnly: solver.setNewtonMaxSteps(10) - else: - solver.setNewtonMaxSteps(0) # add rdatas rdatas[equil_meth] = amici.runAmiciSimulation(model, solver, edata) # assert successful simulation - assert rdatas[equil_meth]['status'] == amici.AMICI_SUCCESS + assert rdatas[equil_meth]["status"] == amici.AMICI_SUCCESS # assert correct results - for variable in ['llh', 'sllh', 'sx0', 'sx_ss', 'x_ss']: - assert np.isclose( + for variable in ["llh", "sllh", "sx0", "sx_ss", "x_ss"]: + assert_allclose( rdatas[settings[0]][variable], rdatas[settings[1]][variable], - 1e-6, 1e-6 - ).all(), variable + atol=1e-5, + rtol=1e-5, + err_msg=str(dict(variable=variable)), + ) + + +def test_newton_steadystate_check(preeq_fixture): + """Test NewtonStepSteadyStateCheck solver flag""" + + ( + model, + solver, + edata, + edata_preeq, + edata_presim, + edata_sim, + pscales, + plists, + ) = preeq_fixture - # test failure for iterative linear solver with sensitivities - edata.fixedParametersPreequilibration = () + # we don't want presim edata.t_presim = 0.0 edata.fixedParametersPresimulation = () - solver.setLinearSolver(amici.LinearSolver.SPBCG) - solver.setSensitivityMethod(amici.SensitivityMethod.adjoint) + # add infty timepoint + y = edata.getObservedData() + stdy = edata.getObservedDataStdDev() + ts = np.hstack([*edata.getTimepoints(), np.inf]) + edata.setTimepoints(sorted(ts)) + edata.setObservedData(np.hstack([y, y[0]])) + edata.setObservedDataStdDev(np.hstack([stdy, stdy[0]])) + + # set sensi method + sensi_meth = amici.SensitivityMethod.forward + solver.setSensitivityMethod(sensi_meth) + + solver.setNewtonMaxSteps(100) + + rdatas = {} + for newton_check in [True, False]: + solver.setNewtonStepSteadyStateCheck(newton_check) + + # add rdatas + rdatas[newton_check] = amici.runAmiciSimulation(model, solver, edata) + + # assert successful simulation + assert rdatas[newton_check]["status"] == amici.AMICI_SUCCESS + + # assert correct results + for variable in ["llh", "sllh", "sx0", "sx_ss", "x_ss"]: + assert_allclose( + rdatas[True][variable], + rdatas[False][variable], + atol=1e-6, + rtol=1e-6, + err_msg=str(dict(variable=variable, sensi_meth=sensi_meth)), + ) + + +def test_steadystate_computation_mode(preeq_fixture): + """Test newtonOnly and integrationOnly steady-state computation modes""" + ( + model, + solver, + edata, + edata_preeq, + edata_presim, + edata_sim, + pscales, + plists, + ) = preeq_fixture + + sensi_meth = amici.SensitivityMethod.forward solver.setSensitivityOrder(amici.SensitivityOrder.first) - model.setSteadyStateSensitivityMode( - amici.SteadyStateSensitivityMode.newtonOnly) + solver.setSensitivityMethodPreequilibration(sensi_meth) solver.setNewtonMaxSteps(10) - solver.setNewtonMaxLinearSteps(10) - rdata_spbcg = amici.runAmiciSimulation(model, solver, edata) - assert rdata_spbcg['status'] == amici.AMICI_ERROR + rdatas = {} + stst_computation_modes = [ + amici.SteadyStateComputationMode.integrationOnly, + amici.SteadyStateComputationMode.newtonOnly, + ] + for mode in stst_computation_modes: + model.setSteadyStateComputationMode(mode) + rdatas[mode] = amici.runAmiciSimulation(model, solver, edata) + + # assert successful simulation + assert rdatas[mode]["status"] == amici.AMICI_SUCCESS + + assert np.all( + rdatas[amici.SteadyStateComputationMode.integrationOnly][ + "preeq_status" + ][0] + == [0, 1, 0] + ) + assert ( + rdatas[amici.SteadyStateComputationMode.integrationOnly][ + "preeq_numsteps" + ][0][0] + == 0 + ) + + assert np.all( + rdatas[amici.SteadyStateComputationMode.newtonOnly]["preeq_status"][0] + == [1, 0, 0] + ) + assert ( + rdatas[amici.SteadyStateComputationMode.newtonOnly]["preeq_numsteps"][ + 0 + ][0] + > 0 + ) + + # assert correct results + for variable in ["llh", "sllh", "sx0", "sx_ss", "x_ss"]: + assert_allclose( + rdatas[stst_computation_modes[0]][variable], + rdatas[stst_computation_modes[1]][variable], + atol=1e-5, + rtol=1e-5, + err_msg=str(dict(variable=variable, sensi_meth=sensi_meth)), + ) + + +def test_simulation_errors(preeq_fixture): + ( + model, + solver, + edata, + edata_preeq, + edata_presim, + edata_sim, + pscales, + plists, + ) = preeq_fixture + + solver.setSensitivityOrder(amici.SensitivityOrder.first) + solver.setSensitivityMethodPreequilibration( + amici.SensitivityMethod.forward + ) + model.setSteadyStateSensitivityMode( + amici.SteadyStateSensitivityMode.integrationOnly + ) + solver.setMaxSteps(1) + + # exceeded maxsteps + # preeq & posteq + for e in [edata, edata_preeq]: + rdata = amici.runAmiciSimulation(model, solver, e) + assert rdata["status"] != amici.AMICI_SUCCESS + assert rdata._swigptr.messages[0].severity == amici.LogSeverity_debug + assert rdata._swigptr.messages[0].identifier == "EQUILIBRATION_FAILURE" + assert ( + "exceeded maximum number of integration steps" + in rdata._swigptr.messages[0].message + ) + 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)) + solver.setRelativeToleranceSteadyState(0.0) + solver.setAbsoluteToleranceSteadyState(0.0) + # preeq & posteq + for e in [edata_preeq, edata]: + rdata = amici.runAmiciSimulation(model, solver, e) + assert rdata["status"] != amici.AMICI_SUCCESS + assert rdata._swigptr.messages[0].severity == amici.LogSeverity_debug + assert ( + rdata._swigptr.messages[0].identifier + == "CVODES:CVode:RHSFUNC_FAIL" + ) + assert rdata._swigptr.messages[1].severity == amici.LogSeverity_debug + assert rdata._swigptr.messages[1].identifier == "EQUILIBRATION_FAILURE" + assert ( + "exceedingly long simulation time" + in rdata._swigptr.messages[1].message + ) + 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" diff --git a/deps/AMICI/python/tests/test_pregenerated_models.py b/deps/AMICI/python/tests/test_pregenerated_models.py index f746ea4d6..5a110cdfc 100755 --- a/deps/AMICI/python/tests/test_pregenerated_models.py +++ b/deps/AMICI/python/tests/test_pregenerated_models.py @@ -10,100 +10,117 @@ import h5py import numpy as np import pytest -from amici.gradient_check import check_derivatives, _check_results - -cpp_test_dir = Path(__file__).parents[2] / 'tests' / 'cpp' -options_file = str(cpp_test_dir / 'testOptions.h5') -expected_results_file = str(cpp_test_dir / 'expectedResults.h5') -expected_results = h5py.File(expected_results_file, 'r') - -model_cases = [(sub_test, case) - for sub_test in expected_results.keys() - for case in list(expected_results[sub_test].keys())] - - -@pytest.mark.skipif(os.environ.get('AMICI_SKIP_CMAKE_TESTS', '') == 'TRUE', - reason='skipping cmake based test') +from amici.gradient_check import _check_results, check_derivatives +from amici.testing import skip_on_valgrind + +cpp_test_dir = Path(__file__).parents[2] / "tests" / "cpp" +options_file = str(cpp_test_dir / "testOptions.h5") +expected_results_file = str(cpp_test_dir / "expectedResults.h5") +expected_results = h5py.File(expected_results_file, "r") + +model_cases = [ + (sub_test, case) + for sub_test in expected_results.keys() + for case in list(expected_results[sub_test].keys()) +] + + +@skip_on_valgrind +@pytest.mark.skipif( + os.environ.get("AMICI_SKIP_CMAKE_TESTS", "") == "TRUE", + reason="skipping cmake based test", +) @pytest.mark.parametrize("sub_test,case", model_cases) def test_pregenerated_model(sub_test, case): - """Tests models that were pregenerated using the the matlab code + """Tests models that were pregenerated using the matlab code generation routines and cmake build routines. NOTE: requires having run `make python-tests` in /build/ before to build the python modules for the test models. """ - if case.startswith('sensi2'): - model_name = sub_test + '_o2' + if case.startswith("sensi2"): + model_name = sub_test + "_o2" else: model_name = sub_test - model_swig_folder = str(Path(__file__).parents[2] / 'build' / 'tests' - / 'cpp' / f'external_{model_name}-prefix' / 'src' - / f'external_{model_name}-build' / 'swig') + model_swig_folder = str( + Path(__file__).parents[2] + / "build" + / "tests" + / "cpp" + / f"external_{model_name}-prefix" + / "src" + / f"external_{model_name}-build" + / "swig" + ) test_model_module = amici.import_model_module( - module_name=model_name, module_path=model_swig_folder) + module_name=model_name, module_path=model_swig_folder + ) model = test_model_module.getModel() solver = model.getSolver() amici.readModelDataFromHDF5( - options_file, model.get(), - f'/{sub_test}/{case}/options' + options_file, model.get(), f"/{sub_test}/{case}/options" ) amici.readSolverSettingsFromHDF5( - options_file, solver.get(), - f'/{sub_test}/{case}/options' + options_file, solver.get(), f"/{sub_test}/{case}/options" ) edata = None - if 'data' in expected_results[sub_test][case].keys(): + if "data" in expected_results[sub_test][case].keys(): edata = amici.readSimulationExpData( - str(expected_results_file), - f'/{sub_test}/{case}/data', model.get() + str(expected_results_file), f"/{sub_test}/{case}/data", model.get() ) - rdata = amici.runAmiciSimulation(model, solver, - edata) + rdata = amici.runAmiciSimulation(model, solver, edata) check_derivative_opts = dict() - if model_name == 'model_nested_events': - check_derivative_opts['rtol'] = 1e-2 - elif model_name == 'model_events': - check_derivative_opts['atol'] = 1e-3 - - if edata \ - and solver.getSensitivityMethod() \ - and solver.getSensitivityOrder() \ - and len(model.getParameterList()) \ - and not model_name.startswith('model_neuron') \ - and not case.endswith('byhandpreeq'): + if model_name == "model_nested_events": + check_derivative_opts["rtol"] = 1e-2 + elif model_name == "model_events": + check_derivative_opts["atol"] = 1e-3 + + if ( + edata + and solver.getSensitivityMethod() + and solver.getSensitivityOrder() + and len(model.getParameterList()) + and not model_name.startswith("model_neuron") + and not case.endswith("byhandpreeq") + ): check_derivatives(model, solver, edata, **check_derivative_opts) verify_simulation_opts = dict() - if model_name.startswith('model_neuron'): - verify_simulation_opts['atol'] = 1e-5 - verify_simulation_opts['rtol'] = 1e-2 + if model_name.startswith("model_neuron"): + verify_simulation_opts["atol"] = 1e-5 + verify_simulation_opts["rtol"] = 1e-2 - if model_name.startswith('model_robertson') and \ - case == 'sensiforwardSPBCG': - verify_simulation_opts['atol'] = 1e-3 - verify_simulation_opts['rtol'] = 1e-3 + if ( + model_name.startswith("model_robertson") + and case == "sensiforwardSPBCG" + ): + verify_simulation_opts["atol"] = 1e-3 + verify_simulation_opts["rtol"] = 1e-3 verify_simulation_results( - rdata, expected_results[sub_test][case]['results'], - **verify_simulation_opts + rdata, + expected_results[sub_test][case]["results"], + **verify_simulation_opts, ) - if model_name == 'model_steadystate' and \ - case == 'sensiforwarderrorint': + if model_name == "model_steadystate" and case == "sensiforwarderrorint": edata = amici.amici.ExpData(model.get()) # Test runAmiciSimulations: ensure running twice # with same ExpData yields same results - if edata and model_name != 'model_neuron_o2' and not ( - model_name == 'model_robertson' and - case == 'sensiforwardSPBCG' + if ( + edata + and model_name != "model_neuron_o2" + and not ( + model_name == "model_robertson" and case == "sensiforwardSPBCG" + ) ): if isinstance(edata, amici.amici.ExpData): edatas = [edata, edata] @@ -111,16 +128,17 @@ def test_pregenerated_model(sub_test, case): edatas = [edata.get(), edata.get()] rdatas = amici.runAmiciSimulations( - model, solver, edatas, num_threads=2, - failfast=False + model, solver, edatas, num_threads=2, failfast=False ) verify_simulation_results( - rdatas[0], expected_results[sub_test][case]['results'], - **verify_simulation_opts + rdatas[0], + expected_results[sub_test][case]["results"], + **verify_simulation_opts, ) verify_simulation_results( - rdatas[1], expected_results[sub_test][case]['results'], - **verify_simulation_opts + rdatas[1], + expected_results[sub_test][case]["results"], + **verify_simulation_opts, ) # test residuals mode @@ -131,9 +149,10 @@ def test_pregenerated_model(sub_test, case): solver.setReturnDataReportingMode(amici.RDataReporting.residuals) rdata = amici.runAmiciSimulation(model, solver, edata) verify_simulation_results( - rdata, expected_results[sub_test][case]['results'], - fields=['t', 'res', 'sres', 'y', 'sy', 'sigmay', 'ssigmay'], - **verify_simulation_opts + rdata, + expected_results[sub_test][case]["results"], + fields=["t", "res", "sres", "y", "sy", "sigmay", "ssigmay"], + **verify_simulation_opts, ) with pytest.raises(RuntimeError): solver.setSensitivityMethod(amici.SensitivityMethod.adjoint) @@ -144,22 +163,30 @@ def test_pregenerated_model(sub_test, case): solver.setReturnDataReportingMode(amici.RDataReporting.likelihood) rdata = amici.runAmiciSimulation(model, solver, edata) verify_simulation_results( - rdata, expected_results[sub_test][case]['results'], - fields=['t', 'llh', 'sllh', 's2llh', 'FIM'], **verify_simulation_opts + rdata, + expected_results[sub_test][case]["results"], + fields=["t", "llh", "sllh", "s2llh", "FIM"], + **verify_simulation_opts, ) # test sigma residuals - if model_name == 'model_jakstat_adjoint' and \ - solver.getSensitivityMethod() != amici.SensitivityMethod.adjoint: + if ( + model_name == "model_jakstat_adjoint" + and solver.getSensitivityMethod() != amici.SensitivityMethod.adjoint + ): model.setAddSigmaResiduals(True) solver.setReturnDataReportingMode(amici.RDataReporting.full) rdata = amici.runAmiciSimulation(model, solver, edata) # check whether activation changes chi2 assert chi2_ref != rdata.chi2 - if edata and solver.getSensitivityMethod() and \ - solver.getSensitivityOrder() and len(model.getParameterList()): + if ( + edata + and solver.getSensitivityMethod() + and solver.getSensitivityOrder() + and len(model.getParameterList()) + ): check_derivatives(model, solver, edata, **check_derivative_opts) chi2_ref = rdata.chi2 @@ -177,11 +204,12 @@ def test_pregenerated_model(sub_test, case): assert np.isnan(rdata.chi2) with pytest.raises(RuntimeError): - model.getParameterByName('thisParameterDoesNotExist') + model.getParameterByName("thisParameterDoesNotExist") -def verify_simulation_results(rdata, expected_results, fields=None, - atol=1e-8, rtol=1e-4): +def verify_simulation_results( + rdata, expected_results, fields=None, atol=1e-8, rtol=1e-4 +): """ compares all fields of the simulation results in rdata against the expectedResults using the provided tolerances @@ -197,42 +225,62 @@ def verify_simulation_results(rdata, expected_results, fields=None, if fields is None: attrs = expected_results.attrs.keys() fields = expected_results.keys() - if 'diagnosis' in expected_results.keys(): - subfields = expected_results['diagnosis'].keys() + if "diagnosis" in expected_results.keys(): + subfields = expected_results["diagnosis"].keys() else: - attrs = [field for field in fields - if field in expected_results.attrs.keys()] - if 'diagnosis' in expected_results.keys(): - subfields = [field for field in fields - if field in expected_results['diagnosis'].keys()] - fields = [field for field in fields - if field in expected_results.keys()] - - if expected_results.attrs['status'][0] != 0: - assert rdata['status'] == expected_results.attrs['status'][0] + attrs = [ + field for field in fields if field in expected_results.attrs.keys() + ] + if "diagnosis" in expected_results.keys(): + subfields = [ + field + for field in fields + if field in expected_results["diagnosis"].keys() + ] + fields = [ + field for field in fields if field in expected_results.keys() + ] + + if expected_results.attrs["status"][0] != 0: + assert rdata["status"] == expected_results.attrs["status"][0] return for field in expected_results.keys(): - if field == 'diagnosis': - for subfield in ['J', 'xdot']: + if field == "diagnosis": + for subfield in ["J", "xdot"]: if subfield not in subfields: assert rdata[subfield] is None, field continue - _check_results(rdata, subfield, - expected_results[field][subfield][()], - atol=1e-8, rtol=1e-8) + _check_results( + rdata, + subfield, + expected_results[field][subfield][()], + atol=1e-8, + rtol=1e-8, + ) else: if field not in fields: assert rdata[field] is None, field continue - if field == 's2llh': - _check_results(rdata, field, expected_results[field][()], - atol=1e-4, rtol=1e-3) + if field == "s2llh": + _check_results( + rdata, + field, + expected_results[field][()], + atol=1e-4, + rtol=1e-3, + ) else: - _check_results(rdata, field, expected_results[field][()], - atol=atol, rtol=rtol) + _check_results( + rdata, + field, + expected_results[field][()], + atol=atol, + rtol=rtol, + ) for attr in attrs: - _check_results(rdata, attr, expected_results.attrs[attr], - atol=atol, rtol=rtol) + _check_results( + rdata, attr, expected_results.attrs[attr], atol=atol, rtol=rtol + ) diff --git a/deps/AMICI/python/tests/test_pysb.py b/deps/AMICI/python/tests/test_pysb.py index e5559f8c9..52ca3a320 100644 --- a/deps/AMICI/python/tests/test_pysb.py +++ b/deps/AMICI/python/tests/test_pysb.py @@ -3,26 +3,28 @@ import importlib import logging import os -import platform -import shutil + import pytest pysb = pytest.importorskip("pysb") import amici import numpy as np -import sympy as sp import pysb.examples import pytest -from amici.pysb_import import pysb2amici +import sympy as sp from amici import ParameterScaling, parameterScalingFromIntVector -from pysb.simulator import ScipyOdeSimulator - from amici.gradient_check import check_derivatives +from amici.pysb_import import pysb2amici +from amici.testing import TemporaryDirectoryWinSafe, skip_on_valgrind +from numpy.testing import assert_allclose +from pysb.simulator import ScipyOdeSimulator -def test_compare_to_sbml_import(pysb_example_presimulation_module, - sbml_example_presimulation_module): +@skip_on_valgrind +def test_compare_to_sbml_import( + pysb_example_presimulation_module, sbml_example_presimulation_module +): # -------------- PYSB ----------------- model_pysb = pysb_example_presimulation_module.getModel() @@ -38,35 +40,51 @@ def test_compare_to_sbml_import(pysb_example_presimulation_module, rdata_sbml = get_results(model_sbml, edata) # check if preequilibration fixed parameters are correctly applied: - for rdata, model, importer in zip([rdata_sbml, rdata_pysb], - [model_sbml, model_pysb], - ['sbml', 'pysb']): + for rdata, model, importer in zip( + [rdata_sbml, rdata_pysb], [model_sbml, model_pysb], ["sbml", "pysb"] + ): # check equilibrium fixed parameters assert np.isclose( [sum(rdata["x_ss"][[1, 3]]), sum(rdata["x_ss"][[2, 4]])], edata.fixedParametersPreequilibration, - atol=1e-6, rtol=1e-6 - ).all(), f'{importer} preequilibration' + atol=1e-6, + rtol=1e-6, + ).all(), f"{importer} preequilibration" # check equilibrium initial parameters assert np.isclose( sum(rdata["x_ss"][[0, 3, 4, 5]]), - model.getParameterByName('PROT_0'), - atol=1e-6, rtol=1e-6 - ), f'{importer} preequilibration' + model.getParameterByName("PROT_0"), + atol=1e-6, + rtol=1e-6, + ), f"{importer} preequilibration" # check reinitialization with fixed parameter after # presimulation assert np.isclose( [rdata["x0"][1], rdata["x0"][2]], edata.fixedParameters, - atol=1e-6, rtol=1e-6 - ).all(), f'{importer} presimulation' - - skip_attrs = ['ptr', 'preeq_t', 'numsteps', 'preeq_numsteps', - 'numrhsevals', 'numerrtestfails', 'order', 'J', 'xdot', - 'preeq_wrms', 'preeq_cpu_time', 'cpu_time', - 'cpu_timeB', 'w'] + atol=1e-6, + rtol=1e-6, + ).all(), f"{importer} presimulation" + + skip_attrs = [ + "ptr", + "preeq_t", + "numsteps", + "preeq_numsteps", + "numrhsevals", + "numerrtestfails", + "order", + "J", + "xdot", + "preeq_wrms", + "preeq_cpu_time", + "cpu_time", + "cpu_timeB", + "cpu_time_total", + "w", + ] for field in rdata_pysb: if field in skip_attrs: @@ -81,130 +99,157 @@ def test_compare_to_sbml_import(pysb_example_presimulation_module, elif np.isnan(rdata_pysb[field]).all(): assert np.isnan(rdata_sbml[field]).all(), field else: - assert np.isclose( - rdata_sbml[field], rdata_pysb[field], - atol=1e-6, rtol=1e-6 - ).all(), field + assert_allclose( + rdata_sbml[field], + rdata_pysb[field], + atol=1e-6, + rtol=1e-6, + err_msg=field, + ) pysb_models = [ - 'tyson_oscillator', 'robertson', 'expression_observables', - 'bax_pore_sequential', 'bax_pore', 'bngwiki_egfr_simple', - 'bngwiki_enzymatic_cycle_mm', 'bngwiki_simple', 'earm_1_0', - 'earm_1_3', 'move_connected', 'michment', 'kinase_cascade', - 'hello_pysb', 'fricker_2010_apoptosis', 'explicit', - 'fixed_initial', + "tyson_oscillator", + "robertson", + "expression_observables", + "bax_pore_sequential", + "bax_pore", + "bngwiki_egfr_simple", + "bngwiki_enzymatic_cycle_mm", + "bngwiki_simple", + "earm_1_0", + "earm_1_3", + "move_connected", + "michment", + "kinase_cascade", + "hello_pysb", + "fricker_2010_apoptosis", + "explicit", + "fixed_initial", + "localfunc", ] custom_models = [ - 'bngwiki_egfr_simple_deletemolecules', + "bngwiki_egfr_simple_deletemolecules", ] -@pytest.mark.parametrize('example', pysb_models + custom_models) +@skip_on_valgrind +@pytest.mark.parametrize("example", pysb_models + custom_models) def test_compare_to_pysb_simulation(example): - atol = 1e-8 rtol = 1e-8 with amici.add_path(os.path.dirname(pysb.examples.__file__)): - with amici.add_path(os.path.join(os.path.dirname(__file__), '..', - 'tests', 'pysb_test_models')): - - if example == 'earm_1_3' \ - and platform.sys.version_info[0] == 3 \ - and platform.sys.version_info[1] < 7: - return - + with amici.add_path( + os.path.join( + os.path.dirname(__file__), "..", "tests", "pysb_test_models" + ) + ): # load example pysb.SelfExporter.cleanup() # reset pysb pysb.SelfExporter.do_export = True module = importlib.import_module(example) pysb_model = module.model - pysb_model.name = pysb_model.name.replace('pysb.examples.', '') + pysb_model.name = pysb_model.name.replace("pysb.examples.", "") # avoid naming clash for custom pysb models - pysb_model.name += '_amici' + pysb_model.name += "_amici" # pysb part tspan = np.linspace(0, 100, 101) sim = ScipyOdeSimulator( pysb_model, tspan=tspan, - integrator_options={'rtol': rtol, 'atol': atol}, - compiler='python' + integrator_options={"rtol": rtol, "atol": atol}, + compiler="python", ) pysb_simres = sim.run() # amici part - - outdir = pysb_model.name - - if pysb_model.name in ['move_connected_amici']: - with pytest.raises(Exception): - pysb2amici(pysb_model, outdir, verbose=logging.INFO, - compute_conservation_laws=True) - compute_conservation_laws = False - else: - compute_conservation_laws = True - - pysb2amici( - pysb_model, - outdir, - verbose=logging.INFO, - compute_conservation_laws=compute_conservation_laws, - observables=list(pysb_model.observables.keys()) - ) - - amici_model_module = amici.import_model_module(pysb_model.name, - outdir) - - model_pysb = amici_model_module.getModel() - - model_pysb.setTimepoints(tspan) - - solver = model_pysb.getSolver() - solver.setMaxSteps(int(1e6)) - solver.setAbsoluteTolerance(atol) - solver.setRelativeTolerance(rtol) - rdata = amici.runAmiciSimulation(model_pysb, solver) - - # check agreement of species simulation - - assert np.isclose(rdata['x'], - pysb_simres.species, 1e-4, 1e-4).all() - - if example not in ['fricker_2010_apoptosis', 'fixed_initial', - 'bngwiki_egfr_simple_deletemolecules']: - if example in ['tyson_oscillator', 'bax_pore_sequential', - 'bax_pore', 'kinase_cascade', - 'bngwiki_egfr_simple', - 'bngwiki_enzymatic_cycle_mm', - 'bngwiki_simple']: - solver.setAbsoluteTolerance(1e-14) - solver.setRelativeTolerance(1e-14) - epsilon = 1e-4 + with TemporaryDirectoryWinSafe(prefix=pysb_model.name) as outdir: + if pysb_model.name in ["move_connected_amici"]: + with pytest.raises(Exception): + pysb2amici( + pysb_model, + outdir, + verbose=logging.INFO, + compute_conservation_laws=True, + ) + compute_conservation_laws = False else: - solver.setAbsoluteTolerance(1e-10) - solver.setRelativeTolerance(1e-10) - epsilon = 1e-3 - model_pysb.setParameterScale(parameterScalingFromIntVector([ - ParameterScaling.log10 if p > 0 else ParameterScaling.none - for p in model_pysb.getParameters() - ])) - check_derivatives(model_pysb, solver, - epsilon=epsilon, - rtol=1e-2, - atol=1e-2, - skip_zero_pars=True) - - shutil.rmtree(outdir, ignore_errors=True) + compute_conservation_laws = True + + pysb2amici( + pysb_model, + outdir, + verbose=logging.INFO, + compute_conservation_laws=compute_conservation_laws, + observables=list(pysb_model.observables.keys()), + ) + + amici_model_module = amici.import_model_module( + pysb_model.name, outdir + ) + model_pysb = amici_model_module.getModel() + model_pysb.setTimepoints(tspan) + + solver = model_pysb.getSolver() + solver.setMaxSteps(int(1e6)) + solver.setAbsoluteTolerance(atol) + solver.setRelativeTolerance(rtol) + rdata = amici.runAmiciSimulation(model_pysb, solver) + + # check agreement of species simulations + assert np.isclose( + rdata["x"], pysb_simres.species, 1e-4, 1e-4 + ).all() + + if example not in [ + "fricker_2010_apoptosis", + "fixed_initial", + "bngwiki_egfr_simple_deletemolecules", + ]: + if example in [ + "tyson_oscillator", + "bax_pore_sequential", + "bax_pore", + "kinase_cascade", + "bngwiki_egfr_simple", + "bngwiki_enzymatic_cycle_mm", + "bngwiki_simple", + ]: + solver.setAbsoluteTolerance(1e-14) + solver.setRelativeTolerance(1e-14) + epsilon = 1e-4 + else: + solver.setAbsoluteTolerance(1e-10) + solver.setRelativeTolerance(1e-10) + epsilon = 1e-3 + model_pysb.setParameterScale( + parameterScalingFromIntVector( + [ + ParameterScaling.log10 + if p > 0 + else ParameterScaling.none + for p in model_pysb.getParameters() + ] + ) + ) + check_derivatives( + model_pysb, + solver, + epsilon=epsilon, + rtol=1e-2, + atol=1e-2, + skip_zero_pars=True, + ) def get_data(model): solver = model.getSolver() model.setTimepoints(np.linspace(0, 60, 61)) model.setSteadyStateSensitivityMode( - amici.SteadyStateSensitivityMode.simulationFSA + amici.SteadyStateSensitivityMode.integrateIfNewtonFails ) rdata = amici.runAmiciSimulation(model, solver) @@ -223,77 +268,119 @@ def get_results(model, edata): edata.reinitializeFixedParameterInitialStates = True model.setTimepoints(np.linspace(0, 60, 61)) model.setSteadyStateSensitivityMode( - amici.SteadyStateSensitivityMode.simulationFSA + amici.SteadyStateSensitivityMode.integrateIfNewtonFails ) return amici.runAmiciSimulation(model, solver, edata) +@skip_on_valgrind def test_names_and_ids(pysb_example_presimulation_module): model_pysb = pysb_example_presimulation_module.getModel() expected = { - 'ExpressionIds': ( - '__s2', - '__s1', - '__s5', - 'pPROT', - 'tPROT', - 'initProt', - 'initDrug', - 'initKin', - 'pPROT_obs'), - 'FixedParameterIds': ('DRUG_0', 'KIN_0'), - 'FixedParameterNames': ('DRUG_0', 'KIN_0'), - 'ObservableIds': ('pPROT_obs',), - 'ObservableNames': ('pPROT_obs',), - 'ParameterIds': ( - 'PROT_0', - 'kon_prot_drug', - 'koff_prot_drug', - 'kon_prot_kin', - 'kphospho_prot_kin', - 'kdephospho_prot' + "ExpressionIds": ( + "__s2", + "__s1", + "__s5", + "pPROT", + "tPROT", + "initProt", + "initDrug", + "initKin", + "pPROT_obs", ), - 'StateIds': ('__s0', '__s1', '__s2', '__s3', '__s4', '__s5'), - 'StateNames': ( + "FixedParameterIds": ("DRUG_0", "KIN_0"), + "FixedParameterNames": ("DRUG_0", "KIN_0"), + "ObservableIds": ("pPROT_obs",), + "ObservableNames": ("pPROT_obs",), + "ParameterIds": ( + "PROT_0", + "kon_prot_drug", + "koff_prot_drug", + "kon_prot_kin", + "kphospho_prot_kin", + "kdephospho_prot", + ), + "StateIds": ("__s0", "__s1", "__s2", "__s3", "__s4", "__s5"), + "StateNames": ( "PROT(kin=None, drug=None, phospho='u')", - 'DRUG(bound=None)', - 'KIN(bound=None)', + "DRUG(bound=None)", + "KIN(bound=None)", "DRUG(bound=1) % PROT(kin=None, drug=1, phospho='u')", "KIN(bound=1) % PROT(kin=1, drug=None, phospho='u')", - "PROT(kin=None, drug=None, phospho='p')" + "PROT(kin=None, drug=None, phospho='p')", ), } # Names and IDs are the same here - expected['ExpressionNames'] = expected['ExpressionIds'] - expected['ParameterNames'] = expected['ParameterIds'] + expected["ExpressionNames"] = expected["ExpressionIds"] + expected["ParameterNames"] = expected["ParameterIds"] for field_name, cur_expected in expected.items(): - actual = getattr(model_pysb, f'get{field_name}')() + actual = getattr(model_pysb, f"get{field_name}")() assert actual == cur_expected +@skip_on_valgrind def test_heavyside_and_special_symbols(): pysb.SelfExporter.cleanup() # reset pysb pysb.SelfExporter.do_export = True - model = pysb.Model('piecewise_test') - a = pysb.Monomer('A') - pysb.Initial(a(), pysb.Parameter('a0')) + model = pysb.Model("piecewise_test") + a = pysb.Monomer("A") + pysb.Initial(a(), pysb.Parameter("a0")) pysb.Rule( - 'deg', + "deg", a() >> None, pysb.Expression( - 'rate', - sp.Piecewise((1, pysb.Observable('a', a()) < 1), - (0.0, True)) + "rate", + sp.Piecewise((1, pysb.Observable("a", a()) < 1), (0.0, True)), + ), + ) + + with TemporaryDirectoryWinSafe(prefix=model.name) as outdir: + pysb2amici(model, outdir, verbose=True, observables=["a"]) + + model_module = amici.import_model_module( + module_name=model.name, module_path=outdir ) + amici_model = model_module.getModel() + assert amici_model.ne + + +@skip_on_valgrind +def test_energy(): + pysb.SelfExporter.cleanup() + model_pysb = pysb.Model("energy") + pysb.Monomer("A", ["a", "b"]) + pysb.Monomer("B", ["a"]) + pysb.Parameter("RT", 2) + pysb.Parameter("A_0", 10) + pysb.Parameter("AB_0", 10) + pysb.Parameter("phi", 0.5) + pysb.Expression("E_AAB_RT", -5 / RT) + pysb.Expression("E0_AA_RT", -1 / RT) + pysb.Rule( + "A_dimerize", + A(a=None) + A(a=None) | A(a=1) % A(a=1), + phi, + E0_AA_RT, + energy=True, ) + pysb.EnergyPattern("epAAB", A(a=1) % A(a=1, b=2) % B(a=2), E_AAB_RT) + pysb.Initial(A(a=None, b=None), A_0) + pysb.Initial(A(a=None, b=1) % B(a=1), AB_0) - outdir = model.name - pysb2amici(model, outdir, verbose=True, - observables=['a']) + with TemporaryDirectoryWinSafe(prefix=model_pysb.name) as outdir: + pysb2amici(model_pysb, output_dir=outdir) - model_module = amici.import_model_module(module_name=model.name, - module_path=outdir) - amici_model = model_module.getModel() - assert amici_model.ne + model_module = amici.import_model_module( + module_name=model_pysb.name, module_path=outdir + ) + amici_model = model_module.getModel() + amici_model.setTimepoints(np.logspace(-4, 5, 10)) + solver = amici_model.getSolver() + solver.setRelativeTolerance(1e-14) + solver.setAbsoluteTolerance(1e-14) + + check_derivatives( + amici_model, solver, epsilon=1e-4, rtol=1e-2, atol=1e-2 + ) diff --git a/deps/AMICI/python/tests/test_rdata.py b/deps/AMICI/python/tests/test_rdata.py new file mode 100644 index 000000000..ac7659f36 --- /dev/null +++ b/deps/AMICI/python/tests/test_rdata.py @@ -0,0 +1,66 @@ +"""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 + + +@pytest.fixture(scope="session") +def rdata_by_id_fixture(sbml_example_presimulation_module): + model_module = sbml_example_presimulation_module + model = model_module.getModel() + model.setTimepoints(np.linspace(0, 60, 61)) + solver = model.getSolver() + solver.setSensitivityMethod(amici.SensitivityMethod.forward) + solver.setSensitivityOrder(amici.SensitivityOrder.first) + rdata = amici.runAmiciSimulation(model, solver) + assert rdata.status == amici.AMICI_SUCCESS + return model, rdata + + +def test_rdata_by_id(rdata_by_id_fixture): + model, rdata = rdata_by_id_fixture + + assert_array_equal(rdata.by_id(model.getStateIds()[1]), rdata.x[:, 1]) + assert_array_equal(rdata.by_id(model.getStateIds()[1], "x"), rdata.x[:, 1]) + assert_array_equal( + rdata.by_id(model.getStateIds()[1], "x", model), rdata.x[:, 1] + ) + + assert_array_equal( + rdata.by_id(model.getObservableIds()[0], "y", model), rdata.y[:, 0] + ) + + assert_array_equal(rdata.by_id(model.getExpressionIds()[1]), rdata.w[:, 1]) + assert_array_equal( + rdata.by_id(model.getExpressionIds()[1], "w", model), rdata.w[:, 1] + ) + + assert_array_equal( + rdata.by_id(model.getStateIds()[1], "sx", model), rdata.sx[:, :, 1] + ) + + +def test_evaluate(rdata_by_id_fixture): + # get IDs of model components + model, rdata = rdata_by_id_fixture + expr0_id = model.getExpressionIds()[0] + state1_id = model.getStateIds()[1] + observable0_id = model.getObservableIds()[0] + + # ensure `evaluate` works for atoms + expr0 = rdata.by_id(expr0_id) + assert_array_equal(expr0, evaluate(expr0_id, rdata=rdata)) + + state1 = rdata.by_id(state1_id) + assert_array_equal(state1, evaluate(state1_id, rdata=rdata)) + + observable0 = rdata.by_id(observable0_id) + assert_array_equal(observable0, evaluate(observable0_id, rdata=rdata)) + + # ensure `evaluate` works for expressions + assert_almost_equal( + expr0 + state1 * observable0, + evaluate(f"{expr0_id} + {state1_id} * {observable0_id}", rdata=rdata), + ) diff --git a/deps/AMICI/python/tests/test_sbml_import.py b/deps/AMICI/python/tests/test_sbml_import.py index ea624e51e..7c4a67c0a 100644 --- a/deps/AMICI/python/tests/test_sbml_import.py +++ b/deps/AMICI/python/tests/test_sbml_import.py @@ -1,17 +1,24 @@ """Tests related to amici.sbml_import""" import os import re -import shutil +from numbers import Number +from pathlib import Path from urllib.request import urlopen +import amici import libsbml import numpy as np import pytest - -import amici from amici.gradient_check import check_derivatives from amici.sbml_import import SbmlImporter from amici.testing import TemporaryDirectoryWinSafe as TemporaryDirectory +from amici.testing import skip_on_valgrind +from numpy.testing import assert_allclose, assert_array_equal + +EXAMPLES_DIR = Path(__file__).parent / ".." / "examples" +STEADYSTATE_MODEL_FILE = ( + EXAMPLES_DIR / "example_steadystate" / "model_steadystate_scaled.xml" +) @pytest.fixture @@ -21,17 +28,17 @@ def simple_sbml_model(): model = document.createModel() model.setTimeUnits("second") model.setExtentUnits("mole") - model.setSubstanceUnits('mole') + model.setSubstanceUnits("mole") c1 = model.createCompartment() - c1.setId('C1') + c1.setId("C1") model.addCompartment(c1) s1 = model.createSpecies() - s1.setId('S1') - s1.setCompartment('C1') + s1.setId("S1") + s1.setCompartment("C1") model.addSpecies(s1) p1 = model.createParameter() - p1.setId('p1') - p1.setValue(0.0) + p1.setId("p1") + p1.setValue(2.0) model.addParameter(p1) return document, model @@ -40,34 +47,58 @@ def simple_sbml_model(): def test_sbml2amici_no_observables(simple_sbml_model): """Test model generation works for model without observables""" sbml_doc, sbml_model = simple_sbml_model - sbml_importer = SbmlImporter(sbml_source=sbml_model, - from_file=False) - + sbml_importer = SbmlImporter(sbml_source=sbml_model, from_file=False) + model_name = "test_sbml2amici_no_observables" with TemporaryDirectory() as tmpdir: - sbml_importer.sbml2amici(model_name="test", - output_dir=tmpdir, - observables=None, - compute_conservation_laws=False) + sbml_importer.sbml2amici( + model_name=model_name, + output_dir=tmpdir, + observables=None, + compute_conservation_laws=False, + ) # Ensure import succeeds (no missing symbols) - module_module = amici.import_model_module("test", tmpdir) - assert hasattr(module_module, 'getModel') + module_module = amici.import_model_module(model_name, tmpdir) + assert hasattr(module_module, "getModel") -def test_nosensi(simple_sbml_model): +@skip_on_valgrind +def test_sbml2amici_nested_observables_fail(simple_sbml_model): + """Test model generation works for model without observables""" sbml_doc, sbml_model = simple_sbml_model - sbml_importer = SbmlImporter(sbml_source=sbml_model, - from_file=False) + sbml_importer = SbmlImporter(sbml_source=sbml_model, from_file=False) + model_name = "test_sbml2amici_nested_observables_fail" + with TemporaryDirectory() as tmpdir: + with pytest.raises(ValueError, match="(?i)nested"): + sbml_importer.sbml2amici( + model_name=model_name, + output_dir=tmpdir, + observables={ + "outer": {"formula": "inner"}, + "inner": {"formula": "S1"}, + }, + compute_conservation_laws=False, + generate_sensitivity_code=False, + compile=False, + ) + +def test_nosensi(simple_sbml_model): + sbml_doc, sbml_model = simple_sbml_model + sbml_importer = SbmlImporter(sbml_source=sbml_model, from_file=False) + model_name = "test_nosensi" with TemporaryDirectory() as tmpdir: - sbml_importer.sbml2amici(model_name="test", - output_dir=tmpdir, - observables=None, - compute_conservation_laws=False, - generate_sensitivity_code=False) + sbml_importer.sbml2amici( + model_name=model_name, + output_dir=tmpdir, + observables=None, + compute_conservation_laws=False, + generate_sensitivity_code=False, + ) - model_module = amici.import_model_module(module_name='test', - module_path=tmpdir) + model_module = amici.import_model_module( + module_name=model_name, module_path=tmpdir + ) model = model_module.getModel() model.setTimepoints(np.linspace(0, 60, 61)) @@ -79,62 +110,147 @@ def test_nosensi(simple_sbml_model): @pytest.fixture +def observable_dependent_error_model(simple_sbml_model): + sbml_doc, sbml_model = simple_sbml_model + # add parameter and rate rule + sbml_model.getSpecies("S1").setInitialConcentration(1.0) + sbml_model.getParameter("p1").setValue(0.2) + rr = sbml_model.createRateRule() + rr.setVariable("S1") + rr.setMath(libsbml.parseL3Formula("p1")) + relative_sigma = sbml_model.createParameter() + relative_sigma.setId("relative_sigma") + relative_sigma.setValue(0.05) + + sbml_importer = SbmlImporter(sbml_source=sbml_model, from_file=False) + + model_name = "observable_dependent_error_model" + with TemporaryDirectory() as tmpdir: + sbml_importer.sbml2amici( + model_name=model_name, + output_dir=tmpdir, + observables={ + "observable_s1": {"formula": "S1"}, + "observable_s1_scaled": {"formula": "0.5 * S1"}, + }, + sigmas={ + "observable_s1": "0.1 + relative_sigma * observable_s1", + "observable_s1_scaled": "0.02 * observable_s1_scaled", + }, + ) + yield amici.import_model_module( + module_name=model_name, module_path=tmpdir + ) + + +@skip_on_valgrind +def test_sbml2amici_observable_dependent_error( + observable_dependent_error_model, +): + """Check gradients for model with observable-dependent error""" + model_module = observable_dependent_error_model + model = model_module.getModel() + model.setTimepoints(np.linspace(0, 60, 61)) + solver = model.getSolver() + + # generate artificial data + rdata = amici.runAmiciSimulation(model, solver) + assert_allclose( + rdata.sigmay[:, 0], + 0.1 + 0.05 * rdata.y[:, 0], + rtol=1.0e-5, + atol=1.0e-8, + ) + assert_allclose( + rdata.sigmay[:, 1], 0.02 * rdata.y[:, 1], rtol=1.0e-5, atol=1.0e-8 + ) + edata = amici.ExpData(rdata, 1.0, 0.0) + edata.setObservedDataStdDev(np.nan) + + # check sensitivities + solver.setSensitivityOrder(amici.SensitivityOrder.first) + # FSA + solver.setSensitivityMethod(amici.SensitivityMethod.forward) + rdata = amici.runAmiciSimulation(model, solver, edata) + assert np.any(rdata.ssigmay != 0.0) + check_derivatives(model, solver, edata) + # ASA + solver.setSensitivityMethod(amici.SensitivityMethod.adjoint) + check_derivatives(model, solver, edata) + + +@skip_on_valgrind +def test_logging_works(observable_dependent_error_model, caplog): + """Check that warnings are forwarded to Python logging""" + model_module = observable_dependent_error_model + model = model_module.getModel() + model.setTimepoints(np.linspace(0, 60, 61)) + solver = model.getSolver() + + # this will prematurely stop the simulation + solver.setMaxSteps(1) + + rdata = amici.runAmiciSimulation(model, solver) + assert rdata.status != amici.AMICI_SUCCESS + assert "mxstep steps taken" in caplog.text + + +@skip_on_valgrind +def test_model_module_is_set(observable_dependent_error_model): + model_module = observable_dependent_error_model + assert isinstance(model_module.getModel().module, amici.ModelModule) + + +@pytest.fixture(scope="session") def model_steadystate_module(): - sbml_file = os.path.join(os.path.dirname(__file__), '..', - 'examples', 'example_steadystate', - 'model_steadystate_scaled.xml') + sbml_file = STEADYSTATE_MODEL_FILE sbml_importer = amici.SbmlImporter(sbml_file) observables = amici.assignmentRules2observables( sbml_importer.sbml, - filter_function=lambda variable: - variable.getId().startswith('observable_') and - not variable.getId().endswith('_sigma') + filter_function=lambda variable: variable.getId().startswith( + "observable_" + ) + and not variable.getId().endswith("_sigma"), ) - outdir = 'test_model_steadystate_scaled' - module_name = 'test_model_steadystate_scaled' - sbml_importer.sbml2amici( - model_name=module_name, - output_dir=outdir, - observables=observables, - constant_parameters=['k0'], - sigmas={'observable_x1withsigma': 'observable_x1withsigma_sigma'}) - - model_module = amici.import_model_module(module_name=module_name, - module_path=outdir) - yield model_module + module_name = "test_model_steadystate_scaled" + with TemporaryDirectory(prefix=module_name) as outdir: + sbml_importer.sbml2amici( + model_name=module_name, + output_dir=outdir, + observables=observables, + constant_parameters=["k0"], + sigmas={"observable_x1withsigma": "observable_x1withsigma_sigma"}, + ) - shutil.rmtree(outdir, ignore_errors=True) + yield amici.import_model_module( + module_name=module_name, module_path=outdir + ) -@pytest.fixture +@pytest.fixture(scope="session") def model_units_module(): - sbml_file = os.path.join(os.path.dirname(__file__), '..', - 'examples', 'example_units', - 'model_units.xml') - sbml_importer = amici.SbmlImporter(sbml_file) + sbml_file = EXAMPLES_DIR / "example_units" / "model_units.xml" + module_name = "test_model_units" - outdir = 'test_model_units' - module_name = 'test_model_units' - sbml_importer.sbml2amici(model_name=module_name, - output_dir=outdir) + sbml_importer = amici.SbmlImporter(sbml_file) - model_module = amici.import_model_module(module_name=module_name, - module_path=outdir) - yield model_module + with TemporaryDirectory() as outdir: + sbml_importer.sbml2amici(model_name=module_name, output_dir=outdir) - shutil.rmtree(outdir, ignore_errors=True) + yield amici.import_model_module( + module_name=module_name, module_path=outdir + ) def test_presimulation(sbml_example_presimulation_module): """Test 'presimulation' test model""" model = sbml_example_presimulation_module.getModel() solver = model.getSolver() - solver.setNewtonMaxSteps(0) model.setTimepoints(np.linspace(0, 60, 61)) model.setSteadyStateSensitivityMode( - amici.SteadyStateSensitivityMode.simulationFSA + amici.SteadyStateSensitivityMode.integrationOnly ) solver.setSensitivityOrder(amici.SensitivityOrder.first) model.setReinitializeFixedParameterInitialStates(True) @@ -145,8 +261,8 @@ def test_presimulation(sbml_example_presimulation_module): edata.fixedParametersPresimulation = [10, 2] edata.fixedParametersPreequilibration = [3, 0] assert isinstance( - amici.runAmiciSimulation(model, solver, edata), - amici.ReturnDataView) + amici.runAmiciSimulation(model, solver, edata), amici.ReturnDataView + ) solver.setRelativeTolerance(1e-12) solver.setAbsoluteTolerance(1e-12) @@ -170,92 +286,152 @@ def test_steadystate_simulation(model_steadystate_module): df_edata = amici.getDataObservablesAsDataFrame(model, edata) edata_reconstructed = amici.getEdataFromDataFrame(model, df_edata) - assert np.isclose( - amici.ExpDataView(edata[0])['observedData'], - amici.ExpDataView(edata_reconstructed[0])['observedData']).all() + assert_allclose( + amici.ExpDataView(edata[0])["observedData"], + amici.ExpDataView(edata_reconstructed[0])["observedData"], + rtol=1.0e-5, + atol=1.0e-8, + ) - assert np.isclose( - amici.ExpDataView(edata[0])['observedDataStdDev'], - amici.ExpDataView(edata_reconstructed[0])['observedDataStdDev']).all() + assert_allclose( + amici.ExpDataView(edata[0])["observedDataStdDev"], + amici.ExpDataView(edata_reconstructed[0])["observedDataStdDev"], + rtol=1.0e-5, + atol=1.0e-8, + ) if len(edata[0].fixedParameters): - assert list(edata[0].fixedParameters) \ - == list(edata_reconstructed[0].fixedParameters) + assert list(edata[0].fixedParameters) == list( + edata_reconstructed[0].fixedParameters + ) else: - assert list(model.getFixedParameters()) \ - == list(edata_reconstructed[0].fixedParameters) + assert list(model.getFixedParameters()) == list( + edata_reconstructed[0].fixedParameters + ) - assert list(edata[0].fixedParametersPreequilibration) == \ - list(edata_reconstructed[0].fixedParametersPreequilibration) + assert list(edata[0].fixedParametersPreequilibration) == list( + edata_reconstructed[0].fixedParametersPreequilibration + ) df_state = amici.getSimulationStatesAsDataFrame(model, edata, rdata) - assert np.isclose(rdata[0]['x'], - df_state[list(model.getStateIds())].values).all() + assert_allclose( + rdata[0]["x"], + df_state[list(model.getStateIds())].values, + rtol=1.0e-5, + atol=1.0e-8, + ) df_obs = amici.getSimulationObservablesAsDataFrame(model, edata, rdata) - assert np.isclose(rdata[0]['y'], - df_obs[list(model.getObservableIds())].values).all() + assert_allclose( + rdata[0]["y"], + df_obs[list(model.getObservableIds())].values, + rtol=1.0e-5, + atol=1.0e-8, + ) amici.getResidualsAsDataFrame(model, edata, rdata) df_expr = amici.pandas.get_expressions_as_dataframe(model, edata, rdata) - assert np.isclose(rdata[0]['w'], - df_expr[list(model.getExpressionIds())].values).all() + assert_allclose( + rdata[0]["w"], + df_expr[list(model.getExpressionIds())].values, + rtol=1.0e-5, + atol=1.0e-8, + ) solver.setRelativeTolerance(1e-12) solver.setAbsoluteTolerance(1e-12) - check_derivatives(model, solver, edata[0], atol=1e-3, - rtol=1e-3, epsilon=1e-4) + check_derivatives( + model, solver, edata[0], atol=1e-3, rtol=1e-3, epsilon=1e-4 + ) # Run some additional tests which need a working Model, # but don't need precomputed expectations. _test_set_parameters_by_dict(model_steadystate_module) +def test_solver_reuse(model_steadystate_module): + model = model_steadystate_module.getModel() + model.setTimepoints(np.linspace(0, 60, 60)) + solver = model.getSolver() + solver.setSensitivityOrder(amici.SensitivityOrder.first) + rdata = amici.runAmiciSimulation(model, solver) + edata = amici.ExpData(rdata, 1, 0) + + for sensi_method in ( + amici.SensitivityMethod.forward, + amici.SensitivityMethod.adjoint, + ): + solver.setSensitivityMethod(sensi_method) + rdata1 = amici.runAmiciSimulation(model, solver, edata) + rdata2 = amici.runAmiciSimulation(model, solver, edata) + + assert rdata1.status == amici.AMICI_SUCCESS + + for attr in rdata1: + if "time" in attr: + continue + + val1 = getattr(rdata1, attr) + val2 = getattr(rdata2, attr) + msg = ( + f"Values for {attr} do not match for sensitivity " + f"method {sensi_method}" + ) + if isinstance(val1, np.ndarray): + assert_array_equal(val1, val2, err_msg=msg) + elif isinstance(val1, Number) and np.isnan(val1): + assert np.isnan(val2) + else: + assert val1 == val2, msg + + @pytest.fixture def model_test_likelihoods(): """Test model for various likelihood functions.""" # load sbml model - sbml_file = os.path.join(os.path.dirname(__file__), '..', - 'examples', 'example_steadystate', - 'model_steadystate_scaled.xml') + sbml_file = STEADYSTATE_MODEL_FILE sbml_importer = amici.SbmlImporter(sbml_file) # define observables observables = { - 'o1': {'formula': 'x1'}, - 'o2': {'formula': '10^x1'}, - 'o3': {'formula': '10^x1'}, - 'o4': {'formula': 'x1'}, - 'o5': {'formula': '10^x1'}, - 'o6': {'formula': '10^x1'}, - 'o7': {'formula': 'x1'} + "o1": {"formula": "x1"}, + "o2": {"formula": "10^x1"}, + "o3": {"formula": "10^x1"}, + "o4": {"formula": "x1"}, + "o5": {"formula": "10^x1"}, + "o6": {"formula": "10^x1"}, + "o7": {"formula": "x1"}, } # define different noise models noise_distributions = { - 'o1': 'normal', 'o2': 'log-normal', 'o3': 'log10-normal', - 'o4': 'laplace', 'o5': 'log-laplace', 'o6': 'log10-laplace', - 'o7': lambda str_symbol: f'Abs({str_symbol} - m{str_symbol}) ' - f'/ sigma{str_symbol}', + "o1": "normal", + "o2": "log-normal", + "o3": "log10-normal", + "o4": "laplace", + "o5": "log-laplace", + "o6": "log10-laplace", + "o7": lambda str_symbol: f"Abs({str_symbol} - m{str_symbol}) " + f"/ sigma{str_symbol}", } - module_name = 'test_likelihoods' - outdir = 'test_likelihoods' - sbml_importer.sbml2amici( - model_name=module_name, - output_dir=outdir, - observables=observables, - constant_parameters=['k0'], - noise_distributions=noise_distributions, - ) - - yield amici.import_model_module(module_name=module_name, - module_path=outdir) + module_name = "model_test_likelihoods" + with TemporaryDirectory(prefix=module_name) as outdir: + sbml_importer.sbml2amici( + model_name=module_name, + output_dir=outdir, + observables=observables, + constant_parameters=["k0"], + noise_distributions=noise_distributions, + ) - shutil.rmtree(outdir, ignore_errors=True) + yield amici.import_model_module( + module_name=module_name, module_path=outdir + ) +@skip_on_valgrind def test_likelihoods(model_test_likelihoods): """Test the custom noise distributions used to define cost functions.""" model = model_test_likelihoods.getModel() @@ -266,7 +442,7 @@ def test_likelihoods(model_test_likelihoods): # run model once to create an edata rdata = amici.runAmiciSimulation(model, solver) - sigmas = rdata['y'].max(axis=0) * 0.05 + sigmas = rdata["y"].max(axis=0) * 0.05 edata = amici.ExpData(rdata, sigmas, []) # just make all observables positive since some are logarithmic while min(edata.getObservedData()) < 0: @@ -276,66 +452,75 @@ def test_likelihoods(model_test_likelihoods): rdata = amici.runAmiciSimulations(model, solver, [edata])[0] # check if the values make overall sense - assert np.isfinite(rdata['llh']) - assert np.all(np.isfinite(rdata['sllh'])) - assert np.any(rdata['sllh']) + assert np.isfinite(rdata["llh"]) + assert np.all(np.isfinite(rdata["sllh"])) + assert np.any(rdata["sllh"]) rdata_df = amici.getSimulationObservablesAsDataFrame( - model, edata, rdata, by_id=True) - edata_df = amici.getDataObservablesAsDataFrame( - model, edata, by_id=True) + model, edata, rdata, by_id=True + ) + edata_df = amici.getDataObservablesAsDataFrame(model, edata, by_id=True) # check correct likelihood value - llh_exp = - sum([ - normal_nllh(edata_df['o1'], rdata_df['o1'], sigmas[0]), - log_normal_nllh(edata_df['o2'], rdata_df['o2'], sigmas[1]), - log10_normal_nllh(edata_df['o3'], rdata_df['o3'], sigmas[2]), - laplace_nllh(edata_df['o4'], rdata_df['o4'], sigmas[3]), - log_laplace_nllh(edata_df['o5'], rdata_df['o5'], sigmas[4]), - log10_laplace_nllh(edata_df['o6'], rdata_df['o6'], sigmas[5]), - custom_nllh(edata_df['o7'], rdata_df['o7'], sigmas[6]), - ]) - assert np.isclose(rdata['llh'], llh_exp) + llh_exp = -sum( + [ + normal_nllh(edata_df["o1"], rdata_df["o1"], sigmas[0]), + log_normal_nllh(edata_df["o2"], rdata_df["o2"], sigmas[1]), + log10_normal_nllh(edata_df["o3"], rdata_df["o3"], sigmas[2]), + laplace_nllh(edata_df["o4"], rdata_df["o4"], sigmas[3]), + log_laplace_nllh(edata_df["o5"], rdata_df["o5"], sigmas[4]), + log10_laplace_nllh(edata_df["o6"], rdata_df["o6"], sigmas[5]), + custom_nllh(edata_df["o7"], rdata_df["o7"], sigmas[6]), + ] + ) + assert np.isclose(rdata["llh"], llh_exp) # check gradient - for sensi_method in [amici.SensitivityMethod.forward, - amici.SensitivityMethod.adjoint]: + for sensi_method in [ + amici.SensitivityMethod.forward, + amici.SensitivityMethod.adjoint, + ]: solver = model.getSolver() solver.setSensitivityMethod(sensi_method) solver.setSensitivityOrder(amici.SensitivityOrder.first) solver.setRelativeTolerance(1e-12) solver.setAbsoluteTolerance(1e-12) check_derivatives( - model, solver, edata, atol=1e-2, rtol=1e-2, - epsilon=1e-5, check_least_squares=False + model, + solver, + edata, + atol=1e-2, + rtol=1e-2, + epsilon=1e-5, + check_least_squares=False, ) +@skip_on_valgrind def test_likelihoods_error(): """Test whether wrong inputs lead to expected errors.""" - sbml_file = os.path.join(os.path.dirname(__file__), '..', - 'examples', 'example_steadystate', - 'model_steadystate_scaled.xml') + sbml_file = STEADYSTATE_MODEL_FILE sbml_importer = amici.SbmlImporter(sbml_file) # define observables - observables = {'o1': {'formula': 'x1'}} + observables = {"o1": {"formula": "x1"}} # define different noise models - noise_distributions = {'o1': 'nörmal'} + noise_distributions = {"o1": "nörmal"} - module_name = 'test_likelihoods' - outdir = 'test_likelihoods' + module_name = "test_likelihoods_error" + outdir = "test_likelihoods_error" with pytest.raises(ValueError): sbml_importer.sbml2amici( model_name=module_name, output_dir=outdir, observables=observables, - constant_parameters=['k0'], + constant_parameters=["k0"], noise_distributions=noise_distributions, ) +@skip_on_valgrind def test_units(model_units_module): """ Test whether SBML import works for models using sbml:units annotations. @@ -345,38 +530,48 @@ def test_units(model_units_module): solver = model.getSolver() rdata = amici.runAmiciSimulation(model, solver) - assert rdata['status'] == amici.AMICI_SUCCESS + assert rdata["status"] == amici.AMICI_SUCCESS -@pytest.mark.skipif(os.name == 'nt', - reason='Avoid `CERTIFICATE_VERIFY_FAILED` error') +@skip_on_valgrind +@pytest.mark.skipif( + os.name == "nt", reason="Avoid `CERTIFICATE_VERIFY_FAILED` error" +) def test_sympy_exp_monkeypatch(): """ This model contains a removeable discontinuity at t=0 that requires 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).read().decode('utf-8'), - from_file=False) - module_name = 'BIOMD0000000529' + 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 + ) + module_name = "BIOMD0000000529" with TemporaryDirectory() as outdir: importer.sbml2amici(module_name, outdir) - model_module = amici.import_model_module(module_name=module_name, - module_path=outdir) + model_module = amici.import_model_module( + module_name=module_name, module_path=outdir + ) model = model_module.getModel() model.setTimepoints(np.linspace(0, 8, 250)) model.requireSensitivitiesForAllParameters() model.setAlwaysCheckFinite(True) - model.setParameterScale(amici.parameterScalingFromIntVector([ - amici.ParameterScaling.none - if re.match(r'n[0-9]+$', par_id) - else amici.ParameterScaling.log10 - for par_id in model.getParameterIds() - ])) + model.setParameterScale( + amici.parameterScalingFromIntVector( + [ + amici.ParameterScaling.none + if re.match(r"n[0-9]+$", par_id) + else amici.ParameterScaling.log10 + for par_id in model.getParameterIds() + ] + ) + ) solver = model.getSolver() solver.setSensitivityMethod(amici.SensitivityMethod.forward) @@ -385,40 +580,53 @@ def test_sympy_exp_monkeypatch(): rdata = amici.runAmiciSimulation(model, solver) # print sensitivity-related results - assert rdata['status'] == amici.AMICI_SUCCESS - check_derivatives(model, solver, None, atol=1e-2, rtol=1e-2, - epsilon=1e-3) + assert rdata["status"] == amici.AMICI_SUCCESS + check_derivatives( + model, solver, None, atol=1e-2, rtol=1e-2, epsilon=1e-3 + ) def normal_nllh(m, y, sigma): - return sum(.5*(np.log(2*np.pi*sigma**2) + ((y-m)/sigma)**2)) + return sum(0.5 * (np.log(2 * np.pi * sigma**2) + ((y - m) / sigma) ** 2)) def log_normal_nllh(m, y, sigma): - return sum(.5*(np.log(2*np.pi*sigma**2*m**2) - + ((np.log(y)-np.log(m))/sigma)**2)) + return sum( + 0.5 + * ( + np.log(2 * np.pi * sigma**2 * m**2) + + ((np.log(y) - np.log(m)) / sigma) ** 2 + ) + ) def log10_normal_nllh(m, y, sigma): - return sum(.5*(np.log(2*np.pi*sigma**2*m**2*np.log(10)**2) - + ((np.log10(y) - np.log10(m))/sigma)**2)) + return sum( + 0.5 + * ( + np.log(2 * np.pi * sigma**2 * m**2 * np.log(10) ** 2) + + ((np.log10(y) - np.log10(m)) / sigma) ** 2 + ) + ) def laplace_nllh(m, y, sigma): - return sum(np.log(2*sigma) + np.abs(y-m)/sigma) + return sum(np.log(2 * sigma) + np.abs(y - m) / sigma) def log_laplace_nllh(m, y, sigma): - return sum(np.log(2*sigma*m) + np.abs(np.log(y)-np.log(m))/sigma) + return sum(np.log(2 * sigma * m) + np.abs(np.log(y) - np.log(m)) / sigma) def log10_laplace_nllh(m, y, sigma): - return sum(np.log(2*sigma*m*np.log(10)) - + np.abs(np.log10(y)-np.log10(m))/sigma) + return sum( + np.log(2 * sigma * m * np.log(10)) + + np.abs(np.log10(y) - np.log10(m)) / sigma + ) def custom_nllh(m, y, sigma): - return sum(np.abs(m-y)/sigma) + return sum(np.abs(m - y) / sigma) def _test_set_parameters_by_dict(model_module): @@ -444,3 +652,71 @@ def _test_set_parameters_by_dict(model_module): assert model.getParameterByName(change_par_name) == new_par_val model.setParameterByName(change_par_name, old_par_val) assert model.getParameters() == old_parameter_values + + +@pytest.mark.parametrize("extract_cse", [True, False]) +def test_code_gen_uses_cse(extract_cse): + """Check that code generation honors AMICI_EXTRACT_CSE""" + old_environ = os.environ.copy() + try: + os.environ["AMICI_EXTRACT_CSE"] = str(extract_cse) + sbml_importer = amici.SbmlImporter(STEADYSTATE_MODEL_FILE) + model_name = "test_code_gen_uses_cse" + with TemporaryDirectory() as tmpdir: + sbml_importer.sbml2amici( + model_name=model_name, + compile=False, + generate_sensitivity_code=False, + output_dir=tmpdir, + ) + xdot = Path(tmpdir, "xdot.cpp").read_text() + assert ("__amici_cse_0 = " in xdot) == extract_cse + finally: + os.environ = old_environ + + +def test_code_gen_uses_lhs_symbol_ids(): + """Check that code generation uses symbol IDs instead of plain array + indices""" + sbml_importer = amici.SbmlImporter(STEADYSTATE_MODEL_FILE) + model_name = "test_code_gen_uses_lhs_symbol_ids" + with TemporaryDirectory() as tmpdir: + sbml_importer.sbml2amici( + model_name=model_name, + compile=False, + generate_sensitivity_code=False, + output_dir=tmpdir, + ) + dwdx = Path(tmpdir, "dwdx.cpp").read_text() + assert "dobservable_x1_dx1 = " in dwdx + + +def test_hardcode_parameters(simple_sbml_model): + """Test model generation works for model without observables""" + sbml_doc, sbml_model = simple_sbml_model + sbml_importer = SbmlImporter(sbml_source=sbml_model, from_file=False) + r = sbml_model.createRateRule() + r.setVariable("S1") + r.setFormula("p1") + assert sbml_model.getParameter("p1").getValue() != 0 + + ode_model = sbml_importer._build_ode_model() + assert str(ode_model.parameters()) == "[p1]" + assert ode_model.differential_states()[0].get_dt().name == "p1" + + ode_model = sbml_importer._build_ode_model( + constant_parameters=[], + hardcode_symbols=["p1"], + ) + assert str(ode_model.parameters()) == "[]" + assert ( + ode_model.differential_states()[0].get_dt() + == sbml_model.getParameter("p1").getValue() + ) + + with pytest.raises(ValueError): + sbml_importer._build_ode_model( + # mutually exclusive + constant_parameters=["p1"], + hardcode_symbols=["p1"], + ) 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 67e1368f3..9d8f44751 100644 --- a/deps/AMICI/python/tests/test_sbml_import_special_functions.py +++ b/deps/AMICI/python/tests/test_sbml_import_special_functions.py @@ -5,51 +5,60 @@ """ import os -import shutil import amici import numpy as np -from scipy.special import loggamma import pytest +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 scipy.special import loggamma -@pytest.fixture +@pytest.fixture(scope="session") def model_special_likelihoods(): """Test model for special likelihood functions.""" # load sbml model - sbml_file = os.path.join(os.path.dirname(__file__), '..', - 'examples', 'example_steadystate', - 'model_steadystate_scaled.xml') + sbml_file = os.path.join( + os.path.dirname(__file__), + "..", + "examples", + "example_steadystate", + "model_steadystate_scaled.xml", + ) sbml_importer = amici.SbmlImporter(sbml_file) # define observables observables = { - 'o1': {'formula': '100*10^x1'}, - 'o2': {'formula': '100*10^x1'}, + "o1": {"formula": "100*10^x1"}, + "o2": {"formula": "100*10^x1"}, } # define different noise models noise_distributions = { - 'o1': 'binomial', 'o2': 'negative-binomial', + "o1": "binomial", + "o2": "negative-binomial", } - module_name = 'test_special_likelihoods' - outdir = 'test_special_likelihoods' - sbml_importer.sbml2amici( - model_name=module_name, - output_dir=outdir, - observables=observables, - constant_parameters=['k0'], - noise_distributions=noise_distributions, - ) - - yield amici.import_model_module(module_name=module_name, - module_path=outdir) + module_name = "test_special_likelihoods" + with TemporaryDirectoryWinSafe(prefix=module_name) as outdir: + sbml_importer.sbml2amici( + model_name=module_name, + output_dir=outdir, + observables=observables, + constant_parameters=["k0"], + noise_distributions=noise_distributions, + ) - shutil.rmtree(outdir, ignore_errors=True) + yield amici.import_model_module( + module_name=module_name, module_path=outdir + ) +@skip_on_valgrind +# FD check fails occasionally, so give some extra tries +@pytest.mark.flaky(reruns=5) def test_special_likelihoods(model_special_likelihoods): """Test special likelihood functions.""" model = model_special_likelihoods.getModel() @@ -65,7 +74,7 @@ def test_special_likelihoods(model_special_likelihoods): # make sure measurements are smaller for non-degenerate probability y = edata.getObservedData() - y = tuple([val * np.random.uniform(0, 1) for val in y]) + y = tuple(val * np.random.uniform(0, 1) for val in y) edata.setObservedData(y) # set sigmas @@ -77,31 +86,40 @@ def test_special_likelihoods(model_special_likelihoods): rdata = amici.runAmiciSimulations(model, solver, [edata])[0] # check if the values make overall sense - assert np.isfinite(rdata['llh']) - assert np.all(np.isfinite(rdata['sllh'])) - assert np.any(rdata['sllh']) + assert np.isfinite(rdata["llh"]) + assert np.all(np.isfinite(rdata["sllh"])) + assert np.any(rdata["sllh"]) rdata_df = amici.getSimulationObservablesAsDataFrame( - model, edata, rdata, by_id=True) - edata_df = amici.getDataObservablesAsDataFrame( - model, edata, by_id=True) + model, edata, rdata, by_id=True + ) + edata_df = amici.getDataObservablesAsDataFrame(model, edata, by_id=True) # check correct likelihood value - llh_exp = - sum([ - binomial_nllh(edata_df['o1'], rdata_df['o1'], sigma), - negative_binomial_nllh(edata_df['o2'], rdata_df['o2'], sigma), - ]) - assert np.isclose(rdata['llh'], llh_exp) + llh_exp = -sum( + [ + binomial_nllh(edata_df["o1"], rdata_df["o1"], sigma), + negative_binomial_nllh(edata_df["o2"], rdata_df["o2"], sigma), + ] + ) + assert np.isclose(rdata["llh"], llh_exp) # check gradient - for sensi_method in [amici.SensitivityMethod.forward, - amici.SensitivityMethod.adjoint]: + for sensi_method in [ + amici.SensitivityMethod.forward, + amici.SensitivityMethod.adjoint, + ]: solver = model.getSolver() solver.setSensitivityMethod(sensi_method) solver.setSensitivityOrder(amici.SensitivityOrder.first) check_derivatives( - model, solver, edata, atol=1e-1, rtol=1e-1, - check_least_squares=False) + model, + solver, + edata, + atol=1e-4, + rtol=1e-3, + check_least_squares=False, + ) # Test for m > y, i.e. in region with 0 density @@ -110,7 +128,7 @@ def test_special_likelihoods(model_special_likelihoods): # make sure measurements are smaller for non-degenerate probability y = edata.getObservedData() - y = tuple([val * np.random.uniform(0.5, 3) for val in y]) + y = tuple(val * np.random.uniform(0.5, 3) for val in y) edata.setObservedData(y) edata.setObservedDataStdDev(sigmas) @@ -118,19 +136,89 @@ def test_special_likelihoods(model_special_likelihoods): rdata = amici.runAmiciSimulations(model, solver, [edata])[0] # m > y -> outside binomial domain -> 0 density - assert rdata['llh'] == -np.inf + assert rdata["llh"] == -np.inf # check for non-informative gradient - assert all(np.isnan(rdata['sllh'])) + assert all(np.isnan(rdata["sllh"])) def binomial_nllh(m: np.ndarray, y: np.ndarray, p: float): if any(m > y): return np.inf - return sum(- loggamma(y+1) + loggamma(m+1) + loggamma(y-m+1) \ - - m * np.log(p) - (y-m) * np.log(1-p)) + return sum( + -loggamma(y + 1) + + loggamma(m + 1) + + loggamma(y - m + 1) + - m * np.log(p) + - (y - m) * np.log(1 - p) + ) def negative_binomial_nllh(m: np.ndarray, y: np.ndarray, p: float): - r = y * (1-p) / p - return sum(- loggamma(m+r) + loggamma(m+1) + loggamma(r) - - r * np.log(1-p) - m * np.log(p)) + r = y * (1 - p) / p + return sum( + -loggamma(m + r) + + loggamma(m + 1) + + loggamma(r) + - r * np.log(1 - p) + - m * np.log(p) + ) + + +@skip_on_valgrind +def test_rateof(): + """Test chained rateOf to verify that model expressions are evaluated in the correct order.""" + ant_model = """ + model test_chained_rateof + species S1, S2, S3, S4; + S1 = 0; + S3 = 0; + p2 = 1; + rate = 1; + S4 = 0.5 * rateOf(S3); + S2' = 2 * rateOf(S3); + S1' = S2 + rateOf(S2); + S3' = rate; + p1 = 2 * rateOf(S1); + p2' = rateOf(S1); + p3 = rateOf(rate); + end + """ + module_name = "test_chained_rateof" + 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() + i_S1 = state_ids_solver.index("S1") + i_S2 = state_ids_solver.index("S2") + i_S3 = state_ids_solver.index("S3") + i_p2 = state_ids_solver.index("p2") + assert_approx_equal(rdata["xdot"][i_S3], 1) + assert_approx_equal(rdata["xdot"][i_S2], 2) + assert_approx_equal( + rdata["xdot"][i_S1], rdata.by_id("S2")[-1] + rdata["xdot"][i_S2] + ) + assert_approx_equal(rdata["xdot"][i_S1], rdata["xdot"][i_p2]) + + assert_array_almost_equal_nulp(rdata.by_id("S3"), t, 10) + assert_array_almost_equal_nulp( + rdata.by_id("S2"), 2 * rdata.by_id("S3") + ) + assert_array_almost_equal_nulp( + rdata.by_id("S4")[1:], 0.5 * np.diff(rdata.by_id("S3")), 10 + ) + assert_array_almost_equal_nulp(rdata.by_id("p3"), 0) + assert_array_almost_equal_nulp( + rdata.by_id("p2"), 1 + rdata.by_id("S1") + ) diff --git a/deps/AMICI/python/tests/test_splines.py b/deps/AMICI/python/tests/test_splines.py new file mode 100644 index 000000000..a7fe01e84 --- /dev/null +++ b/deps/AMICI/python/tests/test_splines.py @@ -0,0 +1,108 @@ +""" +Test AMICI's C++ spline implementation by comparing +the results of simulations of simple SBML models +containing simple splines with a symbolically-computed +ground truth. NB: The test in this file takes a long +time to complete. +""" + +import os + +import numpy as np +from amici.testing import skip_on_valgrind +from splines_utils import ( + check_splines_full, + example_spline_1, + example_spline_2, + example_spline_3, +) + + +@skip_on_valgrind +def test_multiple_splines(**kwargs): + """ + Test a SBML model containing multiple splines. + """ + spline0, params0, tols0 = example_spline_1( + 0, num_nodes=9, fixed_values=[0, 2], extrapolate="linear" + ) + spline1, params1, tols1 = example_spline_1( + 1, num_nodes=14, scale=1.5, offset=5, extrapolate="linear" + ) + spline2, params2, tols2 = example_spline_1( + 2, num_nodes=5, scale=0.5, offset=-5, extrapolate="linear" + ) + spline3, params3, tols3 = example_spline_1( + 3, fixed_values="all", extrapolate="linear" + ) + spline4, params4, tols4 = example_spline_2(4) + spline5, params5, tols5 = example_spline_3(5) + + splines = [spline0, spline1, spline2, spline3, spline4, spline5] + + params = dict(params0) + params.update(params1) + params.update(params2) + params.update(params3) + params.update(params4) + params.update(params5) + + if isinstance(tols0, dict): + tols0 = (tols0, tols0, tols0) + if isinstance(tols1, dict): + tols1 = (tols1, tols1, tols1) + if isinstance(tols2, dict): + tols2 = (tols2, tols2, tols2) + if isinstance(tols3, dict): + tols3 = (tols3, tols3, tols3) + if isinstance(tols4, dict): + tols4 = (tols4, tols4, tols4) + if isinstance(tols5, dict): + tols5 = (tols5, tols5, tols5) + + tols = [] + for t0, t1, t2, t3, t4, t5 in zip( + tols0, tols1, tols2, tols3, tols4, tols5 + ): + keys = set().union( + t0.keys(), t1.keys(), t2.keys(), t3.keys(), t4.keys(), t5.keys() + ) + t = { + key: max( + t0.get(key, 0.0), + t1.get(key, 0.0), + t2.get(key, 0.0), + t3.get(key, 0.0), + t4.get(key, 0.0), + t5.get(key, 0.0), + ) + for key in keys + } + tols.append(t) + + tols[1]["x_rtol"] = max(1e-9, tols[1].get("x_rtol", -np.inf)) + tols[1]["x_atol"] = max(5e-9, tols[1].get("x_atol", -np.inf)) + tols[1]["sx_rtol"] = max(1e-5, tols[1].get("llh_rtol", -np.inf)) + tols[1]["sx_atol"] = max(5e-9, tols[1].get("sx_atol", -np.inf)) + tols[1]["llh_rtol"] = max(5e-14, tols[1].get("llh_rtol", -np.inf)) + tols[1]["sllh_atol"] = max(5e-5, tols[1].get("sllh_atol", -np.inf)) + + tols[2]["x_rtol"] = max(5e-10, tols[2].get("x_rtol", -np.inf)) + tols[2]["x_atol"] = max(1e-8, tols[2].get("x_atol", -np.inf)) + tols[2]["llh_rtol"] = max(5e-14, tols[2].get("llh_rtol", -np.inf)) + tols[2]["sllh_atol"] = max(5e-5, tols[2].get("sllh_atol", -np.inf)) + + if os.name == "nt": + tols[2]["sllh_atol"] = max(5e-4, tols[2]["sllh_atol"]) + + # Load precomputed results + # They be computed again by + # groundtruth = test_multiple_splines(return_groundtruth=True) + # They should be recomputed only if the splines used in the test change + precomputed_path = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "test_splines_precomputed.npz", + ) + kwargs["groundtruth"] = dict(np.load(precomputed_path)) + + return check_splines_full(splines, params, tols, **kwargs) diff --git a/deps/AMICI/python/tests/test_splines_precomputed.npz b/deps/AMICI/python/tests/test_splines_precomputed.npz new file mode 100644 index 000000000..c6ee624a3 Binary files /dev/null and b/deps/AMICI/python/tests/test_splines_precomputed.npz differ diff --git a/deps/AMICI/python/tests/test_splines_python.py b/deps/AMICI/python/tests/test_splines_python.py new file mode 100644 index 000000000..4c4de5ccf --- /dev/null +++ b/deps/AMICI/python/tests/test_splines_python.py @@ -0,0 +1,372 @@ +""" +Test AMICI's Python spline implementation by +creating splines with different properties, +evaluating them and comparing them with +the true analytical values. +""" + +import math + +import amici +import sympy as sp +from amici.testing import skip_on_valgrind + + +@skip_on_valgrind +def test_SplineUniform(): + spline = amici.splines.CubicHermiteSpline( + sbml_id="f", + evaluate_at=amici.sbml_utils.amici_time_symbol, + nodes=amici.splines.UniformGrid(0, 1, number_of_nodes=4), + values_at_nodes=[0.0, 2.0, 0.5, 1.0], + ) + assert math.isclose(float(spline.evaluate(0.0)), 0.0) + assert math.isclose(float(spline.evaluate(0.25)), 1.74609375) + assert math.isclose(float(spline.evaluate(1.0 / 3)), 2.0) + assert math.isclose(float(spline.evaluate(0.50)), 1.3437499999999996) + assert math.isclose(float(spline.evaluate(2.0 / 3)), 0.5) + assert math.isclose(float(spline.evaluate(0.75)), 0.484375) + assert math.isclose(float(spline.evaluate(1.00)), 1.0) + + +@skip_on_valgrind +def test_SplineNonUniform(): + spline = amici.splines.CubicHermiteSpline( + sbml_id="f", + evaluate_at=amici.sbml_utils.amici_time_symbol, + nodes=[0.0, 0.1, 0.5, 1.0], + values_at_nodes=[0.0, 2.0, 0.5, 1.0], + ) + assert math.isclose(float(spline.evaluate(0.00)), 0.0) + assert math.isclose(float(spline.evaluate(0.05)), 1.1484375) + assert math.isclose(float(spline.evaluate(0.10)), 2.0) + assert math.isclose(float(spline.evaluate(0.25)), 2.0498046875) + assert math.isclose(float(spline.evaluate(0.50)), 0.5) + assert math.isclose(float(spline.evaluate(0.75)), 0.6015625) + assert math.isclose(float(spline.evaluate(1.00)), 1.0) + + +@skip_on_valgrind +def test_SplineExplicit(): + spline = amici.splines.CubicHermiteSpline( + sbml_id="f", + evaluate_at=amici.sbml_utils.amici_time_symbol, + nodes=amici.splines.UniformGrid(0, 1, number_of_nodes=5), + values_at_nodes=[0.0, 2.0, 0.5, 1.0, 0.75], + derivatives_at_nodes=[1.0, 0.0, 0.1, -0.1, 0.0], + ) + assert math.isclose(float(spline.evaluate(0.00)), 0.0) + assert math.isclose(float(spline.evaluate(0.20)), 1.8000000000000003) + assert math.isclose(float(spline.evaluate(0.25)), 2.0) + assert math.isclose(float(spline.evaluate(0.40)), 1.02439999999999985) + assert math.isclose(float(spline.evaluate(0.50)), 0.5) + assert math.isclose(float(spline.evaluate(0.60)), 0.6819999999999999) + assert math.isclose(float(spline.evaluate(0.75)), 1.0) + assert math.isclose(float(spline.evaluate(0.80)), 0.9707999999999999) + assert math.isclose(float(spline.evaluate(1.00)), 0.75) + + +@skip_on_valgrind +def test_SplineZeroBC(): + spline = amici.splines.CubicHermiteSpline( + sbml_id="f", + evaluate_at=amici.sbml_utils.amici_time_symbol, + nodes=amici.splines.UniformGrid(0, 1, number_of_nodes=4), + values_at_nodes=[0.0, 2.0, 0.5, 1.0], + bc="zeroderivative", + ) + assert math.isclose(float(spline.evaluate(0.00)), 0.0) + assert math.isclose(float(spline.evaluate(0.25)), 1.65234375) + assert math.isclose(float(spline.evaluate(0.50)), 1.3437499999999996) + assert math.isclose(float(spline.evaluate(0.75)), 0.5078125) + assert math.isclose(float(spline.evaluate(1.00)), 1.0) + + +@skip_on_valgrind +def test_SplineLogarithmic(): + spline = amici.splines.CubicHermiteSpline( + sbml_id="f", + evaluate_at=amici.sbml_utils.amici_time_symbol, + nodes=amici.splines.UniformGrid(0, 1, number_of_nodes=5), + values_at_nodes=[0.2, 2.0, 0.5, 1.0, 0.75], + logarithmic_parametrization=True, + ) + assert math.isclose(float(spline.evaluate(0.00)), 0.2) + assert math.isclose(float(spline.evaluate(0.20)), 2.07939779651678) + assert math.isclose(float(spline.evaluate(0.25)), 2.0) + assert math.isclose(float(spline.evaluate(0.40)), 0.947459046694449) + assert math.isclose(float(spline.evaluate(0.50)), 0.5) + assert math.isclose(float(spline.evaluate(0.60)), 0.545987404053269) + assert math.isclose(float(spline.evaluate(0.75)), 1.0) + assert math.isclose(float(spline.evaluate(0.80)), 0.996753014029391) + assert math.isclose(float(spline.evaluate(1.00)), 0.75) + + +@skip_on_valgrind +def test_SplineUniformConstantExtrapolation(): + spline = amici.splines.CubicHermiteSpline( + sbml_id="f", + evaluate_at=amici.sbml_utils.amici_time_symbol, + nodes=amici.splines.UniformGrid(0, 1, number_of_nodes=4), + values_at_nodes=[0.0, 2.0, 0.5, 1.0], + extrapolate="constant", + ) + assert math.isclose(float(spline.evaluate(-2.00)), 0.0) + assert math.isclose(float(spline.evaluate(-1.00)), 0.0) + assert math.isclose(float(spline.evaluate(0.00)), 0.0) + assert math.isclose(float(spline.evaluate(0.25)), 1.65234375) + assert math.isclose(float(spline.evaluate(1.0 / 3)), 2.0) + assert math.isclose(float(spline.evaluate(0.50)), 1.3437499999999996) + assert math.isclose(float(spline.evaluate(2.0 / 3)), 0.5) + assert math.isclose(float(spline.evaluate(0.75)), 0.5078125) + assert math.isclose(float(spline.evaluate(1.00)), 1.0) + assert math.isclose(float(spline.evaluate(2.00)), 1.0) + assert math.isclose(float(spline.evaluate(3.00)), 1.0) + + +@skip_on_valgrind +def test_SplineUniformLinearExtrapolation(): + spline = amici.splines.CubicHermiteSpline( + sbml_id="f", + evaluate_at=amici.sbml_utils.amici_time_symbol, + nodes=amici.splines.UniformGrid(0, 1, number_of_nodes=4), + values_at_nodes=[0.0, 2.0, 0.5, 1.0], + extrapolate="linear", + ) + assert math.isclose(float(spline.evaluate(-2.00)), -12.0) + assert math.isclose(float(spline.evaluate(-1.00)), -6.0) + assert math.isclose(float(spline.evaluate(0.00)), 0.0) + assert math.isclose(float(spline.evaluate(0.25)), 1.74609375) + assert math.isclose(float(spline.evaluate(1.0 / 3)), 2.0) + assert math.isclose(float(spline.evaluate(0.50)), 1.3437499999999996) + assert math.isclose(float(spline.evaluate(2.0 / 3)), 0.5) + assert math.isclose(float(spline.evaluate(0.75)), 0.484375) + assert math.isclose(float(spline.evaluate(1.00)), 1.0) + assert math.isclose(float(spline.evaluate(2.00)), 2.5) + assert math.isclose(float(spline.evaluate(3.00)), 4.0) + + +@skip_on_valgrind +def test_SplineUniformPolynomialExtrapolation(): + spline = amici.splines.CubicHermiteSpline( + sbml_id="f", + evaluate_at=amici.sbml_utils.amici_time_symbol, + nodes=amici.splines.UniformGrid(0, 1, number_of_nodes=4), + values_at_nodes=[0.0, 2.0, 0.5, 1.0], + extrapolate="polynomial", + ) + assert math.isclose(float(spline.evaluate(-2.00)), 429.0) + assert math.isclose(float(spline.evaluate(-1.00)), 57.0) + assert math.isclose(float(spline.evaluate(0.00)), 0.0) + assert math.isclose(float(spline.evaluate(0.25)), 1.74609375) + assert math.isclose(float(spline.evaluate(1.0 / 3)), 2.0) + assert math.isclose(float(spline.evaluate(0.50)), 1.3437499999999996) + assert math.isclose(float(spline.evaluate(2.0 / 3)), 0.5) + assert math.isclose(float(spline.evaluate(0.75)), 0.484375) + assert math.isclose(float(spline.evaluate(1.00)), 1.0) + assert math.isclose(float(spline.evaluate(2.00)), -33.5) + assert math.isclose(float(spline.evaluate(3.00)), -248.0) + + +@skip_on_valgrind +def test_SplineUniformPeriodicExtrapolation(): + spline = amici.splines.CubicHermiteSpline( + sbml_id="f", + evaluate_at=amici.sbml_utils.amici_time_symbol, + nodes=amici.splines.UniformGrid(0, 1, number_of_nodes=4), + values_at_nodes=[1.0, 2.0, 0.5, 1.0], + extrapolate="periodic", + ) + assert math.isclose(float(spline.evaluate(-4 / 3)), 0.5) + assert math.isclose(float(spline.evaluate(-0.5)), 1.2812499999999996) + assert math.isclose(float(spline.evaluate(0.00)), 1.0) + assert math.isclose(float(spline.evaluate(0.25)), 1.9140625) + assert math.isclose(float(spline.evaluate(1 / 3)), 2.0) + assert math.isclose(float(spline.evaluate(0.50)), 1.2812499999999996) + assert math.isclose(float(spline.evaluate(2 / 3)), 0.5) + assert math.isclose(float(spline.evaluate(0.75)), 0.47265625) + assert math.isclose(float(spline.evaluate(1.00)), 1.0) + assert math.isclose(float(spline.evaluate(1.25)), 1.9140625) + assert math.isclose(float(spline.evaluate(2.75)), 0.47265625) + + +@skip_on_valgrind +def test_SplineNonUniformPeriodicExtrapolation(): + spline = amici.splines.CubicHermiteSpline( + sbml_id="f", + evaluate_at=amici.sbml_utils.amici_time_symbol, + nodes=[0.0, 0.1, 0.5, 1.0], + values_at_nodes=[1.0, 2.0, 0.5, 1.0], + extrapolate="periodic", + ) + assert math.isclose(float(spline.evaluate(-1.90)), 2.0) + assert math.isclose(float(spline.evaluate(-0.25)), 0.3203125) + assert math.isclose(float(spline.evaluate(0.00)), 1.0) + assert math.isclose(float(spline.evaluate(0.05)), 1.5296875) + assert math.isclose(float(spline.evaluate(0.10)), 2.0) + assert math.isclose(float(spline.evaluate(0.25)), 1.7568359375) + assert math.isclose(float(spline.evaluate(0.50)), 0.5) + assert math.isclose(float(spline.evaluate(0.75)), 0.3203125) + assert math.isclose(float(spline.evaluate(1.00)), 1.0) + assert math.isclose(float(spline.evaluate(1.50)), 0.5) + assert math.isclose(float(spline.evaluate(2.05)), 1.5296875) + + +@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): + assert math.isclose( + float(value.diff(p).subs(subs)), exp, rel_tol=rel_tol + ) + + +@skip_on_valgrind +def test_SplineUniformSensitivity(): + params = (a, b, c) = sp.symbols("a b c") + params_values = [0.5, 1.0, 2.5] + spline = amici.splines.CubicHermiteSpline( + sbml_id="f", + evaluate_at=amici.sbml_utils.amici_time_symbol, + nodes=amici.splines.UniformGrid(0, 1, number_of_nodes=4), + values_at_nodes=[3 * a + b, c**2 - 3, 1, sp.log(b) + 3 * c - 6 * a], + ) + check_gradient(spline, 0.00, params, params_values, [3.0, 1.0, 0.0]) + check_gradient( + spline, + 0.25, + params, + params_values, + [0.539062, 0.179688, 4.45312], + rel_tol=1e-5, + ) + check_gradient(spline, 1.0 / 3, params, params_values, [0.0, 0.0, 5.0]) + check_gradient( + spline, 0.50, params, params_values, [0.1875, -0.125, 2.625] + ) + check_gradient(spline, 2.0 / 3, params, params_values, [0.0, 0.0, 0.0]) + check_gradient( + spline, + 0.75, + params, + params_values, + [-1.07812, 0.179688, 0.1875], + rel_tol=1e-5, + ) + check_gradient(spline, 1.00, params, params_values, [-6.0, 1.0, 3.0]) + + +@skip_on_valgrind +def test_SplineNonUniformSensitivity(): + params = (a, b, c) = sp.symbols("a b c") + params_values = [0.5, 1.0, 2.5] + spline = amici.splines.CubicHermiteSpline( + sbml_id="f", + evaluate_at=amici.sbml_utils.amici_time_symbol, + nodes=[0.0, 0.1, 0.5, 1.0], + values_at_nodes=[3 * a + b, c**2 - 3, 1, sp.log(b) + 3 * c - 6 * a], + ) + check_gradient(spline, 0.00, params, params_values, [3.0, 1.0, 0.0]) + check_gradient( + spline, + 0.05, + params, + params_values, + [1.3125, 0.4375, 2.89062], + rel_tol=1e-5, + ) + check_gradient(spline, 0.10, params, params_values, [0.0, 0.0, 5.0]) + check_gradient(spline, 0.30, params, params_values, [-0.45, -0.3, 3.6]) + check_gradient(spline, 0.50, params, params_values, [0.0, 0.0, 0.0]) + check_gradient( + spline, 0.75, params, params_values, [-2.625, 0.4375, 0.921875] + ) + check_gradient(spline, 1.00, params, params_values, [-6.0, 1.0, 3.0]) + + +@skip_on_valgrind +def test_SplineExplicitSensitivity(): + params = (a, b, c) = sp.symbols("a b c") + params_values = [0.5, 1.0, 2.5] + spline = amici.splines.CubicHermiteSpline( + sbml_id="f", + evaluate_at=amici.sbml_utils.amici_time_symbol, + nodes=amici.splines.UniformGrid(0, 1, number_of_nodes=4), + values_at_nodes=[3 * a + b, c**2 - 3, 1, sp.log(b) + 3 * c - 6 * a], + derivatives_at_nodes=[ + c**3 - 2, + sp.sqrt(b) * sp.log(b) + 3 * c, + 4 * a - sp.sin(b), + 1, + ], + ) + check_gradient(spline, 0.00, params, params_values, [3.0, 1.0, 0.0]) + check_gradient( + spline, + 0.25, + params, + params_values, + [0.46875, 0.109375, 4.37109], + rel_tol=1e-6, + ) + check_gradient(spline, 1.0 / 3, params, params_values, [0.0, 0.0, 5.0]) + check_gradient( + spline, + 0.50, + params, + params_values, + [-0.166667, 0.0641793, 2.625], + rel_tol=1e-5, + ) + check_gradient(spline, 2.0 / 3, params, params_values, [0.0, 0.0, 0.0]) + check_gradient( + spline, + 0.75, + params, + params_values, + [-0.75, 0.130923, 0.46875], + rel_tol=1e-5, + ) + check_gradient(spline, 1.00, params, params_values, [-6.0, 1.0, 3.0]) + + +@skip_on_valgrind +def test_SplineLogarithmicSensitivity(): + params = (a, b, c) = sp.symbols("a b c") + params_values = [0.5, 1.0, 2.5] + spline = amici.splines.CubicHermiteSpline( + sbml_id="f", + evaluate_at=amici.sbml_utils.amici_time_symbol, + nodes=amici.splines.UniformGrid(0, 1, number_of_nodes=4), + values_at_nodes=[3 * a + b, c**2 - 3, 1, sp.log(b) + 3 * c - 6 * a], + logarithmic_parametrization=True, + ) + check_gradient(spline, 0.00, params, params_values, [3.0, 1.0, 0.0]) + check_gradient( + spline, + 0.25, + params, + params_values, + [0.585881, 0.195294, 4.38532], + rel_tol=1e-5, + ) + check_gradient(spline, 1.0 / 3, params, params_values, [0.0, 0.0, 5.0]) + check_gradient( + spline, + 0.50, + params, + params_values, + [0.514003, -0.132395, 1.52044], + rel_tol=1e-5, + ) + check_gradient(spline, 2.0 / 3, params, params_values, [0.0, 0.0, 0.0]) + check_gradient( + spline, + 0.75, + params, + params_values, + [-0.820743, 0.13679, -0.0577988], + rel_tol=1e-5, + ) + check_gradient(spline, 1.00, params, params_values, [-6.0, 1.0, 3.0]) diff --git a/deps/AMICI/python/tests/test_splines_short.py b/deps/AMICI/python/tests/test_splines_short.py new file mode 100644 index 000000000..59e54a327 --- /dev/null +++ b/deps/AMICI/python/tests/test_splines_short.py @@ -0,0 +1,142 @@ +""" +Test AMICI's C++ spline implementation by comparing +the results of simulations of simple SBML models +containing simple splines with a symbolically-computed +ground truth. +""" + +import numpy as np +import sympy as sp +from amici.splines import CubicHermiteSpline, UniformGrid +from amici.testing import skip_on_valgrind +from splines_utils import check_splines_full, example_spline_1 + + +def test_spline_piecewise(**kwargs): + """ + Test a SBML model containing a single spline. + AMICI's behaviour in absence of spline annotations is also tested. + """ + spline, params, tols = example_spline_1() + check_splines_full(spline, params, tols, **kwargs) + + +@skip_on_valgrind +def test_two_splines(**kwargs): + """ + Test a SBML model containing two splines. + """ + spline0, params0, tols0 = example_spline_1( + 0, num_nodes=4, fixed_values=[0, 2], extrapolate="linear" + ) + spline1, params1, tols1 = example_spline_1( + 1, num_nodes=5, scale=1.5, offset=5, extrapolate="linear" + ) + + splines = [spline0, spline1] + + params = dict(params0) + params.update(params1) + + if isinstance(tols0, dict): + tols0 = (tols0, tols0, tols0) + if isinstance(tols1, dict): + tols1 = (tols1, tols1, tols1) + + tols = [] + for t0, t1 in zip(tols0, tols1): + keys = set().union(t0.keys(), t1.keys()) + t = { + key: max( + t0.get(key, 0.0), + t1.get(key, 0.0), + ) + for key in keys + } + tols.append(t) + + tols[1]["x_rtol"] = max(1e-9, tols[1].get("x_rtol", -np.inf)) + tols[1]["x_atol"] = max(5e-9, tols[1].get("x_atol", -np.inf)) + tols[1]["sx_rtol"] = max(1e-5, tols[1].get("llh_rtol", -np.inf)) + tols[1]["sx_atol"] = max(5e-9, tols[1].get("sx_atol", -np.inf)) + tols[1]["llh_rtol"] = max(5e-14, tols[1].get("llh_rtol", -np.inf)) + tols[1]["sllh_atol"] = max(5e-5, tols[1].get("sllh_atol", -np.inf)) + + tols[2]["x_rtol"] = max(5e-10, tols[2].get("x_rtol", -np.inf)) + tols[2]["x_atol"] = max(1e-8, tols[2].get("x_atol", -np.inf)) + tols[2]["llh_rtol"] = max(5e-14, tols[2].get("llh_rtol", -np.inf)) + tols[2]["sllh_atol"] = max(5e-5, tols[2].get("sllh_atol", -np.inf)) + + check_splines_full(splines, params, tols, check_piecewise=False, **kwargs) + + +@skip_on_valgrind +def test_splines_plist(): + """ + Test if AMICI's spline implementation + handles correctly a change in the parameter list. + """ + # Dummy spline #1 + xx = UniformGrid(0, 5, number_of_nodes=3) + yy = np.asarray([0.0, 1.0, 0.5]) + spline1 = CubicHermiteSpline( + "y1", + nodes=xx, + values_at_nodes=yy, + bc="auto", + extrapolate=(None, "constant"), + ) + # Dummy spline #2 + xx = UniformGrid(0, 5, number_of_nodes=4) + yy = np.asarray([0.0, 0.5, -0.5, 0.5]) + spline2 = CubicHermiteSpline( + "y2", + nodes=xx, + values_at_nodes=yy, + bc="auto", + extrapolate=(None, "constant"), + ) + # Real spline #3 + xx = UniformGrid(0, 5, number_of_nodes=6) + p1, p2, p3, p4, p5 = sp.symbols("p1 p2 p3 p4 p5") + yy = np.asarray( + [p1 + p2, p2 * p3, p4, sp.cos(p1 + p3), p4 * sp.log(p1), p3] + ) + dd = np.asarray([-0.75, -0.875, p5, 0.125, 1.15057181, 0.0]) + params = {p1: 1.0, p2: 0.5, p3: 1.5, p4: -0.25, p5: -0.5} + # print([y.subs(params).evalf() for y in yy]) + spline3 = CubicHermiteSpline( + "y3", + nodes=xx, + values_at_nodes=yy, + derivatives_at_nodes=dd, + bc="auto", + extrapolate=(None, "constant"), + ) + # Dummy spline 4 + xx = UniformGrid(0, 5, number_of_nodes=3) + yy = np.asarray([0.0, -0.5, 0.5]) + spline4 = CubicHermiteSpline( + "y4", + nodes=xx, + values_at_nodes=yy, + bc="auto", + extrapolate=(None, "constant"), + ) + tols = dict( + x_rtol=1e-6, + x_atol=1e-11, + sx_rtol=1e-6, + sx_atol=5e-11, + llh_rtol=1e-14, + sllh_atol=5e-9, + ) + check_splines_full( + [spline1, spline2, spline3, spline4], + params, + tols, + check_piecewise=False, + check_forward=False, + check_adjoint=True, # plist cannot be checked, but complex parameter dependence can + parameter_lists=[[0, 1, 4], [2, 3]], + ) diff --git a/deps/AMICI/python/tests/test_swig_interface.py b/deps/AMICI/python/tests/test_swig_interface.py index 1b8e733f8..a746552b5 100644 --- a/deps/AMICI/python/tests/test_swig_interface.py +++ b/deps/AMICI/python/tests/test_swig_interface.py @@ -7,6 +7,7 @@ import numbers import amici +import numpy as np def test_version_number(pysb_example_presimulation_module): @@ -21,10 +22,12 @@ def test_copy_constructors(pysb_example_presimulation_module): for obj in [model, solver]: for attr in dir(obj): - if attr.startswith('__') \ - or attr == 'this' \ - or attr == 'thisown' \ - or is_callable_but_not_getter(obj, attr): + if ( + attr.startswith("__") + or attr == "this" + or attr == "thisown" + or is_callable_but_not_getter(obj, attr) + ): continue # objects will be initialized with default values so we @@ -47,8 +50,9 @@ def test_copy_constructors(pysb_example_presimulation_module): obj_clone = obj.clone() - assert get_val(obj, attr) == get_val(obj_clone, attr), \ - f"{obj} - {attr}" + assert get_val(obj, attr) == get_val( + obj_clone, attr + ), f"{obj} - {attr}" # `None` values are skipped in `test_model_instance_settings`. @@ -61,57 +65,55 @@ def test_copy_constructors(pysb_example_presimulation_module): # Default values are based on `pysb_example_presimulation_module`. model_instance_settings0 = { # setting name: [default value, custom value] - 'AddSigmaResiduals': [ - False, - True - ], - 'AlwaysCheckFinite': [ + "AddSigmaResiduals": [False, True], + "AlwaysCheckFinite": [ False, True, ], # Skipped due to model dependency in `'InitialStates'`. - 'FixedParameters': None, - 'InitialStates': [ + "FixedParameters": None, + "InitialStates": [ (10.0, 9.0, 1.0, 0.0, 0.0, 0.0), - tuple([.1]*6), + tuple([0.1] * 6), ], - 'InitialStateSensitivities': [ - tuple([1.0] + [0.0]*35), - tuple([.1]*36), + ("getInitialStateSensitivities", "setUnscaledInitialStateSensitivities"): [ + tuple([1.0] + [0.0] * 35), + tuple([0.1] * 36), ], - 'MinimumSigmaResiduals': [ + "MinimumSigmaResiduals": [ 50.0, 60.0, ], - ('nMaxEvent', 'setNMaxEvent'): [ + ("nMaxEvent", "setNMaxEvent"): [ 10, 20, ], - 'Parameters': [ - (10.0, 0.1, 0.1, 0.1, 0.1, 0.1), - tuple([1.0] * 6) - ], + "Parameters": [(10.0, 0.1, 0.1, 0.1, 0.1, 0.1), tuple([1.0] * 6)], # Skipped due to interdependency with `'InitialStateSensitivities'`. - 'ParameterList': None, + "ParameterList": None, # Skipped due to interdependency with `'InitialStateSensitivities'`. - 'ParameterScale': None, + "ParameterScale": None, # Skipped due to interdependencies with # `'ReinitializeFixedParameterInitialStates'`. - 'ReinitializationStateIdxs': None, + "ReinitializationStateIdxs": None, # Skipped due to interdependencies with `'ReinitializationStateIdxs'`. - 'ReinitializeFixedParameterInitialStates': None, + "ReinitializeFixedParameterInitialStates": None, # Skipped due to conservation laws in the test model # `pysb_example_presimulation_module.getModel()`. - 'StateIsNonNegative': None, - 'SteadyStateSensitivityMode': [ - 0, + "StateIsNonNegative": None, + "SteadyStateComputationMode": [ + 2, 1, ], - ('t0', 'setT0'): [ + "SteadyStateSensitivityMode": [ + 2, + 1, + ], + ("t0", "setT0"): [ 0.0, 1.0, ], - 'Timepoints': [ + "Timepoints": [ tuple(), (1.0, 2.0, 3.0), ], @@ -129,23 +131,26 @@ def test_model_instance_settings(pysb_example_presimulation_module): i_setter = 1 # All settings are tested. - assert set(model_instance_settings0) == set(amici.model_instance_settings) + assert set(model_instance_settings0) == set( + amici.swig_wrappers.model_instance_settings + ) # Skip settings with interdependencies. - model_instance_settings = \ - {k: v for k, v in model_instance_settings0.items() if v is not None} + model_instance_settings = { + k: v for k, v in model_instance_settings0.items() if v is not None + } # All custom values are different to default values. - assert all([ + assert all( default != custom for name, (default, custom) in model_instance_settings.items() - if name != 'ReinitializeFixedParameterInitialStates' - ]) + if name != "ReinitializeFixedParameterInitialStates" + ) # All default values are as expected. for name, (default, custom) in model_instance_settings.items(): - getter = name[i_getter] if isinstance(name, tuple) else f'get{name}' - setter = name[i_setter] if isinstance(name, tuple) else f'set{name}' + getter = name[i_getter] if isinstance(name, tuple) else f"get{name}" + setter = name[i_setter] if isinstance(name, tuple) else f"set{name}" # Default values are as expected. assert getattr(model0, getter)() == default # Custom value is set correctly. @@ -163,8 +168,23 @@ def test_model_instance_settings(pysb_example_presimulation_module): # The new model has the default settings. model_default_settings = amici.get_model_settings(model) for name in model_instance_settings: - assert model_default_settings[name] == \ - model_instance_settings[name][i_default] + if ( + name == "InitialStates" and not model.hasCustomInitialStates() + ) or ( + name + == ( + "getInitialStateSensitivities", + "setUnscaledInitialStateSensitivities", + ) + and not model.hasCustomInitialStateSensitivities() + ): + # Here the expected value differs from what the getter would return + assert model_default_settings[name] == [] + else: + assert ( + model_default_settings[name] + == model_instance_settings[name][i_default] + ), name # The grouped setter method works. custom_settings_not_none = { @@ -173,11 +193,11 @@ def test_model_instance_settings(pysb_example_presimulation_module): if model_instance_settings0[name] is not None } amici.set_model_settings(model, custom_settings_not_none) - assert all([ + assert all( value == custom_settings_not_none[name] for name, value in amici.get_model_settings(model).items() if name in custom_settings_not_none - ]) + ) def test_interdependent_settings(pysb_example_presimulation_module): @@ -189,20 +209,20 @@ def test_interdependent_settings(pysb_example_presimulation_module): model = pysb_example_presimulation_module.getModel() original_settings = { - 'FixedParameters': (9.0, 1.0), - 'ParameterList': (0, 1, 2, 3, 4, 5), - 'ParameterScale': [0, 0, 0, 0, 0, 0], - 'ReinitializationStateIdxs': tuple(), - 'ReinitializeFixedParameterInitialStates': False, - 'StateIsNonNegative': (False, False, False), + "FixedParameters": (9.0, 1.0), + "ParameterList": (0, 1, 2, 3, 4, 5), + "ParameterScale": [0, 0, 0, 0, 0, 0], + "ReinitializationStateIdxs": tuple(), + "ReinitializeFixedParameterInitialStates": False, + "StateIsNonNegative": (False, False, False), } expected_settings = { - 'FixedParameters': (8.0, 2.0), - 'ParameterList': (0, 1, 2, 3, 4), - 'ParameterScale': [1, 0, 0, 0, 0, 0], - 'ReinitializationStateIdxs': (0,), - 'ReinitializeFixedParameterInitialStates': True, + "FixedParameters": (8.0, 2.0), + "ParameterList": (0, 1, 2, 3, 4), + "ParameterScale": [1, 0, 0, 0, 0, 0], + "ReinitializationStateIdxs": (0,), + "ReinitializeFixedParameterInitialStates": True, # Skipped due to conservation laws in the test model. # 'StateIsNonNegative': None, } @@ -211,13 +231,14 @@ def test_interdependent_settings(pysb_example_presimulation_module): # (e.g. SWIG objects). Default transformer is no transformation # (the identity function). getter_transformers = { - setting: (lambda x: x) - for setting in original_settings + setting: (lambda x: x) for setting in original_settings } - getter_transformers.update({ - # Convert from SWIG object. - 'ParameterScale': lambda x: list(x) - }) + getter_transformers.update( + { + # Convert from SWIG object. + "ParameterScale": lambda x: list(x) + } + ) default_settings = amici.get_model_settings(model) for original_setting, original_setting_value in original_settings.items(): @@ -233,18 +254,14 @@ def test_interdependent_settings(pysb_example_presimulation_module): amici.set_model_settings(model, input_settings) output_settings = amici.get_model_settings(model) - test_value = getter_transformers[setting]( - output_settings[setting] - ) + test_value = getter_transformers[setting](output_settings[setting]) # The setter works. assert test_value == expected_value input_settings = {setting: output_settings[setting]} amici.set_model_settings(model, input_settings) output_settings = amici.get_model_settings(model) - test_value = getter_transformers[setting]( - output_settings[setting] - ) + test_value = getter_transformers[setting](output_settings[setting]) # (round-trip) The output of the getter can be used as input to the # setter, and does not change the value. assert test_value == expected_value @@ -264,52 +281,55 @@ def test_unhandled_settings(pysb_example_presimulation_module): model = pysb_example_presimulation_module.getModel() not_handled = [ - 'get', - 'getAmiciCommit', - 'getAmiciVersion', - 'getExpressionIds', - 'getExpressionNames', - 'getFixedParameterById', - 'getFixedParameterByName', - 'getFixedParameterIds', - 'getFixedParameterNames', - 'getName', - 'getObservableIds', - 'getObservableNames', - 'getObservableScaling', - 'getParameterById', - 'getParameterByName', - 'getParameterIds', - 'getParameterNames', - 'getSolver', - 'getStateIds', - 'getStateNames', - 'getTimepoint', - 'getUnscaledParameters', - 'setAllStatesNonNegative', - 'setFixedParameterById', - 'setFixedParameterByName', - 'setFixedParametersByIdRegex', - 'setFixedParametersByNameRegex', - 'setParameterById', - 'setParameterByName', - 'setParametersByIdRegex', - 'setParametersByNameRegex', - 'setUnscaledInitialStateSensitivities', + "get", + "getAmiciCommit", + "getAmiciVersion", + "getExpressionIds", + "getExpressionNames", + "getFixedParameterById", + "getFixedParameterByName", + "getFixedParameterIds", + "getFixedParameterNames", + "getName", + "getObservableIds", + "getObservableNames", + "getObservableScaling", + "getParameterById", + "getParameterByName", + "getParameterIds", + "getParameterNames", + "getSolver", + "getStateIds", + "getStateNames", + "getStateIdsSolver", + "getStateNamesSolver", + "getTimepoint", + "getUnscaledParameters", + "setAllStatesNonNegative", + "setFixedParameterById", + "setFixedParameterByName", + "setFixedParametersByIdRegex", + "setFixedParametersByNameRegex", + "setParameterById", + "setParameterByName", + "setParametersByIdRegex", + "setParametersByNameRegex", + "setInitialStateSensitivities", ] + from amici.swig_wrappers import model_instance_settings handled = [ name - for names in amici.model_instance_settings + for names in model_instance_settings for name in ( names - if isinstance(names, tuple) else - (f'get{names}', f'set{names}') + if isinstance(names, tuple) + else (f"get{names}", f"set{names}") ) ] for attribute in dir(model): - if attribute[:3] in ['get', 'set'] and attribute not in not_handled: + if attribute[:3] in ["get", "set"] and attribute not in not_handled: assert attribute in handled, attribute @@ -317,11 +337,12 @@ def is_callable_but_not_getter(obj, attr): if not callable(getattr(obj, attr)): return False - if attr.startswith('get'): - return \ - 'set' + attr[3:] not in dir(obj) \ - or attr.endswith('ById') \ - or attr.endswith('ByName') + if attr.startswith("get"): + return ( + "set" + attr[3:] not in dir(obj) + or attr.endswith("ById") + or attr.endswith("ByName") + ) else: return True @@ -334,12 +355,12 @@ def get_val(obj, attr): def get_mod_val(val, attr): - if attr == 'getReturnDataReportingMode': + if attr == "getReturnDataReportingMode": return amici.RDataReporting.likelihood - elif attr == 'getParameterList': - return tuple(get_mod_val(val[0], '') for _ in val) - elif attr == 'getStateIsNonNegative': - raise ValueError('Cannot modify value') + elif attr == "getParameterList": + return tuple(get_mod_val(val[0], "") for _ in val) + elif attr == "getStateIsNonNegative": + raise ValueError("Cannot modify value") elif isinstance(val, bool): return not val elif isinstance(val, numbers.Number): @@ -347,13 +368,105 @@ def get_mod_val(val, attr): elif isinstance(val, tuple): return tuple(get_mod_val(v, attr) for v in val) - raise ValueError('Cannot modify value') + raise ValueError("Cannot modify value") def set_val(obj, attr, val): if callable(getattr(obj, attr)): - getattr(obj, 'set' + attr[3:])( - val - ) + getattr(obj, "set" + attr[3:])(val) else: setattr(obj, attr, val) + + +def test_model_instance_settings_custom_x0(pysb_example_presimulation_module): + """Check that settings are applied in the correct order, and only if + required""" + model = pysb_example_presimulation_module.getModel() + + # ensure no-custom-(s)x0 is restored + assert not model.hasCustomInitialStates() + assert not model.hasCustomInitialStateSensitivities() + settings = amici.get_model_settings(model) + model.setInitialStates(model.getInitialStates()) + model.setUnscaledInitialStateSensitivities( + model.getInitialStateSensitivities() + ) + amici.set_model_settings(model, settings) + assert not model.hasCustomInitialStates() + assert not model.hasCustomInitialStateSensitivities() + # ensure everything was set correctly, and there wasn't any problem + # due to, e.g. interactions of different setters + assert settings == amici.get_model_settings(model) + + # ensure custom (s)x0 is restored + model.setInitialStates(model.getInitialStates()) + model.setParameterScale(amici.ParameterScaling.log10) + sx0 = model.getInitialStateSensitivities() + model.setUnscaledInitialStateSensitivities(sx0) + assert model.hasCustomInitialStates() + assert model.hasCustomInitialStateSensitivities() + settings = amici.get_model_settings(model) + model2 = pysb_example_presimulation_module.getModel() + amici.set_model_settings(model2, settings) + assert model2.hasCustomInitialStates() + assert model2.hasCustomInitialStateSensitivities() + assert model2.getInitialStateSensitivities() == sx0 + assert settings == amici.get_model_settings(model2) + + +def test_solver_repr(): + for solver in (amici.CVodeSolver(), amici.IDASolver()): + solver_ptr = amici.SolverPtr(solver.this) + 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(): + ny = 1 + nz = 2 + ne = 3 + nt = 4 + edata = amici.ExpData(ny, nz, ne, range(nt)) + edata_ptr = amici.ExpDataPtr(edata.this) + expected_strs = ( + f"{nt}x{ny} time-resolved datapoints", + f"{ne}x{nz} event-resolved datapoints", + f"(0/{ny * nt} measurements", + f"(0/{nz * ne} measurements", + ) + for e in [edata, edata_ptr]: + 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(): + e1 = amici.ExpData(1, 2, 3, [3]) + e2 = amici.ExpData(1, 2, 3, [3]) + assert e1 == e2 + # check that comparison with other types works + # this is not implemented by swig by default + assert e1 != 1 + + +def test_expdata_and_expdataview_are_deepcopyable(): + edata1 = amici.ExpData(3, 2, 3, range(4)) + edata1.setObservedData(np.zeros((3, 4)).flatten()) + + # ExpData + edata2 = copy.deepcopy(edata1) + assert edata1 == edata2 + assert edata1.this != edata2.this + edata2.setTimepoints([0]) + assert edata1 != edata2 + + # ExpDataView + ev1 = amici.ExpDataView(edata1) + ev2 = copy.deepcopy(ev1) + assert ev2._swigptr.this != ev1._swigptr.this + assert ev1 == ev2 diff --git a/deps/AMICI/python/tests/util.py b/deps/AMICI/python/tests/util.py index cac811b11..dde10eb45 100644 --- a/deps/AMICI/python/tests/util.py +++ b/deps/AMICI/python/tests/util.py @@ -1,45 +1,51 @@ """Tests for SBML events, including piecewise expressions.""" -import libsbml -import numpy as np +import sys +import tempfile from pathlib import Path +import libsbml +import numpy as np from amici import ( AmiciModel, - import_model_module, - runAmiciSimulation, SbmlImporter, SensitivityMethod, - SensitivityOrder + SensitivityOrder, + import_model_module, + runAmiciSimulation, ) +from amici.gradient_check import _check_close -def create_amici_model(sbml_model, model_name) -> AmiciModel: +def create_amici_model(sbml_model, model_name, **kwargs) -> AmiciModel: """ Import an sbml file and create an AMICI model from it """ - sbml_test_models = Path('sbml_test_models') - sbml_test_models_output_dir = sbml_test_models / 'amici_models' + sbml_test_models_output_dir = Path("amici_models") sbml_test_models_output_dir.mkdir(parents=True, exist_ok=True) sbml_importer = SbmlImporter(sbml_model) - output_dir = sbml_test_models_output_dir / model_name + # try not to exceed the stupid maximum path length on windows 💩 + output_dir = ( + sbml_test_models_output_dir / model_name + if sys.platform != "win32" + else tempfile.mkdtemp() + ) + sbml_importer.sbml2amici( - model_name=model_name, - output_dir=str(output_dir) + model_name=model_name, output_dir=output_dir, **kwargs ) - model_module = import_model_module(model_name, str(output_dir.resolve())) - model = model_module.getModel() - return model + model_module = import_model_module(model_name, output_dir) + return model_module.getModel() def create_sbml_model( - initial_assignments, - parameters, - rate_rules, - species, - events, - to_file: str = None, + initial_assignments, + parameters, + rate_rules, + species, + events, + to_file: str = None, ): """Create an SBML model from simple definitions. @@ -52,18 +58,18 @@ def create_sbml_model( model = document.createModel() compartment = model.createCompartment() - compartment.setId('compartment') + compartment.setId("compartment") compartment.setConstant(True) compartment.setSize(1) compartment.setSpatialDimensions(3) - compartment.setUnits('dimensionless') + compartment.setUnits("dimensionless") for species_id in species: species = model.createSpecies() species.setId(species_id) - species.setCompartment('compartment') + species.setCompartment("compartment") species.setConstant(False) - species.setSubstanceUnits('dimensionless') + species.setSubstanceUnits("dimensionless") species.setBoundaryCondition(False) species.setHasOnlySubstanceUnits(False) species.setInitialConcentration(1.0) @@ -83,7 +89,7 @@ def create_sbml_model( parameter.setId(parameter_id) parameter.setConstant(True) parameter.setValue(parameter_value) - parameter.setUnits('dimensionless') + parameter.setUnits("dimensionless") for event_id, event_def in events.items(): event = model.createEvent() @@ -91,27 +97,28 @@ def create_sbml_model( event.setName(event_id) event.setUseValuesFromTriggerTime(True) trigger = event.createTrigger() - trigger.setMath(libsbml.parseL3Formula(event_def['trigger'])) + trigger.setMath(libsbml.parseL3Formula(event_def["trigger"])) trigger.setPersistent(True) trigger.setInitialValue(True) - if isinstance(event_def['target'], list): - assignments = [] - for ia, event_target in enumerate(event_def['target']): - event_assignment = event_def['assignment'][ia] - assignments.append(event.createEventAssignment()) - assignments[ia].setVariable(event_target) - assignments[ia].setMath( - libsbml.parseL3Formula(event_assignment)) + + def create_event_assignment(target, assignment): + ea = event.createEventAssignment() + ea.setVariable(target) + ea.setMath(libsbml.parseL3Formula(assignment)) + + if isinstance(event_def["target"], list): + for event_target, event_assignment in zip( + event_def["target"], event_def["assignment"] + ): + create_event_assignment(event_target, event_assignment) + else: - assignment = event.createEventAssignment() - assignment.setVariable(event_def['target']) - assignment.setMath(libsbml.parseL3Formula(event_def['assignment'])) + create_event_assignment( + event_def["target"], event_def["assignment"] + ) if to_file: - libsbml.writeSBMLToFile( - document, - str(to_file), - ) + libsbml.writeSBMLToFile(document, to_file) # Need to return document, else AMICI throws an error. # (possibly due to garbage collection?) @@ -119,48 +126,31 @@ def create_sbml_model( def check_trajectories_without_sensitivities( - amici_model: AmiciModel, - result_expected_x: np.ndarray, + amici_model: AmiciModel, + result_expected_x: np.ndarray, ): """ Check whether the AMICI simulation matches a known solution (ideally an analytically calculated one). """ - - # Does the AMICI simulation match the analytical solution? - solver = amici_model.getSolver() - solver.setAbsoluteTolerance(1e-15) - rdata = runAmiciSimulation(amici_model, solver=solver) - np.testing.assert_almost_equal(rdata['x'], result_expected_x, decimal=5) - - # Show that we can do arbitrary precision here (test 8 digits) solver = amici_model.getSolver() solver.setAbsoluteTolerance(1e-15) solver.setRelativeTolerance(1e-12) rdata = runAmiciSimulation(amici_model, solver=solver) - np.testing.assert_almost_equal(rdata['x'], result_expected_x, decimal=8) + _check_close( + rdata["x"], result_expected_x, field="x", rtol=5e-9, atol=1e-13 + ) def check_trajectories_with_forward_sensitivities( - amici_model: AmiciModel, - result_expected_x: np.ndarray, - result_expected_sx: np.ndarray, + amici_model: AmiciModel, + result_expected_x: np.ndarray, + result_expected_sx: np.ndarray, ): """ Check whether the forward sensitivities of the AMICI simulation match a known solution (ideally an analytically calculated one). """ - - # Show that we can do arbitrary precision here (test 8 digits) - solver = amici_model.getSolver() - solver.setAbsoluteTolerance(1e-15) - solver.setSensitivityOrder(SensitivityOrder.first) - solver.setSensitivityMethod(SensitivityMethod.forward) - rdata = runAmiciSimulation(amici_model, solver=solver) - np.testing.assert_almost_equal(rdata['x'], result_expected_x, decimal=5) - np.testing.assert_almost_equal(rdata['sx'], result_expected_sx, decimal=5) - - # Show that we can do arbitrary precision here (test 8 digits) solver = amici_model.getSolver() solver.setSensitivityOrder(SensitivityOrder.first) solver.setSensitivityMethod(SensitivityMethod.forward) @@ -169,5 +159,9 @@ def check_trajectories_with_forward_sensitivities( solver.setAbsoluteToleranceFSA(1e-15) solver.setRelativeToleranceFSA(1e-13) rdata = runAmiciSimulation(amici_model, solver=solver) - np.testing.assert_almost_equal(rdata['x'], result_expected_x, decimal=8) - np.testing.assert_almost_equal(rdata['sx'], result_expected_sx, decimal=8) + _check_close( + rdata["x"], result_expected_x, field="x", rtol=1e-10, atol=1e-12 + ) + _check_close( + rdata["sx"], result_expected_sx, field="sx", rtol=1e-7, atol=1e-9 + ) diff --git a/deps/AMICI/python/tests/valgrind-python.supp b/deps/AMICI/python/tests/valgrind-python.supp index 26499682b..26a9f0e7d 100644 --- a/deps/AMICI/python/tests/valgrind-python.supp +++ b/deps/AMICI/python/tests/valgrind-python.supp @@ -172,7 +172,7 @@ Memcheck:Cond fun:PyUnicode_Decode fun:PyUnicode_FromEncodedObject - obj:/usr/bin/python3.? + ... } { @@ -202,18 +202,106 @@ fun:__Pyx__PyObject_CallOneArg } +{ + other + Memcheck:Value8 + ... + fun:PyBytes_Repr + fun:PyObject_Str + ... + fun:PyObject_Format + ... +} + { other Memcheck:Cond ... fun:PyBytes_Repr fun:PyObject_Str - obj:/usr/bin/python3.? ... fun:PyObject_Format ... } +{ + _Py_HashBytes + Memcheck:Cond + ... + fun:_Py_HashBytes + ... +} + +{ + _Py_HashBytes + Memcheck:Value8 + ... + fun:_Py_HashBytes + ... +} + +{ + PyDict_SetItem + Memcheck:Cond + fun:PyDict_SetItem + ... +} + +{ + PyDict_SetItem + Memcheck:Value8 + ... + fun:PyDict_SetItem + ... +} + +{ + tuplehash + Memcheck:Cond + fun:tuplehash +} + +{ + _PyUnicodeWriter_WriteSubstring + Memcheck:Cond + ... + fun:_PyUnicodeWriter_WriteSubstring + fun:do_markup + ... +} + +{ + _PyUnicodeWriter_WriteSubstring + Memcheck:Value8 + fun:memmove + fun:_PyUnicodeWriter_WriteSubstring + fun:do_markup +} + +{ + sre_match + Memcheck:Cond + ... + fun:sre_ucs1_match + fun:sre_match + ... +} + +{ + sre_ucs1_count + Memcheck:Value8 + ... + fun:sre_ucs1_match + fun:sre_match +} + +{ + PyUnicode_Splitlines + Memcheck:Cond + fun:asciilib_splitlines + fun:PyUnicode_Splitlines +} + { other Memcheck:Value8 @@ -242,6 +330,23 @@ ... } +{ + other + Memcheck:Cond + ... + fun:PyUnicode_Append + ... +} + +{ + other + Memcheck:Value8 + ... + fun:PyUnicode_Append + ... +} + + { other Memcheck:Cond @@ -289,7 +394,7 @@ other Memcheck:Cond fun:memmove - obj:/usr/bin/python3.? + fun:unicode_concatenate ... } @@ -297,7 +402,7 @@ other Memcheck:Value8 fun:memmove - obj:/usr/bin/python3.? + fun:unicode_concatenate ... } @@ -311,6 +416,23 @@ ... } +{ + dict_get_impl + Memcheck:Cond + ... + fun:dict_get_impl + fun:dict_get + ... +} + +{ + lookdict + Memcheck:Value8 + ... + fun:lookdict + ... +} + { other Memcheck:Value8 @@ -325,9 +447,7 @@ other Memcheck:Cond ... - obj:/usr/bin/python3.? fun:PyDict_SetItem - obj:/usr/bin/python3.? ... } @@ -409,17 +529,7 @@ other Memcheck:Leak fun:realloc - obj:* - obj:* - obj:* - fun:call_init.part.0 - fun:call_init - fun:_dl_init - fun:_dl_catch_exception - fun:dl_open_worker - fun:_dl_catch_exception - fun:_dl_open - fun:dlopen_doit + ... fun:_dl_catch_exception } @@ -427,17 +537,7 @@ other Memcheck:Leak fun:malloc - obj:* - obj:* - obj:* - fun:call_init.part.0 - fun:call_init - fun:_dl_init - fun:_dl_catch_exception - fun:dl_open_worker - fun:_dl_catch_exception - fun:_dl_open - fun:dlopen_doit + ... fun:_dl_catch_exception } @@ -486,10 +586,35 @@ } { - PyDict_SetDefault + other + Memcheck:Value8 + ... + fun:PyDict_SetDefault + ... +} + +{ + other + Memcheck:Cond + ... + fun:PyDict_SetDefault + ... +} + +{ + _PyObject_GC_Alloc + Memcheck:Leak + match-leak-kinds: definite + fun:malloc + fun:_PyObject_GC_Alloc + ... +} + +{ + unicode_eq Memcheck:Cond fun:bcmp - obj:/usr/bin/python3.* + fun:unicode_eq ... } @@ -520,3 +645,133 @@ ... obj:/usr/bin/python3.* } + + +{ + Python lookdict + Memcheck:Cond + fun:lookdict + fun:insertdict + fun:warn_explicit.cold + ... +} + +{ + Python find_empty_slot + Memcheck:Value8 + fun:find_empty_slot + fun:insertdict + fun:warn_explicit.cold + ... +} + +{ + Python dictkeys_set_index + Memcheck:Value8 + fun:dictkeys_set_index + fun:insertdict + fun:warn_explicit.cold + ... +} + +{ + Python _copy_characters + Memcheck:Cond + fun:memmove + fun:memcpy + fun:_copy_characters + fun:unicode_concatenate + ... +} + +{ + Python _copy_characters + Memcheck:Value8 + fun:memmove + fun:memcpy + fun:_copy_characters + fun:unicode_concatenate + ... +} + +{ + __libc_unwind_link_get + Memcheck:Leak + match-leak-kinds: definite + fun:malloc + fun:malloc + fun:_dl_find_object_update + fun:dl_open_worker_begin + fun:_dl_catch_exception + fun:dl_open_worker + fun:_dl_catch_exception + fun:_dl_open + fun:do_dlopen + fun:_dl_catch_exception + fun:_dl_catch_error + fun:dlerror_run + fun:__libc_dlopen_mode + fun:__libc_unwind_link_get + fun:__libc_unwind_link_get +} + +{ + dlopen_implementation + Memcheck:Leak + match-leak-kinds: definite + fun:malloc + fun:malloc + fun:_dl_map_object_deps + fun:dl_open_worker_begin + fun:_dl_catch_exception + fun:dl_open_worker + fun:_dl_catch_exception + fun:_dl_open + fun:dlopen_doit + fun:_dl_catch_exception + fun:_dl_catch_error + fun:_dlerror_run + fun:dlopen_implementation + fun:dlopen@@GLIBC_2.34 +} + +{ + dlopen_implementation + Memcheck:Leak + match-leak-kinds: definite + fun:malloc + fun:malloc + fun:resize_scopes + fun:dl_open_worker_begin + fun:_dl_catch_exception + fun:dl_open_worker + fun:_dl_catch_exception + fun:_dl_open + fun:dlopen_doit + fun:_dl_catch_exception + fun:_dl_catch_error + fun:_dlerror_run + fun:dlopen_implementation + fun:dlopen@@GLIBC_2.34 + fun:_PyImport_FindSharedFuncptr +} + +{ + Python dictkeys_get_index + Memcheck:Value8 + fun:dictkeys_get_index +} + +{ + Python os_stat / PyFloat_FromDouble + Memcheck:Leak + match-leak-kinds: definite + fun:malloc + fun:PyFloat_FromDouble + fun:fill_time + fun:_pystat_fromstructstat + fun:posix_do_stat.constprop.0 + fun:os_stat_impl + fun:os_stat + ... +} diff --git a/deps/AMICI/scripts/README.md b/deps/AMICI/scripts/README.md index d656d9d49..f7e75f34b 100644 --- a/deps/AMICI/scripts/README.md +++ b/deps/AMICI/scripts/README.md @@ -1,9 +1,9 @@ # Contents of `scripts/` -This directory contains a number of build, installation, and CI scripts. +This directory contains a number of build, installation, and CI scripts. * `buildAll.sh` - + Build AMICI along with dependencies and test suite * `buildAmici.sh` @@ -12,14 +12,14 @@ This directory contains a number of build, installation, and CI scripts. * `buildBNGL.sh` - Download and build + Download and build [BioNetGen](https://github.com/RuleWorld/bionetgen) (required for some tests) - + * `buildSuiteSparse.sh` Build [SuiteSparse](http://faculty.cse.tamu.edu/davis/suitesparse.html) included in this repository - + * `buildSundials.sh` Build [Sundials](https://computation.llnl.gov/projects/sundials/) @@ -41,14 +41,14 @@ This directory contains a number of build, installation, and CI scripts. * `downloadAndBuildSwig.sh` - Download and build [SWIG](http://www.swig.org/) + Download and build [SWIG](http://www.swig.org/) * `installAmiciArchive.sh` Create a Python virtual environment and do an AMICI development installation * `installAmiciSource.sh` - + Create a Python virtual environment and do a regular AMICI installation * `run-codecov.sh` @@ -78,10 +78,10 @@ This directory contains a number of build, installation, and CI scripts. * `run-SBMLTestsuite.sh` - Download and run the semantic + Download and run the semantic [SBML Test Suite](https://github.com/sbmlteam/sbml-test-suite/) * `run-valgrind.sh` Run memory leak check using valgrind for all unit and integration tests. - Assumes they have been built before in the default location. + Assumes they have been built before in the default location. diff --git a/deps/AMICI/scripts/buildAmici.sh b/deps/AMICI/scripts/buildAmici.sh index 735e05fd3..507a39162 100755 --- a/deps/AMICI/scripts/buildAmici.sh +++ b/deps/AMICI/scripts/buildAmici.sh @@ -13,17 +13,22 @@ amici_build_dir="${amici_path}/build" mkdir -p "${amici_build_dir}" cd "${amici_build_dir}" -if [ "${TRAVIS:-}" = true ] || - [ "${GITHUB_ACTIONS:-}" = true ] || +if [ "${GITHUB_ACTIONS:-}" = true ] || [ "${ENABLE_AMICI_DEBUGGING:-}" = TRUE ]; then # Running on CI server build_type="Debug" + # exceptions instead of terminate() + extra_cxx_flags=";-Dgsl_CONFIG_CONTRACT_VIOLATION_THROWS;-Dgsl_CONFIG_NARROW_THROWS_ON_TRUNCATION=1;-Werror;-Wno-error=deprecated-declarations" else build_type="RelWithDebInfo" + extra_cxx_flags="" fi +# required for build swig interface +pip show numpy > /dev/null || python3 -m pip install numpy + ${cmake} \ - -DCMAKE_CXX_FLAGS="-Wall -Wextra -Werror" \ + -Wdev -DAMICI_CXX_OPTIONS="-Wall;-Wextra${extra_cxx_flags}" \ -DCMAKE_BUILD_TYPE=$build_type \ -DPython3_EXECUTABLE="$(command -v python3)" .. @@ -32,7 +37,7 @@ if [ "${CI_SONARCLOUD:-}" = "TRUE" ]; then build-wrapper-linux-x86-64 \ --out-dir "${amici_path}/bw-output" \ cmake --build . --parallel -elif [ "${TRAVIS:-}" = "true" ]; then +elif [ "${GITHUB_ACTIONS:-}" = "true" ]; then cmake --build . ${make} python-sdist else diff --git a/deps/AMICI/scripts/buildSuiteSparse.sh b/deps/AMICI/scripts/buildSuiteSparse.sh index 1c339d59b..e916530de 100755 --- a/deps/AMICI/scripts/buildSuiteSparse.sh +++ b/deps/AMICI/scripts/buildSuiteSparse.sh @@ -8,7 +8,7 @@ script_path=$(dirname "$BASH_SOURCE") amici_path=$(cd "$script_path/.." && pwd) suitesparse_root="${amici_path}/ThirdParty/SuiteSparse" - -for subdir in SuiteSparse_config BTF AMD CAMD COLAMD KLU - do cd "${suitesparse_root}/${subdir}" && make library +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 done diff --git a/deps/AMICI/scripts/buildSundials.sh b/deps/AMICI/scripts/buildSundials.sh index dfd71e69c..c898a17d0 100755 --- a/deps/AMICI/scripts/buildSundials.sh +++ b/deps/AMICI/scripts/buildSundials.sh @@ -14,7 +14,7 @@ sundials_build_path="${amici_path}/ThirdParty/sundials/build/" cmake=${CMAKE:-cmake} make=${MAKE:-make} -if [[ $TRAVIS = true ]]; then +if [[ $GITHUB_ACTIONS = true ]]; then # Running on CI server build_type="Debug" else @@ -49,7 +49,7 @@ ${cmake} -DCMAKE_INSTALL_PREFIX="${sundials_build_path}" \ -DENABLE_KLU=ON \ -DKLU_LIBRARY_DIR="${suitesparse_root}/lib" \ -DKLU_INCLUDE_DIR="${suitesparse_root}/include" \ - "${SuperLUMT}" \ + ${SuperLUMT} \ .. ${make} diff --git a/deps/AMICI/scripts/compileBLAS.cmd b/deps/AMICI/scripts/compileBLAS.cmd index f0f0d280d..4fe055284 100644 --- a/deps/AMICI/scripts/compileBLAS.cmd +++ b/deps/AMICI/scripts/compileBLAS.cmd @@ -1,13 +1,14 @@ echo compileBLAS.cmd started for openBLAS version %1 cd /D "C:\BLAS\OpenBLAS-%1\OpenBLAS-%1" -call "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvars64.bat" -cmake -G "Ninja" ^ +call "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Auxiliary\Build\vcvars64.bat" +cmake -S . -B build ^ + -G "Ninja" ^ -DBUILD_DOUBLE=1 ^ -DBUILD_SHARED_LIBS=ON ^ - -DCMAKE_INSTALL_PREFIX:PATH="C:\BLAS\OpenBLAS-%1\OpenBLAS-%1\out\install\x64-Release" ^ + -DCMAKE_INSTALL_PREFIX:PATH="C:/BLAS/OpenBLAS" ^ -DCMAKE_C_COMPILER:FILEPATH=cl ^ -DCMAKE_BUILD_TYPE=Release ^ - -DCMAKE_MAKE_PROGRAM=ninja ^ - "C:\BLAS\OpenBLAS-%1\OpenBLAS-%1" -cmake --build "C:\BLAS\OpenBLAS-%1\OpenBLAS-%1" --parallel 2 + -DCMAKE_MAKE_PROGRAM=ninja +cmake --build build --parallel 2 +cmake --install build echo compileBLAS.cmd completed diff --git a/deps/AMICI/scripts/downloadAndBuildDoxygen.sh b/deps/AMICI/scripts/downloadAndBuildDoxygen.sh index 8cae173e6..19d86be5a 100755 --- a/deps/AMICI/scripts/downloadAndBuildDoxygen.sh +++ b/deps/AMICI/scripts/downloadAndBuildDoxygen.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash # Download and build Doxygen (in case apt or homebrew version is buggy again) -set -e +set -euo pipefail SCRIPT_PATH=$(dirname "$BASH_SOURCE") AMICI_PATH=$(cd "$SCRIPT_PATH"/.. && pwd) @@ -8,8 +8,11 @@ AMICI_PATH=$(cd "$SCRIPT_PATH"/.. && pwd) DOXYGEN_DIR="${AMICI_PATH}"/ThirdParty/doxygen cd "${AMICI_PATH}"/ThirdParty if [[ ! -d ${DOXYGEN_DIR} ]]; then - # git clone --depth 1 https://github.com/doxygen/doxygen.git "${DOXYGEN_DIR}" - git clone --single-branch --branch Release_1_9_1 --depth 1 https://github.com/doxygen/doxygen.git "${DOXYGEN_DIR}" + git clone --single-branch \ + --branch Release_1_9_7 \ + --depth 1 \ + -c advice.detachedHead=false \ + https://github.com/doxygen/doxygen.git "${DOXYGEN_DIR}" fi cd "${DOXYGEN_DIR}" diff --git a/deps/AMICI/scripts/downloadAndBuildSwig.sh b/deps/AMICI/scripts/downloadAndBuildSwig.sh index 8b99a2636..5fa0896f6 100755 --- a/deps/AMICI/scripts/downloadAndBuildSwig.sh +++ b/deps/AMICI/scripts/downloadAndBuildSwig.sh @@ -1,13 +1,15 @@ #!/usr/bin/env bash # Download and build SWIG -set -e +# +# Usage: downloadAndBuildSwig.sh [swig_version] +set -euo pipefail SCRIPT_PATH=$(dirname "$BASH_SOURCE") AMICI_PATH=$(cd "$SCRIPT_PATH/.." && pwd) -swig_version=4.0.2 -SWIG_URL="http://prdownloads.sourceforge.net/swig/swig-${swig_version}.tar.gz" +swig_version="${1:-"4.1.1"}" 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}" PREFIX=${AMICI_PATH}/ThirdParty/${SWIG_DIR}/install SWIG_BIN_DIR=${PREFIX}/bin diff --git a/deps/AMICI/scripts/installAmiciSource.sh b/deps/AMICI/scripts/installAmiciSource.sh index 3002b8a5c..4e693468b 100755 --- a/deps/AMICI/scripts/installAmiciSource.sh +++ b/deps/AMICI/scripts/installAmiciSource.sh @@ -27,8 +27,9 @@ else source ${AMICI_PATH}/build/venv/bin/activate fi -pip install --upgrade pip pkgconfig scipy matplotlib coverage pytest pytest-cov -pip install git+https://github.com/pysb/pysb # pin to develop to fix sympy compatibility - -pip install --verbose -e ${AMICI_PATH}/python/sdist[petab] +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 deactivate diff --git a/deps/AMICI/scripts/installOpenBLAS.ps1 b/deps/AMICI/scripts/installOpenBLAS.ps1 index 11f0e931f..9e23237f1 100644 --- a/deps/AMICI/scripts/installOpenBLAS.ps1 +++ b/deps/AMICI/scripts/installOpenBLAS.ps1 @@ -1,5 +1,5 @@ Write-Host 'script installOpenBLAS.ps1 started' -$version = '0.3.12' +$version = '0.3.19' New-Item -Path 'C:\BLAS' -ItemType Directory -Force # create directory # Enforce stronger cryptography [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12 @@ -9,12 +9,10 @@ $webclient = New-Object System.Net.WebClient $webclient.DownloadFile($uri,"$output") Expand-Archive -Path "C:\BLAS\v$version.zip" -DestinationPath "C:\BLAS\OpenBLAS-$version" -Force # expand zip file cmd /c "scripts\compileBLAS.cmd $version" -New-Item -Path 'C:\BLAS\lib' -ItemType Directory -Force # create directory -Copy-Item "C:\BLAS\OpenBLAS-$version\OpenBLAS-$version\lib\Release\openblas.lib" -Destination "C:\BLAS\lib" -Recurse -New-Item -Path 'C:\BLAS\bin' -ItemType Directory -Force # create directory -Copy-Item "C:\BLAS\OpenBLAS-$version\OpenBLAS-$version\lib\openblas.dll" -Destination "C:\BLAS\bin" -Recurse +cmd /c dumpbin /DEPENDENTS "C:\BLAS\OpenBLAS\bin\openblas.dll" Get-ChildItem -Path "C:\BLAS" -Include "openblas.lib" -Recurse # check for file Get-ChildItem -Path "C:\BLAS" -Include "openblas.dll" -Recurse # check for file +Get-ChildItem -Path "C:\BLAS" -Include "cblas.h" -Recurse # check for file Get-Item -Path Env:BLAS_* # check environment variables $VerbosePreference = "SilentlyContinue" # don't display verbose messages Write-Host 'script installOpenBLAS.ps1 completed' diff --git a/deps/AMICI/scripts/run-SBMLTestsuite.sh b/deps/AMICI/scripts/run-SBMLTestsuite.sh index 1767a16df..97c203375 100755 --- a/deps/AMICI/scripts/run-SBMLTestsuite.sh +++ b/deps/AMICI/scripts/run-SBMLTestsuite.sh @@ -14,9 +14,11 @@ pip show pytest-xdist > /dev/null 2>&1 || pip install pytest-xdist pip install coverage pytest-cov if [[ -z "$*" ]]; then - args="1-1780" # run all tests + # run all tests + cases="" else - args="$@" # use user selection + # use user selection + cases="--cases=$@" fi # delete old result directory and recreate @@ -26,5 +28,5 @@ if [[ -d "${RESULT_DIR}" ]]; then fi mkdir "${RESULT_DIR}" -pytest ./tests/testSBMLSuite.py --cases="${args}" -rfsE -n auto \ +pytest ./tests/testSBMLSuite.py $cases -rfsE -n auto \ --cov=amici --cov-report=xml:"coverage_SBMLSuite.xml" --cov-append diff --git a/deps/AMICI/scripts/run-cpp-tests.sh b/deps/AMICI/scripts/run-cpp-tests.sh index 963ef3c51..6afb96eae 100755 --- a/deps/AMICI/scripts/run-cpp-tests.sh +++ b/deps/AMICI/scripts/run-cpp-tests.sh @@ -13,7 +13,7 @@ fi # run tests cd "${AMICI_PATH}/build" -ctest -V +ctest -V --output-on-failure ret=$? if [[ $ret != 0 ]]; then exit $ret; fi mv "${AMICI_PATH}/tests/cpp/writeResults.h5" \ diff --git a/deps/AMICI/scripts/run-cppcheck.sh b/deps/AMICI/scripts/run-cppcheck.sh index 57c669439..5726bf8e3 100755 --- a/deps/AMICI/scripts/run-cppcheck.sh +++ b/deps/AMICI/scripts/run-cppcheck.sh @@ -15,4 +15,3 @@ cppcheck \ "-I${AMICI_PATH}/include/" \ --enable=style \ "--exitcode-suppressions=${AMICI_PATH}/.cppcheck-exitcode-suppressions" - diff --git a/deps/AMICI/scripts/run-doxygen.sh b/deps/AMICI/scripts/run-doxygen.sh index 030c8fef7..c766d93ee 100755 --- a/deps/AMICI/scripts/run-doxygen.sh +++ b/deps/AMICI/scripts/run-doxygen.sh @@ -60,6 +60,11 @@ mv "${DOXY_WARNING_FILE}_tmp" "${DOXY_WARNING_FILE}" grep -v "error: Problem.* running g.*. Check your installation!" "${DOXY_WARNING_FILE}" > "${DOXY_WARNING_FILE}_tmp" || [[ $? == 1 ]] mv "${DOXY_WARNING_FILE}_tmp" "${DOXY_WARNING_FILE}" +# unclear error parsing ghostscript-generated eps files +# result seems fine despite error +grep -v "Couldn't extract bounding box from" "${DOXY_WARNING_FILE}" > "${DOXY_WARNING_FILE}_tmp" || [[ $? == 1 ]] +mv "${DOXY_WARNING_FILE}_tmp" "${DOXY_WARNING_FILE}" + # check if warnings log was created if [ -f "${DOXY_WARNING_FILE}" ]; then # check if warnings log is empty diff --git a/deps/AMICI/scripts/run-python-tests.sh b/deps/AMICI/scripts/run-python-tests.sh index e0b33eb47..982aa02f0 100755 --- a/deps/AMICI/scripts/run-python-tests.sh +++ b/deps/AMICI/scripts/run-python-tests.sh @@ -15,4 +15,4 @@ source "${amici_path}"/build/venv/bin/activate pip install scipy h5py pytest pytest-cov # PEtab tests are run separately -pytest --ignore-glob=*petab* +pytest --ignore-glob=*petab* --ignore-glob=*test_splines.py diff --git a/deps/AMICI/scripts/run-sphinx.sh b/deps/AMICI/scripts/run-sphinx.sh index 0619c54c5..e7b1ce086 100755 --- a/deps/AMICI/scripts/run-sphinx.sh +++ b/deps/AMICI/scripts/run-sphinx.sh @@ -6,9 +6,9 @@ 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 -python -m pip install git+https://github.com/readthedocs/readthedocs-sphinx-ext -python -m pip install --exists-action=w --no-cache-dir -r ${AMICI_PATH}/documentation/rtd_requirements.txt +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 diff --git a/deps/AMICI/scripts/run-valgrind-cpp.sh b/deps/AMICI/scripts/run-valgrind-cpp.sh index 7d868d98e..fd08cbda5 100755 --- a/deps/AMICI/scripts/run-valgrind-cpp.sh +++ b/deps/AMICI/scripts/run-valgrind-cpp.sh @@ -8,12 +8,6 @@ AMICI_PATH=$(cd "$SCRIPT_PATH/.." && pwd) set -eou pipefail # run tests -cd "${AMICI_PATH}/build/tests/cpp/" - +cd "${AMICI_PATH}/build/" VALGRIND_OPTS="--leak-check=full --error-exitcode=1 --trace-children=yes --show-leak-kinds=definite" -set -x -for MODEL in $(ctest -N | grep "Test[ ]*#" | grep -v unittests | sed --regexp-extended 's/ *Test[ ]*#[0-9]+: model_(.*)_test/\1/') - do cd "${AMICI_PATH}/build/tests/cpp/${MODEL}/" && valgrind ${VALGRIND_OPTS} "./model_${MODEL}_test" -done -cd "${AMICI_PATH}/build/tests/cpp/unittests/" -valgrind ${VALGRIND_OPTS} ./unittests +valgrind ${VALGRIND_OPTS} ctest diff --git a/deps/AMICI/scripts/run-valgrind-py.sh b/deps/AMICI/scripts/run-valgrind-py.sh index 241a6bd23..510e27868 100755 --- a/deps/AMICI/scripts/run-valgrind-py.sh +++ b/deps/AMICI/scripts/run-valgrind-py.sh @@ -11,8 +11,21 @@ if [[ -z "${BNGPATH}" ]]; then fi cd "${amici_path}"/python/tests + source "${amici_path}"/build/venv/bin/activate -pip install scipy h5py pytest -# PEtab tests are run separately -PYTHONMALLOC=malloc valgrind --suppressions=valgrind-python.supp --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* +pip install scipy h5py pytest pytest-rerunfailures + +PYTHONMALLOC=malloc valgrind \ + --suppressions=valgrind-python.supp \ + --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. diff --git a/deps/AMICI/scripts/runNotebook.sh b/deps/AMICI/scripts/runNotebook.sh index 437fb7217..bf1ef8d5e 100755 --- a/deps/AMICI/scripts/runNotebook.sh +++ b/deps/AMICI/scripts/runNotebook.sh @@ -10,7 +10,7 @@ AMICI_PATH=$(cd $SCRIPT_PATH/.. && pwd) runNotebook () { set +e tempfile=$(mktemp) - jupyter nbconvert --debug --stdout --execute --ExecutePreprocessor.timeout=300 --to markdown $@ &> $tempfile + jupyter nbconvert --debug --stdout --execute --ExecutePreprocessor.timeout=600 --to markdown $@ &> $tempfile ret=$? if [[ $ret != 0 ]]; then cat $tempfile @@ -26,7 +26,8 @@ if [ $# -eq 0 ]; then fi source ${AMICI_PATH}/build/venv/bin/activate -pip3 show ipython || (pip3 install --upgrade jupyter jupyter_contrib_nbextensions && python3 -m ipykernel install --user --name amici --display-name "Python (amici)") +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)") for arg in "$@"; do if [ -d $arg ]; then diff --git a/deps/AMICI/sonar-project.properties b/deps/AMICI/sonar-project.properties index f376ff3bd..4ded52891 100644 --- a/deps/AMICI/sonar-project.properties +++ b/deps/AMICI/sonar-project.properties @@ -4,7 +4,6 @@ # https://sonarcloud.io/documentation/analysis/languages/cfamily/ sonar.host.url=https://sonarcloud.io -sonar.login=af35cb17710485d21c8e453a77f1f008eae1f7a4 sonar.organization=icb-dcm sonar.projectKey=ICB-DCM_AMICI @@ -27,8 +26,7 @@ sonar.sourceEncoding=UTF-8 sonar.cfamily.threads=2 sonar.cfamily.gcov.reportsPath=build -sonar.cfamily.cache.enabled=true -sonar.cfamily.cache.path=sonar_cache -sonar.cpp.std=c++14 +sonar.cpp.std=c++17 sonar.python.coverage.reportPaths=build/coverage_py.xml +sonar.python.version=3.9, 3.10, 3.11 diff --git a/deps/AMICI/src/CMakeLists.template.cmake b/deps/AMICI/src/CMakeLists.template.cmake index 6aa13400b..43df61ff6 100644 --- a/deps/AMICI/src/CMakeLists.template.cmake +++ b/deps/AMICI/src/CMakeLists.template.cmake @@ -1,92 +1,102 @@ -cmake_minimum_required(VERSION 3.3) - -if(POLICY CMP0060) - cmake_policy(SET CMP0060 NEW) -endif(POLICY CMP0060) -if(POLICY CMP0065) - cmake_policy(SET CMP0065 NEW) -endif(POLICY CMP0065) -if(POLICY CMP0074) - # Use package_ROOT environment variables - cmake_policy(SET CMP0074 NEW) -endif(POLICY CMP0074) +# 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(TPL_MODELNAME) -set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_POSITION_INDEPENDENT_CODE ON) include(CheckCXXCompilerFlag) set(MY_CXX_FLAGS -Wall -Wno-unused-function -Wno-unused-variable) -if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - list(APPEND MY_CXX_FLAGS -Wno-unused-but-set-variable) +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + list(APPEND MY_CXX_FLAGS -Wno-unused-but-set-variable) +endif() +foreach(flag ${MY_CXX_FLAGS}) + unset(CUR_FLAG_SUPPORTED CACHE) + check_cxx_compiler_flag(-Werror ${flag} CUR_FLAG_SUPPORTED) + if(${CUR_FLAG_SUPPORTED}) + string(APPEND CMAKE_CXX_FLAGS " ${flag}") + endif() +endforeach() + +if(DEFINED ENV{AMICI_CXXFLAGS}) + message(STATUS "Appending flags from AMICI_CXXFLAGS: $ENV{AMICI_CXXFLAGS}") + add_compile_options("$ENV{AMICI_CXXFLAGS}") +endif() +if(DEFINED ENV{AMICI_LDFLAGS}) + message(STATUS "Appending flags from AMICI_LDFLAGS: $ENV{AMICI_LDFLAGS}") + link_libraries("$ENV{AMICI_LDFLAGS}") endif() -foreach(FLAG ${MY_CXX_FLAGS}) - unset(CUR_FLAG_SUPPORTED CACHE) - CHECK_CXX_COMPILER_FLAG(-Werror ${FLAG} CUR_FLAG_SUPPORTED) - if(${CUR_FLAG_SUPPORTED}) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FLAG}") - endif() -endforeach(FLAG) - -find_package(Amici TPL_AMICI_VERSION HINTS ${CMAKE_CURRENT_LIST_DIR}/../../build) + +find_package(Amici TPL_AMICI_VERSION REQUIRED HINTS + ${CMAKE_CURRENT_LIST_DIR}/../../build) message(STATUS "Found AMICI ${Amici_DIR}") set(MODEL_DIR ${CMAKE_CURRENT_LIST_DIR}) -set(SRC_LIST_LIB TPL_SOURCES -${MODEL_DIR}/wrapfunctions.cpp -) +set(SRC_LIST_LIB TPL_SOURCES ${MODEL_DIR}/wrapfunctions.cpp) add_library(${PROJECT_NAME} ${SRC_LIST_LIB}) add_library(model ALIAS ${PROJECT_NAME}) -# This option can be helpful when using the Intel compiler and compilation of -# wrapfunctions.cpp fails due to insufficient memory. -option(ENABLE_WRAPFUNCTIONS_OPTIMIZATIONS "Enable compiler optimizations for wrapfunctions.cpp?" ON) -if(NOT ENABLE_WRAPFUNCTIONS_OPTIMIZATIONS) - set_source_files_properties(wrapfunctions.cpp PROPERTIES COMPILE_FLAGS -O0) -endif() +# Some special functions require boost +# +# TODO: set some flag during code generation whether the given model requires +# boost. for now, try to find it, add include directories and link against it. +# let the compiler/linker error if it is required but not found +find_package(Boost) target_include_directories(${PROJECT_NAME} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") -target_link_libraries(${PROJECT_NAME} - PUBLIC Upstream::amici -) +target_link_libraries( + ${PROJECT_NAME} + PUBLIC Upstream::amici + PRIVATE $<$:Boost::boost>) -set(SRC_LIST_EXE main.cpp) - -add_executable(simulate_${PROJECT_NAME} ${SRC_LIST_EXE}) +if(NOT "${AMICI_PYTHON_BUILD_EXT_ONLY}") + set(SRC_LIST_EXE main.cpp) + add_executable(simulate_${PROJECT_NAME} ${SRC_LIST_EXE}) + target_link_libraries(simulate_${PROJECT_NAME} ${PROJECT_NAME}) +endif() -target_link_libraries(simulate_${PROJECT_NAME} ${PROJECT_NAME}) +# 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}) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 --coverage") + string(APPEND CMAKE_CXX_FLAGS_DEBUG " --coverage") + string(APPEND CMAKE_EXE_LINKER_FLAGS_DEBUG " --coverage") endif() -## SWIG +# SWIG option(ENABLE_SWIG "Build swig/python library?" ON) if(ENABLE_SWIG) - if(NOT(${CMAKE_VERSION} VERSION_LESS 3.8)) - add_subdirectory(swig) - else() - message(WARNING "Unable to build SWIG interface, upgrade CMake to >=3.8.") - endif() + add_subdirectory(swig) endif() - # include(GNUInstallDirs) -install(TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME}Targets - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} - PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} -) -export(EXPORT ${PROJECT_NAME}Targets FILE ${PROJECT_NAME}Config.cmake - NAMESPACE Upstream:: - ) +install( + TARGETS ${PROJECT_NAME} + EXPORT ${PROJECT_NAME}Targets + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + INCLUDES + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +export( + EXPORT ${PROJECT_NAME}Targets + FILE ${PROJECT_NAME}Config.cmake + NAMESPACE Upstream::) # - diff --git a/deps/AMICI/src/abstract_model.cpp b/deps/AMICI/src/abstract_model.cpp index f866eb7db..dc2c46917 100644 --- a/deps/AMICI/src/abstract_model.cpp +++ b/deps/AMICI/src/abstract_model.cpp @@ -2,629 +2,672 @@ namespace amici { -std::string -AbstractModel::getAmiciVersion() const -{ +std::string AbstractModel::getAmiciVersion() const { throw AmiException("Version not set during code generation"); } -std::string -AbstractModel::getAmiciCommit() const -{ +std::string AbstractModel::getAmiciCommit() const { throw AmiException("Commit not set during code generation"); } -void -AbstractModel::fx0(realtype* /*x0*/, - const realtype /*t*/, - const realtype* /*p*/, - const realtype* /*k*/) -{ - throw AmiException("Requested functionality is not supported as %s is " - "not implemented for this model!", - __func__); +void AbstractModel:: + fx0(realtype* /*x0*/, const realtype /*t*/, realtype const* /*p*/, + realtype const* /*k*/) { + throw AmiException( + "Requested functionality is not supported as %s is " + "not implemented for this model!", + __func__ + ); } -bool -AbstractModel::isFixedParameterStateReinitializationAllowed() const -{ +bool AbstractModel::isFixedParameterStateReinitializationAllowed() const { return false; } -void -AbstractModel::fx0_fixedParameters(realtype* /*x0*/, - const realtype /*t*/, - const realtype* /*p*/, - const realtype* /*k*/, - gsl::span /*reinitialization_state_idxs*/) -{ +void AbstractModel::fx0_fixedParameters( + realtype* /*x0*/, const realtype /*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*/, - const realtype* /*x0*/, - const realtype* /*p*/, - const realtype* /*k*/, - const int /*ip*/, - gsl::span /*reinitialization_state_idxs*/) -{ +void AbstractModel::fsx0_fixedParameters( + realtype* /*sx0*/, const realtype /*t*/, realtype const* /*x0*/, + realtype const* /*p*/, realtype const* /*k*/, int const /*ip*/, + gsl::span /*reinitialization_state_idxs*/ +) { // no-op default implementation } -void -AbstractModel::fsx0(realtype* /*sx0*/, - const realtype /*t*/, - const realtype* /*x0*/, - const realtype* /*p*/, - const realtype* /*k*/, - const int /*ip*/) -{ - throw AmiException("Requested functionality is not supported as %s is " - "not implemented for this model!", - __func__); +void AbstractModel::fsx0( + realtype* /*sx0*/, const realtype /*t*/, realtype const* /*x0*/, + realtype const* /*p*/, realtype const* /*k*/, int const /*ip*/ +) { + throw AmiException( + "Requested functionality is not supported as %s is " + "not implemented for this model!", + __func__ + ); } -void -AbstractModel::fdx0(AmiVector& /*x0*/, AmiVector& /*dx0*/) -{ +void AbstractModel::fdx0(AmiVector& /*x0*/, AmiVector& /*dx0*/) { // no-op default implementation } -void -AbstractModel::fstau(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*/) -{ - throw AmiException("Requested functionality is not supported as %s is " - "not implemented for this model!", - __func__); -} - -void -AbstractModel::fy(realtype* /*y*/, - const realtype /*t*/, - const realtype* /*x*/, - const realtype* /*p*/, - const realtype* /*k*/, - const realtype* /*h*/, - const realtype* /*w*/) -{ - throw AmiException("Requested functionality is not supported as %s is " - "not implemented for this model!", - __func__); -} - -void -AbstractModel::fdydp(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* /*dwdp*/) -{ - throw AmiException("Requested functionality is not supported as %s is " - "not implemented for this model!", - __func__); -} - -void -AbstractModel::fdydx(realtype* /*dydx*/, - const realtype /*t*/, - const realtype* /*x*/, - const realtype* /*p*/, - const realtype* /*k*/, - const realtype* /*h*/, - const realtype* /*w*/, - const realtype* /*dwdx*/) -{ - throw AmiException("Requested functionality is not supported as %s is " - "not implemented for this model!", - __func__); -} - -void -AbstractModel::fz(realtype* /*z*/, - const int /*ie*/, - const realtype /*t*/, - const realtype* /*x*/, - const realtype* /*p*/, - const realtype* /*k*/, - const realtype* /*h*/) -{ - throw AmiException("Requested functionality is not supported as %s is " - "not implemented for this model!", - __func__); -} - -void -AbstractModel::fsz(realtype* /*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*/) -{ - throw AmiException("Requested functionality is not supported as %s is " - "not implemented for this model!", - __func__); -} - -void -AbstractModel::frz(realtype* /*rz*/, - const int /*ie*/, - const realtype /*t*/, - const realtype* /*x*/, - const realtype* /*p*/, - const realtype* /*k*/, - const realtype* /*h*/) -{ - throw AmiException("Requested functionality is not supported as %s is " - "not implemented for this model!", - __func__); -} - -void -AbstractModel::fsrz(realtype* /*srz*/, - const int /*ie*/, - const realtype /*t*/, - const realtype* /*x*/, - const realtype* /*p*/, - const realtype* /*k*/, - const realtype* /*h*/, - const realtype* /*sx*/, - const int /*ip*/) -{ - throw AmiException("Requested functionality is not supported as %s is " - "not implemented for this model!", - __func__); -} - -void -AbstractModel::fdzdp(realtype* /*dzdp*/, - const int /*ie*/, - const realtype /*t*/, - const realtype* /*x*/, - const realtype* /*p*/, - const realtype* /*k*/, - const realtype* /*h*/, - const int /*ip*/) -{ - throw AmiException("Requested functionality is not supported as %s is " - "not implemented for this model!", - __func__); -} - -void -AbstractModel::fdzdx(realtype* /*dzdx*/, - const int /*ie*/, - const realtype /*t*/, - const realtype* /*x*/, - const realtype* /*p*/, - const realtype* /*k*/, - const realtype* /*h*/) -{ - throw AmiException("Requested functionality is not supported as %s is " - "not implemented for this model!", - __func__); -} - -void -AbstractModel::fdrzdp(realtype* /*drzdp*/, - const int /*ie*/, - const realtype /*t*/, - const realtype* /*x*/, - const realtype* /*p*/, - const realtype* /*k*/, - const realtype* /*h*/, - const int /*ip*/) -{ - throw AmiException("Requested functionality is not supported as %s is " - "not implemented for this model!", - __func__); -} - -void -AbstractModel::fdrzdx(realtype* /*drzdx*/, - const int /*ie*/, - const realtype /*t*/, - const realtype* /*x*/, - const realtype* /*p*/, - const realtype* /*k*/, - const realtype* /*h*/) -{ - throw AmiException("Requested functionality is not supported as %s is " - "not implemented for this model!", - __func__); -} - -void -AbstractModel::fdeltax(realtype* /*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*/) -{ - throw AmiException("Requested functionality is not supported as %s is " - "not implemented for this model!", - __func__); -} - -void -AbstractModel::fdeltasx(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*/) -{ - throw AmiException("Requested functionality is not supported as %s is " - "not implemented for this model!", - __func__); -} - -void -AbstractModel::fdeltaxB(realtype* /*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*/) -{ - throw AmiException("Requested functionality is not supported as %s is " - "not implemented for this model!", - __func__); -} - -void -AbstractModel::fdeltaqB(realtype* /*deltaqB*/, - const realtype /*t*/, - const realtype* /*x*/, - const realtype* /*p*/, - const realtype* /*k*/, - const realtype* /*h*/, - const int /*ip*/, - const int /*ie*/, - const realtype* /*xdot*/, - const realtype* /*xdot_old*/, - const realtype* /*xB*/) -{ - throw AmiException("Requested functionality is not supported as %s is " - "not implemented for this model!", - __func__); -} - -void -AbstractModel::fsigmay(realtype* /*sigmay*/, - const realtype /*t*/, - const realtype* /*p*/, - const realtype* /*k*/) -{ - throw AmiException("Requested functionality is not supported as %s is " - "not implemented for this model!", - __func__); -} - -void -AbstractModel::fdsigmaydp(realtype* /*dsigmaydp*/, - const realtype /*t*/, - const realtype* /*p*/, - const realtype* /*k*/, - const int /*ip*/) -{ - throw AmiException("Requested functionality is not supported as %s is " - "not implemented for this model!", - __func__); -} - -void -AbstractModel::fsigmaz(realtype* /*sigmaz*/, - const realtype /*t*/, - const realtype* /*p*/, - const realtype* /*k*/) -{ - throw AmiException("Requested functionality is not supported as %s is " - "not implemented for this model!", - __func__); -} - -void -AbstractModel::fdsigmazdp(realtype* /*dsigmazdp*/, - const realtype /*t*/, - const realtype* /*p*/, - const realtype* /*k*/, - const int /*ip*/) -{ - throw AmiException("Requested functionality is not supported as %s is " - "not implemented for this model!", - __func__); -} - -void -AbstractModel::fJy(realtype* /*nllh*/, - const int /*iy*/, - const realtype* /*p*/, - const realtype* /*k*/, - const realtype* /*y*/, - const realtype* /*sigmay*/, - const realtype* /*my*/) -{ - throw AmiException("Requested functionality is not supported as %s is " - "not implemented for this model!", - __func__); -} - -void -AbstractModel::fJz(realtype* /*nllh*/, - const int /*iz*/, - const realtype* /*p*/, - const realtype* /*k*/, - const realtype* /*z*/, - const realtype* /*sigmaz*/, - const realtype* /*mz*/) -{ - throw AmiException("Requested functionality is not supported as %s is " - "not implemented for this model!", - __func__); -} - -void -AbstractModel::fJrz(realtype* /*nllh*/, - const int /*iz*/, - const realtype* /*p*/, - const realtype* /*k*/, - const realtype* /*z*/, - const realtype* /*sigmaz*/) -{ - throw AmiException("Requested functionality is not supported as %s is " - "not implemented for this model!", - __func__); -} - -void -AbstractModel::fdJydy(realtype* /*dJydy*/, - const int /*iy*/, - const realtype* /*p*/, - const realtype* /*k*/, - const realtype* /*y*/, - const realtype* /*sigmay*/, - const realtype* /*my*/) -{ - throw AmiException("Requested functionality is not supported as %s is " - "not implemented for this model!", - __func__); -} - -void -AbstractModel::fdJydy_colptrs(SUNMatrixWrapper &/*indexptrs*/, - int /*index*/) { - throw AmiException("Requested functionality is not supported as %s is " - "not implemented for this model!", - __func__); -} - -void -AbstractModel::fdJydy_rowvals(SUNMatrixWrapper & /*indexptrs*/, - int /*index*/) { - throw AmiException("Requested functionality is not supported as %s is " - "not implemented for this model!", - __func__); -} - -void -AbstractModel::fdJydsigma(realtype* /*dJydsigma*/, - const int /*iy*/, - const realtype* /*p*/, - const realtype* /*k*/, - const realtype* /*y*/, - const realtype* /*sigmay*/, - const realtype* /*my*/) -{ - throw AmiException("Requested functionality is not supported as %s is " - "not implemented for this model!", - __func__); -} - -void -AbstractModel::fdJzdz(realtype* /*dJzdz*/, - const int /*iz*/, - const realtype* /*p*/, - const realtype* /*k*/, - const realtype* /*z*/, - const realtype* /*sigmaz*/, - const realtype* /*mz*/) -{ - throw AmiException("Requested functionality is not supported as %s is " - "not implemented for this model!", - __func__); -} - -void -AbstractModel::fdJzdsigma(realtype* /*dJzdsigma*/, - const int /*iz*/, - const realtype* /*p*/, - const realtype* /*k*/, - const realtype* /*z*/, - const realtype* /*sigmaz*/, - const realtype* /*mz*/) -{ - throw AmiException("Requested functionality is not supported as %s is " - "not implemented for this model!", - __func__); -} - -void -AbstractModel::fdJrzdz(realtype* /*dJrzdz*/, - const int /*iz*/, - const realtype* /*p*/, - const realtype* /*k*/, - const realtype* /*rz*/, - const realtype* /*sigmaz*/) -{ - throw AmiException("Requested functionality is not supported as %s is " - "not implemented for this model!", - __func__); -} - -void -AbstractModel::fdJrzdsigma(realtype* /*dJrzdsigma*/, - const int /*iz*/, - const realtype* /*p*/, - const realtype* /*k*/, - const realtype* /*rz*/, - const realtype* /*sigmaz*/) -{ - throw AmiException("Requested functionality is not supported as %s is " - "not implemented for this model!", - __func__); -} - -void -AbstractModel::fw(realtype* /*w*/, - const realtype /*t*/, - const realtype* /*x*/, - const realtype* /*p*/, - const realtype* /*k*/, - const realtype* /*h*/, - const realtype* /*tcl*/) -{ - throw AmiException("Requested functionality is not supported as %s is " - "not implemented for this model!", - __func__); -} - -void -AbstractModel::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*/) -{ - throw AmiException("Requested functionality is not supported as %s is " - "not implemented for this model!", - __func__); -} - -void -AbstractModel::fdwdp_colptrs(SUNMatrixWrapper &/*dwdp*/) -{ - throw AmiException("Requested functionality is not supported as %s is " - "not implemented for this model!", - __func__); -} - -void -AbstractModel::fdwdp_rowvals(SUNMatrixWrapper &/*dwdp*/) -{ - throw AmiException("Requested functionality is not supported as %s is " - "not implemented for this model!", - __func__); -} - -void -AbstractModel::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 int /*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*/, - const realtype* /*x*/, - const realtype* /*p*/, - const realtype* /*k*/, - const realtype* /*h*/, - const realtype* /*w*/, - const realtype* /*tcl*/) -{ - throw AmiException("Requested functionality is not supported as %s is " - "not implemented for this model!", - __func__); -} - -void -AbstractModel::fdwdx_colptrs(SUNMatrixWrapper &/*dwdx*/) -{ - throw AmiException("Requested functionality is not supported as %s is " - "not implemented for this model!", - __func__); -} - -void -AbstractModel::fdwdx_rowvals(SUNMatrixWrapper &/*dwdx*/) -{ - throw AmiException("Requested functionality is not supported as %s is " - "not implemented for this model!", - __func__); -} - -void -AbstractModel::fdwdw(realtype */*dwdw*/, - realtype /*t*/, - const realtype */*x*/, - const realtype */*p*/, - const realtype */*k*/, - const realtype */*h*/, - const realtype */*w*/, - const realtype */*tcl*/) { - throw AmiException("Requested functionality is not supported as %s " - "is not implemented for this model!", - __func__); -} - -void AbstractModel::fdwdw_colptrs(SUNMatrixWrapper &/*dwdw*/) { - throw AmiException("Requested functionality is not supported as %s " - "is not implemented for this model!", - __func__); -} - -void AbstractModel::fdwdw_rowvals(SUNMatrixWrapper &/*dwdw*/) { - throw AmiException("Requested functionality is not supported as %s " - "is not implemented for this model!", - __func__); +void AbstractModel::fstau( + realtype* /*stau*/, const realtype /*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*/ +) { + throw AmiException( + "Requested functionality is not supported as %s is " + "not implemented for this model!", + __func__ + ); +} + +void AbstractModel:: + fy(realtype* /*y*/, const realtype /*t*/, realtype const* /*x*/, + realtype const* /*p*/, realtype const* /*k*/, realtype const* /*h*/, + realtype const* /*w*/) { + throw AmiException( + "Requested functionality is not supported as %s is " + "not implemented for this model!", + __func__ + ); +} + +void AbstractModel::fdydp( + realtype* /*dydp*/, const realtype /*t*/, realtype const* /*x*/, + realtype const* /*p*/, realtype const* /*k*/, realtype const* /*h*/, + int const /*ip*/, realtype const* /*w*/, realtype const* /*dwdp*/ +) { + throw AmiException( + "Requested functionality is not supported as %s is " + "not implemented for this model!", + __func__ + ); +} + +void AbstractModel::fdydp( + realtype* /*dydp*/, const realtype /*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*/ +) { + throw AmiException( + "Requested functionality is not supported as %s is " + "not implemented for this model!", + __func__ + ); +} + +void AbstractModel::fdydx( + realtype* /*dydx*/, const realtype /*t*/, realtype const* /*x*/, + realtype const* /*p*/, realtype const* /*k*/, realtype const* /*h*/, + realtype const* /*w*/, realtype const* /*dwdx*/ +) { + throw AmiException( + "Requested functionality is not supported as %s is " + "not implemented for this model!", + __func__ + ); +} + +void AbstractModel:: + fz(realtype* /*z*/, int const /*ie*/, const realtype /*t*/, + realtype const* /*x*/, realtype const* /*p*/, realtype const* /*k*/, + realtype const* /*h*/) { + throw AmiException( + "Requested functionality is not supported as %s is " + "not implemented for this model!", + __func__ + ); +} + +void AbstractModel:: + fsz(realtype* /*sz*/, int const /*ie*/, const realtype /*t*/, + realtype const* /*x*/, realtype const* /*p*/, realtype const* /*k*/, + realtype const* /*h*/, realtype const* /*sx*/, int const /*ip*/) { + throw AmiException( + "Requested functionality is not supported as %s is " + "not implemented for this model!", + __func__ + ); +} + +void AbstractModel:: + frz(realtype* /*rz*/, int const /*ie*/, const realtype /*t*/, + realtype const* /*x*/, realtype const* /*p*/, realtype const* /*k*/, + realtype const* /*h*/) { + throw AmiException( + "Requested functionality is not supported as %s is " + "not implemented for this model!", + __func__ + ); +} + +void AbstractModel::fsrz( + realtype* /*srz*/, int const /*ie*/, const realtype /*t*/, + realtype const* /*x*/, realtype const* /*p*/, realtype const* /*k*/, + realtype const* /*h*/, realtype const* /*sx*/, int const /*ip*/ +) { + throw AmiException( + "Requested functionality is not supported as %s is " + "not implemented for this model!", + __func__ + ); +} + +void AbstractModel::fdzdp( + realtype* /*dzdp*/, int const /*ie*/, const realtype /*t*/, + realtype const* /*x*/, realtype const* /*p*/, realtype const* /*k*/, + realtype const* /*h*/, int const /*ip*/ +) { + throw AmiException( + "Requested functionality is not supported as %s is " + "not implemented for this model!", + __func__ + ); +} + +void AbstractModel::fdzdx( + realtype* /*dzdx*/, int const /*ie*/, const realtype /*t*/, + realtype const* /*x*/, realtype const* /*p*/, realtype const* /*k*/, + realtype const* /*h*/ +) { + throw AmiException( + "Requested functionality is not supported as %s is " + "not implemented for this model!", + __func__ + ); +} + +void AbstractModel::fdrzdp( + realtype* /*drzdp*/, int const /*ie*/, const realtype /*t*/, + realtype const* /*x*/, realtype const* /*p*/, realtype const* /*k*/, + realtype const* /*h*/, int const /*ip*/ +) { + throw AmiException( + "Requested functionality is not supported as %s is " + "not implemented for this model!", + __func__ + ); +} + +void AbstractModel::fdrzdx( + realtype* /*drzdx*/, int const /*ie*/, const realtype /*t*/, + realtype const* /*x*/, realtype const* /*p*/, realtype const* /*k*/, + realtype const* /*h*/ +) { + throw AmiException( + "Requested functionality is not supported as %s is " + "not implemented for this model!", + __func__ + ); +} + +void AbstractModel::fdeltax( + realtype* /*deltax*/, const realtype /*t*/, realtype const* /*x*/, + realtype const* /*p*/, realtype const* /*k*/, realtype const* /*h*/, + int const /*ie*/, realtype const* /*xdot*/, realtype const* /*xdot_old*/ +) { + throw AmiException( + "Requested functionality is not supported as %s is " + "not implemented for this model!", + __func__ + ); +} + +void AbstractModel::fdeltasx( + realtype* /*deltasx*/, const realtype /*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*/, + realtype const* /*sx*/, realtype const* /*stau*/, realtype const* /*tcl*/ +) { + throw AmiException( + "Requested functionality is not supported as %s is " + "not implemented for this model!", + __func__ + ); +} + +void AbstractModel::fdeltaxB( + realtype* /*deltaxB*/, const realtype /*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*/ +) { + throw AmiException( + "Requested functionality is not supported as %s is " + "not implemented for this model!", + __func__ + ); +} + +void AbstractModel::fdeltaqB( + realtype* /*deltaqB*/, const realtype /*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*/ +) { + throw AmiException( + "Requested functionality is not supported as %s is " + "not implemented for this model!", + __func__ + ); +} + +void AbstractModel::fsigmay( + realtype* /*sigmay*/, const realtype /*t*/, realtype const* /*p*/, + realtype const* /*k*/, realtype const* /*y*/ +) { + throw AmiException( + "Requested functionality is not supported as %s is " + "not implemented for this model!", + __func__ + ); +} + +void AbstractModel::fdsigmaydp( + realtype* /*dsigmaydp*/, const realtype /*t*/, realtype const* /*p*/, + realtype const* /*k*/, realtype const* /*y*/, int const /*ip*/ +) { + throw AmiException( + "Requested functionality is not supported as %s is " + "not implemented for this model!", + __func__ + ); +} + +void AbstractModel::fdsigmaydy( + realtype* /*dsigmaydy*/, const realtype /*t*/, realtype const* /*p*/, + realtype const* /*k*/, realtype const* /*y*/ +) { + throw AmiException( + "Requested functionality is not supported as %s is " + "not implemented for this model!", + __func__ + ); +} + +void AbstractModel::fsigmaz( + realtype* /*sigmaz*/, const realtype /*t*/, realtype const* /*p*/, + realtype const* /*k*/ +) { + throw AmiException( + "Requested functionality is not supported as %s is " + "not implemented for this model!", + __func__ + ); +} + +void AbstractModel::fdsigmazdp( + realtype* /*dsigmazdp*/, const realtype /*t*/, realtype const* /*p*/, + realtype const* /*k*/, int const /*ip*/ +) { + throw AmiException( + "Requested functionality is not supported as %s is " + "not implemented for this model!", + __func__ + ); +} + +void AbstractModel:: + fJy(realtype* /*nllh*/, int const /*iy*/, realtype const* /*p*/, + realtype const* /*k*/, realtype const* /*y*/, + realtype const* /*sigmay*/, realtype const* /*my*/) { + throw AmiException( + "Requested functionality is not supported as %s is " + "not implemented for this model!", + __func__ + ); +} + +void AbstractModel:: + fJz(realtype* /*nllh*/, int const /*iz*/, realtype const* /*p*/, + realtype const* /*k*/, realtype const* /*z*/, + realtype const* /*sigmaz*/, realtype const* /*mz*/) { + throw AmiException( + "Requested functionality is not supported as %s is " + "not implemented for this model!", + __func__ + ); +} + +void AbstractModel::fJrz( + realtype* /*nllh*/, int const /*iz*/, realtype const* /*p*/, + realtype const* /*k*/, realtype const* /*z*/, realtype const* /*sigmaz*/ +) { + throw AmiException( + "Requested functionality is not supported as %s is " + "not implemented for this model!", + __func__ + ); +} + +void AbstractModel::fdJydy( + realtype* /*dJydy*/, int const /*iy*/, realtype const* /*p*/, + realtype const* /*k*/, realtype const* /*y*/, realtype const* /*sigmay*/, + realtype const* /*my*/ +) { + throw AmiException( + "Requested functionality is not supported as %s is " + "not implemented for this model!", + __func__ + ); +} + +void AbstractModel:: + fdJydy_colptrs(SUNMatrixWrapper& /*indexptrs*/, int /*index*/) { + throw AmiException( + "Requested functionality is not supported as %s is " + "not implemented for this model!", + __func__ + ); +} + +void AbstractModel:: + fdJydy_rowvals(SUNMatrixWrapper& /*indexptrs*/, int /*index*/) { + throw AmiException( + "Requested functionality is not supported as %s is " + "not implemented for this model!", + __func__ + ); +} + +void AbstractModel::fdJydsigma( + realtype* /*dJydsigma*/, int const /*iy*/, realtype const* /*p*/, + realtype const* /*k*/, realtype const* /*y*/, realtype const* /*sigmay*/, + realtype const* /*my*/ +) { + throw AmiException( + "Requested functionality is not supported as %s is " + "not implemented for this model!", + __func__ + ); +} + +void AbstractModel::fdJzdz( + realtype* /*dJzdz*/, int const /*iz*/, realtype const* /*p*/, + realtype const* /*k*/, realtype const* /*z*/, realtype const* /*sigmaz*/, + realtype const* /*mz*/ +) { + throw AmiException( + "Requested functionality is not supported as %s is " + "not implemented for this model!", + __func__ + ); +} + +void AbstractModel::fdJzdsigma( + realtype* /*dJzdsigma*/, int const /*iz*/, realtype const* /*p*/, + realtype const* /*k*/, realtype const* /*z*/, realtype const* /*sigmaz*/, + realtype const* /*mz*/ +) { + throw AmiException( + "Requested functionality is not supported as %s is " + "not implemented for this model!", + __func__ + ); +} + +void AbstractModel::fdJrzdz( + realtype* /*dJrzdz*/, int const /*iz*/, realtype const* /*p*/, + realtype const* /*k*/, realtype const* /*rz*/, realtype const* /*sigmaz*/ +) { + throw AmiException( + "Requested functionality is not supported as %s is " + "not implemented for this model!", + __func__ + ); +} + +void AbstractModel::fdJrzdsigma( + realtype* /*dJrzdsigma*/, int const /*iz*/, realtype const* /*p*/, + realtype const* /*k*/, realtype const* /*rz*/, realtype const* /*sigmaz*/ +) { + throw AmiException( + "Requested functionality is not supported as %s is " + "not implemented for this model!", + __func__ + ); +} + +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*/) { + throw AmiException( + "Requested functionality is not supported as %s is " + "not implemented for this model!", + __func__ + ); +} + +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*/ +) { + throw AmiException( + "Requested functionality is not supported as %s is " + "not implemented for this model!", + __func__ + ); +} + +void AbstractModel::fdwdp_colptrs(SUNMatrixWrapper& /*dwdp*/) { + throw AmiException( + "Requested functionality is not supported as %s is " + "not implemented for this model!", + __func__ + ); +} + +void AbstractModel::fdwdp_rowvals(SUNMatrixWrapper& /*dwdp*/) { + throw AmiException( + "Requested functionality is not supported as %s is " + "not implemented for this model!", + __func__ + ); +} + +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 const* /*p*/, realtype const* /*k*/, realtype const* /*h*/, + realtype const* /*w*/, realtype const* /*tcl*/, realtype const* /*spl*/ +) { + throw AmiException( + "Requested functionality is not supported as %s is " + "not implemented for this model!", + __func__ + ); +} + +void AbstractModel::fdwdx_colptrs(SUNMatrixWrapper& /*dwdx*/) { + throw AmiException( + "Requested functionality is not supported as %s is " + "not implemented for this model!", + __func__ + ); +} + +void AbstractModel::fdwdx_rowvals(SUNMatrixWrapper& /*dwdx*/) { + throw AmiException( + "Requested functionality is not supported as %s is " + "not implemented for this model!", + __func__ + ); +} + +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*/ +) { + throw AmiException( + "Requested functionality is not supported as %s " + "is not implemented for this model!", + __func__ + ); +} + +void AbstractModel::fdwdw_colptrs(SUNMatrixWrapper& /*dwdw*/) { + throw AmiException( + "Requested functionality is not supported as %s " + "is not implemented for this model!", + __func__ + ); +} + +void AbstractModel::fdwdw_rowvals(SUNMatrixWrapper& /*dwdw*/) { + throw AmiException( + "Requested functionality is not supported as %s " + "is not implemented for this model!", + __func__ + ); +} + +void AbstractModel::fdx_rdatadx_solver( + realtype* /*dx_rdatadx_solver*/, realtype const* /*x*/, + realtype const* /*tcl*/, realtype const* /*p*/, realtype const* /*k*/ +) { + throw AmiException( + "Requested functionality is not supported as %s is " + "not implemented for this model!", + __func__ + ); +} + +void AbstractModel::fdx_rdatadx_solver_rowvals(SUNMatrixWrapper& /*dxrdxs*/) { + throw AmiException( + "Requested functionality is not supported as %s is " + "not implemented for this model!", + __func__ + ); +} + +void AbstractModel::fdx_rdatadx_solver_colptrs(SUNMatrixWrapper& /*dxrdxs*/) { + throw AmiException( + "Requested functionality is not supported as %s is " + "not implemented for this model!", + __func__ + ); +} + +void AbstractModel::fdx_rdatadp( + realtype* /*dx_rdatadp*/, realtype const* /*x*/, realtype const* /*tcl*/, + realtype const* /*p*/, realtype const* /*k*/, int const /*ip*/ +) { + throw AmiException( + "Requested functionality is not supported as %s is " + "not implemented for this model!", + __func__ + ); +} + +void AbstractModel::fdx_rdatadtcl( + realtype* /*dx_rdatadtcl*/, realtype const* /*x*/, realtype const* /*tcl*/, + realtype const* /*p*/, realtype const* /*k*/ +) { + throw AmiException( + "Requested functionality is not supported as %s is " + "not implemented for this model!", + __func__ + ); +} + +void AbstractModel::fdx_rdatadtcl_rowvals(SUNMatrixWrapper& /*dxrdtcl*/) { + throw AmiException( + "Requested functionality is not supported as %s is " + "not implemented for this model!", + __func__ + ); +} + +void AbstractModel::fdx_rdatadtcl_colptrs(SUNMatrixWrapper& /*dxrdtcl*/) { + throw AmiException( + "Requested functionality is not supported as %s is " + "not implemented for this model!", + __func__ + ); +} + +void AbstractModel::fdtotal_cldp( + realtype* /*dtotal_cldp*/, realtype const* /*x_rdata*/, + realtype const* /*p*/, realtype const* /*k*/, int const /*ip*/ +) { + throw AmiException( + "Requested functionality is not supported as %s is " + "not implemented for this model!", + __func__ + ); +} + +void AbstractModel::fdtotal_cldx_rdata( + realtype* /*dtotal_cldx_rdata*/, realtype const* /*x_rdata*/, + realtype const* /*p*/, realtype const* /*k*/, realtype const* /*tcl*/ +) { + throw AmiException( + "Requested functionality is not supported as %s is " + "not implemented for this model!", + __func__ + ); +} + +void AbstractModel:: + fdtotal_cldx_rdata_colptrs(SUNMatrixWrapper& /*dtotal_cldx_rdata*/) { + throw AmiException( + "Requested functionality is not supported as %s is " + "not implemented for this model!", + __func__ + ); +} + +void AbstractModel:: + fdtotal_cldx_rdata_rowvals(SUNMatrixWrapper& /*dtotal_cldx_rdata*/) { + throw AmiException( + "Requested functionality is not supported as %s is " + "not implemented for this model!", + __func__ + ); +} + +std::vector +AbstractModel::fcreate_splines(realtype const* /*p*/, realtype const* /*k*/) { + return std::vector(); +} + +void AbstractModel::fdspline_valuesdp( + realtype* /*dspline_valuesdp*/, realtype const* /*p*/, + realtype const* /*k*/, int const /*ip*/ +) { + // no-op default implementation +} + +void AbstractModel::fdspline_slopesdp( + realtype* /*dspline_slopesdp*/, realtype const* /*p*/, + realtype const* /*k*/, int const /*ip*/ +) { + // no-op default implementation } } // namespace amici diff --git a/deps/AMICI/src/amici.cpp b/deps/AMICI/src/amici.cpp index ee7bb8fcd..ee3949b0b 100644 --- a/deps/AMICI/src/amici.cpp +++ b/deps/AMICI/src/amici.cpp @@ -5,10 +5,10 @@ #include "amici/amici.h" -#include "amici/steadystateproblem.h" #include "amici/backwardproblem.h" #include "amici/forwardproblem.h" -#include "amici/misc.h" +#include "amici/logging.h" +#include "amici/steadystateproblem.h" #include //return codes #include //realtype @@ -18,141 +18,126 @@ #include #include #include +#include #include #include // ensure definitions are in sync -static_assert(amici::AMICI_SUCCESS == CV_SUCCESS, - "AMICI_SUCCESS != CV_SUCCESS"); -static_assert(amici::AMICI_DATA_RETURN == CV_TSTOP_RETURN, - "AMICI_DATA_RETURN != CV_TSTOP_RETURN"); -static_assert(amici::AMICI_ROOT_RETURN == CV_ROOT_RETURN, - "AMICI_ROOT_RETURN != CV_ROOT_RETURN"); -static_assert(amici::AMICI_ILL_INPUT == CV_ILL_INPUT, - "AMICI_ILL_INPUT != CV_ILL_INPUT"); -static_assert(amici::AMICI_NORMAL == CV_NORMAL, - "AMICI_NORMAL != CV_NORMAL"); -static_assert(amici::AMICI_ONE_STEP == CV_ONE_STEP, - "AMICI_ONE_STEP != CV_ONE_STEP"); -static_assert(amici::AMICI_SINGULAR_JACOBIAN == SUNLS_PACKAGE_FAIL_UNREC, - "AMICI_SINGULAR_JACOBIAN != SUNLS_PACKAGE_FAIL_UNREC"); -static_assert(amici::AMICI_SINGULAR_JACOBIAN == SUNLS_PACKAGE_FAIL_UNREC, - "AMICI_SINGULAR_JACOBIAN != SUNLS_PACKAGE_FAIL_UNREC"); -static_assert(std::is_same::value, - "Definition of realtype does not match"); +static_assert( + amici::AMICI_SUCCESS == CV_SUCCESS, "AMICI_SUCCESS != CV_SUCCESS" +); +static_assert( + amici::AMICI_DATA_RETURN == CV_TSTOP_RETURN, + "AMICI_DATA_RETURN != CV_TSTOP_RETURN" +); +static_assert( + amici::AMICI_ROOT_RETURN == CV_ROOT_RETURN, + "AMICI_ROOT_RETURN != CV_ROOT_RETURN" +); +static_assert( + amici::AMICI_ILL_INPUT == CV_ILL_INPUT, "AMICI_ILL_INPUT != CV_ILL_INPUT" +); +static_assert(amici::AMICI_NORMAL == CV_NORMAL, "AMICI_NORMAL != CV_NORMAL"); +static_assert( + amici::AMICI_ONE_STEP == CV_ONE_STEP, "AMICI_ONE_STEP != CV_ONE_STEP" +); +static_assert( + amici::AMICI_SINGULAR_JACOBIAN == SUNLS_PACKAGE_FAIL_UNREC, + "AMICI_SINGULAR_JACOBIAN != SUNLS_PACKAGE_FAIL_UNREC" +); +static_assert( + amici::AMICI_SINGULAR_JACOBIAN == SUNLS_PACKAGE_FAIL_UNREC, + "AMICI_SINGULAR_JACOBIAN != SUNLS_PACKAGE_FAIL_UNREC" +); +static_assert( + std::is_same::value, + "Definition of realtype does not match" +); namespace amici { -/** AMICI default application context, kept around for convenience for using - * amici::runAmiciSimulation or instantiating Solver and Model without special - * needs. - */ -AmiciApplication defaultContext = AmiciApplication(); - -std::unique_ptr -runAmiciSimulation(Solver& solver, - const ExpData* edata, - Model& model, - bool rethrow) -{ - return defaultContext.runAmiciSimulation(solver, edata, model, rethrow); -} - -void -printErrMsgIdAndTxt(std::string const& id, std::string const& message) -{ - std::cerr << "[Error] "; - if (!id.empty()) { - std::cerr << id << ": "; - } - std::cerr << message << std::endl; -} - -void -printWarnMsgIdAndTxt(std::string const& id, std::string const& message) -{ - std::cerr << "[Warning] "; - if (!id.empty()) { - std::cerr << id << ": "; - } - std::cerr << message << std::endl; -} - -std::vector> -runAmiciSimulations(const Solver& solver, - const std::vector& edatas, - const Model& model, - const bool failfast, -#if defined(_OPENMP) - int num_threads -#else - int /* num_threads */ -#endif -) -{ -#if defined(_OPENMP) - return defaultContext.runAmiciSimulations( - solver, edatas, model, failfast, num_threads); -#else - return defaultContext.runAmiciSimulations(solver, edatas, model, failfast, 1); -#endif -} - -std::unique_ptr -AmiciApplication::runAmiciSimulation(Solver& solver, - const ExpData* edata, - Model& model, - bool rethrow) -{ +std::map simulation_status_to_str_map = { + {AMICI_RECOVERABLE_ERROR, "AMICI_RECOVERABLE_ERROR"}, + {AMICI_UNRECOVERABLE_ERROR, "AMICI_UNRECOVERABLE_ERROR"}, + {AMICI_TOO_MUCH_WORK, "AMICI_TOO_MUCH_WORK"}, + {AMICI_TOO_MUCH_ACC, "AMICI_TOO_MUCH_ACC"}, + {AMICI_ERR_FAILURE, "AMICI_ERR_FAILURE"}, + {AMICI_CONV_FAILURE, "AMICI_CONV_FAILURE"}, + {AMICI_FIRST_RHSFUNC_ERR, "AMICI_FIRST_RHSFUNC_ERR"}, + {AMICI_RHSFUNC_FAIL, "AMICI_RHSFUNC_FAIL"}, + {AMICI_ILL_INPUT, "AMICI_ILL_INPUT"}, + {AMICI_ERROR, "AMICI_ERROR"}, + {AMICI_NO_STEADY_STATE, "AMICI_NO_STEADY_STATE"}, + {AMICI_DAMPING_FACTOR_ERROR, "AMICI_DAMPING_FACTOR_ERROR"}, + {AMICI_SINGULAR_JACOBIAN, "AMICI_SINGULAR_JACOBIAN"}, + {AMICI_NOT_IMPLEMENTED, "AMICI_NOT_IMPLEMENTED"}, + {AMICI_MAX_TIME_EXCEEDED, "AMICI_MAX_TIME_EXCEEDED"}, + {AMICI_SUCCESS, "AMICI_SUCCESS"}, + {AMICI_NOT_RUN, "AMICI_NOT_RUN"}, +}; + +std::unique_ptr runAmiciSimulation( + Solver& solver, ExpData const* edata, Model& model, bool rethrow +) { + // create a temporary logger instance for Solver and Model to capture + // messages from only this simulation + Logger logger; + solver.logger = &logger; + model.logger = &logger; + // prevent dangling pointer + auto _ = gsl::finally([&solver, &model] { + solver.logger = model.logger = nullptr; + }); + + CpuTimer cpu_timer; solver.startTimer(); /* Applies condition-specific model settings and restores them when going * out of scope */ ConditionContext cc1(&model, edata, FixedParameterContext::simulation); - std::unique_ptr rdata = std::make_unique(solver, - model); - if(edata) { + std::unique_ptr rdata + = std::make_unique(solver, model); + if (edata) { rdata->id = edata->id; } - std::unique_ptr preeq {}; - std::unique_ptr fwd {}; - std::unique_ptr bwd {}; - std::unique_ptr posteq {}; + std::unique_ptr preeq{}; + std::unique_ptr fwd{}; + std::unique_ptr bwd{}; + std::unique_ptr posteq{}; // tracks whether backwards integration finished without exceptions bool bwd_success = true; try { - if (solver.getPreequilibration() || - (edata && !edata->fixedParametersPreequilibration.empty())) { + if (edata && !edata->fixedParametersPreequilibration.empty()) { ConditionContext cc2( &model, edata, FixedParameterContext::preequilibration ); preeq = std::make_unique(solver, model); - preeq->workSteadyStateProblem(&solver, &model, -1); + preeq->workSteadyStateProblem(solver, model, -1); } - - fwd = std::make_unique(edata, &model, &solver, - preeq.get()); + fwd = std::make_unique( + edata, &model, &solver, preeq.get() + ); fwd->workForwardProblem(); - if (fwd->getCurrentTimeIteration() < model.nt()) { posteq = std::make_unique(solver, model); - posteq->workSteadyStateProblem(&solver, &model, - fwd->getCurrentTimeIteration()); + posteq->workSteadyStateProblem( + solver, model, fwd->getCurrentTimeIteration() + ); } - if (edata && solver.computingASA()) { fwd->getAdjointUpdates(model, *edata); if (posteq) { posteq->getAdjointUpdates(model, *edata); - posteq->workSteadyStateBackwardProblem(&solver, &model, - bwd.get()); + posteq->workSteadyStateBackwardProblem( + solver, model, bwd.get() + ); } bwd_success = false; @@ -163,93 +148,121 @@ AmiciApplication::runAmiciSimulation(Solver& solver, bwd_success = true; if (preeq) { - ConditionContext cc2(&model, edata, - FixedParameterContext::preequilibration); - preeq->workSteadyStateBackwardProblem(&solver, &model, - bwd.get()); + ConditionContext cc2( + &model, edata, FixedParameterContext::preequilibration + ); + preeq->workSteadyStateBackwardProblem(solver, model, bwd.get()); } } rdata->status = AMICI_SUCCESS; } catch (amici::IntegrationFailure const& ex) { - if(ex.error_code == AMICI_RHSFUNC_FAIL && solver.timeExceeded()) { + if (ex.error_code == AMICI_RHSFUNC_FAIL && solver.timeExceeded()) { rdata->status = AMICI_MAX_TIME_EXCEEDED; - if(rethrow) + if (rethrow) throw; - warningF("AMICI:simulation", - "AMICI forward simulation failed at t = %f: " - "Maximum time exceeed.\n", - ex.time); + logger.log( + LogSeverity::error, "MAXTIME_EXCEEDED", + "AMICI forward simulation failed at t = %g: " + "Maximum time exceeded in forward solve.", + ex.time + ); } else { rdata->status = ex.error_code; if (rethrow) throw; - warningF("AMICI:simulation", - "AMICI forward simulation failed at t = %f:\n%s\n", - ex.time, - ex.what()); - + logger.log( + LogSeverity::error, "FORWARD_FAILURE", + "AMICI forward simulation failed at t = %g: %s", ex.time, + ex.what() + ); } } catch (amici::IntegrationFailureB const& ex) { - if(ex.error_code == AMICI_RHSFUNC_FAIL && solver.timeExceeded()) { + if (ex.error_code == AMICI_RHSFUNC_FAIL && solver.timeExceeded()) { rdata->status = AMICI_MAX_TIME_EXCEEDED; if (rethrow) throw; - warningF( - "AMICI:simulation", + logger.log( + LogSeverity::error, "MAXTIME_EXCEEDED", "AMICI backward simulation failed when trying to solve until " - "t = %f: Maximum time exceeed.\n", - ex.time); + "t = %g: Maximum time exceeded in backward solve.", + ex.time + ); } else { rdata->status = ex.error_code; if (rethrow) throw; - warningF( - "AMICI:simulation", - "AMICI backward simulation failed when trying to solve until t = %f" - " (see message above):\n%s\n", - ex.time, - ex.what()); + logger.log( + LogSeverity::error, "BACKWARD_FAILURE", + "AMICI backward simulation failed when trying to solve until t " + "= %g" + " (check debug logs for details): %s", + ex.time, ex.what() + ); } } catch (amici::AmiException const& ex) { rdata->status = AMICI_ERROR; if (rethrow) throw; - warningF("AMICI:simulation", - "AMICI simulation failed:\n%s\nError occurred in:\n%s", - ex.what(), - ex.getBacktrace()); + logger.log( + LogSeverity::error, "OTHER", "AMICI simulation failed: %s", + ex.what() + ); + logger.log( + LogSeverity::debug, "BACKTRACE", + "The previous error occurred at:\n%s", ex.getBacktrace() + ); + } catch (std::exception const& ex) { rdata->status = AMICI_ERROR; if (rethrow) throw; - warningF("AMICI:simulation", - "AMICI simulation failed:\n%s\n", - ex.what()); + logger.log( + LogSeverity::error, "OTHER", "AMICI simulation failed: %s", + ex.what() + ); } rdata->processSimulationObjects( - preeq.get(), fwd.get(), - bwd_success ? bwd.get() : nullptr, - posteq.get(), model, solver, edata); + preeq.get(), fwd.get(), bwd_success ? bwd.get() : nullptr, posteq.get(), + model, solver, edata + ); + + rdata->cpu_time_total = cpu_timer.elapsed_milliseconds(); + + // verify that reported CPU times are plausible + gsl_EnsuresDebug(rdata->cpu_time <= rdata->cpu_time_total); + gsl_EnsuresDebug(rdata->cpu_timeB <= rdata->cpu_time_total); + gsl_EnsuresDebug(rdata->preeq_cpu_time <= rdata->cpu_time_total); + gsl_EnsuresDebug(rdata->preeq_cpu_timeB <= rdata->cpu_time_total); + gsl_EnsuresDebug(rdata->posteq_cpu_time <= rdata->cpu_time_total); + gsl_EnsuresDebug(rdata->posteq_cpu_timeB <= rdata->cpu_time_total); + if (!posteq) + gsl_EnsuresDebug( + std::is_sorted(rdata->numsteps.begin(), rdata->numsteps.end()) + || rdata->status != AMICI_SUCCESS + ); + if (!preeq) + gsl_EnsuresDebug( + std::is_sorted(rdata->numstepsB.begin(), rdata->numstepsB.end()) + || rdata->status != AMICI_SUCCESS + ); + rdata->messages = logger.items; return rdata; } -std::vector> -AmiciApplication::runAmiciSimulations(const Solver& solver, - const std::vector& edatas, - const Model& model, - bool failfast, +std::vector> runAmiciSimulations( + Solver const& solver, std::vector const& edatas, + Model const& model, bool failfast, #if defined(_OPENMP) - int num_threads + int num_threads #else - int /* num_threads */ + int /* num_threads */ #endif -) -{ +) { std::vector> results(edatas.size()); // is set to true if one simulation fails and we should skip the rest. // shared across threads. @@ -266,8 +279,8 @@ AmiciApplication::runAmiciSimulations(const Solver& solver, interface */ if (skipThrough) { ConditionContext conditionContext(myModel.get(), edatas[i]); - results[i] = - std::unique_ptr(new ReturnData(solver, model)); + results[i] + = std::unique_ptr(new ReturnData(solver, model)); } else { results[i] = runAmiciSimulation(*mySolver, edatas[i], *myModel); } @@ -278,45 +291,15 @@ AmiciApplication::runAmiciSimulations(const Solver& solver, return results; } -void -AmiciApplication::warningF(const char* identifier, const char* format, ...) const -{ - va_list argptr; - va_start(argptr, format); - auto str = printfToString(format, argptr); - va_end(argptr); - warning(identifier, str); -} - -void -AmiciApplication::errorF(const char* identifier, const char* format, ...) const -{ - va_list argptr; - va_start(argptr, format); - auto str = printfToString(format, argptr); - va_end(argptr); - error(identifier, str); -} - -int -AmiciApplication::checkFinite(gsl::span array, const char* fun) -{ - - for (int idx = 0; idx < (int)array.size(); idx++) { - if (isNaN(array[idx])) { - warningF("AMICI:NaN", - "AMICI encountered a NaN value for %s[%i]!", - fun, idx); - return AMICI_RECOVERABLE_ERROR; - } - if (isInf(array[idx])) { - warningF("AMICI:Inf", - "AMICI encountered an Inf value for %s[%i]!", - fun, idx); - return AMICI_RECOVERABLE_ERROR; - } +std::string simulation_status_to_str(int status) { + try { + return simulation_status_to_str_map.at(status); + } catch (std::out_of_range const&) { + // Missing mapping - terminate if this is a debug build, + // but show the number if non-debug. + gsl_ExpectsDebug(false); + return std::to_string(status); } - return AMICI_SUCCESS; } } // namespace amici diff --git a/deps/AMICI/src/backwardproblem.cpp b/deps/AMICI/src/backwardproblem.cpp index 495837ea1..d1b89967d 100644 --- a/deps/AMICI/src/backwardproblem.cpp +++ b/deps/AMICI/src/backwardproblem.cpp @@ -1,67 +1,68 @@ #include "amici/backwardproblem.h" -#include "amici/model.h" -#include "amici/solver.h" -#include "amici/exception.h" #include "amici/edata.h" +#include "amici/exception.h" #include "amici/forwardproblem.h" -#include "amici/steadystateproblem.h" #include "amici/misc.h" +#include "amici/model.h" +#include "amici/solver.h" +#include "amici/steadystateproblem.h" -#include #include +#include namespace amici { -BackwardProblem::BackwardProblem(const ForwardProblem &fwd, - const SteadystateProblem *posteq): - model_(fwd.model), - solver_(fwd.solver), - edata_(fwd.edata), - t_(fwd.getTime()), - xB_(fwd.model->nx_solver), - dxB_(fwd.model->nx_solver), - xQB_(fwd.model->nJ*fwd.model->nplist()), - x_disc_(fwd.getStatesAtDiscontinuities()), - xdot_disc_(fwd.getRHSAtDiscontinuities()), - xdot_old_disc_(fwd.getRHSBeforeDiscontinuities()), - sx0_(fwd.getStateSensitivity()), - nroots_(fwd.getNumberOfRoots()), - discs_(fwd.getDiscontinuities()), - root_idx_(fwd.getRootIndexes()), - dJydx_(fwd.getDJydx()), - dJzdx_(fwd.getDJzdx()) { - /* complement dJydx from postequilibration. This should overwrite - * anything but only fill in previously 0 values, as only non-inf - * timepoints are filled from fwd. - */ - for (int it = 0; it < fwd.model->nt(); it++) { - if (std::isinf(fwd.model->getTimepoint(it))) { - if (!posteq) - throw AmiException("Model has non-finite timepoint but, " - "postequilibration did not run"); - - /* copy adjoint update to postequilibration */ - writeSlice(slice(posteq->getDJydx(), it, - fwd.model->nx_solver * fwd.model->nJ), - slice(dJydx_, it, - fwd.model->nx_solver * fwd.model->nJ)); - - /* If adjoint sensis were computed, copy also quadratures */ - xQB_.zero(); - xQB_ = posteq->getEquilibrationQuadratures(); - } +BackwardProblem::BackwardProblem( + ForwardProblem const& fwd, SteadystateProblem const* posteq +) + : model_(fwd.model) + , solver_(fwd.solver) + , edata_(fwd.edata) + , t_(fwd.getTime()) + , xB_(fwd.model->nx_solver) + , dxB_(fwd.model->nx_solver) + , xQB_(fwd.model->nJ * fwd.model->nplist()) + , x_disc_(fwd.getStatesAtDiscontinuities()) + , xdot_disc_(fwd.getRHSAtDiscontinuities()) + , xdot_old_disc_(fwd.getRHSBeforeDiscontinuities()) + , sx0_(fwd.getStateSensitivity()) + , nroots_(fwd.getNumberOfRoots()) + , discs_(fwd.getDiscontinuities()) + , root_idx_(fwd.getRootIndexes()) + , dJydx_(fwd.getDJydx()) + , dJzdx_(fwd.getDJzdx()) { + /* complement dJydx from postequilibration. This shouldn't overwrite + * anything but only fill in previously 0 values, as only non-inf + * timepoints are filled from fwd. + */ + for (int it = 0; it < fwd.model->nt(); it++) { + if (std::isinf(fwd.model->getTimepoint(it))) { + if (!posteq) + throw AmiException("Model has non-finite timepoint but, " + "postequilibration did not run"); + + /* copy adjoint update to postequilibration */ + writeSlice( + slice( + posteq->getDJydx(), it, fwd.model->nx_solver * fwd.model->nJ + ), + slice(dJydx_, it, fwd.model->nx_solver * fwd.model->nJ) + ); + + /* If adjoint sensis were computed, copy also quadratures */ + xQB_.zero(); + xQB_ = posteq->getEquilibrationQuadratures(); } - } - +} void BackwardProblem::workBackwardProblem() { - if (model_->nx_solver <= 0 || - solver_->getSensitivityOrder() < SensitivityOrder::first || - solver_->getSensitivityMethod() != SensitivityMethod::adjoint || - model_->nplist() == 0) { + if (model_->nx_solver <= 0 + || solver_->getSensitivityOrder() < SensitivityOrder::first + || solver_->getSensitivityMethod() != SensitivityMethod::adjoint + || model_->nplist() == 0) { return; } @@ -74,10 +75,15 @@ void BackwardProblem::workBackwardProblem() { /* initialize state vectors, depending on postequilibration */ model_->initializeB(xB_, dxB_, xQB_, it < model_->nt() - 1); - if ((it >= 0 || !discs_.empty()) && model_->getTimepoint(it) > model_->t0()) - { + if ((it >= 0 || !discs_.empty()) + && model_->getTimepoint(it) > model_->t0()) { handleDataPointB(it); - solver_->setupB(&which, model_->getTimepoint(it), model_, xB_, dxB_, xQB_); + solver_->setupB( + &which, model_->getTimepoint(it), model_, xB_, dxB_, xQB_ + ); + /* for initial datapoint diagnosis needs to be stored after setup as + it is not called in handleDataPointB*/ + solver_->storeDiagnosisB(which); --it; while (it >= 0 || discs_.size() > 0) { @@ -96,7 +102,7 @@ void BackwardProblem::workBackwardProblem() { } /* handle data-point */ - if (it >=0 && tnext == model_->getTimepoint(it)) { + if (it >= 0 && tnext == model_->getTimepoint(it)) { handleDataPointB(it); it--; } @@ -115,13 +121,14 @@ void BackwardProblem::workBackwardProblem() { } if (edata_ && edata_->t_presim > 0) { - ConditionContext cc(model_, edata_, FixedParameterContext::presimulation); + ConditionContext cc( + model_, edata_, FixedParameterContext::presimulation + ); solver_->runB(model_->t0() - edata_->t_presim); solver_->writeSolutionB(&t_, xB_, dxB_, xQB_, which); } } - void BackwardProblem::handleEventB() { auto rootidx = root_idx_.back(); this->root_idx_.pop_back(); @@ -141,19 +148,19 @@ void BackwardProblem::handleEventB() { continue; } - model_->addAdjointQuadratureEventUpdate(xQB_, ie, t_, x_disc, xB_, - xdot_disc, - xdot_old_disc); - model_->addAdjointStateEventUpdate(xB_, ie, t_, x_disc, - xdot_disc, - xdot_old_disc); + model_->addAdjointQuadratureEventUpdate( + xQB_, ie, t_, x_disc, xB_, xdot_disc, xdot_old_disc + ); + model_->addAdjointStateEventUpdate( + xB_, ie, t_, x_disc, xdot_disc, xdot_old_disc + ); if (model_->nz > 0) { for (int ix = 0; ix < model_->nxtrue_solver; ++ix) { for (int iJ = 0; iJ < model_->nJ; ++iJ) { - xB_[ix + iJ * model_->nxtrue_solver] += - dJzdx_[iJ + ( ix + nroots_[ie] * model_->nx_solver ) - * model_->nJ]; + xB_[ix + iJ * model_->nxtrue_solver] += dJzdx_ + [iJ + + (ix + nroots_[ie] * model_->nx_solver) * model_->nJ]; } } } @@ -164,18 +171,22 @@ void BackwardProblem::handleEventB() { model_->updateHeavisideB(rootidx.data()); } -void BackwardProblem::handleDataPointB(const int it) { - solver_->storeDiagnosisB(which); +void BackwardProblem::handleDataPointB(int const it) { + /* solver wasn't reset yet, as xB_ is necessary for solver setup. + For initial time point (we are integrating backwards!), diagnosis needs + to be stored outside this function. */ + if (it < model_->nt() - 1) + solver_->storeDiagnosisB(which); for (int ix = 0; ix < model_->nxtrue_solver; ix++) { for (int iJ = 0; iJ < model_->nJ; iJ++) // we only need the 1:nxtrue_solver (not the nx_true) slice here! - xB_[ix + iJ * model_->nxtrue_solver] += - dJydx_[iJ + ( ix + it * model_->nx_solver ) * model_->nJ]; + xB_[ix + iJ * model_->nxtrue_solver] + += dJydx_[iJ + (ix + it * model_->nx_solver) * model_->nJ]; } } -realtype BackwardProblem::getTnext(const int it) { +realtype BackwardProblem::getTnext(int const it) { if (it < 0 && discs_.empty()) { throw AmiException( "No more timepoints (it=%d, ie=%d) available at %f. This should " @@ -185,8 +196,8 @@ realtype BackwardProblem::getTnext(const int it) { ); } - if (!discs_.empty() && - (it < 0 || discs_.back() > model_->getTimepoint(it))) { + if (!discs_.empty() + && (it < 0 || discs_.back() > model_->getTimepoint(it))) { double tdisc = discs_.back(); return tdisc; } diff --git a/deps/AMICI/src/cblas.cpp b/deps/AMICI/src/cblas.cpp index 377ca092f..386514ef1 100644 --- a/deps/AMICI/src/cblas.cpp +++ b/deps/AMICI/src/cblas.cpp @@ -11,33 +11,39 @@ #elif defined(AMICI_BLAS_MKL) #include #else -extern "C" -{ - #include +extern "C" { +#include } #endif namespace amici { -void amici_dgemm(BLASLayout layout, BLASTranspose TransA, - BLASTranspose TransB, const int M, const int N, - const int K, const double alpha, const double *A, - const int lda, const double *B, const int ldb, - const double beta, double *C, const int ldc) { - cblas_dgemm((CBLAS_ORDER)layout, (CBLAS_TRANSPOSE)TransA, - (CBLAS_TRANSPOSE)TransB, M, N, K, alpha, A, lda, B, ldb, beta, - C, ldc); +void amici_dgemm( + BLASLayout layout, BLASTranspose TransA, BLASTranspose TransB, int const M, + int const N, int const K, double const alpha, double const* A, + int const lda, double const* B, int const ldb, double const beta, double* C, + int const ldc +) { + cblas_dgemm( + (CBLAS_ORDER)layout, (CBLAS_TRANSPOSE)TransA, (CBLAS_TRANSPOSE)TransB, + M, N, K, alpha, A, lda, B, ldb, beta, C, ldc + ); } -void amici_dgemv(BLASLayout layout, BLASTranspose TransA, - const int M, const int N, const double alpha, const double *A, - const int lda, const double *X, const int incX, - const double beta, double *Y, const int incY) { - cblas_dgemv((CBLAS_ORDER)layout, (CBLAS_TRANSPOSE)TransA, M, N, alpha, A, - lda, X, incX, beta, Y, incY); +void amici_dgemv( + BLASLayout layout, BLASTranspose TransA, int const M, int const N, + double const alpha, double const* A, int const lda, double const* X, + int const incX, double const beta, double* Y, int const incY +) { + cblas_dgemv( + (CBLAS_ORDER)layout, (CBLAS_TRANSPOSE)TransA, M, N, alpha, A, lda, X, + incX, beta, Y, incY + ); } -void amici_daxpy(int n, double alpha, const double *x, const int incx, double *y, int incy) { +void amici_daxpy( + int n, double alpha, double const* x, int const incx, double* y, int incy +) { cblas_daxpy(n, alpha, x, incx, y, incy); } diff --git a/deps/AMICI/src/edata.cpp b/deps/AMICI/src/edata.cpp index 1842cf669..ddc3b6599 100644 --- a/deps/AMICI/src/edata.cpp +++ b/deps/AMICI/src/edata.cpp @@ -1,50 +1,56 @@ #include "amici/edata.h" -#include "amici/rdata.h" -#include "amici/symbolic_functions.h" // getNaN #include "amici/defines.h" #include "amici/model.h" +#include "amici/rdata.h" +#include "amici/symbolic_functions.h" // getNaN +#include #include #include #include -#include namespace amici { ExpData::ExpData(int nytrue, int nztrue, int nmaxevent) - : nytrue_(nytrue), nztrue_(nztrue), nmaxevent_(nmaxevent) -{ + : nytrue_(nytrue) + , nztrue_(nztrue) + , nmaxevent_(nmaxevent) { applyDimensions(); } -ExpData::ExpData(int nytrue, int nztrue, int nmaxevent, - std::vector ts) - : SimulationParameters(ts), nytrue_(nytrue), - nztrue_(nztrue), nmaxevent_(nmaxevent) -{ +ExpData::ExpData( + int nytrue, int nztrue, int nmaxevent, std::vector ts +) + : SimulationParameters(ts) + , nytrue_(nytrue) + , nztrue_(nztrue) + , nmaxevent_(nmaxevent) { applyDimensions(); } -ExpData::ExpData(int nytrue, int nztrue, int nmaxevent, - std::vector ts, - std::vector fixedParameters - ) - : SimulationParameters(ts), nytrue_(nytrue), - nztrue_(nztrue), nmaxevent_(nmaxevent) -{ +ExpData::ExpData( + int nytrue, int nztrue, int nmaxevent, std::vector ts, + std::vector fixedParameters +) + : SimulationParameters(ts) + , nytrue_(nytrue) + , nztrue_(nztrue) + , nmaxevent_(nmaxevent) { this->fixedParameters = std::move(fixedParameters); applyDimensions(); } -ExpData::ExpData(int nytrue, int nztrue, int nmaxevent, - std::vector ts, - std::vector const& observedData, - std::vector const& observedDataStdDev, - std::vector const& observedEvents, - std::vector const& observedEventsStdDev) - : SimulationParameters(ts), nytrue_(nytrue), nztrue_(nztrue), - nmaxevent_(nmaxevent) -{ +ExpData::ExpData( + int nytrue, int nztrue, int nmaxevent, std::vector ts, + std::vector const& observedData, + std::vector const& observedDataStdDev, + std::vector const& observedEvents, + std::vector const& observedEventsStdDev +) + : SimulationParameters(ts) + , nytrue_(nytrue) + , nztrue_(nztrue) + , nmaxevent_(nmaxevent) { applyDimensions(); setObservedData(observedData); setObservedDataStdDev(observedDataStdDev); @@ -52,27 +58,41 @@ ExpData::ExpData(int nytrue, int nztrue, int nmaxevent, setObservedEventsStdDev(observedEventsStdDev); } -ExpData::ExpData(Model const &model) - : ExpData(model.nytrue, model.nztrue, model.nMaxEvent(), - model.getTimepoints(), model.getFixedParameters()) { - reinitializeFixedParameterInitialStates = - model.getReinitializeFixedParameterInitialStates() - && model.getReinitializationStateIdxs().empty(); +ExpData::ExpData(Model const& model) + : ExpData( + model.nytrue, model.nztrue, model.nMaxEvent(), model.getTimepoints(), + model.getFixedParameters() + ) { + reinitializeFixedParameterInitialStates + = model.getReinitializeFixedParameterInitialStates() + && model.getReinitializationStateIdxs().empty(); reinitialization_state_idxs_sim = model.getReinitializationStateIdxs(); } 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)) {} - -ExpData::ExpData(ReturnData const& rdata, std::vector sigma_y, - std::vector sigma_z) - : ExpData(rdata.nytrue, rdata.nztrue, rdata.nmaxevent, rdata.ts) -{ - if (sigma_y.size() != (unsigned) nytrue_ && sigma_y.size() != (unsigned) nytrue_*nt()) - throw AmiException("Dimension of sigma_y must be %d or %d, was %d", nytrue_, nytrue_*nt(), sigma_y.size()); - - if (sigma_z.size() != (unsigned) nztrue_ && sigma_z.size() != (unsigned) nztrue_*nmaxevent_) - throw AmiException("Dimension of sigma_z must be %d or %d, was %d", nztrue_, nztrue_*nmaxevent_, sigma_z.size()); + : ExpData( + 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, + std::vector sigma_z +) + : ExpData(rdata.nytrue, rdata.nztrue, rdata.nmaxevent, rdata.ts) { + if (sigma_y.size() != (unsigned)nytrue_ + && sigma_y.size() != (unsigned)nytrue_ * nt()) + throw AmiException( + "Dimension of sigma_y must be %d or %d, was %d", nytrue_, + nytrue_ * nt(), sigma_y.size() + ); + + if (sigma_z.size() != (unsigned)nztrue_ + && sigma_z.size() != (unsigned)nztrue_ * nmaxevent_) + throw AmiException( + "Dimension of sigma_z must be %d or %d, was %d", nztrue_, + nztrue_ * nmaxevent_, sigma_z.size() + ); std::random_device rd{}; std::mt19937 gen{rd()}; @@ -84,18 +104,24 @@ ExpData::ExpData(ReturnData const& rdata, std::vector sigma_y, for (int iy = 0; iy < nytrue_; ++iy) { for (int it = 0; it < nt(); ++it) { - sigma = sigma_y.size() == (unsigned) nytrue_ ? sigma_y.at(iy) : sigma_y.at(iy + nytrue_ * it); + sigma = sigma_y.size() == (unsigned)nytrue_ + ? sigma_y.at(iy) + : sigma_y.at(iy + nytrue_ * it); std::normal_distribution<> e{0, sigma}; - observed_data_.at(iy + nytrue_ * it) = rdata.y.at(iy + rdata.ny * it) + e(gen); + observed_data_.at(iy + nytrue_ * it) + = rdata.y.at(iy + rdata.ny * it) + e(gen); observed_data_std_dev_.at(iy + nytrue_ * it) = sigma; } } for (int iz = 0; iz < nztrue_; ++iz) { for (int ie = 0; ie < nmaxevent_; ++ie) { - sigma = sigma_z.size() == (unsigned) nztrue_ ? sigma_z.at(iz) : sigma_z.at(iz + nztrue_ * ie); + sigma = sigma_z.size() == (unsigned)nztrue_ + ? sigma_z.at(iz) + : sigma_z.at(iz + nztrue_ * ie); std::normal_distribution<> e{0, sigma}; - observed_events_.at(iz + rdata.nztrue * ie) = rdata.z.at(iz + rdata.nz * ie) + e(gen); + observed_events_.at(iz + rdata.nztrue * ie) + = rdata.z.at(iz + rdata.nz * ie) + e(gen); observed_data_std_dev_.at(iz + rdata.nztrue * ie) = sigma; } } @@ -103,62 +129,67 @@ ExpData::ExpData(ReturnData const& rdata, std::vector sigma_y, id = rdata.id; } -void ExpData::setTimepoints(const std::vector &ts) { +void ExpData::setTimepoints(std::vector const& ts) { if (!std::is_sorted(ts.begin(), ts.end())) - throw AmiException("Encountered non-monotonic timepoints, please order timepoints such that they are monotonically increasing!"); + throw AmiException( + "Encountered non-monotonic timepoints, please order timepoints " + "such that they are monotonically increasing!" + ); ts_ = ts; applyDataDimension(); } -std::vector const& ExpData::getTimepoints() const { - return ts_; -} +std::vector const& ExpData::getTimepoints() const { return ts_; } -int ExpData::nt() const { - return static_cast(ts_.size()); -} +int ExpData::nt() const { return gsl::narrow(ts_.size()); } -realtype ExpData::getTimepoint(int it) const { - return ts_.at(it); -} +realtype ExpData::getTimepoint(int it) const { return ts_.at(it); } -void ExpData::setObservedData(const std::vector &observedData) { +void ExpData::setObservedData(std::vector const& observedData) { checkDataDimension(observedData, "observedData"); - if (observedData.size() == (unsigned) nt() * nytrue_) + if (observedData.size() == (unsigned)nt() * nytrue_) observed_data_ = observedData; else if (observedData.empty()) observed_data_.clear(); } -void ExpData::setObservedData(const std::vector &observedData, int iy) { - if (observedData.size() != (unsigned) nt()) - throw AmiException("Input observedData did not match dimensions nt (%i), was %i", nt(), observedData.size()); +void ExpData::setObservedData( + std::vector const& observedData, int iy +) { + if (observedData.size() != (unsigned)nt()) + throw AmiException( + "Input observedData did not match dimensions nt (%i), was %i", nt(), + observedData.size() + ); for (int it = 0; it < nt(); ++it) - observed_data_.at(iy + it*nytrue_) = observedData.at(it); + observed_data_.at(iy + it * nytrue_) = observedData.at(it); } bool ExpData::isSetObservedData(int it, int iy) const { - return !observed_data_.empty() && !isNaN(observed_data_.at(it * nytrue_ + iy)); + return !observed_data_.empty() + && !isNaN(observed_data_.at(it * nytrue_ + iy)); } std::vector const& ExpData::getObservedData() const { return observed_data_; } -const realtype *ExpData::getObservedDataPtr(int it) const { +realtype const* ExpData::getObservedDataPtr(int it) const { if (!observed_data_.empty()) - return &observed_data_.at(it*nytrue_); + return &observed_data_.at(it * nytrue_); return nullptr; } -void ExpData::setObservedDataStdDev(const std::vector &observedDataStdDev) { +void ExpData::setObservedDataStdDev( + std::vector const& observedDataStdDev +) { checkDataDimension(observedDataStdDev, "observedDataStdDev"); checkSigmaPositivity(observedDataStdDev, "observedDataStdDev"); - if (observedDataStdDev.size() == (unsigned) nt()*nytrue_) + if (observedDataStdDev.size() == (unsigned)nt() * nytrue_) observed_data_std_dev_ = observedDataStdDev; else if (observedDataStdDev.empty()) observed_data_std_dev_.clear(); @@ -166,77 +197,95 @@ void ExpData::setObservedDataStdDev(const std::vector &observedDataStd void ExpData::setObservedDataStdDev(const realtype stdDev) { checkSigmaPositivity(stdDev, "stdDev"); - std::fill(observed_data_std_dev_.begin() ,observed_data_std_dev_.end(), stdDev); -} - -void ExpData::setObservedDataStdDev(const std::vector &observedDataStdDev, int iy) { - if (observedDataStdDev.size() != (unsigned) nt()) - throw AmiException("Input observedDataStdDev did not match dimensions nt (%i), was %i", nt(), observedDataStdDev.size()); + std::fill( + observed_data_std_dev_.begin(), observed_data_std_dev_.end(), stdDev + ); +} + +void ExpData::setObservedDataStdDev( + std::vector const& observedDataStdDev, int iy +) { + if (observedDataStdDev.size() != (unsigned)nt()) + throw AmiException( + "Input observedDataStdDev did not match dimensions nt (%i), was %i", + nt(), observedDataStdDev.size() + ); checkSigmaPositivity(observedDataStdDev, "observedDataStdDev"); for (int it = 0; it < nt(); ++it) - observed_data_std_dev_.at(iy + it*nytrue_) = observedDataStdDev.at(it); + observed_data_std_dev_.at(iy + it * nytrue_) + = observedDataStdDev.at(it); } void ExpData::setObservedDataStdDev(const realtype stdDev, int iy) { checkSigmaPositivity(stdDev, "stdDev"); for (int it = 0; it < nt(); ++it) - observed_data_std_dev_.at(iy + it*nytrue_) = stdDev; + observed_data_std_dev_.at(iy + it * nytrue_) = stdDev; } bool ExpData::isSetObservedDataStdDev(int it, int iy) const { - return !observed_data_std_dev_.empty() && !isNaN(observed_data_std_dev_.at(it * nytrue_ + iy)); + return !observed_data_std_dev_.empty() + && !isNaN(observed_data_std_dev_.at(it * nytrue_ + iy)); } std::vector const& ExpData::getObservedDataStdDev() const { return observed_data_std_dev_; } -const realtype *ExpData::getObservedDataStdDevPtr(int it) const { +realtype const* ExpData::getObservedDataStdDevPtr(int it) const { if (!observed_data_std_dev_.empty()) - return &observed_data_std_dev_.at(it*nytrue_); + return &observed_data_std_dev_.at(it * nytrue_); return nullptr; } -void ExpData::setObservedEvents(const std::vector &observedEvents) { +void ExpData::setObservedEvents(std::vector const& observedEvents) { checkEventsDimension(observedEvents, "observedEvents"); - if (observedEvents.size() == (unsigned) nmaxevent_*nztrue_) + if (observedEvents.size() == (unsigned)nmaxevent_ * nztrue_) observed_events_ = observedEvents; else if (observedEvents.empty()) observed_events_.clear(); } -void ExpData::setObservedEvents(const std::vector &observedEvents, int iz) { - if (observedEvents.size() != (unsigned) nmaxevent_) { - throw AmiException("Input observedEvents did not match dimensions nmaxevent (%i), was %i", nmaxevent_, observedEvents.size()); +void ExpData::setObservedEvents( + std::vector const& observedEvents, int iz +) { + if (observedEvents.size() != (unsigned)nmaxevent_) { + throw AmiException( + "Input observedEvents did not match dimensions nmaxevent (%i), was " + "%i", + nmaxevent_, observedEvents.size() + ); } for (int ie = 0; ie < nmaxevent_; ++ie) - observed_events_.at(iz + ie*nztrue_) = observedEvents.at(ie); + observed_events_.at(iz + ie * nztrue_) = observedEvents.at(ie); } bool ExpData::isSetObservedEvents(int ie, int iz) const { - return !observed_events_.empty() && !isNaN(observed_events_.at(ie * nztrue_ + iz)); + return !observed_events_.empty() + && !isNaN(observed_events_.at(ie * nztrue_ + iz)); } std::vector const& ExpData::getObservedEvents() const { return observed_events_; } -const realtype *ExpData::getObservedEventsPtr(int ie) const { +realtype const* ExpData::getObservedEventsPtr(int ie) const { if (!observed_events_.empty()) - return &observed_events_.at(ie*nztrue_); + return &observed_events_.at(ie * nztrue_); return nullptr; } -void ExpData::setObservedEventsStdDev(const std::vector &observedEventsStdDev) { +void ExpData::setObservedEventsStdDev( + std::vector const& observedEventsStdDev +) { checkEventsDimension(observedEventsStdDev, "observedEventsStdDev"); checkSigmaPositivity(observedEventsStdDev, "observedEventsStdDev"); - if (observedEventsStdDev.size() == (unsigned) nmaxevent_*nztrue_) + if (observedEventsStdDev.size() == (unsigned)nmaxevent_ * nztrue_) observed_events_std_dev_ = observedEventsStdDev; else if (observedEventsStdDev.empty()) observed_events_std_dev_.clear(); @@ -244,23 +293,32 @@ void ExpData::setObservedEventsStdDev(const std::vector &observedEvent void ExpData::setObservedEventsStdDev(const realtype stdDev) { checkSigmaPositivity(stdDev, "stdDev"); - std::fill(observed_events_std_dev_.begin() ,observed_events_std_dev_.end(), stdDev); -} - -void ExpData::setObservedEventsStdDev(const std::vector &observedEventsStdDev, int iz) { - if (observedEventsStdDev.size() != (unsigned) nmaxevent_) - throw AmiException("Input observedEventsStdDev did not match dimensions nmaxevent (%i), was %i", nmaxevent_, observedEventsStdDev.size()); + std::fill( + observed_events_std_dev_.begin(), observed_events_std_dev_.end(), stdDev + ); +} + +void ExpData::setObservedEventsStdDev( + std::vector const& observedEventsStdDev, int iz +) { + if (observedEventsStdDev.size() != (unsigned)nmaxevent_) + throw AmiException( + "Input observedEventsStdDev did not match dimensions nmaxevent " + "(%i), was %i", + nmaxevent_, observedEventsStdDev.size() + ); checkSigmaPositivity(observedEventsStdDev, "observedEventsStdDev"); for (int ie = 0; ie < nmaxevent_; ++ie) - observed_events_std_dev_.at(iz + ie*nztrue_) = observedEventsStdDev.at(ie); + observed_events_std_dev_.at(iz + ie * nztrue_) + = observedEventsStdDev.at(ie); } void ExpData::setObservedEventsStdDev(const realtype stdDev, int iz) { checkSigmaPositivity(stdDev, "stdDev"); for (int ie = 0; ie < nmaxevent_; ++ie) - observed_events_std_dev_.at(iz + ie*nztrue_) = stdDev; + observed_events_std_dev_.at(iz + ie * nztrue_) = stdDev; } bool ExpData::isSetObservedEventsStdDev(int ie, int iz) const { @@ -274,9 +332,9 @@ std::vector const& ExpData::getObservedEventsStdDev() const { return observed_events_std_dev_; } -const realtype *ExpData::getObservedEventsStdDevPtr(int ie) const { +realtype const* ExpData::getObservedEventsStdDevPtr(int ie) const { if (!observed_events_std_dev_.empty()) - return &observed_events_std_dev_.at(ie*nztrue_); + return &observed_events_std_dev_.at(ie * nztrue_); return nullptr; } @@ -287,200 +345,219 @@ void ExpData::applyDimensions() { } void ExpData::applyDataDimension() { - observed_data_.resize(nt()*nytrue_, getNaN()); - observed_data_std_dev_.resize(nt()*nytrue_, getNaN()); + observed_data_.resize(nt() * nytrue_, getNaN()); + observed_data_std_dev_.resize(nt() * nytrue_, getNaN()); } void ExpData::applyEventDimension() { - observed_events_.resize(nmaxevent_*nztrue_, getNaN()); - observed_events_std_dev_.resize(nmaxevent_*nztrue_, getNaN()); + observed_events_.resize(nmaxevent_ * nztrue_, getNaN()); + observed_events_std_dev_.resize(nmaxevent_ * nztrue_, getNaN()); } -void ExpData::checkDataDimension(std::vector const& input, const char *fieldname) const { - if (input.size() != (unsigned) nt()*nytrue_ && !input.empty()) - throw AmiException("Input %s did not match dimensions nt (%i) x nytrue (%i), was %i", fieldname, nt(), nytrue_, input.size()); +void ExpData::checkDataDimension( + std::vector const& input, char const* fieldname +) const { + if (input.size() != (unsigned)nt() * nytrue_ && !input.empty()) + throw AmiException( + "Input %s did not match dimensions nt (%i) x nytrue (%i), was %i", + fieldname, nt(), nytrue_, input.size() + ); } -void ExpData::checkEventsDimension(std::vector const& input, const char *fieldname) const { - if (input.size() != (unsigned) nmaxevent_*nztrue_ && !input.empty()) - throw AmiException("Input %s did not match dimensions nt (%i) x nytrue (%i), was %i", fieldname, nmaxevent_, nztrue_, input.size()); +void ExpData::checkEventsDimension( + std::vector const& input, char const* fieldname +) const { + if (input.size() != (unsigned)nmaxevent_ * nztrue_ && !input.empty()) + throw AmiException( + "Input %s did not match dimensions nt (%i) x nytrue (%i), was %i", + fieldname, nmaxevent_, nztrue_, input.size() + ); } -void checkSigmaPositivity(std::vector const& sigmaVector, const char *vectorName) { +void checkSigmaPositivity( + std::vector const& sigmaVector, char const* vectorName +) { for (auto&& sigma : sigmaVector) checkSigmaPositivity(sigma, vectorName); } -void checkSigmaPositivity(const realtype sigma, const char *sigmaName) { +void checkSigmaPositivity(const realtype sigma, char const* sigmaName) { if (sigma <= 0.0) - throw AmiException("Encountered sigma <= 0 in %s! value: %f", sigmaName, sigma); + throw AmiException( + "Encountered sigma <= 0 in %s! value: %f", sigmaName, sigma + ); } -int ExpData::nytrue() const -{ - return nytrue_; -} +int ExpData::nytrue() const { return nytrue_; } -int ExpData::nztrue() const -{ - return nztrue_; -} +int ExpData::nztrue() const { return nztrue_; } -int ExpData::nmaxevent() const -{ - return nmaxevent_; -} +int ExpData::nmaxevent() const { return nmaxevent_; } -ConditionContext::ConditionContext(Model *model, const ExpData *edata, - FixedParameterContext fpc) - : model_(model), - original_parameters_(model->getParameters()), - original_fixed_parameters_(model->getFixedParameters()), - original_timepoints_(model->getTimepoints()), - original_parameter_list_(model->getParameterList()), - original_scaling_(model->getParameterScale()), - original_reinitialize_fixed_parameter_initial_states_( +ConditionContext::ConditionContext( + Model* model, ExpData const* edata, FixedParameterContext fpc +) + : model_(model) + , original_parameters_(model->getParameters()) + , original_fixed_parameters_(model->getFixedParameters()) + , original_tstart_(model->t0()) + , original_timepoints_(model->getTimepoints()) + , original_parameter_list_(model->getParameterList()) + , original_scaling_(model->getParameterScale()) + , original_reinitialize_fixed_parameter_initial_states_( model->getReinitializeFixedParameterInitialStates() - && model->getReinitializationStateIdxs().empty()), - original_reinitialization_state_idxs( - model->getReinitializationStateIdxs()) -{ - if(model->hasCustomInitialStates()) + && model->getReinitializationStateIdxs().empty() + ) + , original_reinitialization_state_idxs(model->getReinitializationStateIdxs() + ) { + if (model->hasCustomInitialStates()) original_x0_ = model->getInitialStates(); - if(model->hasCustomInitialStateSensitivities()) + if (model->hasCustomInitialStateSensitivities()) original_sx0_ = model->getInitialStateSensitivities(); applyCondition(edata, fpc); } -ConditionContext::~ConditionContext() -{ - restore(); -} +ConditionContext::~ConditionContext() { restore(); } -void ConditionContext::applyCondition(const ExpData *edata, - FixedParameterContext fpc) -{ - if(!edata) +void ConditionContext::applyCondition( + ExpData const* edata, FixedParameterContext fpc +) { + if (!edata) return; // this needs to go first, otherwise nplist will not have the right // dimension for all other fields that depend on Model::nplist - if(!edata->plist.empty()) + if (!edata->plist.empty()) model_->setParameterList(edata->plist); // this needs to go second as setParameterScale will reset sx0 - if(!edata->pscale.empty()) { - if(edata->pscale.size() != (unsigned) model_->np()) - throw AmiException("Number of parameters (%d) in model does not" - " match ExpData (%zd).", - model_->np(), edata->pscale.size()); + if (!edata->pscale.empty()) { + if (edata->pscale.size() != (unsigned)model_->np()) + throw AmiException( + "Number of parameters (%d) in model does not" + " match ExpData (%zd).", + model_->np(), edata->pscale.size() + ); model_->setParameterScale(edata->pscale); } - if(!edata->x0.empty()) { - if(edata->x0.size() != (unsigned) model_->nx_rdata) - throw AmiException("Number of initial conditions (%d) in model does" - " not match ExpData (%zd).", - model_->nx_rdata, edata->x0.size()); - model_->setInitialStates(edata->x0); + // this needs to be set in the model before handling initial state + // sensitivities, which may be unscaled using model parameter values + if (!edata->parameters.empty()) { + if (edata->parameters.size() != (unsigned)model_->np()) + throw AmiException( + "Number of parameters (%d) in model does not" + " match ExpData (%zd).", + model_->np(), edata->parameters.size() + ); + model_->setParameters(edata->parameters); } - if(!edata->sx0.empty()) { - if(edata->sx0.size() != (unsigned) model_->nx_rdata * model_->nplist()) - throw AmiException("Number of initial conditions sensitivities (%d)" - " in model does not match ExpData (%zd).", - model_->nx_rdata * model_->nplist(), - edata->sx0.size()); - model_->setInitialStateSensitivities(edata->sx0); + if (!edata->x0.empty()) { + if (edata->x0.size() != (unsigned)model_->nx_rdata) + throw AmiException( + "Number of initial conditions (%d) in model does" + " not match ExpData (%zd).", + model_->nx_rdata, edata->x0.size() + ); + model_->setInitialStates(edata->x0); } - if(!edata->parameters.empty()) { - if(edata->parameters.size() != (unsigned) model_->np()) - throw AmiException("Number of parameters (%d) in model does not" - " match ExpData (%zd).", - model_->np(), edata->parameters.size()); - model_->setParameters(edata->parameters); + if (!edata->sx0.empty()) { + if (edata->sx0.size() != (unsigned)model_->nx_rdata * model_->nplist()) + throw AmiException( + "Number of initial conditions sensitivities (%d)" + " in model does not match ExpData (%zd).", + model_->nx_rdata * model_->nplist(), edata->sx0.size() + ); + model_->setInitialStateSensitivities(edata->sx0); } model_->setReinitializeFixedParameterInitialStates( - edata->reinitializeFixedParameterInitialStates); + edata->reinitializeFixedParameterInitialStates + ); switch (fpc) { case FixedParameterContext::simulation: - if (!edata->fixedParameters.empty()) { - // fixed parameter in model are superseded by those provided in - // edata - if (edata->fixedParameters.size() - != (unsigned)model_->nk()) - throw AmiException("Number of fixed parameters (%d) in model does" - "not match ExpData (%zd).", - model_->nk(), edata->fixedParameters.size()); - model_->setFixedParameters(edata->fixedParameters); - if(!edata->reinitializeFixedParameterInitialStates) - model_->setReinitializationStateIdxs( - edata->reinitialization_state_idxs_sim); - } - break; + if (!edata->fixedParameters.empty()) { + // fixed parameter in model are superseded by those provided in + // edata + if (edata->fixedParameters.size() != (unsigned)model_->nk()) + throw AmiException( + "Number of fixed parameters (%d) in model does" + "not match ExpData (%zd).", + model_->nk(), edata->fixedParameters.size() + ); + model_->setFixedParameters(edata->fixedParameters); + if (!edata->reinitializeFixedParameterInitialStates) + model_->setReinitializationStateIdxs( + edata->reinitialization_state_idxs_sim + ); + } + break; case FixedParameterContext::preequilibration: - if (!edata->fixedParametersPreequilibration.empty()) { - // fixed parameter in model are superseded by those provided in - // edata - if (edata->fixedParametersPreequilibration.size() != - (unsigned)model_->nk()) - throw AmiException("Number of fixed parameters (%d) in model does" - "not match ExpData (preequilibration) (%zd).", - model_->nk(), - edata->fixedParametersPreequilibration.size()); - model_->setFixedParameters(edata->fixedParametersPreequilibration); - } - break; + if (!edata->fixedParametersPreequilibration.empty()) { + // fixed parameter in model are superseded by those provided in + // edata + if (edata->fixedParametersPreequilibration.size() + != (unsigned)model_->nk()) + throw AmiException( + "Number of fixed parameters (%d) in model does" + "not match ExpData (preequilibration) (%zd).", + model_->nk(), edata->fixedParametersPreequilibration.size() + ); + model_->setFixedParameters(edata->fixedParametersPreequilibration); + } + break; case FixedParameterContext::presimulation: - if (!edata->fixedParametersPresimulation.empty()) { - // fixed parameter in model are superseded by those provided in - // edata - if (edata->fixedParametersPresimulation.size() - != (unsigned)model_->nk()) - throw AmiException("Number of fixed parameters (%d) in model does" - " not match ExpData (presimulation) (%zd).", - model_->nk(), - edata->fixedParametersPresimulation.size()); - model_->setFixedParameters(edata->fixedParametersPresimulation); - if(!edata->reinitializeFixedParameterInitialStates) - model_->setReinitializationStateIdxs( - edata->reinitialization_state_idxs_presim); - } - break; + if (!edata->fixedParametersPresimulation.empty()) { + // fixed parameter in model are superseded by those provided in + // edata + if (edata->fixedParametersPresimulation.size() + != (unsigned)model_->nk()) + throw AmiException( + "Number of fixed parameters (%d) in model does" + " not match ExpData (presimulation) (%zd).", + model_->nk(), edata->fixedParametersPresimulation.size() + ); + model_->setFixedParameters(edata->fixedParametersPresimulation); + if (!edata->reinitializeFixedParameterInitialStates) + model_->setReinitializationStateIdxs( + edata->reinitialization_state_idxs_presim + ); + } + break; } - if(edata->nt()) { + model_->setT0(edata->tstart_); + if (edata->nt()) { // fixed parameter in model are superseded by those provided in edata model_->setTimepoints(edata->getTimepoints()); } } -void ConditionContext::restore() -{ +void ConditionContext::restore() { // parameter list has to be set before initial state sensitivities model_->setParameterList(original_parameter_list_); // parameter scale has to be set before initial state sensitivities model_->setParameterScale(original_scaling_); - if(!original_x0_.empty()) + if (!original_x0_.empty()) model_->setInitialStates(original_x0_); - if(!original_sx0_.empty()) + if (!original_sx0_.empty()) model_->setUnscaledInitialStateSensitivities(original_sx0_); model_->setParameters(original_parameters_); model_->setFixedParameters(original_fixed_parameters_); + model_->setT0(original_tstart_); model_->setTimepoints(original_timepoints_); model_->setReinitializeFixedParameterInitialStates( - original_reinitialize_fixed_parameter_initial_states_); + original_reinitialize_fixed_parameter_initial_states_ + ); model_->setReinitializationStateIdxs(original_reinitialization_state_idxs); - } - } // namespace amici diff --git a/deps/AMICI/src/exception.cpp b/deps/AMICI/src/exception.cpp index 2d1f72ecd..3a5811e4f 100644 --- a/deps/AMICI/src/exception.cpp +++ b/deps/AMICI/src/exception.cpp @@ -5,62 +5,63 @@ #include #include - namespace amici { -AmiException::AmiException() -{ - storeBacktrace(12); +AmiException::AmiException(int const first_frame) { + storeBacktrace(12, first_frame); } -AmiException::AmiException(const char *fmt, ...) - : AmiException() -{ +AmiException::AmiException(char const* fmt, ...) + : AmiException(4) { va_list ap; va_start(ap, fmt); storeMessage(fmt, ap); va_end(ap); } -const char *AmiException::what() const noexcept { - return msg_.data(); -} +char const* AmiException::what() const noexcept { return msg_.data(); } -const char *AmiException::getBacktrace() const { - return trace_.data(); -} +char const* AmiException::getBacktrace() const { return trace_.data(); } -void AmiException::storeBacktrace(const int nMaxFrames) { - snprintf(trace_.data(), trace_.size(), "%s", - backtraceString(nMaxFrames).c_str()); +void AmiException::storeBacktrace(int const nMaxFrames, int const first_frame) { + snprintf( + trace_.data(), trace_.size(), "%s", + backtraceString(nMaxFrames, first_frame).c_str() + ); } -void AmiException::storeMessage(const char *fmt, va_list argptr) -{ +void AmiException::storeMessage(char const* fmt, va_list argptr) { vsnprintf(msg_.data(), msg_.size(), fmt, argptr); } -CvodeException::CvodeException(const int error_code, const char *function) : - AmiException("Cvode routine %s failed with error code %i",function,error_code){} - -IDAException::IDAException(const int error_code, const char *function) : - AmiException("IDA routine %s failed with error code %i",function,error_code){} - -IntegrationFailure::IntegrationFailure(int code, realtype t) : - AmiException("AMICI failed to integrate the forward problem"), - error_code(code), time(t) {} - -IntegrationFailureB::IntegrationFailureB(int code, realtype t) : - AmiException("AMICI failed to integrate the backward problem"), - error_code(code), time(t) {} - -NewtonFailure::NewtonFailure(int code, const char *function) : - AmiException("NewtonSolver routine %s failed with error code %i",function,code) { +CvodeException::CvodeException(int const error_code, char const* function) + : AmiException( + "Cvode routine %s failed with error code %i", function, error_code + ) {} + +IDAException::IDAException(int const error_code, char const* function) + : AmiException( + "IDA routine %s failed with error code %i", function, error_code + ) {} + +IntegrationFailure::IntegrationFailure(int code, realtype t) + : AmiException("AMICI failed to integrate the forward problem") + , error_code(code) + , time(t) {} + +IntegrationFailureB::IntegrationFailureB(int code, realtype t) + : AmiException("AMICI failed to integrate the backward problem") + , error_code(code) + , time(t) {} + +NewtonFailure::NewtonFailure(int code, char const* function) + : AmiException( + "NewtonSolver routine %s failed with error code %i", function, code + ) { error_code = code; } -SetupFailure::SetupFailure(const char *fmt, ...) -{ +SetupFailure::SetupFailure(char const* fmt, ...) { va_list ap; va_start(ap, fmt); storeMessage(fmt, ap); diff --git a/deps/AMICI/src/forwardproblem.cpp b/deps/AMICI/src/forwardproblem.cpp index 8147edce0..c70a9074b 100644 --- a/deps/AMICI/src/forwardproblem.cpp +++ b/deps/AMICI/src/forwardproblem.cpp @@ -1,11 +1,10 @@ #include "amici/forwardproblem.h" -#include "amici/cblas.h" +#include "amici/edata.h" +#include "amici/exception.h" #include "amici/misc.h" #include "amici/model.h" #include "amici/solver.h" -#include "amici/exception.h" -#include "amici/edata.h" #include "amici/steadystateproblem.h" #include @@ -14,28 +13,29 @@ namespace amici { -ForwardProblem::ForwardProblem(const ExpData *edata, Model *model, - Solver *solver, const SteadystateProblem *preeq) - : model(model), - solver(solver), - edata(edata), - nroots_(static_cast(model->ne), 0), - rootvals_(static_cast(model->ne), 0.0), - rval_tmp_(static_cast(model->ne), 0.0), - dJydx_(model->nJ * model->nx_solver * model->nt(), 0.0), - dJzdx_(model->nJ * model->nx_solver * model->nMaxEvent(), 0.0), - t_(model->t0()), - roots_found_(model->ne, 0), - x_(model->nx_solver), - x_old_(model->nx_solver), - dx_(model->nx_solver), - dx_old_(model->nx_solver), - xdot_(model->nx_solver), - xdot_old_(model->nx_solver), - sx_(model->nx_solver,model->nplist()), - sdx_(model->nx_solver,model->nplist()), - stau_(model->nplist()) -{ +ForwardProblem::ForwardProblem( + ExpData const* edata, Model* model, Solver* solver, + SteadystateProblem const* preeq +) + : model(model) + , solver(solver) + , edata(edata) + , nroots_(gsl::narrow(model->ne), 0) + , rootvals_(gsl::narrow(model->ne), 0.0) + , rval_tmp_(gsl::narrow(model->ne), 0.0) + , dJydx_(model->nJ * model->nx_solver * model->nt(), 0.0) + , dJzdx_(model->nJ * model->nx_solver * model->nMaxEvent(), 0.0) + , t_(model->t0()) + , roots_found_(model->ne, 0) + , x_(model->nx_solver) + , x_old_(model->nx_solver) + , dx_(model->nx_solver) + , dx_old_(model->nx_solver) + , xdot_(model->nx_solver) + , xdot_old_(model->nx_solver) + , sx_(model->nx_solver, model->nplist()) + , sdx_(model->nx_solver, model->nplist()) + , stau_(model->nplist()) { if (preeq) { x_ = preeq->getState(); sx_ = preeq->getStateSensitivity(); @@ -50,11 +50,14 @@ void ForwardProblem::workForwardProblem() { /* if preequilibration was done, model was already initialized */ if (!preequilibrated_) - model->initialize(x_, dx_, sx_, sdx_, - solver->getSensitivityOrder() >= - SensitivityOrder::first); - else if (model->ne) - model->initHeaviside(x_, dx_); + model->initialize( + x_, dx_, sx_, sdx_, + solver->getSensitivityOrder() >= SensitivityOrder::first, + roots_found_ + ); + else if (model->ne) { + model->initEvents(x_, dx_, roots_found_); + } /* compute initial time and setup solver for (pre-)simulation */ auto t0 = model->t0(); @@ -62,6 +65,12 @@ void ForwardProblem::workForwardProblem() { t0 -= edata->t_presim; solver->setup(t0, model, x_, dx_, sx_, sdx_); + if (model->ne + && std::any_of(roots_found_.begin(), roots_found_.end(), [](int rf) { + return rf == 1; + })) + handleEvent(&t0, false, true); + /* perform presimulation if necessary */ if (presimulate) { if (solver->computingASA()) @@ -69,9 +78,16 @@ void ForwardProblem::workForwardProblem() { " is currently not implemented."); handlePresimulation(); t_ = model->t0(); - if (model->ne) - model->initHeaviside(x_, dx_); + if (model->ne) { + model->initEvents(x_, dx_, roots_found_); + if (std::any_of( + roots_found_.begin(), roots_found_.end(), + [](int rf) { return rf == 1; } + )) + handleEvent(&t0, false, true); + } } + /* when computing adjoint sensitivity analysis with presimulation, we need to store sx after the reinitialization after preequilibration but before reinitialization after presimulation. As presimulation with ASA @@ -89,10 +105,9 @@ void ForwardProblem::workForwardProblem() { wont harm. when computing ASA, we only want to update here, if we didn't update before presimulation (if applicable). */ - if (solver->computingFSA() || (solver->computingASA() && !presimulate )) + if (solver->computingFSA() || (solver->computingASA() && !presimulate)) sx_ = solver->getStateSensitivity(model->t0()); - /* store initial state and sensitivity*/ initial_state_ = getSimulationState(); @@ -114,7 +129,7 @@ void ForwardProblem::workForwardProblem() { /* clustering of roots => turn off rootfinding */ solver->turnOffRootFinding(); } else if (status == AMICI_ROOT_RETURN) { - handleEvent(&tlastroot_, false); + handleEvent(&tlastroot_, false, false); } } } @@ -127,8 +142,7 @@ void ForwardProblem::workForwardProblem() { } } -void ForwardProblem::handlePresimulation() -{ +void ForwardProblem::handlePresimulation() { // Are there dedicated condition preequilibration parameters provided? ConditionContext cond(model, edata, FixedParameterContext::presimulation); solver->updateAndReinitStatesAndSensitivities(model); @@ -137,8 +151,9 @@ void ForwardProblem::handlePresimulation() solver->writeSolution(&t_, x_, dx_, sx_, dx_); } - -void ForwardProblem::handleEvent(realtype *tlastroot, const bool seflag) { +void ForwardProblem::handleEvent( + realtype* tlastroot, bool const seflag, bool const initial_event +) { /* store Heaviside information at event occurrence */ model->froot(t_, x_, dx_, rootvals_); @@ -146,14 +161,14 @@ void ForwardProblem::handleEvent(realtype *tlastroot, const bool seflag) { discs_.push_back(t_); /* extract and store which events occurred */ - if (!seflag) { + if (!seflag && !initial_event) { solver->getRootInfo(roots_found_.data()); } root_idx_.push_back(roots_found_); rval_tmp_ = rootvals_; - if (!seflag) { + if (!seflag && !initial_event) { /* only check this in the first event fired, otherwise this will always * be true */ if (t_ == *tlastroot) { @@ -165,24 +180,24 @@ void ForwardProblem::handleEvent(realtype *tlastroot, const bool seflag) { *tlastroot = t_; } - if(model->nz>0) + if (model->nz > 0) storeEvent(); /* 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) { /* store x and xdot to compute jump in sensitivities */ - x_old_ = x_; + x_old_.copy(x_); } if (solver->computingFSA()) { model->fxdot(t_, x_, dx_, xdot_); - xdot_old_ = xdot_; - dx_old_ = dx_; + xdot_old_.copy(xdot_); + dx_old_.copy(dx_); /* compute event-time derivative only for primary events, we get * into trouble with multiple simultaneously firing events here (but * is this really well defined then?), in that case just use the * last ie and hope for the best. */ - if (!seflag) { + if (!seflag && !initial_event) { for (int ie = 0; ie < model->ne; ie++) { if (roots_found_.at(ie) == 1) { /* only consider transitions false -> true */ @@ -190,6 +205,8 @@ void ForwardProblem::handleEvent(realtype *tlastroot, const bool seflag) { } } } + if (initial_event) // t0 has no parameter dependency + std::fill(stau_.begin(), stau_.end(), 0.0); } else if (solver->computingASA()) { /* store x to compute jump in discontinuity */ x_disc_.push_back(x_); @@ -197,7 +214,8 @@ void ForwardProblem::handleEvent(realtype *tlastroot, const bool seflag) { xdot_old_disc_.push_back(xdot_old_); } - model->updateHeaviside(roots_found_); + if (!initial_event) + model->updateHeaviside(roots_found_); applyEventBolus(); @@ -234,12 +252,14 @@ void ForwardProblem::handleEvent(realtype *tlastroot, const bool seflag) { if (secondevent > 0) { /* Secondary events may result in wrong forward sensitivities, * if the secondary event has a bolus... */ - if (solver->computingFSA()) - solver->app->warning("AMICI:simulation", - "Secondary event was triggered. Depending on " - "the bolus of the secondary event, forward " - "sensitivities can be incorrect."); - handleEvent(tlastroot, true); + if (solver->computingFSA() && solver->logger) + solver->logger->log( + LogSeverity::warning, "SECONDARY_EVENT", + "Secondary event was triggered. Depending on " + "the bolus of the secondary event, forward " + "sensitivities can be incorrect." + ); + handleEvent(tlastroot, true, false); } /* only reinitialise in the first event fired */ @@ -276,15 +296,16 @@ void ForwardProblem::storeEvent() { continue; /* only consider transitions false -> true or event filling */ - if (roots_found_.at(ie) != 1 && - t_ != model->getTimepoint(model->nt() - 1)) { + 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); + model->getAdjointStateEventUpdate( + slice(dJzdx_, nroots_.at(ie), model->nx_solver * model->nJ), ie, + nroots_.at(ie), t_, x_, *edata + ); nroots_.at(ie)++; } @@ -315,12 +336,12 @@ void ForwardProblem::applyEventSensiBolusFSA() { for (int ie = 0; ie < model->ne; ie++) if (roots_found_.at(ie) == 1) // only consider transitions false -> true /* */ - model->addStateSensitivityEventUpdate(sx_, ie, t_, x_old_, xdot_, - xdot_old_, stau_); + model->addStateSensitivityEventUpdate( + sx_, ie, t_, x_old_, xdot_, xdot_old_, stau_ + ); } -void ForwardProblem::getAdjointUpdates(Model &model, - const ExpData &edata) { +void ForwardProblem::getAdjointUpdates(Model& model, ExpData const& edata) { for (int it = 0; it < model.nt(); it++) { if (std::isinf(model.getTimepoint(it))) return; diff --git a/deps/AMICI/src/hdf5.cpp b/deps/AMICI/src/hdf5.cpp index 7248a5c96..e0cdde88c 100644 --- a/deps/AMICI/src/hdf5.cpp +++ b/deps/AMICI/src/hdf5.cpp @@ -7,23 +7,14 @@ * issues. */ -#include "amici/hdf5.h" -#include "amici/amici.h" +#include -#include +#include +#include +#include +#include -#include -#include -#include -#ifdef AMI_HDF5_H_DEBUG -#ifndef __APPLE__ -#include -#else -#include -#endif -#endif -#include -#include +#include namespace amici { @@ -35,25 +26,27 @@ namespace hdf5 { * @param n * @param model */ -void checkMeasurementDimensionsCompatible(hsize_t m, hsize_t n, - Model const& model) { +void checkMeasurementDimensionsCompatible( + hsize_t m, hsize_t n, Model const& model +) { bool compatible = true; // if this is rank 1, n and m can be swapped if (n == 1) { - compatible &= (n == (unsigned)model.nt() || n == (unsigned)model.nytrue); - compatible &= (m == (unsigned)model.nytrue || m == (unsigned)model.nt()); + compatible + &= (n == (unsigned)model.nt() || n == (unsigned)model.nytrue); + compatible + &= (m == (unsigned)model.nytrue || m == (unsigned)model.nt()); compatible &= (m * n == (unsigned)model.nytrue * model.nt()); } else { compatible &= (n == (unsigned)model.nytrue); compatible &= (m == (unsigned)model.nt()); } - if(!compatible) + if (!compatible) throw(AmiException("HDF5 measurement data does not match model. " "Incompatible dimensions.")); } - /** * @brief assertEventDimensionsCompatible * @param m @@ -65,26 +58,31 @@ void checkEventDimensionsCompatible(hsize_t m, hsize_t n, Model const& model) { // if this is rank 1, n and m can be swapped if (n == 1) { - compatible &= (n == (unsigned)model.nMaxEvent() || - n == (unsigned)model.nztrue); - compatible &= (m == (unsigned)model.nztrue || - m == (unsigned)model.nMaxEvent()); + compatible + &= (n == (unsigned)model.nMaxEvent() || n == (unsigned)model.nztrue + ); + compatible + &= (m == (unsigned)model.nztrue || m == (unsigned)model.nMaxEvent() + ); compatible &= (m * n == (unsigned)model.nytrue * model.nMaxEvent()); } else { compatible &= (n == (unsigned)model.nztrue); compatible &= (m == (unsigned)model.nMaxEvent()); } - if(!compatible) + if (!compatible) throw(AmiException("HDF5 event data does not match model. " "Incompatible dimensions.")); } - -void createGroup(H5::H5File const& file, - std::string const& groupPath, - bool recursively) { - +void createGroup( + H5::H5File const& file, std::string const& groupPath, bool recursively +) { +#if H5_VERSION_GE(1, 10, 6) + H5::LinkCreatPropList lcpl; + lcpl.setCreateIntermediateGroup(recursively); + file.createGroup(groupPath.c_str(), lcpl); +#else auto groupCreationPropertyList = H5P_DEFAULT; if (recursively) { @@ -92,177 +90,206 @@ void createGroup(H5::H5File const& file, H5Pset_create_intermediate_group(groupCreationPropertyList, 1); } - hid_t group = H5Gcreate(file.getId(), groupPath.c_str(), - groupCreationPropertyList, - H5P_DEFAULT, H5P_DEFAULT); + hid_t group = H5Gcreate( + file.getId(), groupPath.c_str(), groupCreationPropertyList, H5P_DEFAULT, + H5P_DEFAULT + ); H5Pclose(groupCreationPropertyList); if (group < 0) - throw(AmiException("Failed to create group in hdf5CreateGroup: %s", - groupPath.c_str())); + throw(AmiException( + "Failed to create group in hdf5CreateGroup: %s", groupPath.c_str() + )); H5Gclose(group); +#endif } -std::unique_ptr readSimulationExpData(std::string const& hdf5Filename, - std::string const& hdf5Root, - Model const& model) { +std::unique_ptr readSimulationExpData( + std::string const& hdf5Filename, std::string const& hdf5Root, + Model const& model +) { H5::H5File file(hdf5Filename.c_str(), H5F_ACC_RDONLY); hsize_t m, n; auto edata = std::unique_ptr(new ExpData(model)); - if(attributeExists(file, hdf5Root, "id")) { + if (attributeExists(file, hdf5Root, "id")) { edata->id = getStringAttribute(file, hdf5Root, "id"); } if (model.ny * model.nt() > 0) { - if(locationExists(file, hdf5Root + "/Y")) { + if (locationExists(file, hdf5Root + "/Y")) { auto my = getDoubleDataset2D(file, hdf5Root + "/Y", m, n); checkMeasurementDimensionsCompatible(m, n, model); edata->setObservedData(my); } else { - throw AmiException("Missing %s/Y in %s", hdf5Root.c_str(), - hdf5Filename.c_str()); + throw AmiException( + "Missing %s/Y in %s", hdf5Root.c_str(), hdf5Filename.c_str() + ); } - if(locationExists(file, hdf5Root + "/Sigma_Y")) { + if (locationExists(file, hdf5Root + "/Sigma_Y")) { auto sigmay = getDoubleDataset2D(file, hdf5Root + "/Sigma_Y", m, n); checkMeasurementDimensionsCompatible(m, n, model); edata->setObservedDataStdDev(sigmay); } else { - throw AmiException("Missing %s/Sigma_Y in %s", hdf5Root.c_str(), - hdf5Filename.c_str()); + throw AmiException( + "Missing %s/Sigma_Y in %s", hdf5Root.c_str(), + hdf5Filename.c_str() + ); } } if (model.nz * model.nMaxEvent() > 0) { - if(locationExists(file, hdf5Root + "/Z")) { + if (locationExists(file, hdf5Root + "/Z")) { auto mz = getDoubleDataset2D(file, hdf5Root + "/Z", m, n); checkEventDimensionsCompatible(m, n, model); edata->setObservedEvents(mz); } else { - throw AmiException("Missing %s/Z in %s", hdf5Root.c_str(), - hdf5Filename.c_str()); + throw AmiException( + "Missing %s/Z in %s", hdf5Root.c_str(), hdf5Filename.c_str() + ); } - if(locationExists(file, hdf5Root + "/Sigma_Z")) { + if (locationExists(file, hdf5Root + "/Sigma_Z")) { auto sigmaz = getDoubleDataset2D(file, hdf5Root + "/Sigma_Z", m, n); checkEventDimensionsCompatible(m, n, model); edata->setObservedEventsStdDev(sigmaz); } else { - throw AmiException("Missing %s/Sigma_Z in %s", hdf5Root.c_str(), - hdf5Filename.c_str()); + throw AmiException( + "Missing %s/Sigma_Z in %s", hdf5Root.c_str(), + hdf5Filename.c_str() + ); } } - if(locationExists(file, hdf5Root + "/condition")) { - edata->fixedParameters = getDoubleDataset1D(file, - hdf5Root + "/condition"); + if (locationExists(file, hdf5Root + "/condition")) { + edata->fixedParameters + = getDoubleDataset1D(file, hdf5Root + "/condition"); } - if(locationExists(file, hdf5Root + "/conditionPreequilibration")) { - edata->fixedParametersPreequilibration = getDoubleDataset1D( - file, hdf5Root + "/conditionPreequilibration"); + if (locationExists(file, hdf5Root + "/conditionPreequilibration")) { + edata->fixedParametersPreequilibration + = getDoubleDataset1D(file, hdf5Root + "/conditionPreequilibration"); } - if(locationExists(file, hdf5Root + "/conditionPresimulation")) { - edata->fixedParametersPresimulation = getDoubleDataset1D( - file, hdf5Root + "/conditionPresimulation"); + if (locationExists(file, hdf5Root + "/conditionPresimulation")) { + edata->fixedParametersPresimulation + = getDoubleDataset1D(file, hdf5Root + "/conditionPresimulation"); } - if(attributeExists(file, hdf5Root, "t_presim")) { + if (attributeExists(file, hdf5Root, "t_presim")) { edata->t_presim = getDoubleScalarAttribute(file, hdf5Root, "t_presim"); } - if(locationExists(file, hdf5Root + "/ts")) { + if (locationExists(file, hdf5Root + "/ts")) { edata->setTimepoints(getDoubleDataset1D(file, hdf5Root + "/ts")); } - if(attributeExists(file, hdf5Root, - "/reinitializeFixedParameterInitialStates")) { - edata->reinitializeFixedParameterInitialStates = static_cast( - getIntScalarAttribute(file, hdf5Root, - "/reinitializeFixedParameterInitialStates")); + if (attributeExists( + file, hdf5Root, "/reinitializeFixedParameterInitialStates" + )) { + edata->reinitializeFixedParameterInitialStates + = static_cast(getIntScalarAttribute( + file, hdf5Root, "/reinitializeFixedParameterInitialStates" + )); } return edata; } -void writeSimulationExpData(const ExpData &edata, H5::H5File const& file, - const std::string &hdf5Location) -{ +void writeSimulationExpData( + ExpData const& edata, H5::H5File const& file, + std::string const& hdf5Location +) { - if(!locationExists(file, hdf5Location)) + if (!locationExists(file, hdf5Location)) createGroup(file, hdf5Location); - H5LTset_attribute_string(file.getId(), hdf5Location.c_str(), "id", - edata.id.c_str()); - + H5LTset_attribute_string( + file.getId(), hdf5Location.c_str(), "id", edata.id.c_str() + ); if (edata.nt()) - createAndWriteDouble1DDataset(file, hdf5Location + "/ts", - edata.getTimepoints()); + createAndWriteDouble1DDataset( + file, hdf5Location + "/ts", edata.getTimepoints() + ); if (!edata.fixedParameters.empty()) - createAndWriteDouble1DDataset(file, hdf5Location + "/condition", - edata.fixedParameters); + createAndWriteDouble1DDataset( + file, hdf5Location + "/condition", edata.fixedParameters + ); if (!edata.fixedParametersPreequilibration.empty()) createAndWriteDouble1DDataset( - file, hdf5Location + "/conditionPreequilibration", - edata.fixedParametersPreequilibration); + file, hdf5Location + "/conditionPreequilibration", + edata.fixedParametersPreequilibration + ); if (!edata.fixedParametersPresimulation.empty()) createAndWriteDouble1DDataset( - file, hdf5Location + "/conditionPresimulation", - edata.fixedParametersPresimulation); + file, hdf5Location + "/conditionPresimulation", + edata.fixedParametersPresimulation + ); - H5LTset_attribute_double(file.getId(), hdf5Location.c_str(), "t_presim", - &edata.t_presim, 1); + H5LTset_attribute_double( + file.getId(), hdf5Location.c_str(), "t_presim", &edata.t_presim, 1 + ); if (!edata.getObservedData().empty()) createAndWriteDouble2DDataset( - file, hdf5Location + "/Y", edata.getObservedData(), - edata.nt(), edata.nytrue()); + file, hdf5Location + "/Y", edata.getObservedData(), edata.nt(), + edata.nytrue() + ); if (!edata.getObservedDataStdDev().empty()) createAndWriteDouble2DDataset( - file, hdf5Location + "/Sigma_Y", - edata.getObservedDataStdDev(), edata.nt(), edata.nytrue()); + file, hdf5Location + "/Sigma_Y", edata.getObservedDataStdDev(), + edata.nt(), edata.nytrue() + ); if (!edata.getObservedEvents().empty()) - createAndWriteDouble2DDataset(file, hdf5Location + "/Z", - edata.getObservedEvents(), - edata.nmaxevent(), edata.nztrue()); + createAndWriteDouble2DDataset( + file, hdf5Location + "/Z", edata.getObservedEvents(), + edata.nmaxevent(), edata.nztrue() + ); if (!edata.getObservedEventsStdDev().empty()) - createAndWriteDouble2DDataset(file, hdf5Location + "/Sigma_Z", - edata.getObservedEventsStdDev(), - edata.nmaxevent(), edata.nztrue()); + createAndWriteDouble2DDataset( + file, hdf5Location + "/Sigma_Z", edata.getObservedEventsStdDev(), + edata.nmaxevent(), edata.nztrue() + ); int int_attr = edata.reinitializeFixedParameterInitialStates; - H5LTset_attribute_int(file.getId(), hdf5Location.c_str(), - "reinitializeFixedParameterInitialStates", - &int_attr, 1); + H5LTset_attribute_int( + file.getId(), hdf5Location.c_str(), + "reinitializeFixedParameterInitialStates", &int_attr, 1 + ); } -void writeReturnData(const ReturnData &rdata, H5::H5File const& file, const std::string &hdf5Location) -{ +void writeReturnData( + ReturnData const& rdata, H5::H5File const& file, + std::string const& hdf5Location +) { - if(!locationExists(file, hdf5Location)) + if (!locationExists(file, hdf5Location)) createGroup(file, hdf5Location); if (!rdata.ts.empty()) createAndWriteDouble1DDataset(file, hdf5Location + "/t", rdata.ts); - H5LTset_attribute_string(file.getId(), hdf5Location.c_str(), "id", - rdata.id.c_str()); + H5LTset_attribute_string( + file.getId(), hdf5Location.c_str(), "id", rdata.id.c_str() + ); - H5LTset_attribute_double(file.getId(), hdf5Location.c_str(), - "llh", &rdata.llh, 1); - H5LTset_attribute_double(file.getId(), hdf5Location.c_str(), - "chi2", &rdata.chi2, 1); + H5LTset_attribute_double( + file.getId(), hdf5Location.c_str(), "llh", &rdata.llh, 1 + ); + H5LTset_attribute_double( + file.getId(), hdf5Location.c_str(), "chi2", &rdata.chi2, 1 + ); - H5LTset_attribute_int(file.getId(), hdf5Location.c_str(), - "status", &rdata.status, 1); + H5LTset_attribute_int( + file.getId(), hdf5Location.c_str(), "status", &rdata.status, 1 + ); if (!rdata.sllh.empty()) createAndWriteDouble1DDataset(file, hdf5Location + "/sllh", rdata.sllh); @@ -270,834 +297,1005 @@ void writeReturnData(const ReturnData &rdata, H5::H5File const& file, const std: if (!rdata.res.empty()) createAndWriteDouble1DDataset(file, hdf5Location + "/res", rdata.res); if (!rdata.sres.empty()) - createAndWriteDouble2DDataset(file, hdf5Location + "/sres", rdata.sres, - rdata.nt*rdata.nytrue, rdata.nplist); + createAndWriteDouble2DDataset( + file, hdf5Location + "/sres", rdata.sres, rdata.nt * rdata.nytrue, + rdata.nplist + ); if (!rdata.FIM.empty()) - createAndWriteDouble2DDataset(file, hdf5Location + "/FIM", - rdata.FIM, rdata.nplist, rdata.nplist); + createAndWriteDouble2DDataset( + file, hdf5Location + "/FIM", rdata.FIM, rdata.nplist, rdata.nplist + ); if (!rdata.x0.empty()) createAndWriteDouble1DDataset(file, hdf5Location + "/x0", rdata.x0); if (!rdata.x.empty()) - createAndWriteDouble2DDataset(file, hdf5Location + "/x", rdata.x, - rdata.nt, rdata.nx); + createAndWriteDouble2DDataset( + file, hdf5Location + "/x", rdata.x, rdata.nt, rdata.nx + ); if (!rdata.y.empty()) - createAndWriteDouble2DDataset(file, hdf5Location + "/y", rdata.y, - rdata.nt, rdata.ny); + createAndWriteDouble2DDataset( + file, hdf5Location + "/y", rdata.y, rdata.nt, rdata.ny + ); if (!rdata.z.empty()) - createAndWriteDouble2DDataset(file, hdf5Location + "/z", rdata.z, - rdata.nmaxevent, rdata.nz); + createAndWriteDouble2DDataset( + file, hdf5Location + "/z", rdata.z, rdata.nmaxevent, rdata.nz + ); if (!rdata.rz.empty()) - createAndWriteDouble2DDataset(file, hdf5Location + "/rz", rdata.rz, - rdata.nmaxevent, rdata.nz); + createAndWriteDouble2DDataset( + file, hdf5Location + "/rz", rdata.rz, rdata.nmaxevent, rdata.nz + ); if (!rdata.sigmay.empty()) - createAndWriteDouble2DDataset(file, hdf5Location + "/sigmay", - rdata.sigmay, rdata.nt, rdata.ny); + createAndWriteDouble2DDataset( + file, hdf5Location + "/sigmay", rdata.sigmay, rdata.nt, rdata.ny + ); if (!rdata.sigmaz.empty()) - createAndWriteDouble2DDataset(file, hdf5Location + "/sigmaz", - rdata.sigmaz, rdata.nmaxevent, rdata.nz); + createAndWriteDouble2DDataset( + file, hdf5Location + "/sigmaz", rdata.sigmaz, rdata.nmaxevent, + rdata.nz + ); if (!rdata.s2llh.empty()) - createAndWriteDouble2DDataset(file, hdf5Location + "/s2llh", - rdata.s2llh, rdata.nJ - 1, rdata.nplist); + createAndWriteDouble2DDataset( + file, hdf5Location + "/s2llh", rdata.s2llh, rdata.nJ - 1, + rdata.nplist + ); if (!rdata.sx0.empty()) - createAndWriteDouble2DDataset(file, hdf5Location + "/sx0", rdata.sx0, - rdata.nplist, rdata.nx); + createAndWriteDouble2DDataset( + file, hdf5Location + "/sx0", rdata.sx0, rdata.nplist, rdata.nx + ); if (!rdata.sx.empty()) - createAndWriteDouble3DDataset(file, hdf5Location + "/sx", rdata.sx, - rdata.nt, rdata.nplist, rdata.nx); + createAndWriteDouble3DDataset( + file, hdf5Location + "/sx", rdata.sx, rdata.nt, rdata.nplist, + rdata.nx + ); if (!rdata.sy.empty()) - createAndWriteDouble3DDataset(file, hdf5Location + "/sy", rdata.sy, - rdata.nt, rdata.nplist, rdata.ny); + createAndWriteDouble3DDataset( + file, hdf5Location + "/sy", rdata.sy, rdata.nt, rdata.nplist, + rdata.ny + ); if (!rdata.ssigmay.empty()) - createAndWriteDouble3DDataset(file, hdf5Location + "/ssigmay", - rdata.ssigmay, rdata.nt, - rdata.nplist, rdata.ny); + createAndWriteDouble3DDataset( + file, hdf5Location + "/ssigmay", rdata.ssigmay, rdata.nt, + rdata.nplist, rdata.ny + ); if (!rdata.sz.empty()) - createAndWriteDouble3DDataset(file, hdf5Location + "/sz", rdata.sz, - rdata.nmaxevent, rdata.nplist, rdata.nz); + createAndWriteDouble3DDataset( + file, hdf5Location + "/sz", rdata.sz, rdata.nmaxevent, rdata.nplist, + rdata.nz + ); if (!rdata.srz.empty()) - createAndWriteDouble3DDataset(file, hdf5Location + "/srz", rdata.srz, - rdata.nmaxevent, rdata.nplist, rdata.nz); + createAndWriteDouble3DDataset( + file, hdf5Location + "/srz", rdata.srz, rdata.nmaxevent, + rdata.nplist, rdata.nz + ); if (!rdata.ssigmaz.empty()) - createAndWriteDouble3DDataset(file, hdf5Location + "/ssigmaz", - rdata.ssigmaz, - rdata.nmaxevent, rdata.nplist, rdata.nz); + createAndWriteDouble3DDataset( + file, hdf5Location + "/ssigmaz", rdata.ssigmaz, rdata.nmaxevent, + rdata.nplist, rdata.nz + ); writeReturnDataDiagnosis(rdata, file, hdf5Location + "/diagnosis"); } -void writeReturnDataDiagnosis(const ReturnData &rdata, - H5::H5File const& file, - const std::string& hdf5Location) { +void writeReturnDataDiagnosis( + ReturnData const& rdata, H5::H5File const& file, + std::string const& hdf5Location +) { - if(!locationExists(file, hdf5Location)) + if (!locationExists(file, hdf5Location)) createGroup(file, hdf5Location); if (!rdata.xdot.empty()) createAndWriteDouble1DDataset(file, hdf5Location + "/xdot", rdata.xdot); if (!rdata.numsteps.empty()) - createAndWriteInt1DDataset(file, hdf5Location + "/numsteps", - rdata.numsteps); + createAndWriteInt1DDataset( + file, hdf5Location + "/numsteps", rdata.numsteps + ); if (!rdata.numrhsevals.empty()) - createAndWriteInt1DDataset(file, hdf5Location + "/numrhsevals", - rdata.numrhsevals); + createAndWriteInt1DDataset( + file, hdf5Location + "/numrhsevals", rdata.numrhsevals + ); if (!rdata.numerrtestfails.empty()) - createAndWriteInt1DDataset(file, hdf5Location + "/numerrtestfails", - rdata.numerrtestfails); + createAndWriteInt1DDataset( + file, hdf5Location + "/numerrtestfails", rdata.numerrtestfails + ); if (!rdata.numnonlinsolvconvfails.empty()) - createAndWriteInt1DDataset(file, - hdf5Location + "/numnonlinsolvconvfails", - rdata.numnonlinsolvconvfails); + createAndWriteInt1DDataset( + file, hdf5Location + "/numnonlinsolvconvfails", + rdata.numnonlinsolvconvfails + ); if (!rdata.order.empty()) createAndWriteInt1DDataset(file, hdf5Location + "/order", rdata.order); if (!rdata.numstepsB.empty()) - createAndWriteInt1DDataset(file, hdf5Location + "/numstepsB", - rdata.numstepsB); + createAndWriteInt1DDataset( + file, hdf5Location + "/numstepsB", rdata.numstepsB + ); if (!rdata.numrhsevalsB.empty()) - createAndWriteInt1DDataset(file, hdf5Location + "/numrhsevalsB", - rdata.numrhsevalsB); + createAndWriteInt1DDataset( + file, hdf5Location + "/numrhsevalsB", rdata.numrhsevalsB + ); if (!rdata.numerrtestfailsB.empty()) - createAndWriteInt1DDataset(file, hdf5Location + "/numerrtestfailsB", - rdata.numerrtestfailsB); + createAndWriteInt1DDataset( + file, hdf5Location + "/numerrtestfailsB", rdata.numerrtestfailsB + ); if (!rdata.numnonlinsolvconvfailsB.empty()) createAndWriteInt1DDataset( - file, hdf5Location + "/numnonlinsolvconvfailsB", - rdata.numnonlinsolvconvfailsB); + file, hdf5Location + "/numnonlinsolvconvfailsB", + rdata.numnonlinsolvconvfailsB + ); if (!rdata.preeq_status.empty()) { - std::vector preeq_status_int (rdata.preeq_status.size()); + std::vector preeq_status_int(rdata.preeq_status.size()); for (int i = 0; (unsigned)i < rdata.preeq_status.size(); i++) preeq_status_int[i] = static_cast(rdata.preeq_status[i]); - createAndWriteInt1DDataset(file, hdf5Location + "/preeq_status", - preeq_status_int); + createAndWriteInt1DDataset( + file, hdf5Location + "/preeq_status", preeq_status_int + ); } if (!rdata.preeq_numsteps.empty()) - createAndWriteInt1DDataset(file, hdf5Location + "/preeq_numsteps", - rdata.preeq_numsteps); - - if (!rdata.preeq_numlinsteps.empty()) - createAndWriteInt2DDataset(file, hdf5Location + "/preeq_numlinsteps", - rdata.preeq_numlinsteps, - rdata.newton_maxsteps, 2); + createAndWriteInt1DDataset( + file, hdf5Location + "/preeq_numsteps", rdata.preeq_numsteps + ); - H5LTset_attribute_int(file.getId(), hdf5Location.c_str(), - "preeq_numstepsB", &rdata.preeq_numstepsB, 1); + H5LTset_attribute_int( + file.getId(), hdf5Location.c_str(), "preeq_numstepsB", + &rdata.preeq_numstepsB, 1 + ); - H5LTset_attribute_double(file.getId(), hdf5Location.c_str(), - "preeq_cpu_time", &rdata.preeq_cpu_time, 1); + H5LTset_attribute_double( + file.getId(), hdf5Location.c_str(), "preeq_cpu_time", + &rdata.preeq_cpu_time, 1 + ); - H5LTset_attribute_double(file.getId(), hdf5Location.c_str(), - "preeq_cpu_timeB", &rdata.preeq_cpu_timeB, 1); + H5LTset_attribute_double( + file.getId(), hdf5Location.c_str(), "preeq_cpu_timeB", + &rdata.preeq_cpu_timeB, 1 + ); - H5LTset_attribute_double(file.getId(), hdf5Location.c_str(), "preeq_t", - &rdata.preeq_t, 1); + H5LTset_attribute_double( + file.getId(), hdf5Location.c_str(), "preeq_t", &rdata.preeq_t, 1 + ); - H5LTset_attribute_double(file.getId(), hdf5Location.c_str(), "preeq_wrms", - &rdata.preeq_wrms, 1); + H5LTset_attribute_double( + file.getId(), hdf5Location.c_str(), "preeq_wrms", &rdata.preeq_wrms, 1 + ); if (!rdata.posteq_status.empty()) { - std::vector posteq_status_int (rdata.posteq_status.size()); + std::vector posteq_status_int(rdata.posteq_status.size()); for (int i = 0; (unsigned)i < rdata.posteq_status.size(); i++) posteq_status_int[i] = static_cast(rdata.posteq_status[i]); - createAndWriteInt1DDataset(file, hdf5Location + "/posteq_status", - posteq_status_int); + createAndWriteInt1DDataset( + file, hdf5Location + "/posteq_status", posteq_status_int + ); } if (!rdata.posteq_numsteps.empty()) - createAndWriteInt1DDataset(file, hdf5Location + "/posteq_numsteps", - rdata.posteq_numsteps); + createAndWriteInt1DDataset( + file, hdf5Location + "/posteq_numsteps", rdata.posteq_numsteps + ); - if (!rdata.posteq_numlinsteps.empty()) - createAndWriteInt2DDataset(file, hdf5Location + "/posteq_numlinsteps", - rdata.posteq_numlinsteps, - rdata.newton_maxsteps, 2); + H5LTset_attribute_int( + file.getId(), hdf5Location.c_str(), "posteq_numstepsB", + &rdata.posteq_numstepsB, 1 + ); - H5LTset_attribute_int(file.getId(), hdf5Location.c_str(), - "posteq_numstepsB", &rdata.posteq_numstepsB, 1); + H5LTset_attribute_double( + file.getId(), hdf5Location.c_str(), "posteq_cpu_time", + &rdata.posteq_cpu_time, 1 + ); - H5LTset_attribute_double(file.getId(), hdf5Location.c_str(), - "posteq_cpu_time", &rdata.posteq_cpu_time, 1); + H5LTset_attribute_double( + file.getId(), hdf5Location.c_str(), "posteq_cpu_timeB", + &rdata.posteq_cpu_timeB, 1 + ); - H5LTset_attribute_double(file.getId(), hdf5Location.c_str(), - "posteq_cpu_timeB", &rdata.posteq_cpu_timeB, 1); + H5LTset_attribute_double( + file.getId(), hdf5Location.c_str(), "posteq_t", &rdata.posteq_t, 1 + ); - H5LTset_attribute_double(file.getId(), hdf5Location.c_str(), "posteq_t", - &rdata.posteq_t, 1); + H5LTset_attribute_double( + file.getId(), hdf5Location.c_str(), "posteq_wrms", &rdata.posteq_wrms, 1 + ); - H5LTset_attribute_double(file.getId(), hdf5Location.c_str(), "posteq_wrms", - &rdata.posteq_wrms, 1); + H5LTset_attribute_double( + file.getId(), hdf5Location.c_str(), "cpu_time", &rdata.cpu_time, 1 + ); - H5LTset_attribute_double(file.getId(), hdf5Location.c_str(), - "cpu_time", &rdata.cpu_time, 1); + H5LTset_attribute_double( + file.getId(), hdf5Location.c_str(), "cpu_timeB", &rdata.cpu_timeB, 1 + ); - H5LTset_attribute_double(file.getId(), hdf5Location.c_str(), - "cpu_timeB", &rdata.cpu_timeB, 1); + H5LTset_attribute_double( + file.getId(), hdf5Location.c_str(), "cpu_time_total", + &rdata.cpu_time_total, 1 + ); if (!rdata.J.empty()) - createAndWriteDouble2DDataset(file, hdf5Location + "/J", rdata.J, - rdata.nx, rdata.nx); - + createAndWriteDouble2DDataset( + file, hdf5Location + "/J", rdata.J, rdata.nx, rdata.nx + ); } - -void writeReturnData(ReturnData const& rdata, - std::string const& hdf5Filename, - std::string const& hdf5Location) { +void writeReturnData( + ReturnData const& rdata, std::string const& hdf5Filename, + std::string const& hdf5Location +) { auto file = createOrOpenForWriting(hdf5Filename); writeReturnData(rdata, file, hdf5Location); } -std::string getStringAttribute(H5::H5File const& file, - std::string const& optionsObject, - std::string const& attributeName) { +std::string getStringAttribute( + H5::H5File const& file, std::string const& optionsObject, + std::string const& attributeName +) { hsize_t dims; H5T_class_t type_class; size_t type_size; - auto status = H5LTget_attribute_info(file.getId(), optionsObject.c_str(), - attributeName.c_str(), &dims, - &type_class,&type_size); - if(status < 0) { - throw AmiException("Could get info for attribute %s for object %s.", - attributeName.c_str(), optionsObject.c_str()); + auto status = H5LTget_attribute_info( + file.getId(), optionsObject.c_str(), attributeName.c_str(), &dims, + &type_class, &type_size + ); + if (status < 0) { + throw AmiException( + "Could get info for attribute %s for object %s.", + attributeName.c_str(), optionsObject.c_str() + ); } std::vector value(type_size); - status = H5LTget_attribute_string(file.getId(), optionsObject.c_str(), - attributeName.c_str(), value.data()); + status = H5LTget_attribute_string( + file.getId(), optionsObject.c_str(), attributeName.c_str(), value.data() + ); #ifdef AMI_HDF5_H_DEBUG printf("%s: %s\n", attributeName.c_str(), value.data()); #endif - if(status < 0) - throw AmiException("Attribute %s not found for object %s.", - attributeName.c_str(), optionsObject.c_str()); + if (status < 0) + throw AmiException( + "Attribute %s not found for object %s.", attributeName.c_str(), + optionsObject.c_str() + ); return std::string(value.data()); } -double getDoubleScalarAttribute(H5::H5File const& file, - std::string const& optionsObject, - std::string const& attributeName) { +double getDoubleScalarAttribute( + H5::H5File const& file, std::string const& optionsObject, + std::string const& attributeName +) { double data = NAN; - herr_t status = H5LTget_attribute_double(file.getId(), optionsObject.c_str(), - attributeName.c_str(), &data); + herr_t status = H5LTget_attribute_double( + file.getId(), optionsObject.c_str(), attributeName.c_str(), &data + ); #ifdef AMI_HDF5_H_DEBUG printf("%s: %e\n", attributeName.c_str(), data); #endif - if(status < 0) - throw AmiException("Attribute %s not found for object %s.", - attributeName.c_str(), optionsObject.c_str()); + if (status < 0) + throw AmiException( + "Attribute %s not found for object %s.", attributeName.c_str(), + optionsObject.c_str() + ); return data; } -int getIntScalarAttribute(H5::H5File const& file, - std::string const& optionsObject, - std::string const& attributeName) { +int getIntScalarAttribute( + H5::H5File const& file, std::string const& optionsObject, + std::string const& attributeName +) { int data = 0; - herr_t status = H5LTget_attribute_int(file.getId(), optionsObject.c_str(), - attributeName.c_str(), &data); + herr_t status = H5LTget_attribute_int( + file.getId(), optionsObject.c_str(), attributeName.c_str(), &data + ); #ifdef AMI_HDF5_H_DEBUG printf("%s: %d\n", attributeName.c_str(), data); #endif - if(status < 0) - throw AmiException("Attribute %s not found for object %s.", - attributeName.c_str(), optionsObject.c_str()); + if (status < 0) + throw AmiException( + "Attribute %s not found for object %s.", attributeName.c_str(), + optionsObject.c_str() + ); return data; } - -void createAndWriteInt1DDataset(H5::H5File const& file, - std::string const& datasetName, - gsl::span buffer) { +void createAndWriteInt1DDataset( + H5::H5File const& file, std::string const& datasetName, + gsl::span buffer +) { hsize_t size = buffer.size(); H5::DataSpace dataspace(1, &size); - auto dataset = file.createDataSet(datasetName.c_str(), H5::PredType::NATIVE_INT, - dataspace); + auto dataset = file.createDataSet( + datasetName.c_str(), H5::PredType::NATIVE_INT, dataspace + ); dataset.write(buffer.data(), H5::PredType::NATIVE_INT); } -void createAndWriteDouble1DDataset(const H5::H5File &file, - std::string const& datasetName, - gsl::span buffer) { +void createAndWriteDouble1DDataset( + const H5::H5File& file, std::string const& datasetName, + gsl::span buffer +) { hsize_t size = buffer.size(); H5::DataSpace dataspace(1, &size); - auto dataset = file.createDataSet(datasetName.c_str(), H5::PredType::NATIVE_DOUBLE, - dataspace); + auto dataset = file.createDataSet( + datasetName.c_str(), H5::PredType::NATIVE_DOUBLE, dataspace + ); dataset.write(buffer.data(), H5::PredType::NATIVE_DOUBLE); } -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}; +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}; H5::DataSpace dataspace(2, adims); - auto dataset = file.createDataSet(datasetName.c_str(), H5::PredType::NATIVE_DOUBLE, - dataspace); + auto dataset = file.createDataSet( + datasetName.c_str(), H5::PredType::NATIVE_DOUBLE, dataspace + ); dataset.write(buffer.data(), H5::PredType::NATIVE_DOUBLE); } -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}; +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}; H5::DataSpace dataspace(2, adims); - auto dataset = file.createDataSet(datasetName.c_str(), H5::PredType::NATIVE_INT, - dataspace); + auto dataset = file.createDataSet( + datasetName.c_str(), H5::PredType::NATIVE_INT, dataspace + ); dataset.write(buffer.data(), H5::PredType::NATIVE_INT); } -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}; +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}; H5::DataSpace dataspace(3, adims); - auto dataset = file.createDataSet(datasetName.c_str(), H5::PredType::NATIVE_DOUBLE, - dataspace); + auto dataset = file.createDataSet( + datasetName.c_str(), H5::PredType::NATIVE_DOUBLE, dataspace + ); dataset.write(buffer.data(), H5::PredType::NATIVE_DOUBLE); } - -bool attributeExists(H5::H5File const& file, - const std::string &optionsObject, - const std::string &attributeName) { +bool attributeExists( + H5::H5File const& file, std::string const& optionsObject, + std::string const& attributeName +) { AMICI_H5_SAVE_ERROR_HANDLER; - int result = H5Aexists_by_name(file.getId(), optionsObject.c_str(), - attributeName.c_str(), H5P_DEFAULT); + int result = H5Aexists_by_name( + file.getId(), optionsObject.c_str(), attributeName.c_str(), H5P_DEFAULT + ); AMICI_H5_RESTORE_ERROR_HANDLER; return result > 0; } -bool attributeExists(H5::H5Object const& object, - const std::string &attributeName) { +bool attributeExists( + H5::H5Object const& object, std::string const& attributeName +) { AMICI_H5_SAVE_ERROR_HANDLER; int result = H5Aexists(object.getId(), attributeName.c_str()); AMICI_H5_RESTORE_ERROR_HANDLER; return result > 0; } -void writeSolverSettingsToHDF5(Solver const& solver, - std::string const& hdf5Filename, - std::string const& hdf5Location) { +void writeSolverSettingsToHDF5( + Solver const& solver, std::string const& hdf5Filename, + std::string const& hdf5Location +) { auto file = createOrOpenForWriting(hdf5Filename); writeSolverSettingsToHDF5(solver, file, hdf5Location); } -void writeSolverSettingsToHDF5(Solver const& solver, - H5::H5File const& file, - const std::string& hdf5Location) { - if(!locationExists(file, hdf5Location)) +void writeSolverSettingsToHDF5( + Solver const& solver, H5::H5File const& file, + std::string const& hdf5Location +) { + if (!locationExists(file, hdf5Location)) createGroup(file, hdf5Location); double dbuffer; int ibuffer; dbuffer = solver.getAbsoluteTolerance(); - H5LTset_attribute_double(file.getId(), hdf5Location.c_str(), - "atol", &dbuffer, 1); + H5LTset_attribute_double( + file.getId(), hdf5Location.c_str(), "atol", &dbuffer, 1 + ); dbuffer = solver.getRelativeTolerance(); - H5LTset_attribute_double(file.getId(), hdf5Location.c_str(), - "rtol", &dbuffer, 1); + H5LTset_attribute_double( + file.getId(), hdf5Location.c_str(), "rtol", &dbuffer, 1 + ); dbuffer = solver.getAbsoluteToleranceFSA(); - H5LTset_attribute_double(file.getId(), hdf5Location.c_str(), - "atol_fsa", &dbuffer, 1); + H5LTset_attribute_double( + file.getId(), hdf5Location.c_str(), "atol_fsa", &dbuffer, 1 + ); dbuffer = solver.getRelativeToleranceFSA(); - H5LTset_attribute_double(file.getId(), hdf5Location.c_str(), - "rtol_fsa", &dbuffer, 1); + H5LTset_attribute_double( + file.getId(), hdf5Location.c_str(), "rtol_fsa", &dbuffer, 1 + ); dbuffer = solver.getAbsoluteToleranceB(); - H5LTset_attribute_double(file.getId(), hdf5Location.c_str(), - "atolB", &dbuffer, 1); + H5LTset_attribute_double( + file.getId(), hdf5Location.c_str(), "atolB", &dbuffer, 1 + ); dbuffer = solver.getRelativeToleranceB(); - H5LTset_attribute_double(file.getId(), hdf5Location.c_str(), - "rtolB", &dbuffer, 1); + H5LTset_attribute_double( + file.getId(), hdf5Location.c_str(), "rtolB", &dbuffer, 1 + ); dbuffer = solver.getAbsoluteToleranceQuadratures(); - H5LTset_attribute_double(file.getId(), hdf5Location.c_str(), - "quad_atol", &dbuffer, 1); + H5LTset_attribute_double( + file.getId(), hdf5Location.c_str(), "quad_atol", &dbuffer, 1 + ); dbuffer = solver.getRelativeToleranceQuadratures(); - H5LTset_attribute_double(file.getId(), hdf5Location.c_str(), - "quad_rtol", &dbuffer, 1); + H5LTset_attribute_double( + file.getId(), hdf5Location.c_str(), "quad_rtol", &dbuffer, 1 + ); + + dbuffer = solver.getSteadyStateToleranceFactor(); + H5LTset_attribute_double( + file.getId(), hdf5Location.c_str(), "ss_tol_factor", &dbuffer, 1 + ); dbuffer = solver.getAbsoluteToleranceSteadyState(); - H5LTset_attribute_double(file.getId(), hdf5Location.c_str(), - "ss_atol", &dbuffer, 1); + H5LTset_attribute_double( + file.getId(), hdf5Location.c_str(), "ss_atol", &dbuffer, 1 + ); dbuffer = solver.getRelativeToleranceSteadyState(); - H5LTset_attribute_double(file.getId(), hdf5Location.c_str(), - "ss_rtol", &dbuffer, 1); + H5LTset_attribute_double( + file.getId(), hdf5Location.c_str(), "ss_rtol", &dbuffer, 1 + ); + + dbuffer = solver.getSteadyStateSensiToleranceFactor(); + H5LTset_attribute_double( + file.getId(), hdf5Location.c_str(), "ss_tol_sensi_factor", &dbuffer, 1 + ); dbuffer = solver.getAbsoluteToleranceSteadyStateSensi(); - H5LTset_attribute_double(file.getId(), hdf5Location.c_str(), - "ss_atol_sensi", &dbuffer, 1); + H5LTset_attribute_double( + file.getId(), hdf5Location.c_str(), "ss_atol_sensi", &dbuffer, 1 + ); dbuffer = solver.getRelativeToleranceSteadyStateSensi(); - H5LTset_attribute_double(file.getId(), hdf5Location.c_str(), - "ss_rtol_sensi", &dbuffer, 1); + H5LTset_attribute_double( + file.getId(), hdf5Location.c_str(), "ss_rtol_sensi", &dbuffer, 1 + ); dbuffer = solver.getMaxTime(); - H5LTset_attribute_double(file.getId(), hdf5Location.c_str(), - "maxtime", &dbuffer, 1); + H5LTset_attribute_double( + file.getId(), hdf5Location.c_str(), "maxtime", &dbuffer, 1 + ); - ibuffer = static_cast(solver.getMaxSteps()); - H5LTset_attribute_int(file.getId(), hdf5Location.c_str(), - "maxsteps", &ibuffer, 1); + ibuffer = gsl::narrow(solver.getMaxSteps()); + H5LTset_attribute_int( + file.getId(), hdf5Location.c_str(), "maxsteps", &ibuffer, 1 + ); - ibuffer = static_cast(solver.getMaxStepsBackwardProblem()); - H5LTset_attribute_int(file.getId(), hdf5Location.c_str(), - "maxstepsB", &ibuffer, 1); + ibuffer = gsl::narrow(solver.getMaxStepsBackwardProblem()); + H5LTset_attribute_int( + file.getId(), hdf5Location.c_str(), "maxstepsB", &ibuffer, 1 + ); ibuffer = static_cast(solver.getLinearMultistepMethod()); - H5LTset_attribute_int(file.getId(), hdf5Location.c_str(), - "lmm", &ibuffer, 1); + H5LTset_attribute_int( + file.getId(), hdf5Location.c_str(), "lmm", &ibuffer, 1 + ); ibuffer = static_cast(solver.getNonlinearSolverIteration()); - H5LTset_attribute_int(file.getId(), hdf5Location.c_str(), - "iter", &ibuffer, 1); + H5LTset_attribute_int( + file.getId(), hdf5Location.c_str(), "iter", &ibuffer, 1 + ); ibuffer = static_cast(solver.getStabilityLimitFlag()); - H5LTset_attribute_int(file.getId(), hdf5Location.c_str(), - "stldet", &ibuffer, 1); + H5LTset_attribute_int( + file.getId(), hdf5Location.c_str(), "stldet", &ibuffer, 1 + ); ibuffer = static_cast(solver.getStateOrdering()); - H5LTset_attribute_int(file.getId(), hdf5Location.c_str(), - "ordering", &ibuffer, 1); + H5LTset_attribute_int( + file.getId(), hdf5Location.c_str(), "ordering", &ibuffer, 1 + ); ibuffer = static_cast(solver.getInterpolationType()); - H5LTset_attribute_int(file.getId(), hdf5Location.c_str(), - "interpType", &ibuffer, 1); + H5LTset_attribute_int( + file.getId(), hdf5Location.c_str(), "interpType", &ibuffer, 1 + ); ibuffer = static_cast(solver.getSensitivityMethod()); - H5LTset_attribute_int(file.getId(), hdf5Location.c_str(), - "sensi_meth", &ibuffer, 1); + H5LTset_attribute_int( + file.getId(), hdf5Location.c_str(), "sensi_meth", &ibuffer, 1 + ); ibuffer = static_cast(solver.getSensitivityMethodPreequilibration()); - H5LTset_attribute_int(file.getId(), hdf5Location.c_str(), - "sensi_meth_preeq", &ibuffer, 1); + H5LTset_attribute_int( + file.getId(), hdf5Location.c_str(), "sensi_meth_preeq", &ibuffer, 1 + ); ibuffer = static_cast(solver.getSensitivityOrder()); - H5LTset_attribute_int(file.getId(), hdf5Location.c_str(), - "sensi", &ibuffer, 1); - - ibuffer = static_cast(solver.getNewtonMaxSteps()); - H5LTset_attribute_int(file.getId(), hdf5Location.c_str(), - "newton_maxsteps", &ibuffer, 1); + H5LTset_attribute_int( + file.getId(), hdf5Location.c_str(), "sensi", &ibuffer, 1 + ); - ibuffer = static_cast(solver.getPreequilibration()); - H5LTset_attribute_int(file.getId(), hdf5Location.c_str(), - "newton_preeq", &ibuffer, 1); + ibuffer = gsl::narrow(solver.getNewtonMaxSteps()); + H5LTset_attribute_int( + file.getId(), hdf5Location.c_str(), "newton_maxsteps", &ibuffer, 1 + ); ibuffer = static_cast(solver.getNewtonDampingFactorMode()); - H5LTset_attribute_int(file.getId(), hdf5Location.c_str(), - "newton_damping_factor_mode", &ibuffer, 1); + H5LTset_attribute_int( + file.getId(), hdf5Location.c_str(), "newton_damping_factor_mode", + &ibuffer, 1 + ); dbuffer = solver.getNewtonDampingFactorLowerBound(); - H5LTset_attribute_double(file.getId(), hdf5Location.c_str(), - "newton_damping_factor_lower_bound", &dbuffer, 1); - - ibuffer = static_cast(solver.getNewtonMaxLinearSteps()); - H5LTset_attribute_int(file.getId(), hdf5Location.c_str(), - "newton_maxlinsteps", &ibuffer, 1); + H5LTset_attribute_double( + file.getId(), hdf5Location.c_str(), "newton_damping_factor_lower_bound", + &dbuffer, 1 + ); ibuffer = static_cast(solver.getLinearSolver()); - H5LTset_attribute_int(file.getId(), hdf5Location.c_str(), - "linsol", &ibuffer, 1); + H5LTset_attribute_int( + file.getId(), hdf5Location.c_str(), "linsol", &ibuffer, 1 + ); ibuffer = static_cast(solver.getInternalSensitivityMethod()); - H5LTset_attribute_int(file.getId(), hdf5Location.c_str(), - "ism", &ibuffer, 1); + H5LTset_attribute_int( + file.getId(), hdf5Location.c_str(), "ism", &ibuffer, 1 + ); ibuffer = static_cast(solver.getReturnDataReportingMode()); - H5LTset_attribute_int(file.getId(), hdf5Location.c_str(), - "rdrm", &ibuffer, 1); + H5LTset_attribute_int( + file.getId(), hdf5Location.c_str(), "rdrm", &ibuffer, 1 + ); + + ibuffer = static_cast(solver.getNewtonStepSteadyStateCheck()); + H5LTset_attribute_int( + file.getId(), hdf5Location.c_str(), "newton_step_steadystate_conv", + &ibuffer, 1 + ); + + ibuffer = static_cast(solver.getSensiSteadyStateCheck()); + H5LTset_attribute_int( + file.getId(), hdf5Location.c_str(), "check_sensi_steadystate_conv", + &ibuffer, 1 + ); } -void readSolverSettingsFromHDF5(H5::H5File const& file, Solver &solver, - const std::string &datasetPath) { +void readSolverSettingsFromHDF5( + H5::H5File const& file, Solver& solver, std::string const& datasetPath +) { - if(attributeExists(file, datasetPath, "atol")) { + if (attributeExists(file, datasetPath, "atol")) { solver.setAbsoluteTolerance( - getDoubleScalarAttribute(file, datasetPath, "atol")); + getDoubleScalarAttribute(file, datasetPath, "atol") + ); } - if(attributeExists(file, datasetPath, "rtol")) { + if (attributeExists(file, datasetPath, "rtol")) { solver.setRelativeTolerance( - getDoubleScalarAttribute(file, datasetPath, "rtol")); + getDoubleScalarAttribute(file, datasetPath, "rtol") + ); } - if(attributeExists(file, datasetPath, "atol_fsa")) { + if (attributeExists(file, datasetPath, "atol_fsa")) { solver.setAbsoluteToleranceFSA( - getDoubleScalarAttribute(file, datasetPath, "atol_fsa")); + getDoubleScalarAttribute(file, datasetPath, "atol_fsa") + ); } - if(attributeExists(file, datasetPath, "rtol_fsa")) { + if (attributeExists(file, datasetPath, "rtol_fsa")) { solver.setRelativeToleranceFSA( - getDoubleScalarAttribute(file, datasetPath, "rtol_fsa")); + getDoubleScalarAttribute(file, datasetPath, "rtol_fsa") + ); } - - if(attributeExists(file, datasetPath, "atolB")) { + if (attributeExists(file, datasetPath, "atolB")) { solver.setAbsoluteToleranceB( - getDoubleScalarAttribute(file, datasetPath, "atolB")); + getDoubleScalarAttribute(file, datasetPath, "atolB") + ); } - if(attributeExists(file, datasetPath, "rtolB")) { + if (attributeExists(file, datasetPath, "rtolB")) { solver.setRelativeToleranceB( - getDoubleScalarAttribute(file, datasetPath, "rtolB")); + getDoubleScalarAttribute(file, datasetPath, "rtolB") + ); } - if(attributeExists(file, datasetPath, "quad_atol")) { + if (attributeExists(file, datasetPath, "quad_atol")) { solver.setAbsoluteToleranceQuadratures( - getDoubleScalarAttribute(file, datasetPath, "quad_atol")); + getDoubleScalarAttribute(file, datasetPath, "quad_atol") + ); } - if(attributeExists(file, datasetPath, "quad_rtol")) { + if (attributeExists(file, datasetPath, "quad_rtol")) { solver.setRelativeToleranceQuadratures( - getDoubleScalarAttribute(file, datasetPath, "quad_rtol")); + getDoubleScalarAttribute(file, datasetPath, "quad_rtol") + ); + } + + if (attributeExists(file, datasetPath, "ss_tol_factor")) { + solver.setSteadyStateToleranceFactor( + getDoubleScalarAttribute(file, datasetPath, "ss_tol_factor") + ); } - if(attributeExists(file, datasetPath, "ss_atol")) { + if (attributeExists(file, datasetPath, "ss_atol")) { solver.setAbsoluteToleranceSteadyState( - getDoubleScalarAttribute(file, datasetPath, "ss_atol")); + getDoubleScalarAttribute(file, datasetPath, "ss_atol") + ); } - if(attributeExists(file, datasetPath, "ss_rtol")) { + if (attributeExists(file, datasetPath, "ss_rtol")) { solver.setRelativeToleranceSteadyState( - getDoubleScalarAttribute(file, datasetPath, "ss_rtol")); + getDoubleScalarAttribute(file, datasetPath, "ss_rtol") + ); } - if(attributeExists(file, datasetPath, "ss_atol_sensi")) { + if (attributeExists(file, datasetPath, "ss_tol_sensi_factor")) { + solver.setSteadyStateSensiToleranceFactor( + getDoubleScalarAttribute(file, datasetPath, "ss_tol_sensi_factor") + ); + } + + if (attributeExists(file, datasetPath, "ss_atol_sensi")) { solver.setAbsoluteToleranceSteadyStateSensi( - getDoubleScalarAttribute(file, datasetPath, - "ss_atol_sensi")); + getDoubleScalarAttribute(file, datasetPath, "ss_atol_sensi") + ); } - if(attributeExists(file, datasetPath, "ss_rtol_sensi")) { + if (attributeExists(file, datasetPath, "ss_rtol_sensi")) { solver.setRelativeToleranceSteadyStateSensi( - getDoubleScalarAttribute(file, datasetPath, - "ss_rtol_sensi")); + getDoubleScalarAttribute(file, datasetPath, "ss_rtol_sensi") + ); } - if(attributeExists(file, datasetPath, "maxtime")) { - solver.setMaxTime( - getDoubleScalarAttribute(file, datasetPath, "maxtime")); + if (attributeExists(file, datasetPath, "maxtime")) { + solver.setMaxTime(getDoubleScalarAttribute(file, datasetPath, "maxtime") + ); } - if(attributeExists(file, datasetPath, "maxsteps")) { - solver.setMaxSteps( - getIntScalarAttribute(file, datasetPath, "maxsteps")); + if (attributeExists(file, datasetPath, "maxsteps")) { + solver.setMaxSteps(getIntScalarAttribute(file, datasetPath, "maxsteps") + ); } - if(attributeExists(file, datasetPath, "maxstepsB")) { + if (attributeExists(file, datasetPath, "maxstepsB")) { solver.setMaxStepsBackwardProblem( - getIntScalarAttribute(file, datasetPath, "maxstepsB")); + getIntScalarAttribute(file, datasetPath, "maxstepsB") + ); } - if(attributeExists(file, datasetPath, "lmm")) { - solver.setLinearMultistepMethod( - static_cast( - getIntScalarAttribute(file, datasetPath, "lmm"))); + if (attributeExists(file, datasetPath, "lmm")) { + solver.setLinearMultistepMethod(static_cast( + getIntScalarAttribute(file, datasetPath, "lmm") + )); } - if(attributeExists(file, datasetPath, "iter")) { + if (attributeExists(file, datasetPath, "iter")) { solver.setNonlinearSolverIteration( - static_cast( - getIntScalarAttribute(file, datasetPath, "iter"))); + static_cast( + getIntScalarAttribute(file, datasetPath, "iter") + ) + ); } - if(attributeExists(file, datasetPath, "stldet")) { + if (attributeExists(file, datasetPath, "stldet")) { solver.setStabilityLimitFlag( - getIntScalarAttribute(file, datasetPath, "stldet")); + getIntScalarAttribute(file, datasetPath, "stldet") + ); } - if(attributeExists(file, datasetPath, "ordering")) { + if (attributeExists(file, datasetPath, "ordering")) { solver.setStateOrdering( - getIntScalarAttribute(file, datasetPath, "ordering")); + getIntScalarAttribute(file, datasetPath, "ordering") + ); } - if(attributeExists(file, datasetPath, "interpType")) { - solver.setInterpolationType( - static_cast( - getIntScalarAttribute(file, datasetPath, - "interpType"))); + if (attributeExists(file, datasetPath, "interpType")) { + solver.setInterpolationType(static_cast( + getIntScalarAttribute(file, datasetPath, "interpType") + )); } - if(attributeExists(file, datasetPath, "sensi_meth")) { - solver.setSensitivityMethod( - static_cast( - getIntScalarAttribute(file, datasetPath, "sensi_meth"))); + if (attributeExists(file, datasetPath, "sensi_meth")) { + solver.setSensitivityMethod(static_cast( + getIntScalarAttribute(file, datasetPath, "sensi_meth") + )); } - if(attributeExists(file, datasetPath, "sensi_meth_preeq")) { + if (attributeExists(file, datasetPath, "sensi_meth_preeq")) { solver.setSensitivityMethodPreequilibration( static_cast( - getIntScalarAttribute(file, datasetPath, "sensi_meth_preeq"))); + getIntScalarAttribute(file, datasetPath, "sensi_meth_preeq") + ) + ); } - if(attributeExists(file, datasetPath, "sensi")) { - solver.setSensitivityOrder( - static_cast( - getIntScalarAttribute(file, datasetPath, "sensi"))); + if (attributeExists(file, datasetPath, "sensi")) { + solver.setSensitivityOrder(static_cast( + getIntScalarAttribute(file, datasetPath, "sensi") + )); } - if(attributeExists(file, datasetPath, "newton_maxsteps")) { + if (attributeExists(file, datasetPath, "newton_maxsteps")) { solver.setNewtonMaxSteps( - getIntScalarAttribute(file, datasetPath, "newton_maxsteps")); + getIntScalarAttribute(file, datasetPath, "newton_maxsteps") + ); } - if(attributeExists(file, datasetPath, "newton_preeq")) { - solver.setPreequilibration( - getIntScalarAttribute(file, datasetPath, "newton_preeq")); + if (attributeExists(file, datasetPath, "newton_damping_factor_mode")) { + solver.setNewtonDampingFactorMode( + static_cast(getIntScalarAttribute( + file, datasetPath, "newton_damping_factor_mode" + )) + ); } - if(attributeExists(file, datasetPath, "newton_damping_factor_mode")) { - solver.setNewtonDampingFactorMode( - static_cast( - getIntScalarAttribute(file, datasetPath, "newton_damping_factor_mode"))); + if (attributeExists( + file, datasetPath, "newton_damping_factor_lower_bound" + )) { + solver.setNewtonDampingFactorLowerBound(getDoubleScalarAttribute( + file, datasetPath, "newton_damping_factor_lower_bound" + )); } - if(attributeExists(file, datasetPath, "newton_damping_factor_lower_bound")) { - solver.setNewtonDampingFactorLowerBound( - getDoubleScalarAttribute(file, datasetPath, "newton_damping_factor_lower_bound")); + if (attributeExists(file, datasetPath, "linsol")) { + solver.setLinearSolver(static_cast( + getIntScalarAttribute(file, datasetPath, "linsol") + )); } - if(attributeExists(file, datasetPath, "newton_maxlinsteps")) { - solver.setNewtonMaxLinearSteps( - getIntScalarAttribute(file, datasetPath, - "newton_maxlinsteps")); + if (attributeExists(file, datasetPath, "ism")) { + solver.setInternalSensitivityMethod( + static_cast( + getIntScalarAttribute(file, datasetPath, "ism") + ) + ); } - if(attributeExists(file, datasetPath, "linsol")) { - solver.setLinearSolver( - static_cast( - getIntScalarAttribute(file, datasetPath, "linsol"))); + if (attributeExists(file, datasetPath, "rdrm")) { + solver.setReturnDataReportingMode(static_cast( + getIntScalarAttribute(file, datasetPath, "rdrm") + )); } - if(attributeExists(file, datasetPath, "ism")) { - solver.setInternalSensitivityMethod( - static_cast( - getIntScalarAttribute(file, datasetPath, "ism"))); + if (attributeExists(file, datasetPath, "newton_step_steadystate_conv")) { + solver.setNewtonStepSteadyStateCheck(getIntScalarAttribute( + file, datasetPath, "newton_step_steadystate_conv" + )); } - if(attributeExists(file, datasetPath, "rdrm")) { - solver.setReturnDataReportingMode( - static_cast( - getIntScalarAttribute(file, datasetPath, "rdrm"))); + if (attributeExists(file, datasetPath, "check_sensi_steadystate_conv")) { + solver.setSensiSteadyStateCheck(getIntScalarAttribute( + file, datasetPath, "check_sensi_steadystate_conv" + )); } } -void readSolverSettingsFromHDF5(const std::string &hdffile, Solver &solver, - const std::string &datasetPath) { - H5::H5File file(hdffile.c_str(), H5F_ACC_RDONLY, H5P_DEFAULT); +void readSolverSettingsFromHDF5( + std::string const& hdffile, Solver& solver, std::string const& datasetPath +) { + H5::H5File file( + hdffile.c_str(), H5F_ACC_RDONLY, H5P_DEFAULT, + H5::FileAccPropList::DEFAULT + ); readSolverSettingsFromHDF5(file, solver, datasetPath); } -void readModelDataFromHDF5(const std::string &hdffile, Model &model, - const std::string &datasetPath) { - H5::H5File file(hdffile.c_str(), H5F_ACC_RDONLY, H5P_DEFAULT); +void readModelDataFromHDF5( + std::string const& hdffile, Model& model, std::string const& datasetPath +) { + H5::H5File file( + hdffile.c_str(), H5F_ACC_RDONLY, H5P_DEFAULT, + H5::FileAccPropList::DEFAULT + ); readModelDataFromHDF5(file, model, datasetPath); } -void readModelDataFromHDF5(const H5::H5File &file, Model &model, - const std::string &datasetPath) { - if(attributeExists(file, datasetPath, "tstart")) { +void readModelDataFromHDF5( + const H5::H5File& file, Model& model, std::string const& datasetPath +) { + if (attributeExists(file, datasetPath, "tstart")) { model.setT0(getDoubleScalarAttribute(file, datasetPath, "tstart")); } - if(locationExists(file, datasetPath + "/pscale")) { + if (locationExists(file, datasetPath + "/pscale")) { auto pscaleInt = getIntDataset1D(file, datasetPath + "/pscale"); std::vector pscale(pscaleInt.size()); - for(int i = 0; (unsigned)i < pscaleInt.size(); ++i) + for (int i = 0; (unsigned)i < pscaleInt.size(); ++i) pscale[i] = static_cast(pscaleInt[i]); model.setParameterScale(pscale); } else if (attributeExists(file, datasetPath, "pscale")) { // if pscale is the same for all parameters, // it can be set as scalar attribute for convenience - model.setParameterScale( - static_cast( - getDoubleScalarAttribute(file, datasetPath, "pscale"))); + model.setParameterScale(static_cast( + getDoubleScalarAttribute(file, datasetPath, "pscale") + )); + } + + if (attributeExists(file, datasetPath, "nmaxevent")) { + model.setNMaxEvent(getIntScalarAttribute(file, datasetPath, "nmaxevent") + ); } - if(attributeExists(file, datasetPath, "nmaxevent")) { - model.setNMaxEvent(getIntScalarAttribute(file, datasetPath, "nmaxevent")); + if (attributeExists(file, datasetPath, "steadyStateComputationMode")) { + model.setSteadyStateComputationMode( + static_cast(getIntScalarAttribute( + file, datasetPath, "steadyStateComputationMode" + )) + ); } - if(attributeExists(file, datasetPath, "steadyStateSensitivityMode")) { + if (attributeExists(file, datasetPath, "steadyStateSensitivityMode")) { model.setSteadyStateSensitivityMode( - static_cast( - getIntScalarAttribute(file, datasetPath, - "steadyStateSensitivityMode"))); + static_cast(getIntScalarAttribute( + file, datasetPath, "steadyStateSensitivityMode" + )) + ); } - if(locationExists(file, datasetPath + "/theta")) { + if (locationExists(file, datasetPath + "/theta")) { model.setParameters(getDoubleDataset1D(file, datasetPath + "/theta")); } - if(locationExists(file, datasetPath + "/kappa")) { - model.setFixedParameters(getDoubleDataset1D(file, datasetPath + "/kappa")); + if (locationExists(file, datasetPath + "/kappa")) { + model.setFixedParameters( + getDoubleDataset1D(file, datasetPath + "/kappa") + ); } - if(locationExists(file, datasetPath + "/ts")) { + if (locationExists(file, datasetPath + "/ts")) { model.setTimepoints(getDoubleDataset1D(file, datasetPath + "/ts")); } - if(locationExists(file, datasetPath + "/sens_ind")) { + if (locationExists(file, datasetPath + "/sens_ind")) { auto sensInd = getIntDataset1D(file, datasetPath + "/sens_ind"); model.setParameterList(sensInd); } - if(locationExists(file, datasetPath + "/x0")) { + if (locationExists(file, datasetPath + "/x0")) { auto x0 = getDoubleDataset1D(file, datasetPath + "/x0"); - if(!x0.empty()) + if (!x0.empty()) model.setInitialStates(x0); } - if(locationExists(file, datasetPath + "/sx0")) { + if (locationExists(file, datasetPath + "/sx0")) { hsize_t length0 = 0; hsize_t length1 = 0; - auto sx0 = getDoubleDataset2D(file, datasetPath + "/sx0", - length0, length1); - if(!sx0.empty()) { - if (length0 != (unsigned) model.nplist() - && length1 != (unsigned) model.nx_rdata) - throw(AmiException("Dimension mismatch when reading sx0. " - "Expected %dx%d, got %llu, %llu.", - model.nx_rdata, model.nplist(), length0, length1)); + auto sx0 + = getDoubleDataset2D(file, datasetPath + "/sx0", length0, length1); + if (!sx0.empty()) { + if (length0 != (unsigned)model.nplist() + && length1 != (unsigned)model.nx_rdata) + throw(AmiException( + "Dimension mismatch when reading sx0. " + "Expected %dx%d, got %llu, %llu.", + model.nx_rdata, model.nplist(), length0, length1 + )); model.setUnscaledInitialStateSensitivities(sx0); } } - if(attributeExists(file, datasetPath, "sigma_res")) { + if (attributeExists(file, datasetPath, "sigma_res")) { auto sigma_res = getIntScalarAttribute(file, datasetPath, "sigma_res"); model.setAddSigmaResiduals(static_cast(sigma_res)); } - if(attributeExists(file, datasetPath, "min_sigma")) { - auto min_sigma = getDoubleScalarAttribute(file, datasetPath, - "min_sigma"); + if (attributeExists(file, datasetPath, "min_sigma")) { + auto min_sigma + = getDoubleScalarAttribute(file, datasetPath, "min_sigma"); model.setMinimumSigmaResiduals(min_sigma); } - } -H5::H5File createOrOpenForWriting(const std::string &hdf5filename) -{ +H5::H5File createOrOpenForWriting(std::string const& hdf5filename) { AMICI_H5_SAVE_ERROR_HANDLER; try { H5::H5File file(hdf5filename.c_str(), H5F_ACC_RDWR); AMICI_H5_RESTORE_ERROR_HANDLER; return file; - } catch(...) { + } catch (...) { AMICI_H5_RESTORE_ERROR_HANDLER; return H5::H5File(hdf5filename.c_str(), H5F_ACC_EXCL); } } -bool locationExists(const H5::H5File &file, const std::string &location) -{ +bool locationExists(const H5::H5File& file, std::string const& location) { AMICI_H5_SAVE_ERROR_HANDLER; auto result = H5Lexists(file.getId(), location.c_str(), H5P_DEFAULT) > 0; AMICI_H5_RESTORE_ERROR_HANDLER; return result; } -bool locationExists(const std::string &filename, const std::string &location) -{ +bool locationExists(std::string const& filename, std::string const& location) { H5::H5File file(filename.c_str(), H5F_ACC_RDONLY); return locationExists(file, location); } -std::vector getIntDataset1D(const H5::H5File &file, - std::string const& name) { +std::vector +getIntDataset1D(const H5::H5File& file, std::string const& name) { auto dataset = file.openDataSet(name.c_str()); auto dataspace = dataset.getSpace(); int rank = dataspace.getSimpleExtentNdims(); - if(rank != 1) + if (rank != 1) throw(AmiException("Expected array of rank 1 in %s", name.c_str())); hsize_t dim; dataspace.getSimpleExtentDims(&dim); std::vector result(dim); - if(!result.empty()) + if (!result.empty()) dataset.read(result.data(), H5::PredType::NATIVE_INT); return result; } - -std::vector getDoubleDataset1D(const H5::H5File &file, - const std::string &name) -{ +std::vector +getDoubleDataset1D(const H5::H5File& file, std::string const& name) { auto dataset = file.openDataSet(name.c_str()); auto dataspace = dataset.getSpace(); int rank = dataspace.getSimpleExtentNdims(); - if(rank != 1) + if (rank != 1) throw(AmiException("Expected array of rank 1 in %s", name.c_str())); hsize_t dim; dataspace.getSimpleExtentDims(&dim); std::vector result(dim); - if(!result.empty()) + if (!result.empty()) dataset.read(result.data(), H5::PredType::NATIVE_DOUBLE); return result; - } -std::vector getDoubleDataset2D(const H5::H5File &file, - const std::string &name, - hsize_t &m, hsize_t &n) -{ +std::vector getDoubleDataset2D( + const H5::H5File& file, std::string const& name, hsize_t& m, hsize_t& n +) { m = n = 0; auto dataset = file.openDataSet(name.c_str()); auto dataspace = dataset.getSpace(); int rank = dataspace.getSimpleExtentNdims(); - if(rank != 2) + if (rank != 2) throw(AmiException("Expected array of rank 2 in %s", name.c_str())); hsize_t dims[2]; @@ -1106,23 +1304,23 @@ std::vector getDoubleDataset2D(const H5::H5File &file, n = dims[1]; std::vector result(m * n); - if(!result.empty()) + if (!result.empty()) dataset.read(result.data(), H5::PredType::NATIVE_DOUBLE); return result; } -std::vector getDoubleDataset3D(const H5::H5File &file, - const std::string &name, - hsize_t &m, hsize_t &n, hsize_t &o) -{ +std::vector getDoubleDataset3D( + const H5::H5File& file, std::string const& name, hsize_t& m, hsize_t& n, + hsize_t& o +) { m = n = o = 0; auto dataset = file.openDataSet(name.c_str()); auto dataspace = dataset.getSpace(); int rank = dataspace.getSimpleExtentNdims(); - if(rank != 3) + if (rank != 3) throw(AmiException("Expected array of rank 3 in %s", name.c_str())); hsize_t dims[3]; @@ -1132,7 +1330,7 @@ std::vector getDoubleDataset3D(const H5::H5File &file, o = dims[2]; std::vector result(m * n * o); - if(!result.empty()) + if (!result.empty()) dataset.read(result.data(), H5::PredType::NATIVE_DOUBLE); return result; diff --git a/deps/AMICI/src/interface_matlab.cpp b/deps/AMICI/src/interface_matlab.cpp index a4fff0834..3caae66a9 100644 --- a/deps/AMICI/src/interface_matlab.cpp +++ b/deps/AMICI/src/interface_matlab.cpp @@ -8,10 +8,12 @@ #include "amici/interface_matlab.h" -#include "amici/model.h" -#include "amici/exception.h" +#include "amici/amici.h" #include "amici/edata.h" +#include "amici/exception.h" +#include "amici/model.h" #include "amici/returndata_matlab.h" +#include "amici/solver.h" #include #include @@ -20,7 +22,7 @@ namespace amici { - int dbl2int(const double x); +int dbl2int(double const x); /** * @brief The mexRhsArguments enum takes care of the ordering of mex file @@ -39,7 +41,6 @@ enum mexRhsArguments { RHS_NUMARGS }; - /*! * Translates AMICI_BLAS_TRANSPOSE values to CBLAS readable strings * @@ -57,14 +58,17 @@ char amici_blasCBlasTransToBlasTrans(BLASTranspose trans) { case BLASTranspose::conjTrans: return 'C'; } - throw std::invalid_argument("Invalid argument to amici_blasCBlasTransToBlasTrans"); + throw std::invalid_argument( + "Invalid argument to amici_blasCBlasTransToBlasTrans" + ); } -void amici_dgemm(BLASLayout layout, BLASTranspose TransA, - BLASTranspose TransB, const int M, const int N, - const int K, const double alpha, const double *A, - const int lda, const double *B, const int ldb, - const double beta, double *C, const int ldc) { +void amici_dgemm( + BLASLayout layout, BLASTranspose TransA, BLASTranspose TransB, int const M, + int const N, int const K, double const alpha, double const* A, + int const lda, double const* B, int const ldb, double const beta, double* C, + int const ldc +) { assert(layout == BLASLayout::colMajor); const ptrdiff_t M_ = M; @@ -73,18 +77,19 @@ void amici_dgemm(BLASLayout layout, BLASTranspose TransA, const ptrdiff_t lda_ = lda; const ptrdiff_t ldb_ = ldb; const ptrdiff_t ldc_ = ldc; - const char transA = amici_blasCBlasTransToBlasTrans(TransA); - const char transB = amici_blasCBlasTransToBlasTrans(TransB); + char const transA = amici_blasCBlasTransToBlasTrans(TransA); + char const transB = amici_blasCBlasTransToBlasTrans(TransB); - FORTRAN_WRAPPER(dgemm)(&transA, &transB, - &M_, &N_, &K_, - &alpha, A, &lda_, B, &ldb_, &beta, C, &ldc_); + FORTRAN_WRAPPER(dgemm) + (&transA, &transB, &M_, &N_, &K_, &alpha, A, &lda_, B, &ldb_, &beta, C, + &ldc_); } -void amici_dgemv(BLASLayout layout, BLASTranspose TransA, - const int M, const int N, const double alpha, const double *A, - const int lda, const double *X, const int incX, - const double beta, double *Y, const int incY) { +void amici_dgemv( + BLASLayout layout, BLASTranspose TransA, int const M, int const N, + double const alpha, double const* A, int const lda, double const* X, + int const incX, double const beta, double* Y, int const incY +) { assert(layout == BLASLayout::colMajor); const ptrdiff_t M_ = M; @@ -92,12 +97,15 @@ void amici_dgemv(BLASLayout layout, BLASTranspose TransA, const ptrdiff_t lda_ = lda; const ptrdiff_t incX_ = incX; const ptrdiff_t incY_ = incY; - const char transA = amici_blasCBlasTransToBlasTrans(TransA); + char const transA = amici_blasCBlasTransToBlasTrans(TransA); - FORTRAN_WRAPPER(dgemv)(&transA, &M_, &N_, &alpha, A, &lda_, X, &incX_, &beta, Y, &incY_); + FORTRAN_WRAPPER(dgemv) + (&transA, &M_, &N_, &alpha, A, &lda_, X, &incX_, &beta, Y, &incY_); } -void amici_daxpy(int n, double alpha, const double *x, const int incx, double *y, int incy) { +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; @@ -107,36 +115,40 @@ void amici_daxpy(int n, double alpha, const double *x, const int incx, double *y } /** conversion from mxArray to vector - * @param array Matlab array to create vector from - * @param length Number of elements in array - * @return std::vector with data from array - */ -std::vector mxArrayToVector(const mxArray *array, int length) { + * @param array Matlab array to create vector from + * @param length Number of elements in array + * @return std::vector with data from array + */ +std::vector mxArrayToVector(mxArray const* array, int length) { return {mxGetPr(array), mxGetPr(array) + length}; } -std::unique_ptr expDataFromMatlabCall(const mxArray *prhs[], - Model const &model) { +std::unique_ptr +expDataFromMatlabCall(mxArray const* prhs[], Model const& model) { if (!mxGetPr(prhs[RHS_DATA])) return nullptr; - auto edata = std::unique_ptr(new ExpData(model)); + auto edata = std::make_unique(model); // Y - if (mxArray *dataY = mxGetProperty(prhs[RHS_DATA], 0, "Y")) { + if (mxArray* dataY = mxGetProperty(prhs[RHS_DATA], 0, "Y")) { auto ny_my = static_cast(mxGetN(dataY)); if (ny_my != model.nytrue) { - throw AmiException("Number of observables in data matrix (%i) does " - "not match model ny (%i)", - ny_my, model.nytrue); + throw AmiException( + "Number of observables in data matrix (%i) does " + "not match model ny (%i)", + ny_my, model.nytrue + ); } auto nt_my = static_cast(mxGetM(dataY)); if (nt_my != model.nt()) { - throw AmiException("Number of time-points in data matrix does (%i) " - "not match provided time vector (%i)", - nt_my, model.nt()); + throw AmiException( + "Number of time-points in data matrix does (%i) " + "not match provided time vector (%i)", + nt_my, model.nt() + ); } - mxArray *dataYT; + mxArray* dataYT; mexCallMATLAB(1, &dataYT, 1, &dataY, "transpose"); auto observedData = mxArrayToVector(dataYT, ny_my * nt_my); edata->setObservedData(observedData); @@ -146,43 +158,54 @@ std::unique_ptr expDataFromMatlabCall(const mxArray *prhs[], } // Sigma Y - if (mxArray *dataSigmaY = mxGetProperty(prhs[RHS_DATA], 0, "Sigma_Y")) { + if (mxArray* dataSigmaY = mxGetProperty(prhs[RHS_DATA], 0, "Sigma_Y")) { auto ny_sigmay = static_cast(mxGetN(dataSigmaY)); if (ny_sigmay != model.nytrue) { - throw AmiException("Number of observables in data-sigma matrix (%i) " - "does not match model ny (%i)", - ny_sigmay, model.nytrue); + throw AmiException( + "Number of observables in data-sigma matrix (%i) " + "does not match model ny (%i)", + ny_sigmay, model.nytrue + ); } auto nt_sigmay = static_cast(mxGetM(dataSigmaY)); if (nt_sigmay != model.nt()) { - throw AmiException("Number of time-points in data-sigma matrix (%i) " - "does not match provided time vector (%i)", - nt_sigmay, model.nt()); + throw AmiException( + "Number of time-points in data-sigma matrix (%i) " + "does not match provided time vector (%i)", + nt_sigmay, model.nt() + ); } - mxArray *dataSigmaYT; + mxArray* dataSigmaYT; mexCallMATLAB(1, &dataSigmaYT, 1, &dataSigmaY, "transpose"); - auto observedDataSigma = mxArrayToVector(dataSigmaYT, ny_sigmay * nt_sigmay); + auto observedDataSigma + = mxArrayToVector(dataSigmaYT, ny_sigmay * nt_sigmay); edata->setObservedDataStdDev(observedDataSigma); } else { - throw AmiException("Field Sigma_Y not specified as field in data struct!"); + throw AmiException( + "Field Sigma_Y not specified as field in data struct!" + ); } // Z - if (mxArray *dataZ = mxGetProperty(prhs[RHS_DATA], 0, "Z")) { + if (mxArray* dataZ = mxGetProperty(prhs[RHS_DATA], 0, "Z")) { auto nz_mz = static_cast(mxGetN(dataZ)); if (nz_mz != model.nztrue) { - throw AmiException("Number of events in event matrix (%i) does not " - "match provided nz (%i)", - nz_mz, model.nztrue); + throw AmiException( + "Number of events in event matrix (%i) does not " + "match provided nz (%i)", + nz_mz, model.nztrue + ); } auto ne_mz = static_cast(mxGetM(dataZ)); if (ne_mz != model.nMaxEvent()) { - throw AmiException("Number of time-points in event matrix (%i) does " - "not match provided nmaxevent (%i)", - ne_mz, model.nMaxEvent()); + throw AmiException( + "Number of time-points in event matrix (%i) does " + "not match provided nmaxevent (%i)", + ne_mz, model.nMaxEvent() + ); } - mxArray *dataZT; + mxArray* dataZT; mexCallMATLAB(1, &dataZT, 1, &dataZ, "transpose"); auto observedEvents = mxArrayToVector(dataZT, nz_mz * ne_mz); edata->setObservedEvents(observedEvents); @@ -191,170 +214,223 @@ std::unique_ptr expDataFromMatlabCall(const mxArray *prhs[], } // Sigma Z - if (mxArray *dataSigmaZ = mxGetProperty(prhs[RHS_DATA], 0, "Sigma_Z")) { + if (mxArray* dataSigmaZ = mxGetProperty(prhs[RHS_DATA], 0, "Sigma_Z")) { auto nz_sigmaz = static_cast(mxGetN(dataSigmaZ)); if (nz_sigmaz != model.nztrue) { - throw AmiException("Number of events in event-sigma matrix (%i) does " - "not match provided nz (%i)", - nz_sigmaz, model.nztrue); + throw AmiException( + "Number of events in event-sigma matrix (%i) does " + "not match provided nz (%i)", + nz_sigmaz, model.nztrue + ); } auto ne_sigmaz = static_cast(mxGetM(dataSigmaZ)); if (ne_sigmaz != model.nMaxEvent()) { - throw AmiException("Number of time-points in event-sigma matrix (%i) " - "does not match provided nmaxevent (%i)", - ne_sigmaz, model.nMaxEvent()); + throw AmiException( + "Number of time-points in event-sigma matrix (%i) " + "does not match provided nmaxevent (%i)", + ne_sigmaz, model.nMaxEvent() + ); } - mxArray *dataSigmaZT; + mxArray* dataSigmaZT; mexCallMATLAB(1, &dataSigmaZT, 1, &dataSigmaZ, "transpose"); - auto observedEventsSigma = mxArrayToVector(dataSigmaZT, nz_sigmaz * ne_sigmaz); + auto observedEventsSigma + = mxArrayToVector(dataSigmaZT, nz_sigmaz * ne_sigmaz); edata->setObservedEventsStdDev(observedEventsSigma); } else { - throw AmiException("Field Sigma_Z not specified as field in data struct!"); - + throw AmiException( + "Field Sigma_Z not specified as field in data struct!" + ); } // preequilibration condition parameters - if (mxArray *dataPreeq = mxGetProperty(prhs[RHS_DATA], 0, "conditionPreequilibration")) { + if (mxArray* dataPreeq + = mxGetProperty(prhs[RHS_DATA], 0, "conditionPreequilibration")) { int m = (int)mxGetM(dataPreeq); int n = (int)mxGetN(dataPreeq); - if(m * n > 0) { + if (m * n > 0) { if (m * n != model.nk() || (m != 1 && n != 1)) { - throw AmiException("Number of preequilibration parameters (%dx%d) does " - "not match model (%d)", m, n, model.nk()); + throw AmiException( + "Number of preequilibration parameters (%dx%d) does " + "not match model (%d)", + m, n, model.nk() + ); } - edata->fixedParametersPreequilibration = - std::vector(mxGetPr(dataPreeq), mxGetPr(dataPreeq) + m * n); + edata->fixedParametersPreequilibration = std::vector( + mxGetPr(dataPreeq), mxGetPr(dataPreeq) + m * n + ); } } // preequilibration condition parameters if (mxGetProperty(prhs[RHS_DATA], 0, "reinitializeStates")) - edata->reinitializeFixedParameterInitialStates = - static_cast(mxGetScalar(mxGetProperty(prhs[RHS_DATA], 0, "reinitializeStates"))); + edata->reinitializeFixedParameterInitialStates = static_cast( + mxGetScalar(mxGetProperty(prhs[RHS_DATA], 0, "reinitializeStates")) + ); return edata; } /** conversion from double to int with checking for loss of data - * @param x input - * @return int_x casted value - */ -int dbl2int(const double x){ - if((std::round(x)-x) != 0.0) + * @param x input + * @return int_x casted value + */ +int dbl2int(double const x) { + if ((std::round(x) - x) != 0.0) throw AmiException("Invalid non-integer value for integer option"); - return(static_cast(x)); + return (static_cast(x)); } -void setSolverOptions(const mxArray *prhs[], int nrhs, Solver &solver) -{ +void setSolverOptions(mxArray const* prhs[], int nrhs, Solver& solver) { if (mxGetPr(prhs[RHS_OPTIONS])) { if (mxGetProperty(prhs[RHS_OPTIONS], 0, "atol")) { - solver.setAbsoluteTolerance(mxGetScalar(mxGetProperty(prhs[RHS_OPTIONS], 0, "atol"))); + solver.setAbsoluteTolerance( + mxGetScalar(mxGetProperty(prhs[RHS_OPTIONS], 0, "atol")) + ); } if (mxGetProperty(prhs[RHS_OPTIONS], 0, "rtol")) { - solver.setRelativeTolerance(mxGetScalar(mxGetProperty(prhs[RHS_OPTIONS], 0, "rtol"))); + solver.setRelativeTolerance( + mxGetScalar(mxGetProperty(prhs[RHS_OPTIONS], 0, "rtol")) + ); } if (mxGetProperty(prhs[RHS_OPTIONS], 0, "quad_atol")) { - solver.setAbsoluteToleranceQuadratures(mxGetScalar(mxGetProperty(prhs[RHS_OPTIONS], 0, "quad_atol"))); + solver.setAbsoluteToleranceQuadratures( + mxGetScalar(mxGetProperty(prhs[RHS_OPTIONS], 0, "quad_atol")) + ); } if (mxGetProperty(prhs[RHS_OPTIONS], 0, "quad_rtol")) { - solver.setRelativeToleranceQuadratures(mxGetScalar(mxGetProperty(prhs[RHS_OPTIONS], 0, "quad_rtol"))); + solver.setRelativeToleranceQuadratures( + mxGetScalar(mxGetProperty(prhs[RHS_OPTIONS], 0, "quad_rtol")) + ); } if (mxGetProperty(prhs[RHS_OPTIONS], 0, "ss_atol")) { - solver.setAbsoluteToleranceQuadratures(mxGetScalar(mxGetProperty(prhs[RHS_OPTIONS], 0, "ss_atol"))); + solver.setAbsoluteToleranceQuadratures( + mxGetScalar(mxGetProperty(prhs[RHS_OPTIONS], 0, "ss_atol")) + ); } if (mxGetProperty(prhs[RHS_OPTIONS], 0, "ss_rtol")) { - solver.setRelativeToleranceQuadratures(mxGetScalar(mxGetProperty(prhs[RHS_OPTIONS], 0, "ss_rtol"))); + solver.setRelativeToleranceQuadratures( + mxGetScalar(mxGetProperty(prhs[RHS_OPTIONS], 0, "ss_rtol")) + ); } if (mxGetProperty(prhs[RHS_OPTIONS], 0, "maxsteps")) { - solver.setMaxSteps(dbl2int(mxGetScalar(mxGetProperty(prhs[RHS_OPTIONS], 0, "maxsteps")))); + solver.setMaxSteps(dbl2int( + mxGetScalar(mxGetProperty(prhs[RHS_OPTIONS], 0, "maxsteps")) + )); } if (mxGetProperty(prhs[RHS_OPTIONS], 0, "maxstepsB")) { - solver.setMaxStepsBackwardProblem(dbl2int(mxGetScalar(mxGetProperty(prhs[RHS_OPTIONS], 0, "maxstepsB")))); + solver.setMaxStepsBackwardProblem(dbl2int( + mxGetScalar(mxGetProperty(prhs[RHS_OPTIONS], 0, "maxstepsB")) + )); } if (mxGetProperty(prhs[RHS_OPTIONS], 0, "lmm")) { - solver.setLinearMultistepMethod(static_cast(dbl2int(mxGetScalar(mxGetProperty(prhs[RHS_OPTIONS], 0, "lmm"))))); + solver.setLinearMultistepMethod(static_cast( + dbl2int(mxGetScalar(mxGetProperty(prhs[RHS_OPTIONS], 0, "lmm"))) + )); } if (mxGetProperty(prhs[RHS_OPTIONS], 0, "iter")) { - solver.setNonlinearSolverIteration(static_cast(dbl2int(mxGetScalar(mxGetProperty(prhs[RHS_OPTIONS], 0, "iter"))))); + solver.setNonlinearSolverIteration( + static_cast(dbl2int( + mxGetScalar(mxGetProperty(prhs[RHS_OPTIONS], 0, "iter")) + )) + ); } if (mxGetProperty(prhs[RHS_OPTIONS], 0, "interpType")) { - solver.setInterpolationType(static_cast(dbl2int(mxGetScalar(mxGetProperty(prhs[RHS_OPTIONS], 0, "interpType"))))); + solver.setInterpolationType(static_cast(dbl2int( + mxGetScalar(mxGetProperty(prhs[RHS_OPTIONS], 0, "interpType")) + ))); } if (mxGetProperty(prhs[RHS_OPTIONS], 0, "linsol")) { - solver.setLinearSolver(static_cast(dbl2int(mxGetScalar(mxGetProperty(prhs[RHS_OPTIONS], 0, "linsol"))))); + solver.setLinearSolver(static_cast(dbl2int( + mxGetScalar(mxGetProperty(prhs[RHS_OPTIONS], 0, "linsol")) + ))); } if (mxGetProperty(prhs[RHS_OPTIONS], 0, "sensi")) { - solver.setSensitivityOrder(static_cast(dbl2int(mxGetScalar(mxGetProperty(prhs[RHS_OPTIONS], 0, "sensi"))))); + solver.setSensitivityOrder(static_cast(dbl2int( + mxGetScalar(mxGetProperty(prhs[RHS_OPTIONS], 0, "sensi")) + ))); } if (mxGetProperty(prhs[RHS_OPTIONS], 0, "ism")) { - solver.setInternalSensitivityMethod(static_cast(dbl2int(mxGetScalar(mxGetProperty(prhs[RHS_OPTIONS], 0, "ism"))))); + solver.setInternalSensitivityMethod( + static_cast(dbl2int( + mxGetScalar(mxGetProperty(prhs[RHS_OPTIONS], 0, "ism")) + )) + ); } if (mxGetProperty(prhs[RHS_OPTIONS], 0, "sensi_meth")) { - solver.setSensitivityMethod(static_cast(dbl2int(mxGetScalar(mxGetProperty(prhs[RHS_OPTIONS], 0, "sensi_meth"))))); + solver.setSensitivityMethod(static_cast(dbl2int( + mxGetScalar(mxGetProperty(prhs[RHS_OPTIONS], 0, "sensi_meth")) + ))); } if (mxGetProperty(prhs[RHS_OPTIONS], 0, "sensi_meth_preeq")) { solver.setSensitivityMethodPreequilibration( - static_cast(dbl2int(mxGetScalar(mxGetProperty(prhs[RHS_OPTIONS], 0, "sensi_meth_preeq"))))); + static_cast(dbl2int(mxGetScalar( + mxGetProperty(prhs[RHS_OPTIONS], 0, "sensi_meth_preeq") + ))) + ); } if (mxGetProperty(prhs[RHS_OPTIONS], 0, "ordering")) { - solver.setStateOrdering(dbl2int(mxGetScalar(mxGetProperty(prhs[RHS_OPTIONS], 0, "ordering")))); + solver.setStateOrdering(dbl2int( + mxGetScalar(mxGetProperty(prhs[RHS_OPTIONS], 0, "ordering")) + )); } if (mxGetProperty(prhs[RHS_OPTIONS], 0, "stldet")) { - solver.setStabilityLimitFlag(dbl2int(mxGetScalar(mxGetProperty(prhs[RHS_OPTIONS], 0, "stldet")))); - } - - if (mxGetProperty(prhs[RHS_OPTIONS], 0, "newton_preeq")) { - solver.setPreequilibration(dbl2int(mxGetScalar(mxGetProperty(prhs[RHS_OPTIONS], 0, "newton_preeq")))); + solver.setStabilityLimitFlag(dbl2int( + mxGetScalar(mxGetProperty(prhs[RHS_OPTIONS], 0, "stldet")) + )); } if (mxGetProperty(prhs[RHS_OPTIONS], 0, "newton_maxsteps")) { - solver.setNewtonMaxSteps(dbl2int(mxGetScalar(mxGetProperty(prhs[RHS_OPTIONS], 0, "newton_maxsteps")))); - } - - if (mxGetProperty(prhs[RHS_OPTIONS], 0, "newton_maxlinsteps")) { - solver.setNewtonMaxLinearSteps(dbl2int(mxGetScalar(mxGetProperty(prhs[RHS_OPTIONS], 0, "newton_maxlinsteps")))); + solver.setNewtonMaxSteps(dbl2int(mxGetScalar( + mxGetProperty(prhs[RHS_OPTIONS], 0, "newton_maxsteps") + ))); } } } -void setModelData(const mxArray *prhs[], int nrhs, Model &model) -{ +void setModelData(mxArray const* prhs[], int nrhs, Model& model) { if (mxGetPr(prhs[RHS_OPTIONS])) { if (mxGetProperty(prhs[RHS_OPTIONS], 0, "nmaxevent")) { - model.setNMaxEvent(dbl2int(mxGetScalar(mxGetProperty(prhs[RHS_OPTIONS], 0, "nmaxevent")))); + model.setNMaxEvent(dbl2int( + mxGetScalar(mxGetProperty(prhs[RHS_OPTIONS], 0, "nmaxevent")) + )); } if (mxGetProperty(prhs[RHS_OPTIONS], 0, "tstart")) { - model.setT0(mxGetScalar(mxGetProperty(prhs[RHS_OPTIONS], 0, "tstart"))); - } - - if (mxArray *a = mxGetProperty(prhs[RHS_OPTIONS], 0, "pscale")) { - if(mxGetM(a) == 1 && mxGetN(a) == 1) { - model.setParameterScale(static_cast(dbl2int(mxGetScalar(a)))); - } else if((mxGetM(a) == 1 && mxGetN(a) == model.np()) - || (mxGetN(a) == 1 && mxGetM(a) == model.np())) { - auto pscaleArray = static_cast(mxGetData(a)); + model.setT0( + mxGetScalar(mxGetProperty(prhs[RHS_OPTIONS], 0, "tstart")) + ); + } + + if (mxArray* a = mxGetProperty(prhs[RHS_OPTIONS], 0, "pscale")) { + if (mxGetM(a) == 1 && mxGetN(a) == 1) { + 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())) { + auto pscaleArray = static_cast(mxGetData(a)); std::vector pscale(model.np()); - for(int ip = 0; ip < model.np(); ++ip) { - pscale[ip] = static_cast(dbl2int(pscaleArray[ip])); + for (int ip = 0; ip < model.np(); ++ip) { + pscale[ip] + = static_cast(dbl2int(pscaleArray[ip]) + ); } model.setParameterScale(pscale); } else { @@ -363,54 +439,68 @@ void setModelData(const mxArray *prhs[], int nrhs, Model &model) } } - if (prhs[RHS_TIMEPOINTS] && - mxGetM(prhs[RHS_TIMEPOINTS]) * mxGetN(prhs[RHS_TIMEPOINTS]) > 0) { - model.setTimepoints(std::vector( - mxGetPr(prhs[RHS_TIMEPOINTS]), - mxGetPr(prhs[RHS_TIMEPOINTS]) - + (int)mxGetM(prhs[RHS_TIMEPOINTS]) * mxGetN(prhs[RHS_TIMEPOINTS]))); - + if (prhs[RHS_TIMEPOINTS] + && mxGetM(prhs[RHS_TIMEPOINTS]) * mxGetN(prhs[RHS_TIMEPOINTS]) > 0) { + model.setTimepoints(std::vector( + mxGetPr(prhs[RHS_TIMEPOINTS]), + mxGetPr(prhs[RHS_TIMEPOINTS] + ) + (int)mxGetM(prhs[RHS_TIMEPOINTS]) * mxGetN(prhs[RHS_TIMEPOINTS]) + )); } if (model.np() > 0) { if (mxGetPr(prhs[RHS_PARAMETERS])) { - if (mxGetM(prhs[RHS_PARAMETERS]) * mxGetN(prhs[RHS_PARAMETERS]) == - model.np()) { - model.setParameters(std::vector(mxGetPr(prhs[RHS_PARAMETERS]), - mxGetPr(prhs[RHS_PARAMETERS]) - + mxGetM(prhs[RHS_PARAMETERS]) * mxGetN(prhs[RHS_PARAMETERS]))); + if (gsl::narrow( + mxGetM(prhs[RHS_PARAMETERS]) * mxGetN(prhs[RHS_PARAMETERS]) + ) + == model.np()) { + model.setParameters(std::vector( + mxGetPr(prhs[RHS_PARAMETERS]), + mxGetPr(prhs[RHS_PARAMETERS]) + + mxGetM(prhs[RHS_PARAMETERS]) + * mxGetN(prhs[RHS_PARAMETERS]) + )); } } } if (model.nk() > 0) { if (mxGetPr(prhs[RHS_CONSTANTS])) { - if (mxGetM(prhs[RHS_CONSTANTS]) * mxGetN(prhs[RHS_CONSTANTS]) == - model.nk()) { - model.setFixedParameters(std::vector(mxGetPr(prhs[RHS_CONSTANTS]), - mxGetPr(prhs[RHS_CONSTANTS]) - + mxGetM(prhs[RHS_CONSTANTS]) * mxGetN(prhs[RHS_CONSTANTS]))); + if (gsl::narrow( + mxGetM(prhs[RHS_CONSTANTS]) * mxGetN(prhs[RHS_CONSTANTS]) + ) + == model.nk()) { + model.setFixedParameters(std::vector( + mxGetPr(prhs[RHS_CONSTANTS]), + mxGetPr(prhs[RHS_CONSTANTS]) + + mxGetM(prhs[RHS_CONSTANTS]) + * mxGetN(prhs[RHS_CONSTANTS]) + )); } } } if (mxGetPr(prhs[RHS_PLIST])) { - model.setParameterList(std::vector(mxGetPr(prhs[RHS_PLIST]), - mxGetPr(prhs[RHS_PLIST]) - + mxGetM(prhs[RHS_PLIST]) * mxGetN(prhs[RHS_PLIST]))); + model.setParameterList(std::vector( + mxGetPr(prhs[RHS_PLIST]), + mxGetPr(prhs[RHS_PLIST]) + + mxGetM(prhs[RHS_PLIST]) * mxGetN(prhs[RHS_PLIST]) + )); } else { model.requireSensitivitiesForAllParameters(); } /* Check, if initial states and sensitivities are passed by user or must be - * calculated */ + * calculated */ if (mxGetPr(prhs[RHS_INITIALIZATION])) { - mxArray *x0 = mxGetField(prhs[RHS_INITIALIZATION], 0, "x0"); + mxArray* x0 = mxGetField(prhs[RHS_INITIALIZATION], 0, "x0"); if (x0 && (mxGetM(x0) * mxGetN(x0)) > 0) { /* check dimensions */ if (mxGetN(x0) != 1) { - throw AmiException("Number of rows in x0 field must be equal to 1!"); + throw AmiException( + "Number of rows in x0 field must be equal to 1!" + ); } - if (mxGetM(x0) != model.nx_rdata) { + if (gsl::narrow(mxGetM(x0)) != model.nx_rdata) { throw AmiException("Number of columns in x0 field " "does not agree with number of " "model states!"); @@ -419,50 +509,55 @@ void setModelData(const mxArray *prhs[], int nrhs, Model &model) } /* Check, if initial states and sensitivities are passed by user or must be - * calculated */ + * calculated */ if (mxGetPr(prhs[RHS_INITIALIZATION])) { - mxArray *x0 = mxGetField(prhs[RHS_INITIALIZATION], 0, "x0"); + mxArray* x0 = mxGetField(prhs[RHS_INITIALIZATION], 0, "x0"); if (x0 && (mxGetM(x0) * mxGetN(x0)) > 0) { /* check dimensions */ if (mxGetN(x0) != 1) { - throw AmiException("Number of rows in x0 field must be equal to 1!"); + throw AmiException( + "Number of rows in x0 field must be equal to 1!" + ); } - if (mxGetM(x0) != model.nx_rdata) { + if (gsl::narrow(mxGetM(x0)) != model.nx_rdata) { throw AmiException("Number of columns in x0 field " "does not agree with number of " "model states!"); } - model.setInitialStates(std::vector(mxGetPr(x0), - mxGetPr(x0) + mxGetM(x0) * mxGetN(x0))); + model.setInitialStates(std::vector( + mxGetPr(x0), mxGetPr(x0) + mxGetM(x0) * mxGetN(x0) + )); } - mxArray *sx0 = mxGetField(prhs[RHS_INITIALIZATION], 0, "sx0"); + mxArray* sx0 = mxGetField(prhs[RHS_INITIALIZATION], 0, "sx0"); if (sx0 && (mxGetM(sx0) * mxGetN(sx0)) > 0) { /* check dimensions */ - if (mxGetN(sx0) != model.nplist()) { + if (gsl::narrow(mxGetN(sx0)) != model.nplist()) { throw AmiException("Number of rows in sx0 field " "does not agree with number of " "model parameters!"); } - if (mxGetM(sx0) != model.nx_rdata) { + if (gsl::narrow(mxGetM(sx0)) != model.nx_rdata) { throw AmiException("Number of columns in sx0 " "field does not agree with " "number of model states!"); } - model.setInitialStateSensitivities(std::vector(mxGetPr(sx0), - mxGetPr(sx0) + mxGetM(sx0) * mxGetN(sx0))); + model.setInitialStateSensitivities(std::vector( + mxGetPr(sx0), mxGetPr(sx0) + mxGetM(sx0) * mxGetN(sx0) + )); } } // preequilibration condition parameters - if (mxGetPr(prhs[RHS_DATA]) && mxGetProperty(prhs[RHS_DATA], 0, "reinitializeStates")) - model.setReinitializeFixedParameterInitialStates( - static_cast(mxGetScalar(mxGetProperty(prhs[RHS_DATA], 0, "reinitializeStates")))); + if (mxGetPr(prhs[RHS_DATA]) + && mxGetProperty(prhs[RHS_DATA], 0, "reinitializeStates")) + model.setReinitializeFixedParameterInitialStates(static_cast( + mxGetScalar(mxGetProperty(prhs[RHS_DATA], 0, "reinitializeStates")) + )); } } // namespace amici - /*! * mexFunction is the main interface function for the MATLAB interface. It reads * in input data (udata and edata) and @@ -474,33 +569,21 @@ void setModelData(const mxArray *prhs[], int nrhs, Model &model) * @param nrhs number of input arguments of the matlab call * @param prhs pointer to the array of input arguments */ -void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { - // use matlab error reporting - amici::AmiciApplication amiciApp; - amiciApp.warning = []( - std::string const& identifier, - std::string const& message){ - mexWarnMsgIdAndTxt(identifier.c_str(), message.c_str()); - }; - amiciApp.error = []( - std::string const& identifier, - std::string const& message){ - mexErrMsgIdAndTxt(identifier.c_str(), message.c_str()); - }; - +void mexFunction(int nlhs, mxArray* plhs[], int nrhs, mxArray const* prhs[]) { if (nlhs != 1) { - amiciApp.errorF("AMICI:mex:setup", - "Incorrect number of output arguments (must be 1)!"); - } else if(nrhs < amici::RHS_NUMARGS_REQUIRED) { - amiciApp.errorF("AMICI:mex:setup", - "Incorrect number of input arguments (must be at least 7)!"); + mexErrMsgIdAndTxt( + "AMICI:mex:setup", + "Incorrect number of output arguments (must be 1)!" + ); + } else if (nrhs < amici::RHS_NUMARGS_REQUIRED) { + mexErrMsgIdAndTxt( + "AMICI:mex:setup", + "Incorrect number of input arguments (must be at least 7)!" + ); }; auto model = amici::generic_model::getModel(); - model->app = &amiciApp; - auto solver = model->getSolver(); - solver->app = &amiciApp; setModelData(prhs, nrhs, *model); setSolverOptions(prhs, nrhs, *solver); @@ -509,15 +592,21 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { try { edata = amici::expDataFromMatlabCall(prhs, *model); } catch (amici::AmiException const& ex) { - amiciApp.errorF("AMICI:mex:setup","Failed to read experimental data:\n%s",ex.what()); + mexErrMsgIdAndTxt( + "AMICI:mex:setup", "Failed to read experimental data:\n%s", + ex.what() + ); } - } else if (solver->getSensitivityOrder() >= amici::SensitivityOrder::first && - solver->getSensitivityMethod() == amici::SensitivityMethod::adjoint) { - amiciApp.errorF("AMICI:mex:setup","No data provided!"); + } else if (solver->getSensitivityOrder() >= amici::SensitivityOrder::first && solver->getSensitivityMethod() == amici::SensitivityMethod::adjoint) { + mexErrMsgIdAndTxt("AMICI:mex:setup", "No data provided!"); } /* ensures that plhs[0] is available */ auto rdata = amici::runAmiciSimulation(*solver, edata.get(), *model); plhs[0] = getReturnDataMatlabFromAmiciCall(rdata.get()); + for (auto const& msg : rdata->messages) { + auto identifier = "AMICI:simulation:" + msg.identifier; + mexWarnMsgIdAndTxt(identifier.c_str(), msg.message.c_str()); + } } diff --git a/deps/AMICI/src/logging.cpp b/deps/AMICI/src/logging.cpp new file mode 100644 index 000000000..e974612ea --- /dev/null +++ b/deps/AMICI/src/logging.cpp @@ -0,0 +1,26 @@ +#include "amici/logging.h" +#include "amici/misc.h" + +#include + +namespace amici { + +void Logger::log( + LogSeverity severity, std::string const& identifier, + std::string const& message +) { + items.emplace_back(severity, identifier, message); +} + +void Logger::log( + LogSeverity severity, std::string const& identifier, char const* format, ... +) { + va_list argptr; + va_start(argptr, format); + auto message = printfToString(format, argptr); + va_end(argptr); + + log(severity, identifier, message); +} + +} // namespace amici diff --git a/deps/AMICI/src/main.template.cpp b/deps/AMICI/src/main.template.cpp index 00a5e6b44..ecdff85a4 100644 --- a/deps/AMICI/src/main.template.cpp +++ b/deps/AMICI/src/main.template.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/src/misc.cpp b/deps/AMICI/src/misc.cpp index 60e9d3ea1..f54a45052 100644 --- a/deps/AMICI/src/misc.cpp +++ b/deps/AMICI/src/misc.cpp @@ -1,11 +1,10 @@ #include "amici/misc.h" -#include "amici/amici.h" #include "amici/symbolic_functions.h" +#include #include #include #include -#include #if defined(_WIN32) #define PLATFORM_WINDOWS // Windows @@ -14,19 +13,18 @@ #elif defined(__CYGWIN__) && !defined(_WIN32) #define PLATFORM_WINDOWS // Windows (Cygwin POSIX under Microsoft Window) #else +#include // for __cxa_demangle +#include // for dladdr #include -#include // for dladdr -#include // for __cxa_demangle #endif namespace amici { -void writeSlice(const AmiVector &s, gsl::span b) { +void writeSlice(AmiVector const& s, gsl::span b) { writeSlice(s.getVector(), b); }; -double getUnscaledParameter(double scaledParameter, ParameterScaling scaling) -{ +double getUnscaledParameter(double scaledParameter, ParameterScaling scaling) { switch (scaling) { case ParameterScaling::log10: return pow(10, scaledParameter); @@ -39,22 +37,20 @@ double getUnscaledParameter(double scaledParameter, ParameterScaling scaling) throw AmiException("Invalid value for ParameterScaling."); } -void unscaleParameters(gsl::span bufferScaled, - gsl::span pscale, - gsl::span bufferUnscaled) -{ +void unscaleParameters( + gsl::span bufferScaled, + gsl::span pscale, gsl::span bufferUnscaled +) { Expects(bufferScaled.size() == pscale.size()); Expects(bufferScaled.size() == bufferUnscaled.size()); - for (gsl::span::index_type ip = 0; - ip < bufferScaled.size(); ++ip) { + for (gsl::span::index_type ip = 0; ip < bufferScaled.size(); + ++ip) { bufferUnscaled[ip] = getUnscaledParameter(bufferScaled[ip], pscale[ip]); } } - -double getScaledParameter(double unscaledParameter, ParameterScaling scaling) -{ +double getScaledParameter(double unscaledParameter, ParameterScaling scaling) { switch (scaling) { case ParameterScaling::log10: return log10(unscaledParameter); @@ -67,68 +63,69 @@ double getScaledParameter(double unscaledParameter, ParameterScaling scaling) throw AmiException("Invalid value for ParameterScaling."); } - -void scaleParameters(gsl::span bufferUnscaled, - gsl::span pscale, - gsl::span bufferScaled) -{ +void scaleParameters( + gsl::span bufferUnscaled, + gsl::span pscale, gsl::span bufferScaled +) { Expects(bufferScaled.size() == pscale.size()); Expects(bufferScaled.size() == bufferUnscaled.size()); - for (gsl::span::index_type ip = 0; - ip < bufferUnscaled.size(); ++ip) { + for (gsl::span::index_type ip = 0; ip < bufferUnscaled.size(); + ++ip) { bufferScaled[ip] = getScaledParameter(bufferUnscaled[ip], pscale[ip]); } - } -std::string backtraceString(const int maxFrames) -{ +std::string backtraceString(int const maxFrames, int const first_frame) { std::ostringstream trace_buf; #ifdef PLATFORM_WINDOWS trace_buf << "stacktrace not available on windows platforms\n"; #else - void *callstack[maxFrames]; + int const last_frame = first_frame + maxFrames; + void* callstack[last_frame]; char buf[1024]; - int nFrames = backtrace(callstack, maxFrames); - char **symbols = backtrace_symbols(callstack, nFrames); + int nFrames = backtrace(callstack, last_frame); + char** symbols = backtrace_symbols(callstack, nFrames); - // start at 2 to omit AmiException and storeBacktrace - for (int i = 2; i < nFrames; i++) { + for (int i = first_frame; i < nFrames; i++) { // call Dl_info info; if (dladdr(callstack[i], &info) && info.dli_sname) { - char *demangled = nullptr; + char* demangled = nullptr; int status = -1; if (info.dli_sname[0] == '_') - demangled = abi::__cxa_demangle(info.dli_sname, nullptr, nullptr, - &status); - snprintf(buf, sizeof(buf), "%-3d %*p %s + %zd\n", i - 2, - int(2 + sizeof(void *) * 2), callstack[i], - status == 0 ? demangled - : info.dli_sname == nullptr ? symbols[i] - : info.dli_sname, - static_cast((char *)callstack[i] - - (char *)info.dli_saddr)); + demangled = abi::__cxa_demangle( + info.dli_sname, nullptr, nullptr, &status + ); + snprintf( + buf, sizeof(buf), "%-3d %*p %s + %zd\n", i - 2, + int(2 + sizeof(void*) * 2), callstack[i], + status == 0 ? demangled + : info.dli_sname == nullptr ? symbols[i] + : info.dli_sname, + static_cast( + (char*)callstack[i] - (char*)info.dli_saddr + ) + ); free(demangled); } else { - snprintf(buf, sizeof(buf), "%-3d %*p %s\n", i - 2, - int(2 + sizeof(void *) * 2), callstack[i], - symbols[i]); + snprintf( + buf, sizeof(buf), "%-3d %*p %s\n", i - 2, + int(2 + sizeof(void*) * 2), callstack[i], symbols[i] + ); } trace_buf << buf; } free(symbols); - if (nFrames == maxFrames) - trace_buf << "[truncated]\n"; + if (nFrames == last_frame) + trace_buf << "[possibly truncated]\n"; #endif return trace_buf.str(); } -std::string regexErrorToString(std::regex_constants::error_type err_type) -{ +std::string regexErrorToString(std::regex_constants::error_type err_type) { switch (err_type) { case std::regex_constants::error_collate: return "error_collate"; @@ -161,7 +158,7 @@ std::string regexErrorToString(std::regex_constants::error_type err_type) } } -std::string printfToString(const char *fmt, va_list ap) { +std::string printfToString(char const* fmt, va_list ap) { // Get size of string va_list ap_count; va_copy(ap_count, ap); @@ -178,4 +175,8 @@ std::string printfToString(const char *fmt, va_list ap) { return str; } +std::pair unravel_index(size_t flat_idx, size_t num_cols) { + return {flat_idx / num_cols, flat_idx % num_cols}; +} + } // namespace amici diff --git a/deps/AMICI/src/model.cpp b/deps/AMICI/src/model.cpp index 4916c1e95..46c9e9902 100644 --- a/deps/AMICI/src/model.cpp +++ b/deps/AMICI/src/model.cpp @@ -1,20 +1,90 @@ -#include "amici/model.h" -#include "amici/amici.h" -#include "amici/exception.h" -#include "amici/misc.h" -#include "amici/symbolic_functions.h" +#include +#include +#include +#include +#include +#include #include +#include #include #include #include #include #include #include -#include namespace amici { +/** + * @brief Maps ModelQuantity items to their string value + */ +const std::map model_quantity_to_str{ + {ModelQuantity::J, "J"}, + {ModelQuantity::JB, "JB"}, + {ModelQuantity::Jv, "Jv"}, + {ModelQuantity::JvB, "JvB"}, + {ModelQuantity::JDiag, "JDiag"}, + {ModelQuantity::sx, "sx"}, + {ModelQuantity::sy, "sy"}, + {ModelQuantity::sz, "sz"}, + {ModelQuantity::srz, "srz"}, + {ModelQuantity::ssigmay, "ssigmay"}, + {ModelQuantity::ssigmaz, "ssigmaz"}, + {ModelQuantity::xdot, "xdot"}, + {ModelQuantity::sxdot, "sxdot"}, + {ModelQuantity::xBdot, "xBdot"}, + {ModelQuantity::x0, "x0"}, + {ModelQuantity::x0_rdata, "x0_rdata"}, + {ModelQuantity::x, "x"}, + {ModelQuantity::x_rdata, "x_rdata"}, + {ModelQuantity::dwdw, "dwdw"}, + {ModelQuantity::dwdx, "dwdx"}, + {ModelQuantity::dwdp, "dwdp"}, + {ModelQuantity::y, "y"}, + {ModelQuantity::dydp, "dydp"}, + {ModelQuantity::dydx, "dydx"}, + {ModelQuantity::w, "w"}, + {ModelQuantity::root, "root"}, + {ModelQuantity::qBdot, "qBdot"}, + {ModelQuantity::qBdot_ss, "qBdot_ss"}, + {ModelQuantity::xBdot_ss, "xBdot_ss"}, + {ModelQuantity::JSparseB_ss, "JSparseB_ss"}, + {ModelQuantity::deltax, "deltax"}, + {ModelQuantity::deltasx, "deltasx"}, + {ModelQuantity::deltaxB, "deltaxB"}, + {ModelQuantity::k, "k"}, + {ModelQuantity::p, "p"}, + {ModelQuantity::ts, "ts"}, + {ModelQuantity::dJydy, "dJydy"}, + {ModelQuantity::dJydy_matlab, "dJydy"}, + {ModelQuantity::deltaqB, "deltaqB"}, + {ModelQuantity::dsigmaydp, "dsigmaydp"}, + {ModelQuantity::dsigmaydy, "dsigmaydy"}, + {ModelQuantity::dsigmazdp, "dsigmazdp"}, + {ModelQuantity::dJydsigma, "dJydsigma"}, + {ModelQuantity::dJydx, "dJydx"}, + {ModelQuantity::dJrzdx, "dJrzdx"}, + {ModelQuantity::dJzdx, "dJzdx"}, + {ModelQuantity::dzdp, "dzdp"}, + {ModelQuantity::dzdx, "dzdx"}, + {ModelQuantity::dJrzdsigma, "dJrzdsigma"}, + {ModelQuantity::dJrzdz, "dJrzdz"}, + {ModelQuantity::dJzdsigma, "dJzdsigma"}, + {ModelQuantity::dJzdz, "dJzdz"}, + {ModelQuantity::drzdp, "drzdp"}, + {ModelQuantity::drzdx, "drzdx"}, + +}; + +static void setNaNtoZero(std::vector& vec) { + std::for_each(vec.begin(), vec.end(), [](double& val) { + if (std::isnan(val)) { + val = 0.0; + } + }); +} + /** * @brief local helper function to get parameters * @param ids vector of name/ids of (fixed)Parameters @@ -24,16 +94,17 @@ namespace amici { * @param id_name string indicating whether name or id was specified * @return value of the selected parameter */ -static realtype getValueById(std::vector const &ids, - std::vector const &values, - std::string const &id, const char *variable_name, - const char *id_name) { +static realtype getValueById( + std::vector const& ids, std::vector const& values, + std::string const& id, char const* variable_name, char const* id_name +) { auto it = std::find(ids.begin(), ids.end(), id); if (it != ids.end()) return values.at(it - ids.begin()); - throw AmiException("Could not find %s with specified %s", variable_name, - id_name); + throw AmiException( + "Could not find %s with specified %s", variable_name, id_name + ); } /** @@ -45,16 +116,18 @@ static realtype getValueById(std::vector const &ids, * @param variable_name string indicating what variable we are looking at * @param id_name string indicating whether name or id was specified */ -static void setValueById(std::vector const &ids, - std::vector &values, realtype value, - std::string const &id, const char *variable_name, - const char *id_name) { +static void setValueById( + std::vector const& ids, std::vector& values, + realtype value, std::string const& id, char const* variable_name, + char const* id_name +) { auto it = std::find(ids.begin(), ids.end(), id); if (it != ids.end()) values.at(it - ids.begin()) = value; else - throw AmiException("Could not find %s with specified %s", variable_name, - id_name); + throw AmiException( + "Could not find %s with specified %s", variable_name, id_name + ); } /** @@ -68,14 +141,15 @@ static void setValueById(std::vector const &ids, * @return number of matched names/ids */ -static int setValueByIdRegex(std::vector const &ids, - std::vector &values, realtype value, - std::string const ®ex, - const char *variable_name, const char *id_name) { +static int setValueByIdRegex( + std::vector const& ids, std::vector& values, + realtype value, std::string const& regex, char const* variable_name, + char const* id_name +) { try { std::regex pattern(regex); int n_found = 0; - for (const auto &id : ids) { + for (auto const& id : ids) { if (std::regex_match(id, pattern)) { values.at(&id - &ids[0]) = value; ++n_found; @@ -83,44 +157,64 @@ static int setValueByIdRegex(std::vector const &ids, } if (n_found == 0) - throw AmiException("Could not find %s with specified %s (%s)", - variable_name, id_name, regex.c_str()); + throw AmiException( + "Could not find %s with specified %s (%s)", variable_name, + id_name, regex.c_str() + ); return n_found; - } catch (std::regex_error const &e) { + } catch (std::regex_error const& e) { auto err_string = regexErrorToString(e.code()); - throw AmiException("Specified regex pattern %s could not be compiled:" - " %s (%s)", regex.c_str(), e.what(), - err_string.c_str()); - } -} - -Model::Model(ModelDimensions const & model_dimensions, - SimulationParameters simulation_parameters, - SecondOrderMode o2mode, std::vector idlist, std::vector z2event, - const bool pythonGenerated, const int ndxdotdp_explicit, - const int ndxdotdx_explicit, const int w_recursion_depth) - : ModelDimensions(model_dimensions), pythonGenerated(pythonGenerated), - o2mode(o2mode), idlist(std::move(idlist)), - derived_state_(model_dimensions), - z2event_(std::move(z2event)), - state_is_non_negative_(nx_solver, false), - w_recursion_depth_(w_recursion_depth), - simulation_parameters_(std::move(simulation_parameters)) { - Expects(model_dimensions.np == static_cast(simulation_parameters_.parameters.size())); - Expects(model_dimensions.nk == static_cast(simulation_parameters_.fixedParameters.size())); - - simulation_parameters.pscale = std::vector(model_dimensions.np, ParameterScaling::none); + throw AmiException( + "Specified regex pattern %s could not be compiled:" + " %s (%s)", + regex.c_str(), e.what(), err_string.c_str() + ); + } +} + +Model::Model( + ModelDimensions const& model_dimensions, + 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 +) + : ModelDimensions(model_dimensions) + , pythonGenerated(pythonGenerated) + , o2mode(o2mode) + , idlist(std::move(idlist)) + , derived_state_(model_dimensions) + , z2event_(std::move(z2event)) + , state_is_non_negative_(nx_solver, false) + , w_recursion_depth_(w_recursion_depth) + , simulation_parameters_(std::move(simulation_parameters)) { + Expects( + model_dimensions.np + == gsl::narrow(simulation_parameters_.parameters.size()) + ); + Expects( + model_dimensions.nk + == gsl::narrow(simulation_parameters_.fixedParameters.size()) + ); + + simulation_parameters.pscale = std::vector( + model_dimensions.np, ParameterScaling::none + ); state_.h.resize(ne, 0.0); state_.total_cl.resize(nx_rdata - nx_solver, 0.0); state_.stotal_cl.resize((nx_rdata - nx_solver) * np(), 0.0); state_.unscaledParameters.resize(np()); - unscaleParameters(simulation_parameters_.parameters, - simulation_parameters_.pscale, state_.unscaledParameters); + unscaleParameters( + simulation_parameters_.parameters, simulation_parameters_.pscale, + state_.unscaledParameters + ); state_.fixedParameters = simulation_parameters_.fixedParameters; state_.plist = simulation_parameters_.plist; + root_initial_values_.resize(ne, true); + /* If Matlab wrapped: dxdotdp is a full AmiVector, if Python wrapped: dxdotdp_explicit and dxdotdp_implicit are CSC matrices */ @@ -132,125 +226,203 @@ Model::Model(ModelDimensions const & model_dimensions, derived_state_.dwdp_ = SUNMatrixWrapper(nw, np(), 0, CSC_MAT); for (int irec = 0; irec <= w_recursion_depth_; ++irec) { - /* for the first element we know the exact size, while for all others we - guess the size*/ + /* for the first element we know the exact size, while for all + others we guess the size*/ dwdp_hierarchical_.emplace_back( - SUNMatrixWrapper(nw, np(), irec * ndwdw + ndwdp, CSC_MAT)); + SUNMatrixWrapper(nw, np(), irec * ndwdw + ndwdp, CSC_MAT) + ); dwdx_hierarchical_.emplace_back( - SUNMatrixWrapper(nw, nx_solver, irec * ndwdw + ndwdx, CSC_MAT)); + SUNMatrixWrapper(nw, nx_solver, irec * ndwdw + ndwdx, CSC_MAT) + ); } - assert(static_cast(dwdp_hierarchical_.size()) == - w_recursion_depth_ + 1); - assert(static_cast(dwdx_hierarchical_.size()) == - w_recursion_depth_ + 1); - - derived_state_.dxdotdp_explicit = SUNMatrixWrapper( - nx_solver, np(), ndxdotdp_explicit, CSC_MAT); + assert( + gsl::narrow(dwdp_hierarchical_.size()) + == w_recursion_depth_ + 1 + ); + assert( + gsl::narrow(dwdx_hierarchical_.size()) + == w_recursion_depth_ + 1 + ); + + derived_state_.dxdotdp_explicit + = SUNMatrixWrapper(nx_solver, np(), ndxdotdp_explicit, CSC_MAT); // guess size, will be dynamically reallocated - derived_state_.dxdotdp_implicit = SUNMatrixWrapper( - nx_solver, np(), ndwdp + ndxdotdw, CSC_MAT); + derived_state_.dxdotdp_implicit + = SUNMatrixWrapper(nx_solver, np(), ndwdp + ndxdotdw, CSC_MAT); derived_state_.dxdotdx_explicit = SUNMatrixWrapper( - nx_solver, nx_solver, ndxdotdx_explicit, CSC_MAT); + nx_solver, nx_solver, ndxdotdx_explicit, CSC_MAT + ); // guess size, will be dynamically reallocated - derived_state_.dxdotdx_implicit = SUNMatrixWrapper( - nx_solver, nx_solver, ndwdx + ndxdotdw, CSC_MAT); + derived_state_.dxdotdx_implicit + = SUNMatrixWrapper(nx_solver, nx_solver, ndwdx + ndxdotdw, CSC_MAT); // dynamically allocate on first call - derived_state_.dxdotdp_full = SUNMatrixWrapper( - nx_solver, np(), 0, CSC_MAT); + derived_state_.dxdotdp_full + = SUNMatrixWrapper(nx_solver, np(), 0, CSC_MAT); for (int iytrue = 0; iytrue < nytrue; ++iytrue) derived_state_.dJydy_.emplace_back( - SUNMatrixWrapper(nJ, ny, ndJydy.at(iytrue), CSC_MAT)); + SUNMatrixWrapper(nJ, ny, ndJydy.at(iytrue), CSC_MAT) + ); } else { derived_state_.dwdx_ = SUNMatrixWrapper(nw, nx_solver, ndwdx, CSC_MAT); derived_state_.dwdp_ = SUNMatrixWrapper(nw, np(), ndwdp, CSC_MAT); - derived_state_.dJydy_matlab_ = std::vector( - nJ * nytrue * ny, 0.0); + derived_state_.dJydy_matlab_ + = std::vector(nJ * nytrue * ny, 0.0); } requireSensitivitiesForAllParameters(); } -bool operator==(const Model &a, const Model &b) { +bool operator==(Model const& a, Model const& b) { if (typeid(a) != typeid(b)) return false; return (static_cast(a) == static_cast(b)) - && (a.o2mode == b.o2mode) && - (a.z2event_ == b.z2event_) && (a.idlist == b.idlist) && - (a.state_.h == b.state_.h) && - (a.state_.unscaledParameters == b.state_.unscaledParameters) && - (a.simulation_parameters_ == b.simulation_parameters_) && - (a.state_.fixedParameters == b.state_.fixedParameters) && - (a.state_.plist == b.state_.plist) && (a.x0data_ == b.x0data_) && - (a.sx0data_ == b.sx0data_) && - (a.nmaxevent_ == b.nmaxevent_) && - (a.state_is_non_negative_ == b.state_is_non_negative_) && - (a.sigma_res_ == b.sigma_res_) && - (a.min_sigma_ == b.min_sigma_); -} - -bool operator==(const ModelDimensions &a, const ModelDimensions &b) { + && (a.o2mode == b.o2mode) && (a.z2event_ == b.z2event_) + && (a.idlist == b.idlist) + && (a.simulation_parameters_ == b.simulation_parameters_) + && (a.x0data_ == b.x0data_) && (a.sx0data_ == b.sx0data_) + && (a.nmaxevent_ == b.nmaxevent_) + && (a.state_is_non_negative_ == b.state_is_non_negative_) + && (a.sigma_res_ == b.sigma_res_) && (a.min_sigma_ == b.min_sigma_) + && a.state_ == b.state_; +} + +bool operator==(ModelDimensions const& a, ModelDimensions const& b) { if (typeid(a) != typeid(b)) return false; - return (a.nx_rdata == b.nx_rdata) && (a.nxtrue_rdata == b.nxtrue_rdata) && - (a.nx_solver == b.nx_solver) && - (a.nxtrue_solver == b.nxtrue_solver) && - (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.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) && (a.ubw == b.ubw) && - (a.lbw == b.lbw); -} - - -void Model::initialize(AmiVector &x, AmiVector &dx, AmiVectorArray &sx, - AmiVectorArray & /*sdx*/, bool computeSensitivities) { + return (a.nx_rdata == b.nx_rdata) && (a.nxtrue_rdata == b.nxtrue_rdata) + && (a.nx_solver == b.nx_solver) + && (a.nxtrue_solver == b.nxtrue_solver) + && (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.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) + && (a.ubw == b.ubw) && (a.lbw == b.lbw); +} + +void Model::initialize( + AmiVector& x, AmiVector& dx, AmiVectorArray& sx, AmiVectorArray& /*sdx*/, + bool computeSensitivities, std::vector& roots_found +) { initializeStates(x); - if (computeSensitivities) + initializeSplines(); + if (computeSensitivities) { initializeStateSensitivities(sx, x); + initializeSplineSensitivities(); + } fdx0(x, dx); if (computeSensitivities) fsdx0(); if (ne) - initHeaviside(x, dx); + initEvents(x, dx, roots_found); } -void Model::initializeB(AmiVector &xB, AmiVector &dxB, AmiVector &xQB, - bool posteq) const { +void Model::initializeB( + AmiVector& xB, AmiVector& dxB, AmiVector& xQB, bool posteq +) const { xB.zero(); dxB.zero(); if (!posteq) xQB.zero(); } -void Model::initializeStates(AmiVector &x) { +void Model::initializeStates(AmiVector& x) { if (x0data_.empty()) { fx0(x); } else { std::vector x0_solver(nx_solver, 0.0); - ftotal_cl(state_.total_cl.data(), x0data_.data()); + ftotal_cl( + state_.total_cl.data(), x0data_.data(), + state_.unscaledParameters.data(), state_.fixedParameters.data() + ); fx_solver(x0_solver.data(), x0data_.data()); std::copy(x0_solver.cbegin(), x0_solver.cend(), x.data()); } + + checkFinite(x.getVector(), ModelQuantity::x0); +} + +void Model::initializeSplines() { + splines_ = fcreate_splines( + state_.unscaledParameters.data(), state_.fixedParameters.data() + ); + state_.spl_.resize(splines_.size(), 0.0); + for (auto& spline : splines_) { + spline.compute_coefficients(); + } } -void Model::initializeStateSensitivities(AmiVectorArray &sx, - AmiVector const &x) { +void Model::initializeSplineSensitivities() { + derived_state_.sspl_ = SUNMatrixWrapper(splines_.size(), np()); + int allnodes = 0; + for (auto const& spline : splines_) { + allnodes += spline.n_nodes(); + } + + std::vector dspline_valuesdp(allnodes * nplist(), 0.0); + std::vector dspline_slopesdp(allnodes * nplist(), 0.0); + std::vector tmp_dvalues(allnodes, 0.0); + std::vector tmp_dslopes(allnodes, 0.0); + for (int ip = 0; ip < nplist(); ip++) { + std::fill(tmp_dvalues.begin(), tmp_dvalues.end(), 0.0); + std::fill(tmp_dslopes.begin(), tmp_dslopes.end(), 0.0); + fdspline_valuesdp( + tmp_dvalues.data(), state_.unscaledParameters.data(), + state_.fixedParameters.data(), plist(ip) + ); + fdspline_slopesdp( + tmp_dslopes.data(), state_.unscaledParameters.data(), + state_.fixedParameters.data(), plist(ip) + ); + /* NB dspline_valuesdp/dspline_slopesdp must be filled + * using the following order for the indices + * (from slower to faster): spline, node, parameter. + * That is what the current spline implementation expects. + */ + int k = 0; + int offset = ip; + for (auto const& spline : splines_) { + for (int n = 0; n < spline.n_nodes(); n++) { + dspline_valuesdp[offset] = tmp_dvalues[k]; + dspline_slopesdp[offset] = tmp_dslopes[k]; + offset += nplist(); + k += 1; + } + } + assert(k == allnodes); + } + + int spline_offset = 0; + for (auto& spline : splines_) { + spline.compute_coefficients_sensi( + nplist(), spline_offset, dspline_valuesdp, dspline_slopesdp + ); + spline_offset += spline.n_nodes() * nplist(); + } +} + +void Model::initializeStateSensitivities( + AmiVectorArray& sx, AmiVector const& x +) { if (sx0data_.empty()) { fsx0(sx, x); } else { - realtype *stcl = nullptr; + realtype* stcl = nullptr; std::vector sx0_solver_slice(nx_solver, 0.0); for (int ip = 0; ip < nplist(); ip++) { if (ncl() > 0) stcl = &state_.stotal_cl.at(plist(ip) * ncl()); - fstotal_cl(stcl, &sx0data_.at(ip * nx_rdata), plist(ip)); + fstotal_cl( + stcl, &sx0data_.at(ip * nx_rdata), plist(ip), + derived_state_.x_rdata_.data(), + state_.unscaledParameters.data(), state_.fixedParameters.data(), + state_.total_cl.data() + ); fsx_solver(sx0_solver_slice.data(), &sx0data_.at(ip * nx_rdata)); for (int ix = 0; ix < nx_solver; ix++) { sx.at(ix, ip) = sx0_solver_slice.at(ix); @@ -259,240 +431,302 @@ void Model::initializeStateSensitivities(AmiVectorArray &sx, } } -void Model::initHeaviside(AmiVector const &x, AmiVector const &dx) { +void Model::initEvents( + AmiVector const& x, AmiVector const& dx, std::vector& roots_found +) { std::vector rootvals(ne, 0.0); froot(simulation_parameters_.tstart_, x, dx, rootvals); + std::fill(roots_found.begin(), roots_found.end(), 0); for (int ie = 0; ie < ne; ie++) { if (rootvals.at(ie) < 0) { state_.h.at(ie) = 0.0; } else { state_.h.at(ie) = 1.0; + if (!root_initial_values_.at(ie)) // only false->true triggers event + roots_found.at(ie) = 1; } } } -int Model::nplist() const { return static_cast(state_.plist.size()); } +int Model::nplist() const { return gsl::narrow(state_.plist.size()); } -int Model::np() const { return static_cast(static_cast(*this).np); } +int Model::np() const { + return gsl::narrow(static_cast(*this).np); +} -int Model::nk() const { return static_cast(state_.fixedParameters.size()); } +int Model::nk() const { + return gsl::narrow(state_.fixedParameters.size()); +} int Model::ncl() const { return nx_rdata - nx_solver; } int Model::nx_reinit() const { return nx_solver_reinit; } -const double *Model::k() const { return state_.fixedParameters.data(); } +double const* Model::k() const { return state_.fixedParameters.data(); } int Model::nMaxEvent() const { return nmaxevent_; } void Model::setNMaxEvent(int nmaxevent) { nmaxevent_ = nmaxevent; } -int Model::nt() const { return static_cast(simulation_parameters_.ts_.size()); } +int Model::nt() const { + return gsl::narrow(simulation_parameters_.ts_.size()); +} -const std::vector &Model::getParameterScale() const { +std::vector const& Model::getParameterScale() const { return simulation_parameters_.pscale; } void Model::setParameterScale(ParameterScaling pscale) { - simulation_parameters_.pscale.assign(simulation_parameters_.pscale.size(), pscale); - scaleParameters(state_.unscaledParameters, simulation_parameters_.pscale, - simulation_parameters_.parameters); + simulation_parameters_.pscale.assign( + simulation_parameters_.pscale.size(), pscale + ); + scaleParameters( + state_.unscaledParameters, simulation_parameters_.pscale, + simulation_parameters_.parameters + ); sx0data_.clear(); } -void Model::setParameterScale(std::vector const &pscaleVec) { +void Model::setParameterScale(std::vector const& pscaleVec) { if (pscaleVec.size() != simulation_parameters_.parameters.size()) throw AmiException("Dimension mismatch. Size of parameter scaling does " "not match number of model parameters."); simulation_parameters_.pscale = pscaleVec; - scaleParameters(state_.unscaledParameters, simulation_parameters_.pscale, - simulation_parameters_.parameters); + scaleParameters( + state_.unscaledParameters, simulation_parameters_.pscale, + simulation_parameters_.parameters + ); sx0data_.clear(); } -const std::vector &Model::getUnscaledParameters() const { +std::vector const& Model::getUnscaledParameters() const { return state_.unscaledParameters; } -std::vector const &Model::getParameters() const { +std::vector const& Model::getParameters() const { return simulation_parameters_.parameters; } -realtype Model::getParameterById(std::string const &par_id) const { +realtype Model::getParameterById(std::string const& par_id) const { if (!hasParameterIds()) throw AmiException( - "Could not access parameters by id as they are not set"); - return getValueById(getParameterIds(), simulation_parameters_.parameters, - par_id, "parameters", "id"); + "Could not access parameters by id as they are not set" + ); + return getValueById( + getParameterIds(), simulation_parameters_.parameters, par_id, + "parameters", "id" + ); } -realtype Model::getParameterByName(std::string const &par_name) const { +realtype Model::getParameterByName(std::string const& par_name) const { if (!hasParameterNames()) throw AmiException( - "Could not access parameters by name as they are not set"); - return getValueById(getParameterNames(), simulation_parameters_.parameters, - par_name, "parameters", "name"); + "Could not access parameters by name as they are not set" + ); + return getValueById( + getParameterNames(), simulation_parameters_.parameters, par_name, + "parameters", "name" + ); } -void Model::setParameters(const std::vector &p) { +void Model::setParameters(std::vector const& p) { if (p.size() != (unsigned)np()) throw AmiException("Dimension mismatch. Size of parameters does not " "match number of model parameters."); simulation_parameters_.parameters = p; state_.unscaledParameters.resize(simulation_parameters_.parameters.size()); - unscaleParameters(simulation_parameters_.parameters, - simulation_parameters_.pscale, - state_.unscaledParameters); + unscaleParameters( + simulation_parameters_.parameters, simulation_parameters_.pscale, + state_.unscaledParameters + ); } -void Model::setParameterById(const std::map &p, - bool ignoreErrors) -{ +void Model::setParameterById( + std::map const& p, bool ignoreErrors +) { for (auto& kv : p) { try { setParameterById(kv.first, kv.second); } catch (AmiException const&) { - if(!ignoreErrors) + if (!ignoreErrors) throw; } } } -void Model::setParameterById(std::string const &par_id, realtype value) { +void Model::setParameterById(std::string const& par_id, realtype value) { if (!hasParameterIds()) throw AmiException( - "Could not access parameters by id as they are not set"); - - setValueById(getParameterIds(), simulation_parameters_.parameters, - value, par_id, "parameter", "id"); - unscaleParameters(simulation_parameters_.parameters, - simulation_parameters_.pscale, - state_.unscaledParameters); -} - -int Model::setParametersByIdRegex(std::string const &par_id_regex, - realtype value) { + "Could not access parameters by id as they are not set" + ); + + setValueById( + getParameterIds(), simulation_parameters_.parameters, value, par_id, + "parameter", "id" + ); + unscaleParameters( + simulation_parameters_.parameters, simulation_parameters_.pscale, + state_.unscaledParameters + ); +} + +int Model::setParametersByIdRegex( + std::string const& par_id_regex, realtype value +) { if (!hasParameterIds()) throw AmiException( - "Could not access parameters by id as they are not set"); - int n_found = setValueByIdRegex(getParameterIds(), - simulation_parameters_.parameters, - value, par_id_regex, "parameter", "id"); - unscaleParameters(simulation_parameters_.parameters, - simulation_parameters_.pscale, state_.unscaledParameters); + "Could not access parameters by id as they are not set" + ); + int n_found = setValueByIdRegex( + getParameterIds(), simulation_parameters_.parameters, value, + par_id_regex, "parameter", "id" + ); + unscaleParameters( + simulation_parameters_.parameters, simulation_parameters_.pscale, + state_.unscaledParameters + ); return n_found; } -void Model::setParameterByName(std::string const &par_name, realtype value) { +void Model::setParameterByName(std::string const& par_name, realtype value) { if (!hasParameterNames()) throw AmiException( - "Could not access parameters by name as they are not set"); - - setValueById(getParameterNames(), simulation_parameters_.parameters, - value, par_name, "parameter", "name"); - unscaleParameters(simulation_parameters_.parameters, - simulation_parameters_.pscale, state_.unscaledParameters); -} - -void Model::setParameterByName(const std::map &p, - bool ignoreErrors) -{ + "Could not access parameters by name as they are not set" + ); + + setValueById( + getParameterNames(), simulation_parameters_.parameters, value, par_name, + "parameter", "name" + ); + unscaleParameters( + simulation_parameters_.parameters, simulation_parameters_.pscale, + state_.unscaledParameters + ); +} + +void Model::setParameterByName( + std::map const& p, bool ignoreErrors +) { for (auto& kv : p) { try { setParameterByName(kv.first, kv.second); } catch (AmiException const&) { - if(!ignoreErrors) + if (!ignoreErrors) throw; } } } -int Model::setParametersByNameRegex(std::string const &par_name_regex, - realtype value) { +int Model::setParametersByNameRegex( + std::string const& par_name_regex, realtype value +) { if (!hasParameterNames()) throw AmiException( - "Could not access parameters by name as they are not set"); - - int n_found = setValueByIdRegex(getParameterNames(), - simulation_parameters_.parameters, - value, par_name_regex, "parameter", "name"); - - unscaleParameters(simulation_parameters_.parameters, - simulation_parameters_.pscale, state_.unscaledParameters); + "Could not access parameters by name as they are not set" + ); + + int n_found = setValueByIdRegex( + getParameterNames(), simulation_parameters_.parameters, value, + par_name_regex, "parameter", "name" + ); + + unscaleParameters( + simulation_parameters_.parameters, simulation_parameters_.pscale, + state_.unscaledParameters + ); return n_found; } -const std::vector &Model::getFixedParameters() const { +std::vector const& Model::getFixedParameters() const { return state_.fixedParameters; } -realtype Model::getFixedParameterById(std::string const &par_id) const { +realtype Model::getFixedParameterById(std::string const& par_id) const { if (!hasFixedParameterIds()) throw AmiException( - "Could not access fixed parameters by id as they are not set"); + "Could not access fixed parameters by id as they are not set" + ); - return getValueById(getFixedParameterIds(), state_.fixedParameters, - par_id, "fixedParameters", "id"); + return getValueById( + getFixedParameterIds(), state_.fixedParameters, par_id, + "fixedParameters", "id" + ); } -realtype Model::getFixedParameterByName(std::string const &par_name) const { +realtype Model::getFixedParameterByName(std::string const& par_name) const { if (!hasFixedParameterNames()) throw AmiException( - "Could not access fixed parameters by name as they are not set"); + "Could not access fixed parameters by name as they are not set" + ); - return getValueById(getFixedParameterNames(), state_.fixedParameters, - par_name, "fixedParameters", "name"); + return getValueById( + getFixedParameterNames(), state_.fixedParameters, par_name, + "fixedParameters", "name" + ); } -void Model::setFixedParameters(const std::vector &k) { +void Model::setFixedParameters(std::vector const& k) { if (k.size() != (unsigned)nk()) throw AmiException("Dimension mismatch. Size of fixedParameters does " "not match number of fixed model parameters."); state_.fixedParameters = k; } -void Model::setFixedParameterById(std::string const &par_id, realtype value) { +void Model::setFixedParameterById(std::string const& par_id, realtype value) { if (!hasFixedParameterIds()) throw AmiException( - "Could not access fixed parameters by id as they are not set"); + "Could not access fixed parameters by id as they are not set" + ); - setValueById(getFixedParameterIds(), state_.fixedParameters, value, par_id, - "fixedParameters", "id"); + setValueById( + getFixedParameterIds(), state_.fixedParameters, value, par_id, + "fixedParameters", "id" + ); } -int Model::setFixedParametersByIdRegex(std::string const &par_id_regex, - realtype value) { +int Model::setFixedParametersByIdRegex( + std::string const& par_id_regex, realtype value +) { if (!hasFixedParameterIds()) throw AmiException( - "Could not access fixed parameters by id as they are not set"); + "Could not access fixed parameters by id as they are not set" + ); - return setValueByIdRegex(getFixedParameterIds(), state_.fixedParameters, - value, par_id_regex, "fixedParameters", "id"); + return setValueByIdRegex( + getFixedParameterIds(), state_.fixedParameters, value, par_id_regex, + "fixedParameters", "id" + ); } -void Model::setFixedParameterByName(std::string const &par_name, - realtype value) { +void Model::setFixedParameterByName( + std::string const& par_name, realtype value +) { if (!hasFixedParameterNames()) throw AmiException( - "Could not access fixed parameters by name as they are not set"); + "Could not access fixed parameters by name as they are not set" + ); - setValueById(getFixedParameterNames(), state_.fixedParameters, value, - par_name, "fixedParameters", "name"); + setValueById( + getFixedParameterNames(), state_.fixedParameters, value, par_name, + "fixedParameters", "name" + ); } -int Model::setFixedParametersByNameRegex(std::string const &par_name_regex, - realtype value) { +int Model::setFixedParametersByNameRegex( + std::string const& par_name_regex, realtype value +) { if (!hasFixedParameterNames()) throw AmiException( - "Could not access fixed parameters by name as they are not set"); + "Could not access fixed parameters by name as they are not set" + ); - return setValueByIdRegex(getFixedParameterIds(), state_.fixedParameters, - value, par_name_regex, "fixedParameters", "name"); + return setValueByIdRegex( + getFixedParameterIds(), state_.fixedParameters, value, par_name_regex, + "fixedParameters", "name" + ); } -std::string Model::getName() const { - return ""; -} +std::string Model::getName() const { return ""; } bool Model::hasParameterNames() const { return np() == 0 || !getParameterNames().empty(); @@ -510,6 +744,10 @@ std::vector Model::getStateNames() const { return std::vector(); } +std::vector Model::getStateNamesSolver() const { + return std::vector(); +} + bool Model::hasFixedParameterNames() const { return nk() == 0 || !getFixedParameterNames().empty(); } @@ -550,6 +788,10 @@ std::vector Model::getStateIds() const { return std::vector(); } +std::vector Model::getStateIdsSolver() const { + return std::vector(); +} + bool Model::hasFixedParameterIds() const { return nk() == 0 || !getFixedParameterIds().empty(); } @@ -574,16 +816,17 @@ std::vector Model::getExpressionIds() const { return std::vector(); } +bool Model::hasQuadraticLLH() const { return true; } -bool Model::hasQuadraticLLH() const { - return true; +std::vector const& Model::getTimepoints() const { + return simulation_parameters_.ts_; } -std::vector const &Model::getTimepoints() const { return simulation_parameters_.ts_; } - -double Model::getTimepoint(const int it) const { return simulation_parameters_.ts_.at(it); } +double Model::getTimepoint(int const it) const { + return simulation_parameters_.ts_.at(it); +} -void Model::setTimepoints(const std::vector &ts) { +void Model::setTimepoints(std::vector const& ts) { if (!std::is_sorted(ts.begin(), ts.end())) throw AmiException("Encountered non-monotonic timepoints, please order" " timepoints such that they are monotonically" @@ -595,38 +838,47 @@ double Model::t0() const { return simulation_parameters_.tstart_; } void Model::setT0(double t0) { simulation_parameters_.tstart_ = t0; } -std::vector const &Model::getStateIsNonNegative() const { +std::vector const& Model::getStateIsNonNegative() const { return state_is_non_negative_; } -void Model::setStateIsNonNegative(std::vector const &nonNegative) { +void Model::setStateIsNonNegative(std::vector const& nonNegative) { + auto any_state_non_negative + = std::any_of(nonNegative.begin(), nonNegative.end(), [](bool x) { + return x; + }); if (nx_solver != nx_rdata) { - throw AmiException("Non-negative states are not supported with" - " conservation laws enabled"); + if (any_state_non_negative) + throw AmiException("Non-negative states are not supported with" + " conservation laws enabled."); + // nothing to do, as `state_is_non_negative_` will always be all-false + // in case of conservation laws + return; } - if (state_is_non_negative_.size() != static_cast(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); + if (state_is_non_negative_.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 + ); } state_is_non_negative_ = nonNegative; - any_state_non_negative_ = - std::any_of(state_is_non_negative_.begin(), state_is_non_negative_.end(), - [](bool x) { return x; }); + any_state_non_negative_ = any_state_non_negative; } void Model::setAllStatesNonNegative() { setStateIsNonNegative(std::vector(nx_solver, true)); } -const std::vector &Model::getParameterList() const { return state_.plist; } +std::vector const& Model::getParameterList() const { return state_.plist; } int Model::plist(int pos) const { return state_.plist.at(pos); } -void Model::setParameterList(const std::vector &plist) { +void Model::setParameterList(std::vector const& plist) { int np = this->np(); // cannot capture 'this' in lambda expression - if (std::any_of(plist.begin(), plist.end(), - [&np](int idx) { return idx < 0 || idx >= np; })) { + if (std::any_of(plist.begin(), plist.end(), [&np](int idx) { + return idx < 0 || idx >= np; + })) { throw AmiException("Indices in plist must be in [0..np]"); } state_.plist = plist; @@ -635,7 +887,7 @@ void Model::setParameterList(const std::vector &plist) { } std::vector Model::getInitialStates() { - if(!x0data_.empty()) { + if (!x0data_.empty()) { return x0data_; } @@ -644,12 +896,12 @@ std::vector Model::getInitialStates() { * changing parameters etc. */ std::vector x0(nx_rdata, 0.0); - fx0(x0.data(), simulation_parameters_.tstart_, state_.unscaledParameters.data(), - state_.fixedParameters.data()); + fx0(x0.data(), simulation_parameters_.tstart_, + state_.unscaledParameters.data(), state_.fixedParameters.data()); return x0; } -void Model::setInitialStates(const std::vector &x0) { +void Model::setInitialStates(std::vector const& x0) { if (x0.size() != (unsigned)nx_rdata && !x0.empty()) throw AmiException("Dimension mismatch. Size of x0 does not match " "number of model states."); @@ -662,13 +914,10 @@ void Model::setInitialStates(const std::vector &x0) { x0data_ = x0; } -bool Model::hasCustomInitialStates() const -{ - return !x0data_.empty(); -} +bool Model::hasCustomInitialStates() const { return !x0data_.empty(); } std::vector Model::getInitialStateSensitivities() { - if(!sx0data_.empty()) { + if (!sx0data_.empty()) { return sx0data_; } @@ -679,15 +928,16 @@ std::vector Model::getInitialStateSensitivities() { std::vector sx0(nx_rdata * nplist(), 0.0); auto x0 = getInitialStates(); for (int ip = 0; ip < nplist(); ip++) { - fsx0(sx0.data(), simulation_parameters_.tstart_, x0.data(), - state_.unscaledParameters.data(), - state_.fixedParameters.data(), plist(ip)); + fsx0( + sx0.data(), simulation_parameters_.tstart_, x0.data(), + state_.unscaledParameters.data(), state_.fixedParameters.data(), + plist(ip) + ); } return sx0; - } -void Model::setInitialStateSensitivities(const std::vector &sx0) { +void Model::setInitialStateSensitivities(std::vector const& sx0) { if (sx0.size() != (unsigned)nx_rdata * nplist() && !sx0.empty()) throw AmiException("Dimension mismatch. Size of sx0 does not match " "number of model states * number of parameter " @@ -716,20 +966,20 @@ void Model::setInitialStateSensitivities(const std::vector &sx0) { } for (int ix = 0; ix < nx_rdata; ++ix) { - sx0_rdata.at(ip * nx_rdata + ix) = - sx0.at(ip * nx_rdata + ix) / chainrulefactor; + sx0_rdata.at(ip * nx_rdata + ix) + = sx0.at(ip * nx_rdata + ix) / chainrulefactor; } } setUnscaledInitialStateSensitivities(sx0_rdata); } -bool Model::hasCustomInitialStateSensitivities() const -{ +bool Model::hasCustomInitialStateSensitivities() const { return !sx0data_.empty(); } void Model::setUnscaledInitialStateSensitivities( - const std::vector &sx0) { + std::vector const& sx0 +) { if (sx0.size() != (unsigned)nx_rdata * nplist() && !sx0.empty()) throw AmiException("Dimension mismatch. Size of sx0 does not match " "number of model states * number of parameter " @@ -743,8 +993,17 @@ void Model::setUnscaledInitialStateSensitivities( sx0data_ = sx0; } -void Model::setSteadyStateSensitivityMode( - const SteadyStateSensitivityMode mode) { +void Model::setSteadyStateComputationMode(const SteadyStateComputationMode mode +) { + steadystate_computation_mode_ = mode; +} + +SteadyStateComputationMode Model::getSteadyStateComputationMode() const { + return steadystate_computation_mode_; +} + +void Model::setSteadyStateSensitivityMode(const SteadyStateSensitivityMode mode +) { steadystate_sensitivity_mode_ = mode; } @@ -758,11 +1017,15 @@ void Model::setReinitializeFixedParameterInitialStates(bool flag) { "State reinitialization cannot be enabled for this model " "as this feature was disabled at compile time. Most likely," " this was because some initial states depending on " - "fixedParameters also depended on parameters."); + "fixedParameters also depended on parameters." + ); simulation_parameters_.reinitializeFixedParameterInitialStates = flag; - if(flag) { - simulation_parameters_.reinitializeAllFixedParameterDependentInitialStatesForSimulation(nx_rdata); + if (flag) { + simulation_parameters_ + .reinitializeAllFixedParameterDependentInitialStatesForSimulation( + nx_rdata + ); } else { simulation_parameters_.reinitialization_state_idxs_sim.clear(); } @@ -770,7 +1033,7 @@ void Model::setReinitializeFixedParameterInitialStates(bool flag) { bool Model::getReinitializeFixedParameterInitialStates() const { return simulation_parameters_.reinitializeFixedParameterInitialStates - || !simulation_parameters_.reinitialization_state_idxs_sim.empty(); + || !simulation_parameters_.reinitialization_state_idxs_sim.empty(); } void Model::requireSensitivitiesForAllParameters() { @@ -779,14 +1042,16 @@ void Model::requireSensitivitiesForAllParameters() { initializeVectors(); } -void Model::getExpression(gsl::span w, const realtype t, const AmiVector &x) -{ - fw(t, x.data()); +void Model::getExpression( + gsl::span w, const realtype t, AmiVector const& x +) { + fw(t, computeX_pos(x)); writeSlice(derived_state_.w_, w); } -void Model::getObservable(gsl::span y, const realtype t, - const AmiVector &x) { +void Model::getObservable( + gsl::span y, const realtype t, AmiVector const& x +) { fy(t, x); writeSlice(derived_state_.y_, y); } @@ -795,49 +1060,83 @@ ObservableScaling Model::getObservableScaling(int /*iy*/) const { return ObservableScaling::lin; } -void Model::getObservableSensitivity(gsl::span sy, const realtype t, - const AmiVector &x, - const AmiVectorArray &sx) { +void Model::getObservableSensitivity( + gsl::span sy, const realtype t, AmiVector const& x, + AmiVectorArray const& sx +) { if (!ny) return; fdydx(t, x); + fsspl(t); fdydp(t, x); derived_state_.sx_.resize(nx_solver * nplist()); sx.flatten_to_vector(derived_state_.sx_); - // compute sy = 1.0*dydx*sx + 1.0*sy + // compute sy = 1.0*dydx*sx + 1.0*dydp // dydx A[ny,nx_solver] * sx B[nx_solver,nplist] = sy C[ny,nplist] // M K K N M N // lda ldb ldc - amici_dgemm(BLASLayout::colMajor, BLASTranspose::noTrans, - BLASTranspose::noTrans, ny, nplist(), nx_solver, 1.0, - derived_state_.dydx_.data(), ny, - derived_state_.sx_.data(), nx_solver, 1.0, - derived_state_.dydp_.data(), - ny); + if (nx_solver) { + setNaNtoZero(derived_state_.dydx_); + setNaNtoZero(derived_state_.sx_); + amici_dgemm( + BLASLayout::colMajor, BLASTranspose::noTrans, + BLASTranspose::noTrans, ny, nplist(), nx_solver, 1.0, + derived_state_.dydx_.data(), ny, derived_state_.sx_.data(), + nx_solver, 1.0, derived_state_.dydp_.data(), ny + ); + } writeSlice(derived_state_.dydp_, sy); if (always_check_finite_) - checkFinite(sy, "sy"); + checkFinite(sy, ModelQuantity::sy, nplist()); } -void Model::getObservableSigma(gsl::span sigmay, const int it, - const ExpData *edata) { +void Model::getObservableSigma( + gsl::span sigmay, int const it, ExpData const* edata +) { fsigmay(it, edata); writeSlice(derived_state_.sigmay_, sigmay); } -void Model::getObservableSigmaSensitivity(gsl::span ssigmay, - const int it, const ExpData *edata) { +void Model::getObservableSigmaSensitivity( + gsl::span ssigmay, gsl::span sy, int const it, + ExpData const* edata +) { fdsigmaydp(it, edata); writeSlice(derived_state_.dsigmaydp_, ssigmay); + + if (pythonGenerated) { + // ssigmay = dsigmaydy*(dydx_solver*sx+dydp)+dsigmaydp + // = dsigmaydy*sy+dsigmaydp + + fdsigmaydy(it, edata); + + // compute ssigmay = 1.0 * dsigmaydp + 1.0 * dsigmaydy * sy + // dsigmaydp C[ny,nplist] += dsigmaydy A[ny,ny] * sy B[ny,nplist] + // M N M K K N + // ldc lda ldb + setNaNtoZero(derived_state_.dsigmaydy_); + derived_state_.sy_.assign(sy.begin(), sy.end()); + setNaNtoZero(derived_state_.sy_); + amici_dgemm( + BLASLayout::colMajor, BLASTranspose::noTrans, + BLASTranspose::noTrans, ny, nplist(), ny, 1.0, + derived_state_.dsigmaydy_.data(), ny, derived_state_.sy_.data(), ny, + 1.0, ssigmay.data(), ny + ); + } + + if (always_check_finite_) + checkFinite(ssigmay, ModelQuantity::ssigmay, nplist()); } -void Model::addObservableObjective(realtype &Jy, const int it, - const AmiVector &x, const ExpData &edata) { +void Model::addObservableObjective( + realtype& Jy, int const it, AmiVector const& x, ExpData const& edata +) { fy(edata.getTimepoint(it), x); fsigmay(it, &edata); @@ -846,20 +1145,17 @@ void Model::addObservableObjective(realtype &Jy, const int it, if (edata.isSetObservedData(it, iyt)) { std::fill(nllh.begin(), nllh.end(), 0.0); fJy(nllh.data(), iyt, state_.unscaledParameters.data(), - state_.fixedParameters.data(), - derived_state_.y_.data(), - derived_state_.sigmay_.data(), - edata.getObservedDataPtr(it)); + state_.fixedParameters.data(), derived_state_.y_.data(), + derived_state_.sigmay_.data(), edata.getObservedDataPtr(it)); Jy -= nllh.at(0); } } } -void Model::addObservableObjectiveSensitivity(std::vector &sllh, - std::vector &s2llh, - const int it, const AmiVector &x, - const AmiVectorArray &sx, - const ExpData &edata) { +void Model::addObservableObjectiveSensitivity( + std::vector& sllh, std::vector& s2llh, int const it, + AmiVector const& x, AmiVectorArray const& sx, ExpData const& edata +) { if (!ny) return; @@ -873,19 +1169,22 @@ void Model::addObservableObjectiveSensitivity(std::vector &sllh, sx.flatten_to_vector(derived_state_.sx_); // C := alpha*op(A)*op(B) + beta*C, - amici_dgemm(BLASLayout::colMajor, BLASTranspose::noTrans, - BLASTranspose::noTrans, nJ, nplist(), nx_solver, 1.0, - derived_state_.dJydx_.data(), nJ, - derived_state_.sx_.data(), nx_solver, 1.0, - derived_state_.dJydp_.data(), - nJ); + setNaNtoZero(derived_state_.dJydx_); + setNaNtoZero(derived_state_.sx_); + amici_dgemm( + BLASLayout::colMajor, BLASTranspose::noTrans, BLASTranspose::noTrans, + nJ, nplist(), nx_solver, 1.0, derived_state_.dJydx_.data(), nJ, + derived_state_.sx_.data(), nx_solver, 1.0, derived_state_.dJydp_.data(), + nJ + ); writeLLHSensitivitySlice(derived_state_.dJydp_, sllh, s2llh); } void Model::addPartialObservableObjectiveSensitivity( - std::vector &sllh, std::vector &s2llh, const int it, - const AmiVector &x, const ExpData &edata) { + std::vector& sllh, std::vector& s2llh, int const it, + AmiVector const& x, ExpData const& edata +) { if (!ny) return; @@ -894,31 +1193,64 @@ void Model::addPartialObservableObjectiveSensitivity( writeLLHSensitivitySlice(derived_state_.dJydp_, sllh, s2llh); } -void Model::getAdjointStateObservableUpdate(gsl::span dJydx, - const int it, const AmiVector &x, - const ExpData &edata) { +void Model::getAdjointStateObservableUpdate( + gsl::span dJydx, int const it, AmiVector const& x, + ExpData const& edata +) { fdJydx(it, x, edata); writeSlice(derived_state_.dJydx_, dJydx); } -void Model::getEvent(gsl::span z, const int ie, const realtype t, - const AmiVector &x) { +void Model::getEvent( + gsl::span z, int const ie, const realtype t, AmiVector const& x +) { fz(ie, t, x); writeSliceEvent(derived_state_.z_, z, ie); } -void Model::getEventSensitivity(gsl::span sz, const int ie, - const realtype t, const AmiVector &x, - const AmiVectorArray &sx) { - for (int ip = 0; ip < nplist(); ip++) { - fsz(&sz[ip * nz], ie, t, x.data(), state_.unscaledParameters.data(), - state_.fixedParameters.data(), state_.h.data(), sx.data(ip), - plist(ip)); +void Model::getEventSensitivity( + gsl::span sz, int const ie, const realtype t, AmiVector const& x, + AmiVectorArray const& sx +) { + if (pythonGenerated) { + if (!nz) + return; + + fdzdx(ie, t, x); + fdzdp(ie, t, x); + + derived_state_.sx_.resize(nx_solver * nplist()); + sx.flatten_to_vector(derived_state_.sx_); + + // compute sz = 1.0*dzdx*sx + 1.0*dzdp + // dzdx A[nz,nx_solver] * sx B[nx_solver,nplist] = sz C[nz,nplist] + // M K K N M N + // lda ldb ldc + setNaNtoZero(derived_state_.dzdx_); + setNaNtoZero(derived_state_.sx_); + amici_dgemm( + BLASLayout::colMajor, BLASTranspose::noTrans, + BLASTranspose::noTrans, nz, nplist(), nx_solver, 1.0, + derived_state_.dzdx_.data(), nz, derived_state_.sx_.data(), + nx_solver, 1.0, derived_state_.dzdp_.data(), nz + ); + + addSlice(derived_state_.dzdp_, sz); + + if (always_check_finite_) + checkFinite(sz, ModelQuantity::sz, nplist()); + } else { + for (int ip = 0; ip < nplist(); ip++) { + fsz(&sz[ip * nz], ie, t, computeX_pos(x), + state_.unscaledParameters.data(), state_.fixedParameters.data(), + state_.h.data(), sx.data(ip), plist(ip)); + } } } -void Model::getUnobservedEventSensitivity(gsl::span sz, - const int ie) { +void Model::getUnobservedEventSensitivity( + gsl::span sz, int const ie +) { checkBufferSize(sz, nz * nplist()); for (int iz = 0; iz < nz; ++iz) @@ -927,40 +1259,75 @@ void Model::getUnobservedEventSensitivity(gsl::span sz, sz[ip * nz + iz] = 0.0; } -void Model::getEventRegularization(gsl::span rz, const int ie, - const realtype t, const AmiVector &x) { +void Model::getEventRegularization( + gsl::span rz, int const ie, const realtype t, AmiVector const& x +) { frz(ie, t, x); writeSliceEvent(derived_state_.rz_, rz, ie); } -void Model::getEventRegularizationSensitivity(gsl::span srz, - const int ie, const realtype t, - const AmiVector &x, - const AmiVectorArray &sx) { - for (int ip = 0; ip < nplist(); ip++) { - fsrz(&srz[ip * nz], ie, t, x.data(), state_.unscaledParameters.data(), - state_.fixedParameters.data(), state_.h.data(), sx.data(ip), - plist(ip)); +void Model::getEventRegularizationSensitivity( + gsl::span srz, int const ie, const realtype t, AmiVector const& x, + AmiVectorArray const& sx +) { + if (pythonGenerated) { + if (!nz) + return; + + fdrzdx(ie, t, x); + fdrzdp(ie, t, x); + + derived_state_.sx_.resize(nx_solver * nplist()); + sx.flatten_to_vector(derived_state_.sx_); + + // compute srz = 1.0*drzdx*sx + 1.0*drzdp + // drzdx A[nz,nx_solver] * sx B[nx_solver,nplist] = srz C[nz,nplist] + // M K K N M N + // lda ldb ldc + setNaNtoZero(derived_state_.drzdx_); + setNaNtoZero(derived_state_.sx_); + amici_dgemm( + BLASLayout::colMajor, BLASTranspose::noTrans, + BLASTranspose::noTrans, nz, nplist(), nx_solver, 1.0, + derived_state_.drzdx_.data(), nz, derived_state_.sx_.data(), + nx_solver, 1.0, derived_state_.drzdp_.data(), nz + ); + + addSlice(derived_state_.drzdp_, srz); + + if (always_check_finite_) + checkFinite(srz, ModelQuantity::srz, nplist()); + } else { + for (int ip = 0; ip < nplist(); ip++) { + fsrz( + &srz[ip * nz], ie, t, computeX_pos(x), + state_.unscaledParameters.data(), state_.fixedParameters.data(), + state_.h.data(), sx.data(ip), plist(ip) + ); + } } } -void Model::getEventSigma(gsl::span sigmaz, const int ie, - const int nroots, const realtype t, - const ExpData *edata) { +void Model::getEventSigma( + gsl::span sigmaz, int const ie, int const nroots, + const realtype t, ExpData const* edata +) { fsigmaz(ie, nroots, t, edata); writeSliceEvent(derived_state_.sigmaz_, sigmaz, ie); } -void Model::getEventSigmaSensitivity(gsl::span ssigmaz, const int ie, - const int nroots, const realtype t, - const ExpData *edata) { +void Model::getEventSigmaSensitivity( + gsl::span ssigmaz, int const ie, int const nroots, + const realtype t, ExpData const* edata +) { fdsigmazdp(ie, nroots, t, edata); writeSensitivitySliceEvent(derived_state_.dsigmazdp_, ssigmaz, ie); } -void Model::addEventObjective(realtype &Jz, const int ie, const int nroots, - const realtype t, const AmiVector &x, - const ExpData &edata) { +void Model::addEventObjective( + realtype& Jz, int const ie, int const nroots, const realtype t, + AmiVector const& x, ExpData const& edata +) { fz(ie, t, x); fsigmaz(ie, nroots, t, &edata); @@ -969,18 +1336,18 @@ void Model::addEventObjective(realtype &Jz, const int ie, const int nroots, if (edata.isSetObservedEvents(nroots, iztrue)) { std::fill(nllh.begin(), nllh.end(), 0.0); fJz(nllh.data(), iztrue, state_.unscaledParameters.data(), - state_.fixedParameters.data(), - derived_state_.z_.data(), derived_state_.sigmaz_.data(), + state_.fixedParameters.data(), derived_state_.z_.data(), + derived_state_.sigmaz_.data(), edata.getObservedEventsPtr(nroots)); Jz -= nllh.at(0); } } } -void Model::addEventObjectiveRegularization(realtype &Jrz, const int ie, - const int nroots, const realtype t, - const AmiVector &x, - const ExpData &edata) { +void Model::addEventObjectiveRegularization( + realtype& Jrz, int const ie, int const nroots, const realtype t, + AmiVector const& x, ExpData const& edata +) { frz(ie, t, x); fsigmaz(ie, nroots, t, &edata); @@ -988,20 +1355,21 @@ void Model::addEventObjectiveRegularization(realtype &Jrz, const int ie, for (int iztrue = 0; iztrue < nztrue; iztrue++) { if (edata.isSetObservedEvents(nroots, iztrue)) { std::fill(nllh.begin(), nllh.end(), 0.0); - fJrz(nllh.data(), iztrue, state_.unscaledParameters.data(), - state_.fixedParameters.data(), - derived_state_.rz_.data(), derived_state_.sigmaz_.data()); + fJrz( + nllh.data(), iztrue, state_.unscaledParameters.data(), + state_.fixedParameters.data(), derived_state_.rz_.data(), + derived_state_.sigmaz_.data() + ); Jrz -= nllh.at(0); } } } -void Model::addEventObjectiveSensitivity(std::vector &sllh, - std::vector &s2llh, - const int ie, const int nroots, - const realtype t, const AmiVector &x, - const AmiVectorArray &sx, - const ExpData &edata) { +void Model::addEventObjectiveSensitivity( + std::vector& sllh, std::vector& s2llh, int const ie, + int const nroots, const realtype t, AmiVector const& x, + AmiVectorArray const& sx, ExpData const& edata +) { if (!nz) return; @@ -1020,31 +1388,31 @@ void Model::addEventObjectiveSensitivity(std::vector &sllh, sx.flatten_to_vector(derived_state_.sx_); // C := alpha*op(A)*op(B) + beta*C, - amici_dgemm(BLASLayout::colMajor, BLASTranspose::noTrans, - BLASTranspose::noTrans, nJ, nplist(), nx_solver, 1.0, - derived_state_.dJzdx_.data(), nJ, - derived_state_.sx_.data(), nx_solver, 1.0, - derived_state_.dJzdp_.data(), - nJ); + setNaNtoZero(derived_state_.dJzdx_); + setNaNtoZero(derived_state_.sx_); + amici_dgemm( + BLASLayout::colMajor, BLASTranspose::noTrans, BLASTranspose::noTrans, + nJ, nplist(), nx_solver, 1.0, derived_state_.dJzdx_.data(), nJ, + derived_state_.sx_.data(), nx_solver, 1.0, derived_state_.dJzdp_.data(), + nJ + ); // sJy += multResult + dJydp writeLLHSensitivitySlice(derived_state_.dJzdp_, sllh, s2llh); } -void Model::getAdjointStateEventUpdate(gsl::span dJzdx, const int ie, - const int nroots, const realtype t, - const AmiVector &x, - const ExpData &edata) { +void Model::getAdjointStateEventUpdate( + gsl::span dJzdx, int const ie, int const nroots, const realtype t, + AmiVector const& x, ExpData const& edata +) { fdJzdx(ie, nroots, t, x, edata); writeSlice(derived_state_.dJzdx_, dJzdx); } -void Model::addPartialEventObjectiveSensitivity(std::vector &sllh, - std::vector &s2llh, - const int ie, const int nroots, - const realtype t, - const AmiVector &x, - const ExpData &edata) { +void Model::addPartialEventObjectiveSensitivity( + std::vector& sllh, std::vector& s2llh, int const ie, + int const nroots, const realtype t, AmiVector const& x, ExpData const& edata +) { if (!nz) return; @@ -1053,45 +1421,51 @@ void Model::addPartialEventObjectiveSensitivity(std::vector &sllh, writeLLHSensitivitySlice(derived_state_.dJzdp_, sllh, s2llh); } -void Model::getEventTimeSensitivity(std::vector &stau, - const realtype t, const int ie, - const AmiVector &x, - const AmiVectorArray &sx) { +void Model::getEventTimeSensitivity( + std::vector& stau, const realtype t, int const ie, + AmiVector const& x, AmiVectorArray const& sx +) { std::fill(stau.begin(), stau.end(), 0.0); for (int ip = 0; ip < nplist(); ip++) { - fstau(&stau.at(ip), t, x.data(), state_.unscaledParameters.data(), - state_.fixedParameters.data(), state_.h.data(), sx.data(ip), - plist(ip), ie); + fstau( + &stau.at(ip), t, computeX_pos(x), state_.unscaledParameters.data(), + state_.fixedParameters.data(), state_.h.data(), + state_.total_cl.data(), sx.data(ip), plist(ip), ie + ); } } -void Model::addStateEventUpdate(AmiVector &x, const int ie, const realtype t, - const AmiVector &xdot, - const AmiVector &xdot_old) { +void Model::addStateEventUpdate( + AmiVector& x, int const ie, const realtype t, AmiVector const& xdot, + AmiVector const& xdot_old +) { derived_state_.deltax_.assign(nx_solver, 0.0); + std::copy_n(computeX_pos(x), nx_solver, x.data()); + // compute update - fdeltax(derived_state_.deltax_.data(), t, x.data(), state_.unscaledParameters.data(), - state_.fixedParameters.data(), state_.h.data(), ie, xdot.data(), - xdot_old.data()); + fdeltax( + derived_state_.deltax_.data(), t, x.data(), + state_.unscaledParameters.data(), state_.fixedParameters.data(), + state_.h.data(), ie, xdot.data(), xdot_old.data() + ); if (always_check_finite_) { - app->checkFinite(derived_state_.deltax_, "deltax"); + checkFinite(derived_state_.deltax_, ModelQuantity::deltax); } // update amici_daxpy(nx_solver, 1.0, derived_state_.deltax_.data(), 1, x.data(), 1); } -void Model::addStateSensitivityEventUpdate(AmiVectorArray &sx, const int ie, - const realtype t, - const AmiVector &x_old, - const AmiVector &xdot, - const AmiVector &xdot_old, - const std::vector &stau) { +void Model::addStateSensitivityEventUpdate( + AmiVectorArray& sx, int const ie, const realtype t, AmiVector const& x_old, + AmiVector const& xdot, AmiVector const& xdot_old, + std::vector const& stau +) { fw(t, x_old.data()); for (int ip = 0; ip < nplist(); ip++) { @@ -1099,87 +1473,379 @@ void Model::addStateSensitivityEventUpdate(AmiVectorArray &sx, const int ie, derived_state_.deltasx_.assign(nx_solver, 0.0); // compute update - fdeltasx(derived_state_.deltasx_.data(), t, x_old.data(), - state_.unscaledParameters.data(), state_.fixedParameters.data(), - state_.h.data(), derived_state_.w_.data(), plist(ip), ie, - xdot.data(), xdot_old.data(), sx.data(ip), &stau.at(ip)); + fdeltasx( + derived_state_.deltasx_.data(), t, x_old.data(), + state_.unscaledParameters.data(), state_.fixedParameters.data(), + state_.h.data(), derived_state_.w_.data(), plist(ip), ie, + xdot.data(), xdot_old.data(), sx.data(ip), &stau.at(ip), + state_.total_cl.data() + ); if (always_check_finite_) { - app->checkFinite(derived_state_.deltasx_, "deltasx"); + checkFinite( + derived_state_.deltasx_, ModelQuantity::deltasx, nplist() + ); } - amici_daxpy(nx_solver, 1.0, derived_state_.deltasx_.data(), 1, - sx.data(ip), 1); + amici_daxpy( + nx_solver, 1.0, derived_state_.deltasx_.data(), 1, sx.data(ip), 1 + ); } } -void Model::addAdjointStateEventUpdate(AmiVector &xB, const int ie, - const realtype t, const AmiVector &x, - const AmiVector &xdot, - const AmiVector &xdot_old) { +void Model::addAdjointStateEventUpdate( + AmiVector& xB, int const ie, const realtype t, AmiVector const& x, + AmiVector const& xdot, AmiVector const& xdot_old +) { derived_state_.deltaxB_.assign(nx_solver, 0.0); // compute update - fdeltaxB(derived_state_.deltaxB_.data(), t, x.data(), - state_.unscaledParameters.data(), - state_.fixedParameters.data(), state_.h.data(), ie, xdot.data(), - xdot_old.data(), xB.data()); + fdeltaxB( + derived_state_.deltaxB_.data(), t, computeX_pos(x), + state_.unscaledParameters.data(), state_.fixedParameters.data(), + state_.h.data(), ie, xdot.data(), xdot_old.data(), xB.data() + ); if (always_check_finite_) { - app->checkFinite(derived_state_.deltaxB_, "deltaxB"); + checkFinite(derived_state_.deltaxB_, ModelQuantity::deltaxB); } // apply update for (int ix = 0; ix < nxtrue_solver; ++ix) for (int iJ = 0; iJ < nJ; ++iJ) - xB.at(ix + iJ * nxtrue_solver) += - derived_state_.deltaxB_.at(ix + iJ * nxtrue_solver); + xB.at(ix + iJ * nxtrue_solver) + += derived_state_.deltaxB_.at(ix + iJ * nxtrue_solver); } void Model::addAdjointQuadratureEventUpdate( - AmiVector xQB, const int ie, const realtype t, const AmiVector &x, - const AmiVector &xB, const AmiVector &xdot, const AmiVector &xdot_old) { + AmiVector xQB, int const ie, const realtype t, AmiVector const& x, + AmiVector const& xB, AmiVector const& xdot, AmiVector const& xdot_old +) { for (int ip = 0; ip < nplist(); ip++) { derived_state_.deltaqB_.assign(nJ, 0.0); - fdeltaqB(derived_state_.deltaqB_.data(), t, x.data(), state_.unscaledParameters.data(), - state_.fixedParameters.data(), state_.h.data(), plist(ip), ie, - xdot.data(), xdot_old.data(), xB.data()); + fdeltaqB( + derived_state_.deltaqB_.data(), t, computeX_pos(x), + state_.unscaledParameters.data(), state_.fixedParameters.data(), + state_.h.data(), plist(ip), ie, xdot.data(), xdot_old.data(), + xB.data() + ); for (int iJ = 0; iJ < nJ; ++iJ) xQB.at(iJ) += derived_state_.deltaqB_.at(iJ); } if (always_check_finite_) { - app->checkFinite(derived_state_.deltaqB_, "deltaqB"); + checkFinite(derived_state_.deltaqB_, ModelQuantity::deltaqB, nplist()); } } -void Model::updateHeaviside(const std::vector &rootsfound) { +void Model::updateHeaviside(std::vector const& rootsfound) { for (int ie = 0; ie < ne; ie++) { state_.h.at(ie) += rootsfound.at(ie); } } -void Model::updateHeavisideB(const int *rootsfound) { +void Model::updateHeavisideB(int const* rootsfound) { for (int ie = 0; ie < ne; ie++) { state_.h.at(ie) -= rootsfound[ie]; } } +int Model::checkFinite( + gsl::span array, ModelQuantity model_quantity +) const { + auto it = std::find_if(array.begin(), array.end(), [](realtype x) { + return !std::isfinite(x); + }); + if (it == array.end()) { + return AMICI_SUCCESS; + } + + // there is some issue - produce a meaningful message + auto flat_index = it - array.begin(); + + std::string msg_id; + std::string non_finite_type; + if (std::isnan(array[flat_index])) { + msg_id = "AMICI:NaN"; + non_finite_type = "NaN"; + } else if (std::isinf(array[flat_index])) { + msg_id = "AMICI:Inf"; + non_finite_type = "Inf"; + } + std::string element_id = std::to_string(flat_index); + + switch (model_quantity) { + case ModelQuantity::xdot: + case ModelQuantity::xBdot: + case ModelQuantity::x0: + case ModelQuantity::x: + case ModelQuantity::x_rdata: + case ModelQuantity::x0_rdata: + case ModelQuantity::Jv: + case ModelQuantity::JvB: + case ModelQuantity::JDiag: + case ModelQuantity::deltax: + case ModelQuantity::deltaxB: + if (hasStateIds()) { + element_id = getStateIdsSolver()[flat_index]; + } + break; + case ModelQuantity::y: + if (hasObservableIds()) { + element_id = getObservableIds()[flat_index]; + } + break; + case ModelQuantity::w: + if (hasExpressionIds()) { + element_id = getExpressionIds()[flat_index]; + } + break; + case ModelQuantity::k: + if (hasFixedParameterIds()) { + element_id = getFixedParameterIds()[flat_index]; + } + break; + case ModelQuantity::p: + if (hasParameterIds()) { + element_id = getParameterIds()[flat_index]; + } + break; + default: + break; + } -int Model::checkFinite(gsl::span array, const char *fun) const { - auto result = app->checkFinite(array, fun); + std::string model_quantity_str; + try { + model_quantity_str = model_quantity_to_str.at(model_quantity); + } catch (std::out_of_range const&) { + // Missing model quantity string - terminate if this is a debug build, + // but show the quantity number if non-debug. + gsl_ExpectsDebug(false); + model_quantity_str = std::to_string(static_cast(model_quantity)); + } + if (logger) + logger->log( + LogSeverity::warning, msg_id, + "AMICI encountered a %s value for %s[%i] (%s)", + non_finite_type.c_str(), model_quantity_str.c_str(), + gsl::narrow(flat_index), element_id.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); + if (!always_check_finite_ && model_quantity != ModelQuantity::w) { + // don't check twice if always_check_finite_ is true + checkFinite(derived_state_.w_, ModelQuantity::w); + } + } + return AMICI_RECOVERABLE_ERROR; +} + +int Model::checkFinite( + gsl::span array, ModelQuantity model_quantity, + size_t num_cols +) const { + auto it = std::find_if(array.begin(), array.end(), [](realtype x) { + return !std::isfinite(x); + }); + if (it == array.end()) { + return AMICI_SUCCESS; + } + + // there is some issue - produce a meaningful message + auto flat_index = it - array.begin(); + sunindextype row, col; + std::tie(row, col) = unravel_index(flat_index, num_cols); + std::string msg_id; + std::string non_finite_type; + if (std::isnan(array[flat_index])) { + msg_id = "AMICI:NaN"; + non_finite_type = "NaN"; + } else if (std::isinf(array[flat_index])) { + msg_id = "AMICI:Inf"; + non_finite_type = "Inf"; + } + std::string row_id = std::to_string(row); + std::string col_id = std::to_string(col); + + switch (model_quantity) { + case ModelQuantity::sy: + case ModelQuantity::ssigmay: + case ModelQuantity::dydp: + case ModelQuantity::dsigmaydp: + if (hasObservableIds()) + row_id += " " + getObservableIds()[row]; + if (hasParameterIds()) + col_id += " " + getParameterIds()[col]; + break; + case ModelQuantity::dydx: + if (hasObservableIds()) + row_id += " " + getObservableIds()[row]; + if (hasStateIds()) + col_id += " " + getStateIdsSolver()[col]; + break; + case ModelQuantity::deltasx: + if (hasStateIds()) + row_id += " " + getStateIdsSolver()[row]; + if (hasParameterIds()) + col_id += " " + getParameterIds()[plist(gsl::narrow(col))]; + break; + case ModelQuantity::dJydy: + case ModelQuantity::dJydy_matlab: + case ModelQuantity::dJydsigma: + if (hasObservableIds()) + col_id += " " + getObservableIds()[col]; + break; + case ModelQuantity::dJydx: + case ModelQuantity::dJzdx: + case ModelQuantity::dJrzdx: + case ModelQuantity::dzdx: + case ModelQuantity::drzdx: + if (hasStateIds()) + col_id += " " + getStateIdsSolver()[col]; + break; + case ModelQuantity::deltaqB: + case ModelQuantity::sz: + case ModelQuantity::dzdp: + case ModelQuantity::drzdp: + case ModelQuantity::dsigmazdp: + if (hasParameterIds()) + col_id += " " + getParameterIds()[plist(gsl::narrow(col))]; + break; + case ModelQuantity::dsigmaydy: + if (hasObservableIds()) { + auto obs_ids = getObservableIds(); + row_id += " " + obs_ids[row]; + col_id += " " + obs_ids[col]; + } + break; + default: + break; + } - if (result != AMICI_SUCCESS) { - app->checkFinite(state_.fixedParameters, "k"); - app->checkFinite(state_.unscaledParameters, "p"); - app->checkFinite(derived_state_.w_, "w"); - app->checkFinite(simulation_parameters_.ts_, "t"); + std::string model_quantity_str; + try { + model_quantity_str = model_quantity_to_str.at(model_quantity); + } catch (std::out_of_range const&) { + // Missing model quantity string - terminate if this is a debug build, + // but show the quantity number if non-debug. + gsl_ExpectsDebug(false); + model_quantity_str = std::to_string(static_cast(model_quantity)); + } + + if (logger) + logger->log( + LogSeverity::warning, msg_id, + "AMICI encountered a %s value for %s[%i] (%s, %s)", + non_finite_type.c_str(), model_quantity_str.c_str(), + gsl::narrow(flat_index), row_id.c_str(), col_id.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); + + return AMICI_RECOVERABLE_ERROR; +} + +int Model::checkFinite(SUNMatrix m, ModelQuantity model_quantity, realtype t) + const { + // check flat array, to see if there are any issues + // (faster, in particular for sparse arrays) + auto m_flat = gsl::make_span(m); + auto it = std::find_if(m_flat.begin(), m_flat.end(), [](realtype x) { + return !std::isfinite(x); + }); + if (it == m_flat.end()) { + return AMICI_SUCCESS; + } + + // there is some issue - produce a meaningful message + auto flat_index = it - m_flat.begin(); + sunindextype row, col; + std::tie(row, col) = unravel_index(flat_index, m); + std::string msg_id; + std::string non_finite_type; + if (std::isnan(m_flat[flat_index])) { + msg_id = "AMICI:NaN"; + non_finite_type = "NaN"; + } else if (std::isinf(m_flat[flat_index])) { + msg_id = "AMICI:Inf"; + non_finite_type = "Inf"; + } else { + throw std::runtime_error( + "Value is not finite, but neither infinite nor NaN." + ); + } + std::string row_id = std::to_string(row); + std::string col_id = std::to_string(col); + + switch (model_quantity) { + case ModelQuantity::J: + case ModelQuantity::JB: + if (hasStateIds()) { + auto state_ids = getStateIdsSolver(); + row_id += " " + state_ids[row]; + col_id += " " + state_ids[col]; + } + break; + case ModelQuantity::dwdx: + if (hasExpressionIds()) + row_id += " " + getExpressionIds()[row]; + if (hasStateIds()) + col_id += " " + getStateIdsSolver()[col]; + break; + case ModelQuantity::dwdw: + if (hasExpressionIds()) { + auto expr_ids = getExpressionIds(); + row_id += " " + expr_ids[row]; + col_id += " " + expr_ids[col]; + } + break; + case ModelQuantity::dwdp: + if (hasExpressionIds()) + row_id += " " + getExpressionIds()[row]; + if (hasParameterIds()) + col_id += " " + getParameterIds()[plist(gsl::narrow(col))]; + break; + default: + break; + } + + std::string model_quantity_str; + try { + model_quantity_str = model_quantity_to_str.at(model_quantity); + } catch (std::out_of_range const&) { + // Missing model quantity string - terminate if this is a debug build, + // but show the quantity number if non-debug. + gsl_ExpectsDebug(false); + model_quantity_str = std::to_string(static_cast(model_quantity)); } - return result; + if (logger) + logger->log( + LogSeverity::warning, msg_id, + "AMICI encountered a %s value for %s[%i] (%s, %s) at t=%g", + non_finite_type.c_str(), model_quantity_str.c_str(), + gsl::narrow(flat_index), row_id.c_str(), col_id.c_str(), 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); + + return AMICI_RECOVERABLE_ERROR; } void Model::setAlwaysCheckFinite(bool alwaysCheck) { @@ -1188,94 +1854,128 @@ void Model::setAlwaysCheckFinite(bool alwaysCheck) { bool Model::getAlwaysCheckFinite() const { return always_check_finite_; } -void Model::fx0(AmiVector &x) { - std::fill(derived_state_.x_rdata_.begin(), derived_state_.x_rdata_.end(), 0.0); +void Model::fx0(AmiVector& x) { + std::fill( + derived_state_.x_rdata_.begin(), derived_state_.x_rdata_.end(), 0.0 + ); /* this function also computes initial total abundances */ fx0(derived_state_.x_rdata_.data(), simulation_parameters_.tstart_, - state_.unscaledParameters.data(), - state_.fixedParameters.data()); + state_.unscaledParameters.data(), state_.fixedParameters.data()); fx_solver(x.data(), derived_state_.x_rdata_.data()); - ftotal_cl(state_.total_cl.data(), derived_state_.x_rdata_.data()); + ftotal_cl( + state_.total_cl.data(), derived_state_.x_rdata_.data(), + state_.unscaledParameters.data(), state_.fixedParameters.data() + ); - if (always_check_finite_) { - checkFinite(derived_state_.x_rdata_, "x0 x_rdata"); - checkFinite(x.getVector(), "x0 x"); - } + checkFinite(derived_state_.x_rdata_, ModelQuantity::x0_rdata); } -void Model::fx0_fixedParameters(AmiVector &x) { +void Model::fx0_fixedParameters(AmiVector& x) { if (!getReinitializeFixedParameterInitialStates()) return; + /* we transform to the unreduced states x_rdata and then apply x0_fixedparameters to (i) enable updates to states that were removed from conservation laws and (ii) be able to correctly compute total abundances after updating the state variables */ - fx_rdata(derived_state_.x_rdata_.data(), x.data(), state_.total_cl.data()); - fx0_fixedParameters(derived_state_.x_rdata_.data(), - simulation_parameters_.tstart_, - state_.unscaledParameters.data(), - state_.fixedParameters.data(), - simulation_parameters_.reinitialization_state_idxs_sim - ); + fx_rdata( + derived_state_.x_rdata_.data(), computeX_pos(x), state_.total_cl.data(), + state_.unscaledParameters.data(), state_.fixedParameters.data() + ); + fx0_fixedParameters( + derived_state_.x_rdata_.data(), simulation_parameters_.tstart_, + state_.unscaledParameters.data(), state_.fixedParameters.data(), + simulation_parameters_.reinitialization_state_idxs_sim + ); fx_solver(x.data(), derived_state_.x_rdata_.data()); /* update total abundances */ - ftotal_cl(state_.total_cl.data(), derived_state_.x_rdata_.data()); + ftotal_cl( + state_.total_cl.data(), derived_state_.x_rdata_.data(), + state_.unscaledParameters.data(), state_.fixedParameters.data() + ); } -void Model::fsx0(AmiVectorArray &sx, const AmiVector &x) { +void Model::fsx0(AmiVectorArray& sx, AmiVector const& x) { /* this function also computes initial total abundance sensitivities */ - realtype *stcl = nullptr; + realtype* stcl = nullptr; for (int ip = 0; ip < nplist(); ip++) { if (ncl() > 0) stcl = &state_.stotal_cl.at(plist(ip) * ncl()); - std::fill(derived_state_.sx_rdata_.begin(), - derived_state_.sx_rdata_.end(), 0.0); - fsx0(derived_state_.sx_rdata_.data(), simulation_parameters_.tstart_, - x.data(), state_.unscaledParameters.data(), - state_.fixedParameters.data(), plist(ip)); + std::fill( + derived_state_.sx_rdata_.begin(), derived_state_.sx_rdata_.end(), + 0.0 + ); + fsx0( + derived_state_.sx_rdata_.data(), simulation_parameters_.tstart_, + computeX_pos(x), state_.unscaledParameters.data(), + state_.fixedParameters.data(), plist(ip) + ); fsx_solver(sx.data(ip), derived_state_.sx_rdata_.data()); - fstotal_cl(stcl, derived_state_.sx_rdata_.data(), plist(ip)); + fstotal_cl( + stcl, derived_state_.sx_rdata_.data(), plist(ip), + derived_state_.x_rdata_.data(), state_.unscaledParameters.data(), + state_.fixedParameters.data(), state_.total_cl.data() + ); } } -void Model::fsx0_fixedParameters(AmiVectorArray &sx, const AmiVector &x) { +void Model::fsx0_fixedParameters(AmiVectorArray& sx, AmiVector const& x) { if (!getReinitializeFixedParameterInitialStates()) return; - realtype *stcl = nullptr; + realtype* stcl = nullptr; for (int ip = 0; ip < nplist(); ip++) { if (ncl() > 0) stcl = &state_.stotal_cl.at(plist(ip) * ncl()); - fsx_rdata(derived_state_.sx_rdata_.data(), sx.data(ip), stcl, plist(ip)); - fsx0_fixedParameters(derived_state_.sx_rdata_.data(), - simulation_parameters_.tstart_, x.data(), - state_.unscaledParameters.data(), - state_.fixedParameters.data(), - plist(ip), - simulation_parameters_.reinitialization_state_idxs_sim); + fsx_rdata( + derived_state_.sx_rdata_.data(), sx.data(ip), stcl, + state_.unscaledParameters.data(), state_.fixedParameters.data(), + x.data(), state_.total_cl.data(), plist(ip) + ); + fsx0_fixedParameters( + derived_state_.sx_rdata_.data(), simulation_parameters_.tstart_, + computeX_pos(x), state_.unscaledParameters.data(), + state_.fixedParameters.data(), plist(ip), + simulation_parameters_.reinitialization_state_idxs_sim + ); fsx_solver(sx.data(ip), derived_state_.sx_rdata_.data()); - fstotal_cl(stcl, derived_state_.sx_rdata_.data(), plist(ip)); + fstotal_cl( + stcl, derived_state_.sx_rdata_.data(), plist(ip), + derived_state_.x_rdata_.data(), state_.unscaledParameters.data(), + state_.fixedParameters.data(), state_.total_cl.data() + ); } } void Model::fsdx0() {} -void Model::fx_rdata(AmiVector &x_rdata, const AmiVector &x) { - fx_rdata(x_rdata.data(), x.data(), state_.total_cl.data()); +void Model::fx_rdata(AmiVector& x_rdata, AmiVector const& x) { + fx_rdata( + x_rdata.data(), computeX_pos(x), state_.total_cl.data(), + state_.unscaledParameters.data(), state_.fixedParameters.data() + ); if (always_check_finite_) - checkFinite(x_rdata.getVector(), "x_rdata"); + checkFinite(x_rdata.getVector(), ModelQuantity::x_rdata); } -void Model::fsx_rdata(AmiVectorArray &sx_rdata, const AmiVectorArray &sx) { - realtype *stcl = nullptr; +void Model::fsx_rdata( + AmiVectorArray& sx_rdata, AmiVectorArray const& sx, + AmiVector const& x_solver +) { + realtype* stcl = nullptr; for (int ip = 0; ip < nplist(); ip++) { if (ncl() > 0) stcl = &state_.stotal_cl.at(plist(ip) * ncl()); - fsx_rdata(sx_rdata.data(ip), sx.data(ip), stcl, ip); + fsx_rdata( + sx_rdata.data(ip), sx.data(ip), stcl, + state_.unscaledParameters.data(), state_.fixedParameters.data(), + x_solver.data(), state_.total_cl.data(), plist(ip) + ); } } -void Model::writeSliceEvent(gsl::span slice, - gsl::span buffer, const int ie) { +void Model::writeSliceEvent( + gsl::span slice, gsl::span buffer, int const ie +) { checkBufferSize(buffer, slice.size()); checkBufferSize(buffer, z2event_.size()); for (unsigned izt = 0; izt < z2event_.size(); ++izt) @@ -1283,9 +1983,9 @@ void Model::writeSliceEvent(gsl::span slice, buffer[izt] = slice[izt]; } -void Model::writeSensitivitySliceEvent(gsl::span slice, - gsl::span buffer, - const int ie) { +void Model::writeSensitivitySliceEvent( + gsl::span slice, gsl::span buffer, int const ie +) { checkBufferSize(buffer, slice.size()); checkBufferSize(buffer, z2event_.size() * nplist()); for (int ip = 0; ip < nplist(); ++ip) @@ -1294,26 +1994,33 @@ void Model::writeSensitivitySliceEvent(gsl::span slice, buffer[ip * nztrue + izt] = slice[ip * nztrue + izt]; } -void Model::writeLLHSensitivitySlice(const std::vector &dLLhdp, - std::vector &sllh, - std::vector &s2llh) { +void Model::writeLLHSensitivitySlice( + std::vector const& dLLhdp, std::vector& sllh, + std::vector& s2llh +) { checkLLHBufferSize(sllh, s2llh); amici_daxpy(nplist(), -1.0, dLLhdp.data(), nJ, sllh.data(), 1); for (int iJ = 1; iJ < nJ; ++iJ) - amici_daxpy(nplist(), -1.0, &dLLhdp.at(iJ), nJ, &s2llh.at(iJ - 1), - nJ - 1); + amici_daxpy( + nplist(), -1.0, &dLLhdp.at(iJ), nJ, &s2llh.at(iJ - 1), nJ - 1 + ); } -void Model::checkLLHBufferSize(std::vector const &sllh, - std::vector const &s2llh) const { - if (sllh.size() != static_cast(nplist())) - throw AmiException("Incorrect sllh buffer size! Was %u, expected %i.", - sllh.size(), nplist()); +void Model::checkLLHBufferSize( + std::vector const& sllh, std::vector const& s2llh +) const { + if (sllh.size() != gsl::narrow(nplist())) + throw AmiException( + "Incorrect sllh buffer size! Was %u, expected %i.", sllh.size(), + nplist() + ); - if (s2llh.size() != static_cast((nJ - 1) * nplist())) - throw AmiException("Incorrect s2llh buffer size! Was %u, expected %i.", - s2llh.size(), (nJ - 1) * nplist()); + if (s2llh.size() != gsl::narrow((nJ - 1) * nplist())) + throw AmiException( + "Incorrect s2llh buffer size! Was %u, expected %i.", s2llh.size(), + (nJ - 1) * nplist() + ); } void Model::initializeVectors() { @@ -1322,74 +2029,92 @@ void Model::initializeVectors() { derived_state_.dxdotdp = AmiVectorArray(nx_solver, nplist()); } -void Model::fy(const realtype t, const AmiVector &x) { +void Model::fy(const realtype t, AmiVector const& x) { if (!ny) return; + auto x_pos = computeX_pos(x); + derived_state_.y_.assign(ny, 0.0); - fw(t, x.data()); - fy(derived_state_.y_.data(), t, x.data(), state_.unscaledParameters.data(), - state_.fixedParameters.data(), - state_.h.data(), derived_state_.w_.data()); + fw(t, x_pos); + 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_) { - app->checkFinite(gsl::make_span(derived_state_.y_.data(), ny), "y"); + checkFinite( + gsl::make_span(derived_state_.y_.data(), ny), ModelQuantity::y + ); } } -void Model::fdydp(const realtype t, const AmiVector &x) { +void Model::fdydp(const realtype t, AmiVector const& x) { if (!ny) return; + auto x_pos = computeX_pos(x); + derived_state_.dydp_.assign(ny * nplist(), 0.0); - fw(t, x.data()); - fdwdp(t, x.data()); + fw(t, x_pos); + fdwdp(t, x_pos); /* get dydp slice (ny) for current time and parameter */ for (int ip = 0; ip < nplist(); ip++) if (pythonGenerated) { - fdydp(&derived_state_.dydp_.at(ip * ny), t, x.data(), - state_.unscaledParameters.data(), - state_.fixedParameters.data(), state_.h.data(), plist(ip), - derived_state_.w_.data(), state_.stotal_cl.data()); + fdydp( + &derived_state_.dydp_.at(ip * ny), t, x_pos, + state_.unscaledParameters.data(), state_.fixedParameters.data(), + state_.h.data(), plist(ip), derived_state_.w_.data(), + state_.total_cl.data(), state_.stotal_cl.data(), + state_.spl_.data(), derived_state_.sspl_.data() + ); } else { - fdydp(&derived_state_.dydp_.at(ip * ny), t, x.data(), - state_.unscaledParameters.data(), - state_.fixedParameters.data(), state_.h.data(), plist(ip), - derived_state_.w_.data(), derived_state_.dwdp_.data()); + fdydp( + &derived_state_.dydp_.at(ip * ny), t, x_pos, + state_.unscaledParameters.data(), state_.fixedParameters.data(), + state_.h.data(), plist(ip), derived_state_.w_.data(), + derived_state_.dwdp_.data() + ); } if (always_check_finite_) { - app->checkFinite(derived_state_.dydp_, "dydp"); + checkFinite(derived_state_.dydp_, ModelQuantity::dydp, nplist()); } } -void Model::fdydx(const realtype t, const AmiVector &x) { +void Model::fdydx(const realtype t, AmiVector const& x) { if (!ny) return; + auto x_pos = computeX_pos(x); + derived_state_.dydx_.assign(ny * nx_solver, 0.0); - fw(t, x.data()); - fdwdx(t, x.data()); - fdydx(derived_state_.dydx_.data(), t, x.data(), state_.unscaledParameters.data(), - state_.fixedParameters.data(), state_.h.data(), - derived_state_.w_.data(), derived_state_.dwdx_.data()); + fw(t, x_pos); + fdwdx(t, x_pos); + fdydx( + derived_state_.dydx_.data(), t, x_pos, state_.unscaledParameters.data(), + state_.fixedParameters.data(), state_.h.data(), + derived_state_.w_.data(), derived_state_.dwdx_.data() + ); if (always_check_finite_) { - app->checkFinite(derived_state_.dydx_, "dydx"); + checkFinite(derived_state_.dydx_, ModelQuantity::dydx, ny); } } -void Model::fsigmay(const int it, const ExpData *edata) { +void Model::fsigmay(int const it, ExpData const* edata) { if (!ny) return; derived_state_.sigmay_.assign(ny, 0.0); - fsigmay(derived_state_.sigmay_.data(), getTimepoint(it), state_.unscaledParameters.data(), - state_.fixedParameters.data()); + fsigmay( + derived_state_.sigmay_.data(), getTimepoint(it), + state_.unscaledParameters.data(), state_.fixedParameters.data(), + derived_state_.y_.data() + ); if (edata) { auto sigmay_edata = edata->getObservedDataStdDevPtr(it); @@ -1403,15 +2128,17 @@ void Model::fsigmay(const int it, const ExpData *edata) { * that this is actually what we want */ for (int iJ = 1; iJ < nJ; iJ++) - derived_state_.sigmay_.at(iytrue + iJ*nytrue) = 0; + derived_state_.sigmay_.at(iytrue + iJ * nytrue) = 0; if (edata->isSetObservedData(it, iytrue)) - checkSigmaPositivity(derived_state_.sigmay_.at(iytrue), "sigmay"); + checkSigmaPositivity( + derived_state_.sigmay_.at(iytrue), "sigmay" + ); } } } -void Model::fdsigmaydp(const int it, const ExpData *edata) { +void Model::fdsigmaydp(int const it, ExpData const* edata) { if (!ny) return; @@ -1419,10 +2146,11 @@ void Model::fdsigmaydp(const int it, const ExpData *edata) { for (int ip = 0; ip < nplist(); ip++) // get dsigmaydp slice (ny) for current timepoint and parameter - fdsigmaydp(&derived_state_.dsigmaydp_.at(ip * ny), getTimepoint(it), - state_.unscaledParameters.data(), - state_.fixedParameters.data(), - plist(ip)); + fdsigmaydp( + &derived_state_.dsigmaydp_.at(ip * ny), getTimepoint(it), + state_.unscaledParameters.data(), state_.fixedParameters.data(), + derived_state_.y_.data(), plist(ip) + ); // sigmas in edata override model-sigma -> for those sigmas, set dsigmaydp // to zero @@ -1437,11 +2165,43 @@ void Model::fdsigmaydp(const int it, const ExpData *edata) { } if (always_check_finite_) { - app->checkFinite(derived_state_.dsigmaydp_, "dsigmaydp"); + checkFinite( + derived_state_.dsigmaydp_, ModelQuantity::dsigmaydp, nplist() + ); + } +} + +void Model::fdsigmaydy(int const it, ExpData const* edata) { + if (!ny) + return; + + derived_state_.dsigmaydy_.assign(ny * ny, 0.0); + + // get dsigmaydy slice (ny) for current timepoint + fdsigmaydy( + derived_state_.dsigmaydy_.data(), getTimepoint(it), + state_.unscaledParameters.data(), state_.fixedParameters.data(), + derived_state_.y_.data() + ); + + // sigmas in edata override model-sigma -> for those sigmas, set dsigmaydy + // to zero + if (edata) { + for (int isigmay = 0; isigmay < nytrue; ++isigmay) { + if (!edata->isSetObservedDataStdDev(it, isigmay)) + continue; + for (int iy = 0; iy < nytrue; ++iy) { + derived_state_.dsigmaydy_.at(isigmay * ny + iy) = 0.0; + } + } + } + + if (always_check_finite_) { + checkFinite(derived_state_.dsigmaydy_, ModelQuantity::dsigmaydy, ny); } } -void Model::fdJydy(const int it, const AmiVector &x, const ExpData &edata) { +void Model::fdJydy(int const it, AmiVector const& x, ExpData const& edata) { if (!ny) return; @@ -1449,6 +2209,12 @@ void Model::fdJydy(const int it, const AmiVector &x, const ExpData &edata) { fsigmay(it, &edata); if (pythonGenerated) { + fdJydsigma(it, x, edata); + fdsigmaydy(it, &edata); + SUNMatrixWrapper tmp_dense(nJ, ny); + + setNaNtoZero(derived_state_.dJydsigma_); + setNaNtoZero(derived_state_.dsigmaydy_); for (int iyt = 0; iyt < nytrue; iyt++) { if (!derived_state_.dJydy_.at(iyt).capacity()) continue; @@ -1460,37 +2226,70 @@ void Model::fdJydy(const int it, const AmiVector &x, const ExpData &edata) { continue; // get dJydy slice (ny) for current timepoint and observable - fdJydy(derived_state_.dJydy_.at(iyt).data(), iyt, state_.unscaledParameters.data(), - state_.fixedParameters.data(), derived_state_.y_.data(), - derived_state_.sigmay_.data(), - edata.getObservedDataPtr(it)); + fdJydy( + derived_state_.dJydy_.at(iyt).data(), iyt, + state_.unscaledParameters.data(), state_.fixedParameters.data(), + derived_state_.y_.data(), derived_state_.sigmay_.data(), + edata.getObservedDataPtr(it) + ); + + // dJydy += dJydsigma * dsigmaydy + // C(nJ,ny) A(nJ,ny) * B(ny,ny) + // sparse dense dense + tmp_dense.zero(); + amici_dgemm( + BLASLayout::colMajor, BLASTranspose::noTrans, + BLASTranspose::noTrans, nJ, ny, ny, 1.0, + &derived_state_.dJydsigma_.at(iyt * nJ * ny), nJ, + derived_state_.dsigmaydy_.data(), ny, 1.0, tmp_dense.data(), nJ + ); + + auto tmp_sparse = SUNMatrixWrapper(tmp_dense, 0.0, CSC_MAT); + auto ret = SUNMatScaleAdd( + 1.0, derived_state_.dJydy_.at(iyt).get(), tmp_sparse.get() + ); + if (ret != SUNMAT_SUCCESS) { + throw AmiException( + "SUNMatScaleAdd failed with status %d in %s", ret, __func__ + ); + } + derived_state_.dJydy_.at(iyt).refresh(); if (always_check_finite_) { - app->checkFinite( - gsl::make_span(derived_state_.dJydy_.at(iyt).get()), - "dJydy"); + checkFinite( + gsl::make_span(derived_state_.dJydy_.at(iyt).get()), + ModelQuantity::dJydy, ny + ); } } } else { - std::fill(derived_state_.dJydy_matlab_.begin(), - derived_state_.dJydy_matlab_.end(), 0.0); + std::fill( + derived_state_.dJydy_matlab_.begin(), + derived_state_.dJydy_matlab_.end(), 0.0 + ); for (int iyt = 0; iyt < nytrue; iyt++) { if (!edata.isSetObservedData(it, iyt)) continue; - fdJydy(&derived_state_.dJydy_matlab_.at(iyt * ny * nJ), iyt, - state_.unscaledParameters.data(), - state_.fixedParameters.data(), derived_state_.y_.data(), - derived_state_.sigmay_.data(), - edata.getObservedDataPtr(it)); - } - if (always_check_finite_) { - // get dJydy slice (ny) for current timepoint and observable - app->checkFinite(derived_state_.dJydy_matlab_, "dJydy"); + fdJydy( + &derived_state_.dJydy_matlab_.at(iyt * ny * nJ), iyt, + state_.unscaledParameters.data(), state_.fixedParameters.data(), + derived_state_.y_.data(), derived_state_.sigmay_.data(), + edata.getObservedDataPtr(it) + ); + if (always_check_finite_) { + // get dJydy slice (ny) for current timepoint and observable + checkFinite( + gsl::span( + &derived_state_.dJydy_matlab_[iyt * ny * nJ], ny * nJ + ), + ModelQuantity::dJydy, ny + ); + } } } } -void Model::fdJydsigma(const int it, const AmiVector &x, const ExpData &edata) { +void Model::fdJydsigma(int const it, AmiVector const& x, ExpData const& edata) { if (!ny) return; @@ -1500,25 +2299,30 @@ void Model::fdJydsigma(const int it, const AmiVector &x, const ExpData &edata) { fsigmay(it, &edata); for (int iyt = 0; iyt < nytrue; iyt++) { - if (edata.isSetObservedData(it, iyt)) + if (edata.isSetObservedData(it, iyt)) { // get dJydsigma slice (ny) for current timepoint and observable - fdJydsigma(&derived_state_.dJydsigma_.at(iyt * ny * nJ), iyt, - state_.unscaledParameters.data(), - state_.fixedParameters.data(), derived_state_.y_.data(), - derived_state_.sigmay_.data(), - edata.getObservedDataPtr(it)); - } - - if (always_check_finite_) { - app->checkFinite(derived_state_.dJydsigma_, "dJydsigma"); + fdJydsigma( + &derived_state_.dJydsigma_.at(iyt * ny * nJ), iyt, + state_.unscaledParameters.data(), state_.fixedParameters.data(), + derived_state_.y_.data(), derived_state_.sigmay_.data(), + edata.getObservedDataPtr(it) + ); + if (always_check_finite_) { + checkFinite( + gsl::span( + &derived_state_.dJydsigma_.at(iyt * ny * nJ), ny * nJ + ), + ModelQuantity::dJydsigma, ny + ); + } + } } } -void Model::fdJydp(const int it, const AmiVector &x, const ExpData &edata) { +void Model::fdJydp(int const it, AmiVector const& x, ExpData const& edata) { // dJydy nJ, nytrue x ny // dydp nplist * ny // dJydp nplist x nJ - // dJydsigma if (!ny) return; @@ -1530,6 +2334,8 @@ void Model::fdJydp(const int it, const AmiVector &x, const ExpData &edata) { fdJydsigma(it, x, edata); fdsigmaydp(it, &edata); + setNaNtoZero(derived_state_.dJydsigma_); + setNaNtoZero(derived_state_.dsigmaydp_); for (int iyt = 0; iyt < nytrue; ++iyt) { if (!edata.isSetObservedData(it, iyt)) continue; @@ -1538,26 +2344,35 @@ void Model::fdJydp(const int it, const AmiVector &x, const ExpData &edata) { // dJydp = 1.0 * dJydp + 1.0 * dJydy * dydp for (int iplist = 0; iplist < nplist(); ++iplist) { derived_state_.dJydy_.at(iyt).multiply( - gsl::span(&derived_state_.dJydp_.at(iplist * nJ), nJ), - gsl::span(&derived_state_.dydp_.at(iplist * ny), ny)); + gsl::span( + &derived_state_.dJydp_.at(iplist * nJ), nJ + ), + gsl::span( + &derived_state_.dydp_.at(iplist * ny), ny + ) + ); } } else { - amici_dgemm(BLASLayout::colMajor, BLASTranspose::noTrans, - BLASTranspose::noTrans, nJ, nplist(), ny, 1.0, - &derived_state_.dJydy_matlab_.at(iyt * nJ * ny), nJ, - derived_state_.dydp_.data(), ny, - 1.0, derived_state_.dJydp_.data(), nJ); + amici_dgemm( + BLASLayout::colMajor, BLASTranspose::noTrans, + BLASTranspose::noTrans, nJ, nplist(), ny, 1.0, + &derived_state_.dJydy_matlab_.at(iyt * nJ * ny), nJ, + derived_state_.dydp_.data(), ny, 1.0, + derived_state_.dJydp_.data(), nJ + ); } // dJydp = 1.0 * dJydp + 1.0 * dJydsigma * dsigmaydp - amici_dgemm(BLASLayout::colMajor, BLASTranspose::noTrans, - BLASTranspose::noTrans, nJ, nplist(), ny, 1.0, - &derived_state_.dJydsigma_.at(iyt * nJ * ny), nJ, - derived_state_.dsigmaydp_.data(), ny, 1.0, - derived_state_.dJydp_.data(), nJ); + amici_dgemm( + BLASLayout::colMajor, BLASTranspose::noTrans, + BLASTranspose::noTrans, nJ, nplist(), ny, 1.0, + &derived_state_.dJydsigma_.at(iyt * nJ * ny), nJ, + derived_state_.dsigmaydp_.data(), ny, 1.0, + derived_state_.dJydp_.data(), nJ + ); } } -void Model::fdJydx(const int it, const AmiVector &x, const ExpData &edata) { +void Model::fdJydx(int const it, AmiVector const& x, ExpData const& edata) { if (!ny) return; @@ -1581,116 +2396,135 @@ void Model::fdJydx(const int it, const AmiVector &x, const ExpData &edata) { for (int ix = 0; ix < nx_solver; ++ix) { derived_state_.dJydy_.at(iyt).multiply( gsl::span(&derived_state_.dJydx_.at(ix * nJ), nJ), - gsl::span(&derived_state_.dydx_.at(ix * ny), ny)); + gsl::span( + &derived_state_.dydx_.at(ix * ny), ny + ) + ); } } else { - amici_dgemm(BLASLayout::colMajor, BLASTranspose::noTrans, - BLASTranspose::noTrans, nJ, nx_solver, ny, 1.0, - &derived_state_.dJydy_matlab_.at(iyt * ny * nJ), nJ, - derived_state_.dydx_.data(), ny, - 1.0, derived_state_.dJydx_.data(), nJ); + amici_dgemm( + BLASLayout::colMajor, BLASTranspose::noTrans, + BLASTranspose::noTrans, nJ, nx_solver, ny, 1.0, + &derived_state_.dJydy_matlab_.at(iyt * ny * nJ), nJ, + derived_state_.dydx_.data(), ny, 1.0, + derived_state_.dJydx_.data(), nJ + ); } } if (always_check_finite_) { - app->checkFinite(derived_state_.dJydx_, "dJydx"); + checkFinite(derived_state_.dJydx_, ModelQuantity::dJydx, nx_solver); } } -void Model::fz(const int ie, const realtype t, const AmiVector &x) { +void Model::fz(int const ie, const realtype t, AmiVector const& x) { derived_state_.z_.assign(nz, 0.0); - fz(derived_state_.z_.data(), ie, t, x.data(), state_.unscaledParameters.data(), - state_.fixedParameters.data(), state_.h.data()); + fz(derived_state_.z_.data(), ie, t, computeX_pos(x), + state_.unscaledParameters.data(), state_.fixedParameters.data(), + state_.h.data()); } -void Model::fdzdp(const int ie, const realtype t, const AmiVector &x) { +void Model::fdzdp(int const ie, const realtype t, AmiVector const& x) { if (!nz) return; derived_state_.dzdp_.assign(nz * nplist(), 0.0); for (int ip = 0; ip < nplist(); ip++) { - fdzdp(derived_state_.dzdp_.data(), ie, t, x.data(), - state_.unscaledParameters.data(), - state_.fixedParameters.data(), state_.h.data(), plist(ip)); + fdzdp( + derived_state_.dzdp_.data(), ie, t, computeX_pos(x), + state_.unscaledParameters.data(), state_.fixedParameters.data(), + state_.h.data(), plist(ip) + ); } if (always_check_finite_) { - app->checkFinite(derived_state_.dzdp_, "dzdp"); + checkFinite(derived_state_.dzdp_, ModelQuantity::dzdp, nplist()); } } -void Model::fdzdx(const int ie, const realtype t, const AmiVector &x) { +void Model::fdzdx(int const ie, const realtype t, AmiVector const& x) { if (!nz) return; derived_state_.dzdx_.assign(nz * nx_solver, 0.0); - fdzdx(derived_state_.dzdx_.data(), ie, t, x.data(), state_.unscaledParameters.data(), - state_.fixedParameters.data(), state_.h.data()); + fdzdx( + derived_state_.dzdx_.data(), ie, t, computeX_pos(x), + state_.unscaledParameters.data(), state_.fixedParameters.data(), + state_.h.data() + ); if (always_check_finite_) { - app->checkFinite(derived_state_.dzdx_, "dzdx"); + checkFinite(derived_state_.dzdx_, ModelQuantity::dzdx, nx_solver); } } -void Model::frz(const int ie, const realtype t, const AmiVector &x) { +void Model::frz(int const ie, const realtype t, AmiVector const& x) { derived_state_.rz_.assign(nz, 0.0); - frz(derived_state_.rz_.data(), ie, t, x.data(), - state_.unscaledParameters.data(), - state_.fixedParameters.data(), state_.h.data()); + frz(derived_state_.rz_.data(), ie, t, computeX_pos(x), + state_.unscaledParameters.data(), state_.fixedParameters.data(), + state_.h.data()); } -void Model::fdrzdp(const int ie, const realtype t, const AmiVector &x) { +void Model::fdrzdp(int const ie, const realtype t, AmiVector const& x) { if (!nz) return; derived_state_.drzdp_.assign(nz * nplist(), 0.0); for (int ip = 0; ip < nplist(); ip++) { - fdrzdp(derived_state_.drzdp_.data(), ie, t, x.data(), - state_.unscaledParameters.data(), - state_.fixedParameters.data(), state_.h.data(), plist(ip)); + fdrzdp( + derived_state_.drzdp_.data(), ie, t, computeX_pos(x), + state_.unscaledParameters.data(), state_.fixedParameters.data(), + state_.h.data(), plist(ip) + ); } if (always_check_finite_) { - app->checkFinite(derived_state_.drzdp_, "drzdp"); + checkFinite(derived_state_.drzdp_, ModelQuantity::drzdp, nplist()); } } -void Model::fdrzdx(const int ie, const realtype t, const AmiVector &x) { +void Model::fdrzdx(int const ie, const realtype t, AmiVector const& x) { if (!nz) return; derived_state_.drzdx_.assign(nz * nx_solver, 0.0); - fdrzdx(derived_state_.drzdx_.data(), ie, t, x.data(), state_.unscaledParameters.data(), - state_.fixedParameters.data(), state_.h.data()); + fdrzdx( + derived_state_.drzdx_.data(), ie, t, computeX_pos(x), + state_.unscaledParameters.data(), state_.fixedParameters.data(), + state_.h.data() + ); if (always_check_finite_) { - app->checkFinite(derived_state_.drzdx_, "drzdx"); + checkFinite(derived_state_.drzdx_, ModelQuantity::drzdx, nx_solver); } } -void Model::fsigmaz(const int ie, const int nroots, const realtype t, - const ExpData *edata) { +void Model::fsigmaz( + int const ie, int const nroots, const realtype t, ExpData const* edata +) { if (!nz) return; derived_state_.sigmaz_.assign(nz, 0.0); - fsigmaz(derived_state_.sigmaz_.data(), t, state_.unscaledParameters.data(), - state_.fixedParameters.data()); + fsigmaz( + derived_state_.sigmaz_.data(), t, state_.unscaledParameters.data(), + state_.fixedParameters.data() + ); if (edata) { for (int iztrue = 0; iztrue < nztrue; iztrue++) { if (z2event_.at(iztrue) - 1 == ie) { if (edata->isSetObservedEventsStdDev(nroots, iztrue)) { - auto sigmaz_edata = - edata->getObservedEventsStdDevPtr(nroots); + auto sigmaz_edata + = edata->getObservedEventsStdDevPtr(nroots); derived_state_.sigmaz_.at(iztrue) = sigmaz_edata[iztrue]; } @@ -1698,18 +2532,20 @@ void Model::fsigmaz(const int ie, const int nroots, const realtype t, * that this is actually what we want */ for (int iJ = 1; iJ < nJ; iJ++) - derived_state_.sigmaz_.at(iztrue + iJ*nztrue) = 0; + derived_state_.sigmaz_.at(iztrue + iJ * nztrue) = 0; if (edata->isSetObservedEvents(nroots, iztrue)) - checkSigmaPositivity(derived_state_.sigmaz_.at(iztrue), - "sigmaz"); + checkSigmaPositivity( + derived_state_.sigmaz_.at(iztrue), "sigmaz" + ); } } } } -void Model::fdsigmazdp(const int ie, const int nroots, const realtype t, - const ExpData *edata) { +void Model::fdsigmazdp( + int const ie, int const nroots, const realtype t, ExpData const* edata +) { if (!nz) return; @@ -1717,16 +2553,19 @@ void Model::fdsigmazdp(const int ie, const int nroots, const realtype t, for (int ip = 0; ip < nplist(); ip++) { // get dsigmazdp slice (nz) for current event and parameter - fdsigmazdp(&derived_state_.dsigmazdp_.at(ip * nz), t, state_.unscaledParameters.data(), - state_.fixedParameters.data(), plist(ip)); + fdsigmazdp( + &derived_state_.dsigmazdp_.at(ip * nz), t, + state_.unscaledParameters.data(), state_.fixedParameters.data(), + plist(ip) + ); } // sigmas in edata override model-sigma -> for those sigmas, set dsigmazdp // to zero if (edata) { for (int iz = 0; iz < nztrue; iz++) { - if (z2event_.at(iz) - 1 == ie && - !edata->isSetObservedEventsStdDev(nroots, iz)) { + if (z2event_.at(iz) - 1 == ie + && !edata->isSetObservedEventsStdDev(nroots, iz)) { for (int ip = 0; ip < nplist(); ip++) derived_state_.dsigmazdp_.at(iz + nz * ip) = 0; } @@ -1734,12 +2573,16 @@ void Model::fdsigmazdp(const int ie, const int nroots, const realtype t, } if (always_check_finite_) { - app->checkFinite(derived_state_.dsigmazdp_, "dsigmazdp"); + checkFinite( + derived_state_.dsigmazdp_, ModelQuantity::dsigmazdp, nplist() + ); } } -void Model::fdJzdz(const int ie, const int nroots, const realtype t, - const AmiVector &x, const ExpData &edata) { +void Model::fdJzdz( + int const ie, int const nroots, const realtype t, AmiVector const& x, + ExpData const& edata +) { if (!nz) return; @@ -1750,21 +2593,28 @@ void Model::fdJzdz(const int ie, const int nroots, const realtype t, for (int iztrue = 0; iztrue < nztrue; iztrue++) { if (edata.isSetObservedEvents(nroots, iztrue)) { - fdJzdz(&derived_state_.dJzdz_.at(iztrue * nz * nJ), iztrue, - state_.unscaledParameters.data(), - state_.fixedParameters.data(), - derived_state_.z_.data(), derived_state_.sigmaz_.data(), - edata.getObservedEventsPtr(nroots)); + fdJzdz( + &derived_state_.dJzdz_.at(iztrue * nz * nJ), iztrue, + state_.unscaledParameters.data(), state_.fixedParameters.data(), + derived_state_.z_.data(), derived_state_.sigmaz_.data(), + edata.getObservedEventsPtr(nroots) + ); + if (always_check_finite_) { + checkFinite( + gsl::span( + &derived_state_.dJzdz_.at(iztrue * nz * nJ), nz * nJ + ), + ModelQuantity::dJzdz, nz + ); + } } } - - if (always_check_finite_) { - app->checkFinite(derived_state_.dJzdz_, "dJzdz"); - } } -void Model::fdJzdsigma(const int ie, const int nroots, const realtype t, - const AmiVector &x, const ExpData &edata) { +void Model::fdJzdsigma( + int const ie, int const nroots, const realtype t, AmiVector const& x, + ExpData const& edata +) { if (!nz) return; @@ -1775,21 +2625,28 @@ void Model::fdJzdsigma(const int ie, const int nroots, const realtype t, for (int iztrue = 0; iztrue < nztrue; iztrue++) { if (edata.isSetObservedEvents(nroots, iztrue)) { - fdJzdsigma(&derived_state_.dJzdsigma_.at(iztrue * nz * nJ), iztrue, - state_.unscaledParameters.data(), - state_.fixedParameters.data(), derived_state_.z_.data(), - derived_state_.sigmaz_.data(), - edata.getObservedEventsPtr(nroots)); + fdJzdsigma( + &derived_state_.dJzdsigma_.at(iztrue * nz * nJ), iztrue, + state_.unscaledParameters.data(), state_.fixedParameters.data(), + derived_state_.z_.data(), derived_state_.sigmaz_.data(), + edata.getObservedEventsPtr(nroots) + ); + if (always_check_finite_) { + checkFinite( + gsl::span( + &derived_state_.dJzdsigma_.at(iztrue * nz * nJ), nz * nJ + ), + ModelQuantity::dJzdsigma, nz + ); + } } } - - if (always_check_finite_) { - app->checkFinite(derived_state_.dJzdsigma_, "dJzdsigma"); - } } -void Model::fdJzdp(const int ie, const int nroots, realtype t, - const AmiVector &x, const ExpData &edata) { +void Model::fdJzdp( + int const ie, int const nroots, realtype t, AmiVector const& x, + ExpData const& edata +) { if (!nz) return; // dJzdz nJ x nz x nztrue @@ -1799,61 +2656,82 @@ void Model::fdJzdp(const int ie, const int nroots, realtype t, derived_state_.dJzdp_.assign(nJ * nplist(), 0.0); - fdJzdz(ie, nroots, t, x, edata); fdzdp(ie, t, x); - - fdJzdsigma(ie, nroots, t, x, edata); fdsigmazdp(ie, nroots, t, &edata); - + fdJzdz(ie, nroots, t, x, edata); + fdJrzdz(ie, nroots, t, x, edata); + fdJzdsigma(ie, nroots, t, x, edata); + fdJrzdsigma(ie, nroots, t, x, edata); + + setNaNtoZero(derived_state_.dzdp_); + setNaNtoZero(derived_state_.dsigmazdp_); + setNaNtoZero(derived_state_.dJzdz_); + setNaNtoZero(derived_state_.dJrzdz_); + setNaNtoZero(derived_state_.dJzdsigma_); + setNaNtoZero(derived_state_.dJrzdsigma_); for (int izt = 0; izt < nztrue; ++izt) { if (!edata.isSetObservedEvents(nroots, izt)) continue; if (t < edata.getTimepoint(edata.nt() - 1)) { // with z - fdJzdz(ie, nroots, t, x, edata); - amici_dgemm(BLASLayout::colMajor, BLASTranspose::noTrans, - BLASTranspose::noTrans, nJ, nplist(), nz, 1.0, - &derived_state_.dJzdz_.at(izt * nz * nJ), nJ, - derived_state_.dzdp_.data(), nz, 1.0, - derived_state_.dJzdp_.data(), nJ); + amici_dgemm( + BLASLayout::colMajor, BLASTranspose::noTrans, + BLASTranspose::noTrans, nJ, nplist(), nz, 1.0, + &derived_state_.dJzdz_.at(izt * nz * nJ), nJ, + derived_state_.dzdp_.data(), nz, 1.0, + derived_state_.dJzdp_.data(), nJ + ); } else { // with rz - fdJrzdz(ie, nroots, t, x, edata); - fdJrzdsigma(ie, nroots, t, x, edata); - - amici_dgemm(BLASLayout::colMajor, BLASTranspose::noTrans, - BLASTranspose::noTrans, nJ, nplist(), nz, 1.0, - &derived_state_.dJrzdsigma_.at(izt * nz * nJ), nJ, - derived_state_.dsigmazdp_.data(), nz, - 1.0, derived_state_.dJzdp_.data(), nJ); - - amici_dgemm(BLASLayout::colMajor, BLASTranspose::noTrans, - BLASTranspose::noTrans, nJ, nplist(), nz, 1.0, - &derived_state_.dJrzdz_.at(izt * nz * nJ), nJ, - derived_state_.dzdp_.data(), nz, 1.0, - derived_state_.dJzdp_.data(), nJ); + amici_dgemm( + BLASLayout::colMajor, BLASTranspose::noTrans, + BLASTranspose::noTrans, nJ, nplist(), nz, 1.0, + &derived_state_.dJrzdsigma_.at(izt * nz * nJ), nJ, + derived_state_.dsigmazdp_.data(), nz, 1.0, + derived_state_.dJzdp_.data(), nJ + ); + + amici_dgemm( + BLASLayout::colMajor, BLASTranspose::noTrans, + BLASTranspose::noTrans, nJ, nplist(), nz, 1.0, + &derived_state_.dJrzdz_.at(izt * nz * nJ), nJ, + derived_state_.dzdp_.data(), nz, 1.0, + derived_state_.dJzdp_.data(), nJ + ); } - amici_dgemm(BLASLayout::colMajor, BLASTranspose::noTrans, - BLASTranspose::noTrans, nJ, nplist(), nz, 1.0, - &derived_state_.dJzdsigma_.at(izt * nz * nJ), nJ, - derived_state_.dsigmazdp_.data(), nz, 1.0, - derived_state_.dJzdp_.data(), nJ); + amici_dgemm( + BLASLayout::colMajor, BLASTranspose::noTrans, + BLASTranspose::noTrans, nJ, nplist(), nz, 1.0, + &derived_state_.dJzdsigma_.at(izt * nz * nJ), nJ, + derived_state_.dsigmazdp_.data(), nz, 1.0, + derived_state_.dJzdp_.data(), nJ + ); } } -void Model::fdJzdx(const int ie, const int nroots, const realtype t, - const AmiVector &x, const ExpData &edata) { +void Model::fdJzdx( + int const ie, int const nroots, const realtype t, AmiVector const& x, + ExpData const& edata +) { // dJzdz nJ x nz x nztrue // dzdx nz x nx_solver // dJzdx nJ x nx_solver x nmaxevent - if(!nz) + if (!nz) return; derived_state_.dJzdx_.assign(nJ * nx_solver, 0.0); fdJzdz(ie, nroots, t, x, edata); + fdJrzdz(ie, nroots, t, x, edata); + fdzdx(ie, t, x); + fdrzdx(ie, t, x); + + setNaNtoZero(derived_state_.dJzdz_); + setNaNtoZero(derived_state_.dJrzdz_); + setNaNtoZero(derived_state_.dzdx_); + setNaNtoZero(derived_state_.drzdx_); for (int izt = 0; izt < nztrue; ++izt) { if (!edata.isSetObservedEvents(nroots, izt)) @@ -1861,27 +2739,30 @@ void Model::fdJzdx(const int ie, const int nroots, const realtype t, if (t < edata.getTimepoint(edata.nt() - 1)) { // z - fdzdx(ie, t, x); - amici_dgemm(BLASLayout::colMajor, BLASTranspose::noTrans, - BLASTranspose::noTrans, nJ, nx_solver, nz, 1.0, - &derived_state_.dJzdz_.at(izt * nz * nJ), nJ, - derived_state_.dzdx_.data(), nz, 1.0, - derived_state_.dJzdx_.data(), nJ); + amici_dgemm( + BLASLayout::colMajor, BLASTranspose::noTrans, + BLASTranspose::noTrans, nJ, nx_solver, nz, 1.0, + &derived_state_.dJzdz_.at(izt * nz * nJ), nJ, + derived_state_.dzdx_.data(), nz, 1.0, + derived_state_.dJzdx_.data(), nJ + ); } else { // rz - fdJrzdz(ie, nroots, t, x, edata); - fdrzdx(ie, t, x); - amici_dgemm(BLASLayout::colMajor, BLASTranspose::noTrans, - BLASTranspose::noTrans, nJ, nx_solver, nz, 1.0, - &derived_state_.dJrzdz_.at(izt * nz * nJ), nJ, - derived_state_.drzdx_.data(), nz, 1.0, - derived_state_.dJzdx_.data(), nJ); + amici_dgemm( + BLASLayout::colMajor, BLASTranspose::noTrans, + BLASTranspose::noTrans, nJ, nx_solver, nz, 1.0, + &derived_state_.dJrzdz_.at(izt * nz * nJ), nJ, + derived_state_.drzdx_.data(), nz, 1.0, + derived_state_.dJzdx_.data(), nJ + ); } } } -void Model::fdJrzdz(const int ie, const int nroots, const realtype t, - const AmiVector &x, const ExpData &edata) { +void Model::fdJrzdz( + int const ie, int const nroots, const realtype t, AmiVector const& x, + ExpData const& edata +) { if (!nz) return; @@ -1892,20 +2773,27 @@ void Model::fdJrzdz(const int ie, const int nroots, const realtype t, for (int iztrue = 0; iztrue < nztrue; iztrue++) { if (edata.isSetObservedEvents(nroots, iztrue)) { - fdJrzdz(&derived_state_.dJrzdz_.at(iztrue * nz * nJ), iztrue, - state_.unscaledParameters.data(), - state_.fixedParameters.data(), derived_state_.rz_.data(), - derived_state_.sigmaz_.data()); + fdJrzdz( + &derived_state_.dJrzdz_.at(iztrue * nz * nJ), iztrue, + state_.unscaledParameters.data(), state_.fixedParameters.data(), + derived_state_.rz_.data(), derived_state_.sigmaz_.data() + ); + if (always_check_finite_) { + checkFinite( + gsl::span( + &derived_state_.dJrzdz_.at(iztrue * nz * nJ), nz * nJ + ), + ModelQuantity::dJrzdz, nz + ); + } } } - - if (always_check_finite_) { - app->checkFinite(derived_state_.dJrzdz_, "dJrzdz"); - } } -void Model::fdJrzdsigma(const int ie, const int nroots, const realtype t, - const AmiVector &x, const ExpData &edata) { +void Model::fdJrzdsigma( + int const ie, int const nroots, const realtype t, AmiVector const& x, + ExpData const& edata +) { if (!nz) return; @@ -1916,29 +2804,52 @@ void Model::fdJrzdsigma(const int ie, const int nroots, const realtype t, for (int iztrue = 0; iztrue < nztrue; iztrue++) { if (edata.isSetObservedEvents(nroots, iztrue)) { - fdJrzdsigma(&derived_state_.dJrzdsigma_.at(iztrue * nz * nJ), iztrue, - state_.unscaledParameters.data(), - state_.fixedParameters.data(), derived_state_.rz_.data(), - derived_state_.sigmaz_.data()); + fdJrzdsigma( + &derived_state_.dJrzdsigma_.at(iztrue * nz * nJ), iztrue, + state_.unscaledParameters.data(), state_.fixedParameters.data(), + derived_state_.rz_.data(), derived_state_.sigmaz_.data() + ); + if (always_check_finite_) { + checkFinite( + gsl::span( + &derived_state_.dJrzdsigma_.at(iztrue * nz * nJ), + nz * nJ + ), + ModelQuantity::dJrzdsigma, nz + ); + } } } +} - if (always_check_finite_) { - app->checkFinite(derived_state_.dJrzdsigma_, "dJrzdsigma"); +void Model::fspl(const realtype t) { + for (int ispl = 0; ispl < nspl; ispl++) + state_.spl_[ispl] = splines_[ispl].get_value(t); +} + +void Model::fsspl(const realtype t) { + derived_state_.sspl_.zero(); + realtype* sspl_data = derived_state_.sspl_.data(); + for (int ip = 0; ip < nplist(); ip++) { + for (int ispl = 0; ispl < nspl; ispl++) + sspl_data[ispl + nspl * plist(ip)] + = splines_[ispl].get_sensitivity(t, ip, state_.spl_[ispl]); } } -void Model::fw(const realtype t, const realtype *x) { +void Model::fw(const realtype t, realtype const* x) { std::fill(derived_state_.w_.begin(), derived_state_.w_.end(), 0.0); + fspl(t); fw(derived_state_.w_.data(), t, x, state_.unscaledParameters.data(), - state_.fixedParameters.data(), state_.h.data(), state_.total_cl.data()); + state_.fixedParameters.data(), state_.h.data(), state_.total_cl.data(), + state_.spl_.data()); if (always_check_finite_) { - app->checkFinite(derived_state_.w_, "w"); + checkFinite(derived_state_.w_, ModelQuantity::w); } } -void Model::fdwdp(const realtype t, const realtype *x) { +void Model::fdwdp(const realtype t, realtype const* x) { if (!nw) return; @@ -1947,19 +2858,25 @@ void Model::fdwdp(const realtype t, const realtype *x) { if (pythonGenerated) { if (!dwdp_hierarchical_.at(0).capacity()) return; - fdwdw(t,x); + fsspl(t); + fdwdw(t, x); 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()); + 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() + ); for (int irecursion = 1; irecursion <= w_recursion_depth_; irecursion++) { - dwdw_.sparse_multiply(dwdp_hierarchical_.at(irecursion), - dwdp_hierarchical_.at(irecursion - 1)); + dwdw_.sparse_multiply( + dwdp_hierarchical_.at(irecursion), + dwdp_hierarchical_.at(irecursion - 1) + ); } derived_state_.dwdp_.sparse_sum(dwdp_hierarchical_); @@ -1967,18 +2884,21 @@ void Model::fdwdp(const realtype t, const realtype *x) { if (!derived_state_.dwdp_.capacity()) return; // matlab generated - fdwdp(derived_state_.dwdp_.data(), t, x, - state_.unscaledParameters.data(), state_.fixedParameters.data(), - state_.h.data(), derived_state_.w_.data(), - state_.total_cl.data(), state_.stotal_cl.data()); + fdwdp( + derived_state_.dwdp_.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() + ); } if (always_check_finite_) { - app->checkFinite(gsl::make_span(derived_state_.dwdp_.get()), "dwdp"); + checkFinite(derived_state_.dwdp_.get(), ModelQuantity::dwdp, t); } } -void Model::fdwdx(const realtype t, const realtype *x) { +void Model::fdwdx(const realtype t, realtype const* x) { if (!nw) return; @@ -1987,19 +2907,24 @@ void Model::fdwdx(const realtype t, const realtype *x) { derived_state_.dwdx_.zero(); if (pythonGenerated) { if (!dwdx_hierarchical_.at(0).capacity()) - return; - fdwdw(t,x); + return; + fdwdw(t, x); 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()); + 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() + ); for (int irecursion = 1; irecursion <= w_recursion_depth_; irecursion++) { - dwdw_.sparse_multiply(dwdx_hierarchical_.at(irecursion), - dwdx_hierarchical_.at(irecursion - 1)); + dwdw_.sparse_multiply( + dwdx_hierarchical_.at(irecursion), + dwdx_hierarchical_.at(irecursion - 1) + ); } derived_state_.dwdx_.sparse_sum(dwdx_hierarchical_); @@ -2007,84 +2932,152 @@ void Model::fdwdx(const realtype t, const realtype *x) { if (!derived_state_.dwdx_.capacity()) return; derived_state_.dwdx_.zero(); - fdwdx(derived_state_.dwdx_.data(), t, x, - state_.unscaledParameters.data(), - state_.fixedParameters.data(), state_.h.data(), - derived_state_.w_.data(), - state_.total_cl.data()); + fdwdx( + derived_state_.dwdx_.data(), t, x, state_.unscaledParameters.data(), + state_.fixedParameters.data(), state_.h.data(), + derived_state_.w_.data(), state_.total_cl.data(), state_.spl_.data() + ); } if (always_check_finite_) { - app->checkFinite(gsl::make_span(derived_state_.dwdx_.get()), "dwdx"); + checkFinite(derived_state_.dwdx_.get(), ModelQuantity::dwdx, t); } } -void Model::fdwdw(const realtype t, const realtype *x) { +void Model::fdwdw(const realtype t, realtype const* x) { if (!nw || !dwdw_.capacity()) return; 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()); + fdwdw( + dwdw_.data(), t, x, state_.unscaledParameters.data(), + state_.fixedParameters.data(), state_.h.data(), + derived_state_.w_.data(), state_.total_cl.data() + ); if (always_check_finite_) { - app->checkFinite(gsl::make_span(dwdw_.get()), "dwdw"); + checkFinite(dwdw_.get(), ModelQuantity::dwdw, t); } } -void Model::fx_rdata(realtype *x_rdata, const realtype *x_solver, - const realtype * /*tcl*/) { +void Model::fx_rdata( + realtype* x_rdata, realtype const* x_solver, realtype const* /*tcl*/, + realtype const* /*p*/, realtype const* /*k*/ +) { if (nx_solver != nx_rdata) throw AmiException( "A model that has differing nx_solver and nx_rdata needs " - "to implement its own fx_rdata"); + "to implement its own fx_rdata" + ); std::copy_n(x_solver, nx_solver, x_rdata); } -void Model::fsx_rdata(realtype *sx_rdata, const realtype *sx_solver, - const realtype *stcl, const int /*ip*/) { - fx_rdata(sx_rdata, sx_solver, stcl); -} +void Model::fsx_rdata( + realtype* sx_rdata, realtype const* sx_solver, realtype const* stcl, + realtype const* p, realtype const* k, realtype const* x_solver, + realtype const* tcl, int const ip +) { + if (nx_solver == nx_rdata) { + std::copy_n(sx_solver, nx_solver, sx_rdata); + return; + } -void Model::fx_solver(realtype *x_solver, const realtype *x_rdata) { + // sx_rdata = dx_rdata/dx_solver * sx_solver + // + dx_rdata/d_tcl * stcl + dxrdata/dp + + // 1) sx_rdata(nx_rdata, 1) = dx_rdatadp + std::fill_n(sx_rdata, nx_rdata, 0.0); + fdx_rdatadp(sx_rdata, x_solver, tcl, p, k, ip); + + // the following could be moved to the calling function, as it's independent + // of `ip` + + // 2) sx_rdata(nx_rdata, 1) += + // dx_rdata/dx_solver(nx_rdata,nx_solver) * sx_solver(nx_solver, 1) + derived_state_.dx_rdatadx_solver.zero(); + fdx_rdatadx_solver( + derived_state_.dx_rdatadx_solver.data(), x_solver, tcl, p, k + ); + fdx_rdatadx_solver_colptrs(derived_state_.dx_rdatadx_solver); + fdx_rdatadx_solver_rowvals(derived_state_.dx_rdatadx_solver); + derived_state_.dx_rdatadx_solver.multiply( + gsl::make_span(sx_rdata, nx_rdata), gsl::make_span(sx_solver, nx_solver) + ); + + // 3) sx_rdata(nx_rdata, 1) += dx_rdata/d_tcl(nx_rdata,ntcl) * stcl + derived_state_.dx_rdatadtcl.zero(); + fdx_rdatadtcl(derived_state_.dx_rdatadtcl.data(), x_solver, tcl, p, k); + fdx_rdatadtcl_colptrs(derived_state_.dx_rdatadtcl); + fdx_rdatadtcl_rowvals(derived_state_.dx_rdatadtcl); + derived_state_.dx_rdatadtcl.multiply( + gsl::make_span(sx_rdata, nx_rdata), + gsl::make_span(stcl, (nx_rdata - nx_solver)) + ); +} + +void Model::fx_solver(realtype* x_solver, realtype const* x_rdata) { if (nx_solver != nx_rdata) throw AmiException( "A model that has differing nx_solver and nx_rdata needs " - "to implement its own fx_solver"); + "to implement its own fx_solver" + ); std::copy_n(x_rdata, nx_rdata, x_solver); } -void Model::fsx_solver(realtype *sx_solver, const realtype *sx_rdata) { +void Model::fsx_solver(realtype* sx_solver, realtype const* sx_rdata) { /* for the moment we do not need an implementation of fsx_solver as * we can simply reuse fx_solver and replace states by their * sensitivities */ fx_solver(sx_solver, sx_rdata); } -void Model::ftotal_cl(realtype * /*total_cl*/, const realtype * /*x_rdata*/) { +void Model::ftotal_cl( + realtype* /*total_cl*/, realtype const* /*x_rdata*/, realtype const* /*p*/, + realtype const* /*k*/ +) { if (nx_solver != nx_rdata) throw AmiException( "A model that has differing nx_solver and nx_rdata needs " - "to implement its own ftotal_cl"); + "to implement its own ftotal_cl" + ); } -void Model::fstotal_cl(realtype *stotal_cl, const realtype *sx_rdata, - const int /*ip*/) { - /* for the moment we do not need an implementation of fstotal_cl as - * we can simply reuse ftotal_cl and replace states by their - * sensitivities */ - ftotal_cl(stotal_cl, sx_rdata); +void Model::fstotal_cl( + realtype* stotal_cl, realtype const* sx_rdata, int const ip, + realtype const* x_rdata, realtype const* p, realtype const* k, + realtype const* tcl +) { + if (nx_solver == nx_rdata) + return; + + // stotal_cl(ncl,1) = + // dtotal_cl/dp(ncl,1) + // + dtotal_cl/dx_rdata(ncl,nx_rdata) * sx_rdata(nx_rdata,1) + + // 1) stotal_cl = dtotal_cl/dp + std::fill_n(stotal_cl, ncl(), 0.0); + fdtotal_cldp(stotal_cl, x_rdata, p, k, ip); + + // 2) stotal_cl += dtotal_cl/dx_rdata(ncl,nx_rdata) * sx_rdata(nx_rdata,1) + derived_state_.dtotal_cldx_rdata.zero(); + fdtotal_cldx_rdata( + derived_state_.dtotal_cldx_rdata.data(), x_rdata, tcl, p, k + ); + fdtotal_cldx_rdata_colptrs(derived_state_.dtotal_cldx_rdata); + fdtotal_cldx_rdata_rowvals(derived_state_.dtotal_cldx_rdata); + derived_state_.dtotal_cldx_rdata.multiply( + gsl::make_span(stotal_cl, ncl()), gsl::make_span(sx_rdata, nx_rdata) + ); } 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) { - derived_state_.x_pos_tmp_.at(ix) = - (state_is_non_negative_.at(ix) && NV_Ith_S(x, ix) < 0) - ? 0 - : NV_Ith_S(x, ix); + derived_state_.x_pos_tmp_.at(ix) + = (state_is_non_negative_.at(ix) && NV_Ith_S(x, ix) < 0) + ? 0 + : NV_Ith_S(x, ix); } return derived_state_.x_pos_tmp_.getNVector(); } @@ -2092,9 +3085,16 @@ const_N_Vector Model::computeX_pos(const_N_Vector x) { return x; } -void Model::setReinitializationStateIdxs(std::vector const& idxs) -{ - for(auto idx: idxs) { +realtype const* Model::computeX_pos(AmiVector const& x) { + if (any_state_non_negative_) { + computeX_pos(x.getNVector()); + return derived_state_.x_pos_tmp_.data(); + } + return x.data(); +} + +void Model::setReinitializationStateIdxs(std::vector const& idxs) { + for (auto idx : idxs) { if (idx < 0 || idx >= nx_rdata) throw AmiException("Invalid state index given: %d", idx); } @@ -2102,17 +3102,16 @@ void Model::setReinitializationStateIdxs(std::vector const& idxs) simulation_parameters_.reinitialization_state_idxs_sim = idxs; } -const std::vector &Model::getReinitializationStateIdxs() const -{ +std::vector const& Model::getReinitializationStateIdxs() const { return simulation_parameters_.reinitialization_state_idxs_sim; } -const AmiVectorArray &Model::get_dxdotdp() const{ +AmiVectorArray const& Model::get_dxdotdp() const { assert(!pythonGenerated); return derived_state_.dxdotdp; } -const SUNMatrixWrapper &Model::get_dxdotdp_full() const{ +SUNMatrixWrapper const& Model::get_dxdotdp_full() const { assert(pythonGenerated); return derived_state_.dxdotdp_full; } diff --git a/deps/AMICI/src/model.ODE_template.cpp b/deps/AMICI/src/model.template.cpp similarity index 82% rename from deps/AMICI/src/model.ODE_template.cpp rename to deps/AMICI/src/model.template.cpp index 5c9f69b6a..e43f46e81 100644 --- a/deps/AMICI/src/model.ODE_template.cpp +++ b/deps/AMICI/src/model.template.cpp @@ -1,10 +1,12 @@ -#include "TPL_MODELNAME.h" +#include #include namespace amici { namespace model_TPL_MODELNAME { +// clang-format off + std::array parameterNames = { TPL_PARAMETER_NAMES_INITIALIZER_LIST }; @@ -49,6 +51,16 @@ std::array expressionIds = { TPL_EXPRESSION_IDS_INITIALIZER_LIST }; +std::array stateIdxsSolver = { + TPL_STATE_IDXS_SOLVER_INITIALIZER_LIST +}; + +std::array rootInitialValues = { + TPL_ROOT_INITIAL_VALUES +}; + +// clang-format on + } // namespace model_TPL_MODELNAME } // namespace amici diff --git a/deps/AMICI/src/model_dae.cpp b/deps/AMICI/src/model_dae.cpp index bed9b4c8c..43a8d8131 100644 --- a/deps/AMICI/src/model_dae.cpp +++ b/deps/AMICI/src/model_dae.cpp @@ -3,197 +3,399 @@ namespace amici { -void Model_DAE::fJ(const realtype t, const realtype cj, const AmiVector &x, - const AmiVector &dx, const AmiVector &xdot, SUNMatrix J) { +void Model_DAE::fJ( + const realtype t, const realtype cj, AmiVector const& x, + AmiVector const& dx, AmiVector const& xdot, SUNMatrix J +) { fJ(t, cj, x.getNVector(), dx.getNVector(), xdot.getNVector(), J); } -void Model_DAE::fJ(realtype t, realtype cj, const_N_Vector x, const_N_Vector dx, - const_N_Vector /*xdot*/, SUNMatrix J) { +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()); derived_state_.J_.refresh(); auto JDense = SUNMatrixWrapper(J); derived_state_.J_.to_dense(JDense); } -void Model_DAE::fJSparse(const realtype t, const realtype cj, - const AmiVector &x, const AmiVector &dx, - const AmiVector & /*xdot*/, SUNMatrix J) { +void Model_DAE::fJSparse( + const realtype t, const realtype cj, AmiVector const& x, + AmiVector const& dx, AmiVector const& /*xdot*/, SUNMatrix J +) { fJSparse(t, cj, x.getNVector(), dx.getNVector(), J); } -void Model_DAE::fJSparse(realtype t, realtype cj, const_N_Vector x, - const_N_Vector dx, SUNMatrix J) { +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)); - SUNMatZero(J); - fJSparse(static_cast(SM_CONTENT_S(J)), t, - N_VGetArrayPointerConst(x_pos), - state_.unscaledParameters.data(), - state_.fixedParameters.data(), state_.h.data(), cj, - N_VGetArrayPointerConst(dx), - derived_state_.w_.data(), derived_state_.dwdx_.data()); -} - -void Model_DAE::fJv(const realtype t, const AmiVector &x, const AmiVector &dx, - const AmiVector & /*xdot*/, const AmiVector &v, - AmiVector &Jv, const realtype cj) { + if (pythonGenerated) { + auto JSparse = SUNMatrixWrapper(J); + // python generated + derived_state_.dxdotdx_explicit.zero(); + derived_state_.dxdotdx_implicit.zero(); + if (derived_state_.dxdotdx_explicit.capacity()) { + fdxdotdx_explicit_colptrs(derived_state_.dxdotdx_explicit); + fdxdotdx_explicit_rowvals(derived_state_.dxdotdx_explicit); + fdxdotdx_explicit( + derived_state_.dxdotdx_explicit.data(), t, + N_VGetArrayPointerConst(x_pos), + state_.unscaledParameters.data(), state_.fixedParameters.data(), + state_.h.data(), N_VGetArrayPointerConst(dx), + derived_state_.w_.data() + ); + } + fdxdotdw(t, x_pos, dx); + /* Sparse matrix multiplication + dxdotdx_implicit += dxdotdw * dwdx */ + derived_state_.dxdotdw_.sparse_multiply( + derived_state_.dxdotdx_implicit, derived_state_.dwdx_ + ); + + derived_state_.dfdx_.sparse_add( + derived_state_.dxdotdx_explicit, 1.0, + derived_state_.dxdotdx_implicit, 1.0 + ); + fM(t, x_pos); + JSparse.sparse_add( + derived_state_.MSparse_, -cj, derived_state_.dfdx_, 1.0 + ); + } else { + fJSparse( + static_cast(SM_CONTENT_S(J)), t, + N_VGetArrayPointerConst(x_pos), state_.unscaledParameters.data(), + state_.fixedParameters.data(), state_.h.data(), cj, + N_VGetArrayPointerConst(dx), derived_state_.w_.data(), + derived_state_.dwdx_.data() + ); + } +} + +void Model_DAE::fJv( + const realtype t, AmiVector const& x, AmiVector const& dx, + AmiVector const& /*xdot*/, AmiVector const& v, AmiVector& Jv, + const realtype cj +) { fJv(t, x.getNVector(), dx.getNVector(), v.getNVector(), Jv.getNVector(), cj); } -void Model_DAE::fJv(realtype t, const_N_Vector x, const_N_Vector dx, - const_N_Vector v, N_Vector Jv, realtype cj) { +void Model_DAE::fJv( + realtype t, const_N_Vector x, const_N_Vector dx, const_N_Vector v, + N_Vector Jv, realtype cj +) { N_VConst(0.0, Jv); fJSparse(t, cj, x, dx, derived_state_.J_.get()); derived_state_.J_.refresh(); derived_state_.J_.multiply(Jv, v); } -void Model_DAE::froot(const realtype t, const AmiVector &x, const AmiVector &dx, - gsl::span root) { +void Model_DAE::froot( + const realtype t, AmiVector const& x, AmiVector const& dx, + gsl::span root +) { froot(t, x.getNVector(), dx.getNVector(), root); } -void Model_DAE::froot(realtype t, const_N_Vector x, const_N_Vector dx, - gsl::span root) { +void Model_DAE::froot( + realtype t, const_N_Vector x, const_N_Vector dx, gsl::span root +) { std::fill(root.begin(), root.end(), 0.0); auto x_pos = computeX_pos(x); - froot(root.data(), t, N_VGetArrayPointerConst(x_pos), - state_.unscaledParameters.data(), state_.fixedParameters.data(), - state_.h.data(), N_VGetArrayPointerConst(dx)); + froot( + root.data(), t, N_VGetArrayPointerConst(x_pos), + state_.unscaledParameters.data(), state_.fixedParameters.data(), + state_.h.data(), N_VGetArrayPointerConst(dx) + ); } -void Model_DAE::fxdot(const realtype t, const AmiVector &x, const AmiVector &dx, - AmiVector &xdot) { +void Model_DAE::fxdot( + const realtype t, AmiVector const& x, AmiVector const& dx, AmiVector& xdot +) { fxdot(t, x.getNVector(), dx.getNVector(), xdot.getNVector()); } -void Model_DAE::fxdot(realtype t, const_N_Vector x, const_N_Vector dx, - N_Vector xdot) { +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)); N_VConst(0.0, xdot); - fxdot(N_VGetArrayPointer(xdot), t, - N_VGetArrayPointerConst(x_pos), - state_.unscaledParameters.data(), state_.fixedParameters.data(), - state_.h.data(), N_VGetArrayPointerConst(dx), - derived_state_.w_.data()); + fxdot( + N_VGetArrayPointer(xdot), t, N_VGetArrayPointerConst(x_pos), + state_.unscaledParameters.data(), state_.fixedParameters.data(), + state_.h.data(), N_VGetArrayPointerConst(dx), derived_state_.w_.data() + ); } -void Model_DAE::fJDiag(const realtype t, AmiVector &JDiag, - const realtype /*cj*/, const AmiVector &x, - const AmiVector &dx) { +void Model_DAE::fJDiag( + const realtype t, AmiVector& JDiag, const realtype /*cj*/, + AmiVector const& x, AmiVector const& dx +) { fJSparse(t, 0.0, x.getNVector(), dx.getNVector(), derived_state_.J_.get()); derived_state_.J_.refresh(); derived_state_.J_.to_diag(JDiag.getNVector()); - if (!checkFinite(JDiag.getVector(), "Jacobian")) + if (checkFinite(JDiag.getVector(), ModelQuantity::JDiag) != AMICI_SUCCESS) throw AmiException("Evaluation of fJDiag failed!"); } -void Model_DAE::fdxdotdp(const realtype t, const const_N_Vector x, - const const_N_Vector dx) { +void Model_DAE::fdxdotdw( + const realtype t, const_N_Vector x, const const_N_Vector dx +) { + derived_state_.dxdotdw_.zero(); + if (nw > 0 && derived_state_.dxdotdw_.capacity()) { + auto x_pos = computeX_pos(x); + + fdxdotdw_colptrs(derived_state_.dxdotdw_); + fdxdotdw_rowvals(derived_state_.dxdotdw_); + fdxdotdw( + derived_state_.dxdotdw_.data(), t, N_VGetArrayPointerConst(x_pos), + state_.unscaledParameters.data(), state_.fixedParameters.data(), + state_.h.data(), N_VGetArrayPointerConst(dx), + derived_state_.w_.data() + ); + } +} + +void Model_DAE::fdxdotdp( + const realtype t, const const_N_Vector x, const const_N_Vector dx +) { auto x_pos = computeX_pos(x); if (pythonGenerated) { // python generated, not yet implemented for DAEs - throw AmiException("Wrapping of DAEs is not yet implemented from Python"); + throw AmiException("Wrapping of DAEs is not yet implemented from Python" + ); } else { // matlab generated fdwdp(t, N_VGetArrayPointerConst(x_pos)); for (int ip = 0; ip < nplist(); ip++) { N_VConst(0.0, derived_state_.dxdotdp.getNVector(ip)); - fdxdotdp(derived_state_.dxdotdp.data(ip), t, - N_VGetArrayPointerConst(x_pos), - state_.unscaledParameters.data(), - state_.fixedParameters.data(), state_.h.data(), plist(ip), - N_VGetArrayPointerConst(dx), derived_state_.w_.data(), - derived_state_.dwdp_.data()); + fdxdotdp( + derived_state_.dxdotdp.data(ip), t, + N_VGetArrayPointerConst(x_pos), + state_.unscaledParameters.data(), state_.fixedParameters.data(), + state_.h.data(), plist(ip), N_VGetArrayPointerConst(dx), + derived_state_.w_.data(), derived_state_.dwdp_.data() + ); } } } void Model_DAE::fM(realtype t, const_N_Vector x) { derived_state_.M_.zero(); - auto x_pos = computeX_pos(x); - fM(derived_state_.M_.data(), t, N_VGetArrayPointerConst(x_pos), - state_.unscaledParameters.data(), - state_.fixedParameters.data()); + if (pythonGenerated) { + /* + * non-algebraic states in python generated code always have factor + * 1 in the mass matrix, so we can easily construct this matrix here + * and avoid having to generate c++ code + */ + int ndiff = 0; + for (int ix = 0; ix < nx_solver; ix++) { + derived_state_.MSparse_.set_indexptr(ix, ndiff); + if (this->idlist.at(ix) == 1.0) { + derived_state_.MSparse_.set_data(ndiff, 1.0); + derived_state_.MSparse_.set_indexval(ndiff, ix); + ndiff++; + } + } + derived_state_.MSparse_.set_indexptr(nx_solver, ndiff); + assert(ndiff == derived_state_.MSparse_.capacity()); + } else { + auto x_pos = computeX_pos(x); + fM(derived_state_.M_.data(), t, N_VGetArrayPointerConst(x_pos), + state_.unscaledParameters.data(), state_.fixedParameters.data()); + } } std::unique_ptr Model_DAE::getSolver() { return std::unique_ptr(new amici::IDASolver()); } -void Model_DAE::froot(realtype * /*root*/, const realtype /*t*/, - const realtype * /*x*/, const double * /*p*/, const double * /*k*/, - const realtype * /*h*/, const realtype * /*dx*/) { - throw AmiException("Requested functionality is not supported as %s is not " - "implemented for this model!", - __func__); // not implemented +void Model_DAE::fJSparse( + SUNMatrixContent_Sparse /*JSparse*/, realtype /*t*/, realtype const* /*x*/, + double const* /*p*/, double const* /*k*/, realtype const* /*h*/, + realtype /*cj*/, realtype const* /*dx*/, realtype const* /*w*/, + realtype const* /*dwdx*/ +) { + throw AmiException( + "Requested functionality is not supported as %s " + "is not implemented for this model!", + __func__ + ); // not implemented } -void Model_DAE::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*/) { - throw AmiException("Requested functionality is not supported as %s is not " - "implemented for this model!", - __func__); +void Model_DAE::froot( + realtype* /*root*/, const realtype /*t*/, realtype const* /*x*/, + double const* /*p*/, double const* /*k*/, realtype const* /*h*/, + realtype const* /*dx*/ +) { + throw AmiException( + "Requested functionality is not supported as %s is not " + "implemented for this model!", + __func__ + ); // not implemented } -void Model_DAE::fM(realtype */*M*/, const realtype /*t*/, const realtype */*x*/, - const realtype */*p*/, const realtype */*k*/){} +void Model_DAE::fdxdotdp( + realtype* /*dxdotdp*/, const realtype /*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*/ +) { + throw AmiException( + "Requested functionality is not supported as %s is not " + "implemented for this model!", + __func__ + ); +} + +void Model_DAE::fdxdotdp_explicit( + realtype* /*dxdotdp_explicit*/, const realtype /*t*/, realtype const* /*x*/, + realtype const* /*p*/, realtype const* /*k*/, realtype const* /*h*/, + realtype const* /*dx*/, realtype const* /*w*/ +) { + throw AmiException( + "Requested functionality is not supported as %s " + "is not implemented for this model!", + __func__ + ); // not implemented +} -void Model_DAE::fJB(const realtype t, realtype cj, const AmiVector &x, - const AmiVector &dx, const AmiVector &xB, - const AmiVector &/*dxB*/, const AmiVector & /*xBdot*/, - SUNMatrix JB) { +void Model_DAE::fdxdotdp_explicit_colptrs(SUNMatrixWrapper& /*dxdotdp*/) { + throw AmiException( + "Requested functionality is not supported as %s " + "is not implemented for this model!", + __func__ + ); // not implemented +} + +void Model_DAE::fdxdotdp_explicit_rowvals(SUNMatrixWrapper& /*dxdotdp*/) { + throw AmiException( + "Requested functionality is not supported as %s " + "is not implemented for this model!", + __func__ + ); // not implemented +} + +void Model_DAE::fdxdotdx_explicit( + realtype* /*dxdotdx_explicit*/, const realtype /*t*/, realtype const* /*x*/, + realtype const* /*p*/, realtype const* /*k*/, realtype const* /*h*/, + realtype const* /*dx*/, realtype const* /*w*/ +) { + throw AmiException( + "Requested functionality is not supported as %s " + "is not implemented for this model!", + __func__ + ); // not implemented +} + +void Model_DAE::fdxdotdx_explicit_colptrs(SUNMatrixWrapper& /*dxdotdx*/) { + throw AmiException( + "Requested functionality is not supported as %s " + "is not implemented for this model!", + __func__ + ); // not implemented +} + +void Model_DAE::fdxdotdx_explicit_rowvals(SUNMatrixWrapper& /*dxdotdx*/) { + throw AmiException( + "Requested functionality is not supported as %s " + "is not implemented for this model!", + __func__ + ); // not implemented +} + +void Model_DAE::fdxdotdw( + realtype* /*dxdotdw*/, const realtype /*t*/, realtype const* /*x*/, + realtype const* /*p*/, realtype const* /*k*/, realtype const* /*h*/, + realtype const* /*dx*/, realtype const* /*w*/ +) { + throw AmiException( + "Requested functionality is not supported as %s " + "is not implemented for this model!", + __func__ + ); // not implemented +} + +void Model_DAE::fdxdotdw_colptrs(SUNMatrixWrapper& /*dxdotdw*/) { + throw AmiException( + "Requested functionality is not supported as %s " + "is not implemented for this model!", + __func__ + ); // not implemented +} + +void Model_DAE::fdxdotdw_rowvals(SUNMatrixWrapper& /*dxdotdw*/) { + throw AmiException( + "Requested functionality is not supported as %s " + "is not implemented for this model!", + __func__ + ); // not implemented +} + +void Model_DAE:: + fM(realtype* /*M*/, const realtype /*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, + AmiVector const& xB, AmiVector const& /*dxB*/, AmiVector const& /*xBdot*/, + SUNMatrix JB +) { fJB(t, cj, x.getNVector(), dx.getNVector(), xB.getNVector(), dx.getNVector(), JB); } - -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) { +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()); 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, const AmiVector &x, - const AmiVector &dx, const AmiVector &xB, - const AmiVector &dxB, const AmiVector & /*xBdot*/, - SUNMatrix JB) { - fJSparseB(t, cj, x.getNVector(), dx.getNVector(), xB.getNVector(), dxB.getNVector(), JB); +void Model_DAE::fJSparseB( + const realtype t, realtype cj, AmiVector const& x, AmiVector const& dx, + AmiVector const& xB, AmiVector const& dxB, AmiVector const& /*xBdot*/, + SUNMatrix JB +) { + fJSparseB( + t, cj, x.getNVector(), dx.getNVector(), xB.getNVector(), + dxB.getNVector(), JB + ); } -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) { +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()); derived_state_.J_.refresh(); auto JSparseB = SUNMatrixWrapper(JB); derived_state_.J_.transpose(JSparseB, -1.0, nxtrue_solver); } -void Model_DAE::fJvB(realtype t, const_N_Vector x, const_N_Vector dx, - const_N_Vector xB, const_N_Vector dxB, const_N_Vector vB, - N_Vector JvB, realtype cj) { +void Model_DAE::fJvB( + realtype t, const_N_Vector x, const_N_Vector dx, const_N_Vector xB, + 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()); derived_state_.JB_.refresh(); derived_state_.JB_.multiply(JvB, vB); } -void Model_DAE::fxBdot(realtype t, const_N_Vector x, const_N_Vector dx, - const_N_Vector xB, - const_N_Vector dxB, N_Vector xBdot) { +void Model_DAE::fxBdot( + realtype t, const_N_Vector x, const_N_Vector dx, const_N_Vector xB, + const_N_Vector dxB, N_Vector xBdot +) { N_VConst(0.0, xBdot); fJSparseB(t, 1.0, x, dx, xB, dxB, derived_state_.JB_.get()); derived_state_.JB_.refresh(); @@ -201,33 +403,39 @@ void Model_DAE::fxBdot(realtype t, const_N_Vector x, const_N_Vector dx, derived_state_.JB_.multiply(xBdot, xB); } -void Model_DAE::fqBdot(realtype t, const_N_Vector x, const_N_Vector dx, - const_N_Vector xB, const_N_Vector /*dxB*/, - N_Vector qBdot) { +void Model_DAE::fqBdot( + realtype t, const_N_Vector x, const_N_Vector dx, const_N_Vector xB, + const_N_Vector /*dxB*/, N_Vector qBdot +) { N_VConst(0.0, qBdot); fdxdotdp(t, x, dx); for (int ip = 0; ip < nplist(); ip++) { for (int ix = 0; ix < nxtrue_solver; ix++) - NV_Ith_S(qBdot, ip * nJ) -= NV_Ith_S(xB, ix) - * derived_state_.dxdotdp.at(ix, ip); + NV_Ith_S(qBdot, ip * nJ) + -= NV_Ith_S(xB, ix) * derived_state_.dxdotdp.at(ix, ip); // second order part for (int iJ = 1; iJ < nJ; iJ++) for (int ix = 0; ix < nxtrue_solver; ix++) - NV_Ith_S(qBdot, ip * nJ + iJ) -= - NV_Ith_S(xB, ix) - * derived_state_.dxdotdp.at(ix + iJ * nxtrue_solver, ip) - + NV_Ith_S(xB, ix + iJ * nxtrue_solver) - * derived_state_.dxdotdp.at(ix, ip); + NV_Ith_S(qBdot, ip * nJ + iJ) + -= NV_Ith_S(xB, ix) + * derived_state_.dxdotdp.at( + ix + iJ * nxtrue_solver, ip + ) + + NV_Ith_S(xB, ix + iJ * nxtrue_solver) + * derived_state_.dxdotdp.at(ix, ip); } } -void Model_DAE::fxBdot_ss(const realtype t, const AmiVector &xB, - const AmiVector &dxB, AmiVector &xBdot) { +void Model_DAE::fxBdot_ss( + const realtype t, AmiVector const& xB, AmiVector const& dxB, + AmiVector& xBdot +) { fxBdot_ss(t, xB.getNVector(), dxB.getNVector(), xBdot.getNVector()); } -void Model_DAE::fxBdot_ss(realtype /*t*/, const_N_Vector xB, const_N_Vector /*dxB*/, - N_Vector xBdot) const { +void Model_DAE::fxBdot_ss( + realtype /*t*/, const_N_Vector xB, const_N_Vector /*dxB*/, N_Vector xBdot +) const { /* Right hand side of the adjoint state for steady state computations. J is fixed (as x remains in steady state), so the RHS becomes simple. */ N_VConst(0.0, xBdot); @@ -236,8 +444,9 @@ void Model_DAE::fxBdot_ss(realtype /*t*/, const_N_Vector xB, const_N_Vector /*dx N_VScale(-1.0, xBdot, xBdot); } -void Model_DAE::fqBdot_ss(realtype /*t*/, const_N_Vector xB, - const_N_Vector /*dxB*/, N_Vector qBdot) const { +void Model_DAE::fqBdot_ss( + realtype /*t*/, const_N_Vector xB, const_N_Vector /*dxB*/, N_Vector qBdot +) const { /* Quadratures when computing adjoints for steady state. The integrand is just the adjoint state itself. */ N_VScale(1.0, const_cast(xB), qBdot); @@ -249,27 +458,34 @@ void Model_DAE::fJSparseB_ss(SUNMatrix JB) { derived_state_.JB_.refresh(); } -void Model_DAE::writeSteadystateJB(const realtype t, realtype cj, - const AmiVector &x, const AmiVector & dx, - const AmiVector &xB, const AmiVector & dxB, - const AmiVector &/*xBdot*/) { +void Model_DAE::writeSteadystateJB( + const realtype 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()); + fJSparseB( + t, cj, x.getNVector(), dx.getNVector(), xB.getNVector(), + dxB.getNVector(), derived_state_.JB_.get() + ); derived_state_.JB_.refresh(); /* Switch sign, as we integrate forward in time, not backward */ derived_state_.JB_.scale(-1); } -void Model_DAE::fsxdot(const realtype t, const AmiVector &x, - const AmiVector &dx, const int ip, const AmiVector &sx, - const AmiVector &sdx, AmiVector &sxdot) { - fsxdot(t, x.getNVector(), dx.getNVector(), ip, sx.getNVector(), - sdx.getNVector(), sxdot.getNVector()); +void Model_DAE::fsxdot( + const realtype t, AmiVector const& x, AmiVector const& dx, int const ip, + AmiVector const& sx, AmiVector const& sdx, AmiVector& sxdot +) { + fsxdot( + t, x.getNVector(), dx.getNVector(), ip, sx.getNVector(), + sdx.getNVector(), sxdot.getNVector() + ); } -void Model_DAE::fsxdot(realtype t, const_N_Vector x, const_N_Vector dx, int ip, - const_N_Vector sx, const_N_Vector sdx, N_Vector sxdot) { +void Model_DAE::fsxdot( + realtype t, const_N_Vector x, const_N_Vector dx, int ip, const_N_Vector sx, + const_N_Vector sdx, N_Vector sxdot +) { if (ip == 0) { // we only need to call this for the first parameter index will be // the same for all remaining @@ -281,7 +497,8 @@ void Model_DAE::fsxdot(realtype t, const_N_Vector x, const_N_Vector dx, int ip, if (pythonGenerated) { // python generated, not yet implemented for DAEs - throw AmiException("Wrapping of DAEs is not yet implemented from Python"); + throw AmiException("Wrapping of DAEs is not yet implemented from Python" + ); } else { /* copy dxdotdp over */ N_VScale(1.0, derived_state_.dxdotdp.getNVector(ip), sxdot); diff --git a/deps/AMICI/src/model_header.ODE_template.h b/deps/AMICI/src/model_header.template.h similarity index 53% rename from deps/AMICI/src/model_header.ODE_template.h rename to deps/AMICI/src/model_header.template.h index 84cbfad35..af05c8ccc 100644 --- a/deps/AMICI/src/model_header.ODE_template.h +++ b/deps/AMICI/src/model_header.template.h @@ -4,10 +4,8 @@ #include #include -#include "amici/model_ode.h" -#include "amici/solver_cvodes.h" - -#include "sundials/sundials_types.h" +#include "amici/model_TPL_MODEL_TYPE_LOWER.h" +#include "amici/splinefunctions.h" namespace amici { @@ -26,12 +24,20 @@ extern std::array fixedParameterIds; extern std::array stateIds; extern std::array observableIds; extern std::array expressionIds; +extern std::array stateIdxsSolver; +extern std::array rootInitialValues; TPL_JY_DEF TPL_DJYDSIGMA_DEF TPL_DJYDY_DEF TPL_DJYDY_COLPTRS_DEF TPL_DJYDY_ROWVALS_DEF +TPL_JZ_DEF +TPL_DJZDSIGMA_DEF +TPL_DJZDZ_DEF +TPL_JRZ_DEF +TPL_DJRZDSIGMA_DEF +TPL_DJRZDZ_DEF TPL_ROOT_DEF TPL_DWDP_DEF TPL_DWDP_COLPTRS_DEF @@ -53,8 +59,15 @@ TPL_DXDOTDX_EXPLICIT_COLPTRS_DEF TPL_DXDOTDX_EXPLICIT_ROWVALS_DEF TPL_DYDX_DEF TPL_DYDP_DEF +TPL_DZDX_DEF +TPL_DZDP_DEF +TPL_DRZDX_DEF +TPL_DRZDP_DEF TPL_SIGMAY_DEF +TPL_SIGMAZ_DEF TPL_DSIGMAYDP_DEF +TPL_DSIGMAYDY_DEF +TPL_DSIGMAZDP_DEF TPL_W_DEF TPL_X0_DEF TPL_X0_FIXEDPARAMETERS_DEF @@ -62,23 +75,39 @@ TPL_SX0_DEF TPL_SX0_FIXEDPARAMETERS_DEF TPL_XDOT_DEF TPL_Y_DEF +TPL_Z_DEF +TPL_RZ_DEF TPL_STAU_DEF TPL_DELTAX_DEF TPL_DELTASX_DEF TPL_X_RDATA_DEF TPL_X_SOLVER_DEF TPL_TOTAL_CL_DEF +TPL_DX_RDATADX_SOLVER_DEF +TPL_DX_RDATADX_SOLVER_COLPTRS_DEF +TPL_DX_RDATADX_SOLVER_ROWVALS_DEF +TPL_DX_RDATADP_DEF +TPL_DX_RDATADTCL_DEF +TPL_DX_RDATADTCL_COLPTRS_DEF +TPL_DX_RDATADTCL_ROWVALS_DEF +TPL_DTOTAL_CLDP_DEF +TPL_DTOTAL_CLDX_RDATA_DEF +TPL_DTOTAL_CLDX_RDATA_COLPTRS_DEF +TPL_DTOTAL_CLDX_RDATA_ROWVALS_DEF +TPL_CREATE_SPLINES_DEF +TPL_DSPLINE_VALUESDP_DEF +TPL_DSPLINE_SLOPESDP_DEF /** * @brief AMICI-generated model subclass. */ -class Model_TPL_MODELNAME : public amici::Model_ODE { +class Model_TPL_MODELNAME : public amici::Model_TPL_MODEL_TYPE_UPPER { public: /** * @brief Default constructor. */ Model_TPL_MODELNAME() - : amici::Model_ODE( + : amici::Model_TPL_MODEL_TYPE_UPPER( amici::ModelDimensions( TPL_NX_RDATA, // nx_rdata TPL_NXTRUE_RDATA, // nxtrue_rdata @@ -92,6 +121,7 @@ class Model_TPL_MODELNAME : public amici::Model_ODE { TPL_NZ, // nz TPL_NZTRUE, // nztrue TPL_NEVENT, // nevent + TPL_NSPL, // nspl TPL_NOBJECTIVE, // nobjective TPL_NW, // nw TPL_NDWDX, // ndwdx @@ -99,6 +129,9 @@ class Model_TPL_MODELNAME : public amici::Model_ODE { TPL_NDWDW, // ndwdw TPL_NDXDOTDW, // ndxdotdw TPL_NDJYDY, // ndjydy + TPL_NDXRDATADXSOLVER, // ndxrdatadxsolver + TPL_NDXRDATADTCL, // ndxrdatadtcl + TPL_NDTOTALCLDXRDATA, // ndtotal_cldx_rdata 0, // nnz TPL_UBW, // ubw TPL_LBW // lbw @@ -108,13 +141,17 @@ class Model_TPL_MODELNAME : public amici::Model_ODE { std::vector{TPL_PARAMETERS} // dynamic parameters ), TPL_O2MODE, // o2mode - std::vector(TPL_NX_SOLVER, 0.0), // idlist - std::vector{}, // z2event + std::vector{TPL_ID}, // idlist + std::vector{TPL_Z2EVENT}, // z2events true, // pythonGenerated TPL_NDXDOTDP_EXPLICIT, // ndxdotdp_explicit TPL_NDXDOTDX_EXPLICIT, // ndxdotdx_explicit TPL_W_RECURSION_DEPTH // w_recursion_depth - ) {} + ) { + root_initial_values_ = std::vector( + rootInitialValues.begin(), rootInitialValues.end() + ); + } /** * @brief Clone this model instance. @@ -124,95 +161,21 @@ class Model_TPL_MODELNAME : public amici::Model_ODE { return new Model_TPL_MODELNAME(*this); } - /** - * @brief model specific implementation of fJrz - * @param nllh regularization for event measurements z - * @param iz event output index - * @param p parameter vector - * @param k constant vector - * @param z model event output at timepoint - * @param sigmaz event measurement standard deviation at timepoint - */ - void fJrz(realtype *nllh, const int iz, const realtype *p, - const realtype *k, const realtype *rz, - const realtype *sigmaz) override {} + TPL_JRZ_IMPL TPL_JY_IMPL - /** - * @brief model specific implementation of fJz - * @param nllh negative log-likelihood for event measurements z - * @param iz event output index - * @param p parameter vector - * @param k constant vector - * @param z model event output at timepoint - * @param sigmaz event measurement standard deviation at timepoint - * @param mz event measurements at timepoint - */ - void fJz(realtype *nllh, const int iz, const realtype *p, - const realtype *k, const realtype *z, - const realtype *sigmaz, const realtype *mz) override {} + TPL_JZ_IMPL - /** - * @brief model specific implementation of fdJrzdsigma - * @param dJrzdsigma Sensitivity of event penalization Jrz w.r.t. - * standard deviation sigmaz - * @param iz event output index - * @param p parameter vector - * @param k constant vector - * @param rz model root output at timepoint - * @param sigmaz event measurement standard deviation at timepoint - */ - void fdJrzdsigma(realtype *dJrzdsigma, const int iz, - const realtype *p, const realtype *k, - const realtype *rz, - const realtype *sigmaz) override {} + TPL_DJRZDSIGMA_IMPL - /** - * @brief model specific implementation of fdJrzdz - * @param dJrzdz partial derivative of event penalization Jrz - * @param iz event output index - * @param p parameter vector - * @param k constant vector - * @param rz model root output at timepoint - * @param sigmaz event measurement standard deviation at timepoint - */ - void fdJrzdz(realtype *dJrzdz, const int iz, const realtype *p, - const realtype *k, const realtype *rz, - const realtype *sigmaz) override {} + TPL_DJRZDZ_IMPL TPL_DJYDSIGMA_IMPL - /** - * @brief model specific implementation of fdJzdsigma - * @param dJzdsigma Sensitivity of event measurement - * negative log-likelihood Jz w.r.t. standard deviation sigmaz - * @param iz event output index - * @param p parameter vector - * @param k constant vector - * @param z model event output at timepoint - * @param sigmaz event measurement standard deviation at timepoint - * @param mz event measurement at timepoint - */ - void fdJzdsigma(realtype *dJzdsigma, const int iz, - const realtype *p, const realtype *k, - const realtype *z, const realtype *sigmaz, - const realtype *mz) override {} + TPL_DJZDSIGMA_IMPL - /** - * @brief model specific implementation of fdJzdz - * @param dJzdz partial derivative of event measurement negative - * log-likelihood Jz - * @param iz event output index - * @param p parameter vector - * @param k constant vector - * @param z model event output at timepoint - * @param sigmaz event measurement standard deviation at timepoint - * @param mz event measurement at timepoint - */ - void fdJzdz(realtype *dJzdz, const int iz, const realtype *p, - const realtype *k, const realtype *z, - const realtype *sigmaz, const realtype *mz) override {} + TPL_DJZDZ_IMPL /** * @brief model specific implementation of fdeltasx @@ -258,55 +221,24 @@ class Model_TPL_MODELNAME : public amici::Model_ODE { const realtype *xdot, const realtype *xdot_old, const realtype *xB) override {} - /** - * @brief model specific implementation of fdrzdp - * @param drzdp partial derivative of root output rz w.r.t. model parameters - * p - * @param ie event index - * @param t current time - * @param x current state - * @param p parameter vector - * @param k constant vector - * @param h heaviside vector - * @param ip parameter index w.r.t. which the derivative is requested - */ - void fdrzdp(realtype *drzdp, const int ie, const realtype t, - const realtype *x, const realtype *p, const realtype *k, - const realtype *h, const int ip) override {} + TPL_DRZDP_IMPL - /** - * @brief model specific implementation of fdrzdx - * @param drzdx partial derivative of root output rz w.r.t. model states x - * @param ie event index - * @param t current time - * @param x current state - * @param p parameter vector - * @param k constant vector - * @param h heaviside vector - */ - void fdrzdx(realtype *drzdx, const int ie, const realtype t, - const realtype *x, const realtype *p, const realtype *k, - const realtype *h) override {} + TPL_DRZDX_IMPL TPL_DSIGMAYDP_IMPL - /** - * @brief model specific implementation of fsigmaz - * @param dsigmazdp partial derivative of standard deviation of event - * measurements - * @param t current time - * @param p parameter vector - * @param k constant vector - * @param ip sensitivity index - */ - void fdsigmazdp(realtype *dsigmazdp, const realtype t, - const realtype *p, const realtype *k, - const int ip) override {} + TPL_DSIGMAYDY_IMPL + + TPL_DSIGMAZDP_IMPL TPL_DJYDY_IMPL TPL_DJYDY_COLPTRS_IMPL TPL_DJYDY_ROWVALS_IMPL + TPL_CREATE_SPLINES_IMPL + TPL_DSPLINE_VALUESDP_IMPL + TPL_DSPLINE_SLOPESDP_IMPL + TPL_DWDP_IMPL TPL_DWDP_COLPTRS_IMPL TPL_DWDP_ROWVALS_IMPL @@ -335,104 +267,22 @@ class Model_TPL_MODELNAME : public amici::Model_ODE { TPL_DYDP_IMPL - /** - * @brief model specific implementation of fdzdp - * @param dzdp partial derivative of event-resolved output z w.r.t. model - * parameters p - * @param ie event index - * @param t current time - * @param x current state - * @param p parameter vector - * @param k constant vector - * @param h heaviside vector - * @param ip parameter index w.r.t. which the derivative is requested - */ - void fdzdp(realtype *dzdp, const int ie, const realtype t, - const realtype *x, const realtype *p, const realtype *k, - const realtype *h, const int ip) override {} + TPL_DZDP_IMPL - /** - * @brief model specific implementation of fdzdx - * @param dzdx partial derivative of event-resolved output z w.r.t. model - * states x - * @param ie event index - * @param t current time - * @param x current state - * @param p parameter vector - * @param k constant vector - * @param h heaviside vector - */ - void fdzdx(realtype *dzdx, const int ie, const realtype t, - const realtype *x, const realtype *p, const realtype *k, - const realtype *h) override {} + TPL_DZDX_IMPL TPL_ROOT_IMPL - /** - * @brief model specific implementation of frz - * @param rz value of root function at current timepoint (non-output events - * not included) - * @param ie event index - * @param t current time - * @param x current state - * @param p parameter vector - * @param k constant vector - * @param h heaviside vector - */ - void frz(realtype *rz, const int ie, const realtype t, - const realtype *x, const realtype *p, const realtype *k, - const realtype *h) override {} + TPL_RZ_IMPL TPL_SIGMAY_IMPL - /** - * @brief model specific implementation of fsigmaz - * @param sigmaz standard deviation of event measurements - * @param t current time - * @param p parameter vector - * @param k constant vector - */ - void fsigmaz(realtype *sigmaz, const realtype t, const realtype *p, - const realtype *k) override {} - - /** - * @brief model specific implementation of fsrz - * @param srz Sensitivity of rz, total derivative - * @param ie event index - * @param t current time - * @param x current state - * @param p parameter vector - * @param k constant vector - * @param sx current state sensitivity - * @param h heaviside vector - * @param ip sensitivity index - */ - void fsrz(realtype *srz, 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 {} + TPL_SIGMAZ_IMPL TPL_STAU_IMPL TPL_SX0_IMPL TPL_SX0_FIXEDPARAMETERS_IMPL - /** - * @brief model specific implementation of fsz - * @param sz Sensitivity of rz, total derivative - * @param ie event index - * @param t current time - * @param x current state - * @param p parameter vector - * @param k constant vector - * @param h heaviside vector - * @param sx current state sensitivity - * @param ip sensitivity index - */ - void fsz(realtype *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 {} - TPL_W_IMPL TPL_X0_IMPL @@ -443,19 +293,7 @@ class Model_TPL_MODELNAME : public amici::Model_ODE { TPL_Y_IMPL - /** - * @brief model specific implementation of fz - * @param z value of event output - * @param ie event index - * @param t current time - * @param x current state - * @param p parameter vector - * @param k constant vector - * @param h heaviside vector - */ - void fz(realtype *z, const int ie, const realtype t, - const realtype *x, const realtype *p, const realtype *k, - const realtype *h) override {} + TPL_Z_IMPL TPL_X_RDATA_IMPL @@ -463,6 +301,22 @@ class Model_TPL_MODELNAME : public amici::Model_ODE { TPL_TOTAL_CL_IMPL + TPL_DX_RDATADX_SOLVER_IMPL + TPL_DX_RDATADX_SOLVER_COLPTRS_IMPL + TPL_DX_RDATADX_SOLVER_ROWVALS_IMPL + + TPL_DX_RDATADP_IMPL + + TPL_DX_RDATADTCL_IMPL + TPL_DX_RDATADTCL_COLPTRS_IMPL + TPL_DX_RDATADTCL_ROWVALS_IMPL + + TPL_DTOTAL_CLDP_IMPL + + TPL_DTOTAL_CLDX_RDATA_IMPL + TPL_DTOTAL_CLDX_RDATA_COLPTRS_IMPL + TPL_DTOTAL_CLDX_RDATA_ROWVALS_IMPL + std::string getName() const override { return "TPL_MODELNAME"; } @@ -484,6 +338,19 @@ class Model_TPL_MODELNAME : public amici::Model_ODE { return std::vector(stateNames.begin(), stateNames.end()); } + /** + * @brief Get names of the solver states + * @return the names + */ + std::vector getStateNamesSolver() const override { + std::vector result; + result.reserve(stateIdxsSolver.size()); + for(auto idx: stateIdxsSolver) { + result.push_back(stateNames[idx]); + } + return result; + } + /** * @brief Get names of the fixed model parameters * @return the names @@ -528,6 +395,19 @@ class Model_TPL_MODELNAME : public amici::Model_ODE { return std::vector(stateIds.begin(), stateIds.end()); } + /** + * @brief Get ids of the solver states + * @return the ids + */ + std::vector getStateIdsSolver() const override { + std::vector result; + result.reserve(stateIdxsSolver.size()); + for(auto idx: stateIdxsSolver) { + result.push_back(stateIds[idx]); + } + return result; + } + /** * @brief Get ids of the fixed model parameters * @return the ids diff --git a/deps/AMICI/src/model_ode.cpp b/deps/AMICI/src/model_ode.cpp index bd6e465e7..24787df8a 100644 --- a/deps/AMICI/src/model_ode.cpp +++ b/deps/AMICI/src/model_ode.cpp @@ -1,16 +1,19 @@ -#include #include "amici/model_ode.h" #include "amici/solver_cvodes.h" +#include namespace amici { -void Model_ODE::fJ(const realtype t, const realtype /*cj*/, const AmiVector &x, - const AmiVector & /*dx*/, const AmiVector &xdot, - SUNMatrix J) { +void Model_ODE::fJ( + const realtype t, const realtype /*cj*/, AmiVector const& x, + AmiVector const& /*dx*/, AmiVector const& xdot, SUNMatrix J +) { fJ(t, x.getNVector(), xdot.getNVector(), J); } -void Model_ODE::fJ(realtype t, const_N_Vector x, const_N_Vector /*xdot*/, SUNMatrix J) { +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()); @@ -19,9 +22,10 @@ void Model_ODE::fJ(realtype t, const_N_Vector x, const_N_Vector /*xdot*/, SUNMat derived_state_.J_.to_dense(JDense); } -void Model_ODE::fJSparse(const realtype t, const realtype /*cj*/, - const AmiVector &x, const AmiVector & /*dx*/, - const AmiVector & /*xdot*/, SUNMatrix J) { +void Model_ODE::fJSparse( + const realtype t, const realtype /*cj*/, AmiVector const& x, + AmiVector const& /*dx*/, AmiVector const& /*xdot*/, SUNMatrix J +) { fJSparse(t, x.getNVector(), J); } @@ -40,54 +44,67 @@ void Model_ODE::fJSparse(realtype t, const_N_Vector x, SUNMatrix J) { derived_state_.dxdotdx_explicit.data(), t, N_VGetArrayPointerConst(x_pos), state_.unscaledParameters.data(), state_.fixedParameters.data(), - state_.h.data(), derived_state_.w_.data()); + state_.h.data(), derived_state_.w_.data() + ); } fdxdotdw(t, x_pos); /* Sparse matrix multiplication dxdotdx_implicit += dxdotdw * dwdx */ - derived_state_.dxdotdw_.sparse_multiply(derived_state_.dxdotdx_implicit, - derived_state_.dwdx_); - - JSparse.sparse_add(derived_state_.dxdotdx_explicit, 1.0, - derived_state_.dxdotdx_implicit, 1.0); + derived_state_.dxdotdw_.sparse_multiply( + derived_state_.dxdotdx_implicit, derived_state_.dwdx_ + ); + + JSparse.sparse_add( + derived_state_.dxdotdx_explicit, 1.0, + derived_state_.dxdotdx_implicit, 1.0 + ); } else { - fJSparse(static_cast(SM_CONTENT_S(J)), t, - N_VGetArrayPointerConst(x_pos), - state_.unscaledParameters.data(), - state_.fixedParameters.data(), state_.h.data(), - derived_state_.w_.data(), - derived_state_.dwdx_.data()); + fJSparse( + static_cast(SM_CONTENT_S(J)), t, + N_VGetArrayPointerConst(x_pos), state_.unscaledParameters.data(), + state_.fixedParameters.data(), state_.h.data(), + derived_state_.w_.data(), derived_state_.dwdx_.data() + ); } } -void Model_ODE::fJv(const realtype t, const AmiVector &x, - const AmiVector & /*dx*/, const AmiVector & /*xdot*/, - const AmiVector &v, AmiVector &Jv, const realtype /*cj*/) { +void Model_ODE:: + fJv(const realtype t, AmiVector const& x, AmiVector const& /*dx*/, + AmiVector const& /*xdot*/, AmiVector const& v, AmiVector& Jv, + const realtype /*cj*/) { fJv(v.getNVector(), Jv.getNVector(), t, x.getNVector()); } -void Model_ODE::fJv(const_N_Vector v, N_Vector Jv, realtype t, const_N_Vector x) { +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()); derived_state_.J_.refresh(); derived_state_.J_.multiply(Jv, v); } -void Model_ODE::froot(const realtype t, const AmiVector &x, - const AmiVector & /*dx*/, gsl::span root) { +void Model_ODE::froot( + const realtype t, AmiVector const& x, AmiVector const& /*dx*/, + gsl::span root +) { froot(t, x.getNVector(), root); } void Model_ODE::froot(realtype t, const_N_Vector x, gsl::span root) { auto x_pos = computeX_pos(x); std::fill(root.begin(), root.end(), 0.0); - froot(root.data(), t, N_VGetArrayPointerConst(x_pos), - state_.unscaledParameters.data(), state_.fixedParameters.data(), - state_.h.data()); + froot( + root.data(), t, N_VGetArrayPointerConst(x_pos), + state_.unscaledParameters.data(), state_.fixedParameters.data(), + state_.h.data(), state_.total_cl.data() + ); } -void Model_ODE::fxdot(const realtype t, const AmiVector &x, - const AmiVector & /*dx*/, AmiVector &xdot) { +void Model_ODE::fxdot( + const realtype t, AmiVector const& x, AmiVector const& /*dx*/, + AmiVector& xdot +) { fxdot(t, x.getNVector(), xdot.getNVector()); } @@ -95,17 +112,19 @@ void Model_ODE::fxdot(realtype t, const_N_Vector x, N_Vector xdot) { auto x_pos = computeX_pos(x); fw(t, N_VGetArrayPointerConst(x_pos)); N_VConst(0.0, xdot); - fxdot(N_VGetArrayPointer(xdot), t, - N_VGetArrayPointerConst(x_pos), - state_.unscaledParameters.data(), state_.fixedParameters.data(), - state_.h.data(), derived_state_.w_.data()); + fxdot( + N_VGetArrayPointer(xdot), t, N_VGetArrayPointerConst(x_pos), + state_.unscaledParameters.data(), state_.fixedParameters.data(), + state_.h.data(), derived_state_.w_.data() + ); } -void Model_ODE::fJDiag(const realtype t, AmiVector &JDiag, - const realtype /*cj*/, const AmiVector &x, - const AmiVector & /*dx*/) { +void Model_ODE::fJDiag( + const realtype t, AmiVector& JDiag, const realtype /*cj*/, + AmiVector const& x, AmiVector const& /*dx*/ +) { fJDiag(t, JDiag.getNVector(), x.getNVector()); - if (checkFinite(JDiag.getVector(), "Jacobian") != AMICI_SUCCESS) + if (checkFinite(JDiag.getVector(), ModelQuantity::JDiag) != AMICI_SUCCESS) throw AmiException("Evaluation of fJDiag failed!"); } @@ -116,9 +135,11 @@ void Model_ODE::fdxdotdw(const realtype t, const_N_Vector x) { fdxdotdw_colptrs(derived_state_.dxdotdw_); fdxdotdw_rowvals(derived_state_.dxdotdw_); - fdxdotdw(derived_state_.dxdotdw_.data(), t, N_VGetArrayPointerConst(x_pos), - state_.unscaledParameters.data(), state_.fixedParameters.data(), - state_.h.data(), derived_state_.w_.data()); + fdxdotdw( + derived_state_.dxdotdw_.data(), t, N_VGetArrayPointerConst(x_pos), + state_.unscaledParameters.data(), state_.fixedParameters.data(), + state_.h.data(), derived_state_.w_.data() + ); } } @@ -137,33 +158,38 @@ void Model_ODE::fdxdotdp(const realtype t, const_N_Vector x) { derived_state_.dxdotdp_explicit.data(), t, N_VGetArrayPointerConst(x_pos), state_.unscaledParameters.data(), state_.fixedParameters.data(), - state_.h.data(), derived_state_.w_.data()); + state_.h.data(), derived_state_.w_.data() + ); } fdxdotdw(t, x_pos); /* Sparse matrix multiplication dxdotdp_implicit += dxdotdw * dwdp */ - derived_state_.dxdotdw_.sparse_multiply(derived_state_.dxdotdp_implicit, - derived_state_.dwdp_); + derived_state_.dxdotdw_.sparse_multiply( + derived_state_.dxdotdp_implicit, derived_state_.dwdp_ + ); derived_state_.dxdotdp_full.sparse_add( - derived_state_.dxdotdp_explicit, 1.0, - derived_state_.dxdotdp_implicit, 1.0); + derived_state_.dxdotdp_explicit, 1.0, + derived_state_.dxdotdp_implicit, 1.0 + ); } else { // matlab generated for (int ip = 0; ip < nplist(); ip++) { N_VConst(0.0, derived_state_.dxdotdp.getNVector(ip)); - fdxdotdp(derived_state_.dxdotdp.data(ip), t, - N_VGetArrayPointerConst(x_pos), - state_.unscaledParameters.data(), - state_.fixedParameters.data(), state_.h.data(), plist(ip), - derived_state_.w_.data(), derived_state_.dwdp_.data()); + fdxdotdp( + derived_state_.dxdotdp.data(ip), t, + N_VGetArrayPointerConst(x_pos), + state_.unscaledParameters.data(), state_.fixedParameters.data(), + state_.h.data(), plist(ip), derived_state_.w_.data(), + derived_state_.dwdp_.data() + ); } } } -void Model_ODE::fdxdotdp(const realtype t, const AmiVector &x, - const AmiVector & /*dx*/) { +void Model_ODE:: + fdxdotdp(const realtype t, AmiVector const& x, AmiVector const& /*dx*/) { fdxdotdp(t, x.getNVector()); } @@ -171,142 +197,184 @@ std::unique_ptr Model_ODE::getSolver() { return std::unique_ptr(new amici::CVodeSolver()); } -void Model_ODE::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*/) { - throw AmiException("Requested functionality is not supported as %s " - "is not implemented for this model!", - __func__); // not implemented -} - -void Model_ODE::fJSparse(realtype * /*JSparse*/, const realtype /*t*/, - const realtype * /*x*/, const realtype * /*p*/, - const realtype * /*k*/, const realtype * /*h*/, - const realtype * /*w*/, const realtype * /*dwdx*/) { - throw AmiException("Requested functionality is not supported as %s " - "is not implemented for this model!", - __func__); // not implemented -} - -void Model_ODE::fJSparse_colptrs(SUNMatrixWrapper &/*JSparse*/) { - throw AmiException("Requested functionality is not supported as %s " - "is not implemented for this model!", - __func__); // not implemented -} - -void Model_ODE::fJSparse_rowvals(SUNMatrixWrapper &/*JSparse*/) { - throw AmiException("Requested functionality is not supported as %s " - "is not implemented for this model!", - __func__); // not implemented -} - -void Model_ODE::froot(realtype * /*root*/, const realtype /*t*/, - const realtype * /*x*/, const realtype * /*p*/, - const realtype * /*k*/, const realtype * /*h*/) { - throw AmiException("Requested functionality is not supported as %s is not " - "implemented for this model!", - __func__); // not implemented -} - -void Model_ODE::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*/) { - throw AmiException("Requested functionality is not supported as %s " - "is not implemented for this model!", - __func__); // not implemented -} - -void Model_ODE::fdxdotdp_explicit(realtype * /*dxdotdp_explicit*/, const realtype /*t*/, - const realtype * /*x*/, const realtype * /*p*/, - const realtype * /*k*/, const realtype * /*h*/, - const realtype * /*w*/) { - throw AmiException("Requested functionality is not supported as %s " - "is not implemented for this model!", - __func__); // not implemented -} - -void Model_ODE::fdxdotdp_explicit_colptrs(SUNMatrixWrapper &/*dxdotdp*/) { - throw AmiException("Requested functionality is not supported as %s " - "is not implemented for this model!", - __func__); // not implemented -} - -void Model_ODE::fdxdotdp_explicit_rowvals(SUNMatrixWrapper &/*dxdotdp*/) { - throw AmiException("Requested functionality is not supported as %s " - "is not implemented for this model!", - __func__); // not implemented -} - -void Model_ODE::fdxdotdx_explicit(realtype * /*dxdotdx_explicit*/, const realtype /*t*/, - const realtype * /*x*/, const realtype * /*p*/, - const realtype * /*k*/, const realtype * /*h*/, - const realtype * /*w*/) { - throw AmiException("Requested functionality is not supported as %s " - "is not implemented for this model!", - __func__); // not implemented -} - -void Model_ODE::fdxdotdx_explicit_colptrs(SUNMatrixWrapper &/*dxdotdx*/) { - throw AmiException("Requested functionality is not supported as %s " - "is not implemented for this model!", - __func__); // not implemented -} - -void Model_ODE::fdxdotdx_explicit_rowvals(SUNMatrixWrapper &/*dxdotdx*/) { - throw AmiException("Requested functionality is not supported as %s " - "is not implemented for this model!", - __func__); // not implemented -} - -void Model_ODE::fdxdotdw(realtype * /*dxdotdw*/, const realtype /*t*/, - const realtype * /*x*/, const realtype * /*p*/, - const realtype * /*k*/, const realtype * /*h*/, - const realtype * /*w*/) { - throw AmiException("Requested functionality is not supported as %s " - "is not implemented for this model!", - __func__); // not implemented -} - -void Model_ODE::fdxdotdw_colptrs(SUNMatrixWrapper &/*dxdotdw*/) { - throw AmiException("Requested functionality is not supported as %s " - "is not implemented for this model!", - __func__); // not implemented -} - -void Model_ODE::fdxdotdw_rowvals(SUNMatrixWrapper &/*dxdotdw*/) { - throw AmiException("Requested functionality is not supported as %s " - "is not implemented for this model!", - __func__); // not implemented -} - -void Model_ODE::fJB(const realtype t, realtype /*cj*/, const AmiVector &x, - const AmiVector & /*dx*/, const AmiVector &xB, - const AmiVector & /*dxB*/, const AmiVector &xBdot, - SUNMatrix JB) { +void Model_ODE::fJSparse( + SUNMatrixContent_Sparse /*JSparse*/, const realtype /*t*/, + realtype const* /*x*/, realtype const* /*p*/, realtype const* /*k*/, + realtype const* /*h*/, realtype const* /*w*/, realtype const* /*dwdx*/ +) { + throw AmiException( + "Requested functionality is not supported as %s " + "is not implemented for this model!", + __func__ + ); // not implemented +} + +void Model_ODE::fJSparse( + realtype* /*JSparse*/, const realtype /*t*/, realtype const* /*x*/, + realtype const* /*p*/, realtype const* /*k*/, realtype const* /*h*/, + realtype const* /*w*/, realtype const* /*dwdx*/ +) { + throw AmiException( + "Requested functionality is not supported as %s " + "is not implemented for this model!", + __func__ + ); // not implemented +} + +void Model_ODE::fJSparse_colptrs(SUNMatrixWrapper& /*JSparse*/) { + throw AmiException( + "Requested functionality is not supported as %s " + "is not implemented for this model!", + __func__ + ); // not implemented +} + +void Model_ODE::fJSparse_rowvals(SUNMatrixWrapper& /*JSparse*/) { + throw AmiException( + "Requested functionality is not supported as %s " + "is not implemented for this model!", + __func__ + ); // not implemented +} + +void Model_ODE::froot( + realtype* /*root*/, const realtype /*t*/, realtype const* /*x*/, + realtype const* /*p*/, realtype const* /*k*/, realtype const* /*h*/, + realtype const* /*tcl*/ +) { + throw AmiException( + "Requested functionality is not supported as %s is not " + "implemented for this model!", + __func__ + ); // not implemented +} + +void Model_ODE::fdxdotdp( + realtype* /*dxdotdp*/, const realtype /*t*/, realtype const* /*x*/, + realtype const* /*p*/, realtype const* /*k*/, realtype const* /*h*/, + int const /*ip*/, realtype const* /*w*/, realtype const* /*dwdp*/ +) { + throw AmiException( + "Requested functionality is not supported as %s " + "is not implemented for this model!", + __func__ + ); // not implemented +} + +void Model_ODE::fdxdotdp_explicit( + realtype* /*dxdotdp_explicit*/, const realtype /*t*/, realtype const* /*x*/, + realtype const* /*p*/, realtype const* /*k*/, realtype const* /*h*/, + realtype const* /*w*/ +) { + throw AmiException( + "Requested functionality is not supported as %s " + "is not implemented for this model!", + __func__ + ); // not implemented +} + +void Model_ODE::fdxdotdp_explicit_colptrs(SUNMatrixWrapper& /*dxdotdp*/) { + throw AmiException( + "Requested functionality is not supported as %s " + "is not implemented for this model!", + __func__ + ); // not implemented +} + +void Model_ODE::fdxdotdp_explicit_rowvals(SUNMatrixWrapper& /*dxdotdp*/) { + throw AmiException( + "Requested functionality is not supported as %s " + "is not implemented for this model!", + __func__ + ); // not implemented +} + +void Model_ODE::fdxdotdx_explicit( + realtype* /*dxdotdx_explicit*/, const realtype /*t*/, realtype const* /*x*/, + realtype const* /*p*/, realtype const* /*k*/, realtype const* /*h*/, + realtype const* /*w*/ +) { + throw AmiException( + "Requested functionality is not supported as %s " + "is not implemented for this model!", + __func__ + ); // not implemented +} + +void Model_ODE::fdxdotdx_explicit_colptrs(SUNMatrixWrapper& /*dxdotdx*/) { + throw AmiException( + "Requested functionality is not supported as %s " + "is not implemented for this model!", + __func__ + ); // not implemented +} + +void Model_ODE::fdxdotdx_explicit_rowvals(SUNMatrixWrapper& /*dxdotdx*/) { + throw AmiException( + "Requested functionality is not supported as %s " + "is not implemented for this model!", + __func__ + ); // not implemented +} + +void Model_ODE::fdxdotdw( + realtype* /*dxdotdw*/, const realtype /*t*/, realtype const* /*x*/, + realtype const* /*p*/, realtype const* /*k*/, realtype const* /*h*/, + realtype const* /*w*/ +) { + throw AmiException( + "Requested functionality is not supported as %s " + "is not implemented for this model!", + __func__ + ); // not implemented +} + +void Model_ODE::fdxdotdw_colptrs(SUNMatrixWrapper& /*dxdotdw*/) { + throw AmiException( + "Requested functionality is not supported as %s " + "is not implemented for this model!", + __func__ + ); // not implemented +} + +void Model_ODE::fdxdotdw_rowvals(SUNMatrixWrapper& /*dxdotdw*/) { + throw AmiException( + "Requested functionality is not supported as %s " + "is not implemented for this model!", + __func__ + ); // not implemented +} + +void Model_ODE::fJB( + const realtype t, realtype /*cj*/, AmiVector const& x, + AmiVector const& /*dx*/, AmiVector const& xB, AmiVector const& /*dxB*/, + AmiVector const& xBdot, SUNMatrix JB +) { fJB(t, x.getNVector(), xB.getNVector(), xBdot.getNVector(), JB); } -void Model_ODE::fJB(realtype t, const_N_Vector x, const_N_Vector /*xB*/, - const_N_Vector /*xBdot*/, SUNMatrix JB) { +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()); 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*/, const AmiVector &x, - const AmiVector & /*dx*/, const AmiVector &xB, - const AmiVector & /*dxB*/, const AmiVector &xBdot, - SUNMatrix JB) { +void Model_ODE::fJSparseB( + const realtype t, realtype /*cj*/, AmiVector const& x, + AmiVector const& /*dx*/, AmiVector const& xB, AmiVector const& /*dxB*/, + AmiVector const& xBdot, SUNMatrix JB +) { fJSparseB(t, x.getNVector(), xB.getNVector(), xBdot.getNVector(), JB); } -void Model_ODE::fJSparseB(realtype t, const_N_Vector x, const_N_Vector /*xB*/, - const_N_Vector /*xBdot*/, SUNMatrix JB) { +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()); derived_state_.J_.refresh(); auto JSparseB = SUNMatrixWrapper(JB); @@ -319,8 +387,10 @@ void Model_ODE::fJDiag(realtype t, N_Vector JDiag, const_N_Vector x) { derived_state_.J_.to_diag(JDiag); } -void Model_ODE::fJvB(const_N_Vector vB, N_Vector JvB, realtype t, const_N_Vector x, - const_N_Vector xB) { +void Model_ODE::fJvB( + const_N_Vector vB, N_Vector JvB, realtype t, const_N_Vector x, + const_N_Vector xB +) { N_VConst(0.0, JvB); fJSparseB(t, x, xB, nullptr, derived_state_.JB_.get()); derived_state_.JB_.refresh(); @@ -334,8 +404,9 @@ void Model_ODE::fxBdot(realtype t, N_Vector x, N_Vector xB, N_Vector xBdot) { derived_state_.JB_.multiply(xBdot, xB); } -void Model_ODE::fqBdot(realtype t, const_N_Vector x, const_N_Vector xB, - N_Vector qBdot) { +void Model_ODE::fqBdot( + realtype t, const_N_Vector x, const_N_Vector xB, N_Vector qBdot +) { /* initialize with zeros */ N_VConst(0.0, qBdot); fdxdotdp(t, x); @@ -348,26 +419,31 @@ void Model_ODE::fqBdot(realtype t, const_N_Vector x, const_N_Vector xB, /* was matlab generated */ for (int ip = 0; ip < nplist(); ip++) { for (int ix = 0; ix < nxtrue_solver; ix++) - NV_Ith_S(qBdot, ip * nJ) -= NV_Ith_S(xB, ix) - * derived_state_.dxdotdp.at(ix, ip); + NV_Ith_S(qBdot, ip * nJ) + -= NV_Ith_S(xB, ix) * derived_state_.dxdotdp.at(ix, ip); // second order part for (int iJ = 1; iJ < nJ; iJ++) for (int ix = 0; ix < nxtrue_solver; ix++) - NV_Ith_S(qBdot, ip * nJ + iJ) -= - NV_Ith_S(xB, ix) - * derived_state_.dxdotdp.at(ix + iJ * nxtrue_solver, ip) - + NV_Ith_S(xB, ix + iJ * nxtrue_solver) - * derived_state_.dxdotdp.at(ix, ip); + NV_Ith_S(qBdot, ip * nJ + iJ) + -= NV_Ith_S(xB, ix) + * derived_state_.dxdotdp.at( + ix + iJ * nxtrue_solver, ip + ) + + NV_Ith_S(xB, ix + iJ * nxtrue_solver) + * derived_state_.dxdotdp.at(ix, ip); } } } -void Model_ODE::fxBdot_ss(const realtype t, const AmiVector &xB, - const AmiVector & /*dx*/, AmiVector &xBdot) { +void Model_ODE::fxBdot_ss( + const realtype t, AmiVector const& xB, AmiVector const& /*dx*/, + AmiVector& xBdot +) { fxBdot_ss(t, xB.getNVector(), xBdot.getNVector()); } -void Model_ODE::fxBdot_ss(realtype /*t*/, const_N_Vector xB, N_Vector xBdot) const { +void Model_ODE::fxBdot_ss(realtype /*t*/, const_N_Vector xB, N_Vector xBdot) + const { /* Right hand side of the adjoint state for steady state computations. J is fixed (as x remains in steady state), so the RHS becomes simple. */ N_VConst(0.0, xBdot); @@ -386,27 +462,31 @@ void Model_ODE::fJSparseB_ss(SUNMatrix JB) { derived_state_.JB_.refresh(); } -void Model_ODE::writeSteadystateJB(const realtype t, realtype /*cj*/, - const AmiVector &x, const AmiVector & /*dx*/, - const AmiVector &xB, const AmiVector & /*dxB*/, - const AmiVector &xBdot) { +void Model_ODE::writeSteadystateJB( + const realtype 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()); + fJSparseB( + t, x.getNVector(), xB.getNVector(), xBdot.getNVector(), + derived_state_.JB_.get() + ); derived_state_.JB_.refresh(); /* Switch sign, as we integrate forward in time, not backward */ derived_state_.JB_.scale(-1); } -void Model_ODE::fsxdot(const realtype t, const AmiVector &x, - const AmiVector & /*dx*/, const int ip, - const AmiVector &sx, const AmiVector & /*sdx*/, - AmiVector &sxdot) { +void Model_ODE::fsxdot( + const realtype 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()); } -void Model_ODE::fsxdot(realtype t, const_N_Vector x, int ip, const_N_Vector sx, - N_Vector sxdot) { +void Model_ODE::fsxdot( + realtype t, const_N_Vector x, int ip, const_N_Vector sx, N_Vector sxdot +) { /* sxdot is just the total derivative d(xdot)dp, so we just call dxdotdp and copy the stuff over */ @@ -421,11 +501,12 @@ void Model_ODE::fsxdot(realtype t, const_N_Vector x, int ip, const_N_Vector sx, /* copy dxdotdp and the implicit version over */ // initialize N_VConst(0.0, sxdot); - realtype *sxdot_tmp = N_VGetArrayPointer(sxdot); + realtype* sxdot_tmp = N_VGetArrayPointer(sxdot); - derived_state_.dxdotdp_full.scatter(plist(ip), 1.0, nullptr, - gsl::make_span(sxdot_tmp, nx_solver), - 0, nullptr, 0); + derived_state_.dxdotdp_full.scatter( + plist(ip), 1.0, nullptr, gsl::make_span(sxdot_tmp, nx_solver), 0, + nullptr, 0 + ); } else { /* copy dxdotdp over */ diff --git a/deps/AMICI/src/model_state.cpp b/deps/AMICI/src/model_state.cpp index 913912ae7..b6d1d9c85 100644 --- a/deps/AMICI/src/model_state.cpp +++ b/deps/AMICI/src/model_state.cpp @@ -2,15 +2,23 @@ namespace amici { -ModelStateDerived::ModelStateDerived(const ModelDimensions &dim) - : J_(dim.nx_solver, dim.nx_solver, dim.nnz, CSC_MAT), - JB_(dim.nx_solver, dim.nx_solver, dim.nnz, CSC_MAT), - dxdotdw_(dim.nx_solver, dim.nw, dim.ndxdotdw, CSC_MAT), - w_(dim.nw), - x_rdata_(dim.nx_rdata, 0.0), - sx_rdata_(dim.nx_rdata, 0.0), - x_pos_tmp_(dim.nx_solver) -{} - +ModelStateDerived::ModelStateDerived(ModelDimensions const& dim) + : J_(dim.nx_solver, dim.nx_solver, dim.nnz, CSC_MAT) + , JB_(dim.nx_solver, dim.nx_solver, dim.nnz, CSC_MAT) + , dxdotdw_(dim.nx_solver, dim.nw, dim.ndxdotdw, CSC_MAT) + , dx_rdatadx_solver( + dim.nx_rdata, dim.nx_solver, dim.ndxrdatadxsolver, CSC_MAT + ) + , dx_rdatadtcl( + dim.nx_rdata, dim.nx_rdata - dim.nx_solver, dim.ndxrdatadtcl, CSC_MAT + ) + , dtotal_cldx_rdata( + dim.nx_rdata - dim.nx_solver, dim.nx_rdata, dim.ndtotal_cldx_rdata, + CSC_MAT + ) + , w_(dim.nw) + , x_rdata_(dim.nx_rdata, 0.0) + , sx_rdata_(dim.nx_rdata, 0.0) + , x_pos_tmp_(dim.nx_solver) {} } // namespace amici diff --git a/deps/AMICI/src/newton_solver.cpp b/deps/AMICI/src/newton_solver.cpp index b770f04d8..b8cbe8f34 100644 --- a/deps/AMICI/src/newton_solver.cpp +++ b/deps/AMICI/src/newton_solver.cpp @@ -1,27 +1,27 @@ #include "amici/newton_solver.h" -#include "amici/model.h" -#include "amici/solver.h" +#include +#include +#include -#include "sunlinsol/sunlinsol_klu.h" // sparse solver -#include "sunlinsol/sunlinsol_dense.h" // dense solver +#include // roundoffs +#include // dense solver +#include // sparse solver +#include #include #include -#include namespace amici { -NewtonSolver::NewtonSolver(realtype *t, AmiVector *x, Model *model) - : t_(t), model_(model), xdot_(model->nx_solver), x_(x), - dx_(model->nx_solver), xB_(model->nx_solver), dxB_(model->nx_solver) { -} - -/* ------------------------------------------------------------------------- */ +NewtonSolver::NewtonSolver(Model const& model) + : xdot_(model.nx_solver) + , x_(model.nx_solver) + , xB_(model.nJ * model.nx_solver) + , dxB_(model.nJ * model.nx_solver) {} -std::unique_ptr NewtonSolver::getSolver(realtype *t, AmiVector *x, - Solver &simulationSolver, - Model *model) { +std::unique_ptr +NewtonSolver::getSolver(Solver const& simulationSolver, Model const& model) { std::unique_ptr solver; @@ -29,7 +29,7 @@ std::unique_ptr NewtonSolver::getSolver(realtype *t, AmiVector *x, /* DIRECT SOLVERS */ case LinearSolver::dense: - solver.reset(new NewtonSolverDense(t, x, model)); + solver.reset(new NewtonSolverDense(model)); break; case LinearSolver::band: @@ -49,8 +49,7 @@ std::unique_ptr NewtonSolver::getSolver(realtype *t, AmiVector *x, throw NewtonFailure(AMICI_NOT_IMPLEMENTED, "getSolver"); case LinearSolver::SPBCG: - solver.reset(new NewtonSolverIterative(t, x, model)); - break; + throw NewtonFailure(AMICI_NOT_IMPLEMENTED, "getSolver"); case LinearSolver::SPTFQMR: throw NewtonFailure(AMICI_NOT_IMPLEMENTED, "getSolver"); @@ -59,313 +58,197 @@ std::unique_ptr NewtonSolver::getSolver(realtype *t, AmiVector *x, case LinearSolver::SuperLUMT: throw NewtonFailure(AMICI_NOT_IMPLEMENTED, "getSolver"); case LinearSolver::KLU: - solver.reset(new NewtonSolverSparse(t, x, model)); + solver.reset(new NewtonSolverSparse(model)); break; default: throw NewtonFailure(AMICI_NOT_IMPLEMENTED, "getSolver"); } - solver->atol_ = simulationSolver.getAbsoluteTolerance(); - solver->rtol_ = simulationSolver.getRelativeTolerance(); - solver->max_lin_steps_ = simulationSolver.getNewtonMaxLinearSteps(); - solver->max_steps = simulationSolver.getNewtonMaxSteps(); - solver->damping_factor_mode_ = simulationSolver.getNewtonDampingFactorMode(); - solver->damping_factor_lower_bound = - simulationSolver.getNewtonDampingFactorLowerBound(); - if (simulationSolver.getLinearSolver() == LinearSolver::SPBCG) - solver->num_lin_steps_.resize(simulationSolver.getNewtonMaxSteps(), 0); - return solver; } -/* ------------------------------------------------------------------------- */ - -void NewtonSolver::getStep(int ntry, int nnewt, AmiVector &delta) { - prepareLinearSystem(ntry, nnewt); +void NewtonSolver::getStep( + AmiVector& delta, Model& model, SimulationState const& state +) { + prepareLinearSystem(model, state); delta.minus(); solveLinearSystem(delta); } -/* ------------------------------------------------------------------------- */ +void NewtonSolver::computeNewtonSensis( + AmiVectorArray& sx, Model& model, SimulationState const& state +) { + prepareLinearSystem(model, state); + model.fdxdotdp(state.t, state.x, state.dx); -void NewtonSolver::computeNewtonSensis(AmiVectorArray &sx) { - prepareLinearSystem(0, -1); - model_->fdxdotdp(*t_, *x_, dx_); + if (model.logger && is_singular(model, state)) { + model.logger->log( + LogSeverity::warning, "NEWTON_JAC_SINGULAR", + "Jacobian is singular at steadystate, " + "sensitivities may be inaccurate." + ); + } - if (model_->pythonGenerated) { - for (int ip = 0; ip < model_->nplist(); ip++) { + if (model.pythonGenerated) { + for (int ip = 0; ip < model.nplist(); ip++) { N_VConst(0.0, sx.getNVector(ip)); - model_->get_dxdotdp_full().scatter(model_->plist(ip), -1.0, nullptr, - gsl::make_span(sx.getNVector(ip)), - 0, nullptr, 0); + model.get_dxdotdp_full().scatter( + model.plist(ip), -1.0, nullptr, + gsl::make_span(sx.getNVector(ip)), 0, nullptr, 0 + ); solveLinearSystem(sx[ip]); } } else { - for (int ip = 0; ip < model_->nplist(); ip++) { - for (int ix = 0; ix < model_->nx_solver; ix++) - sx.at(ix,ip) = -model_->get_dxdotdp().at(ix, ip); + for (int ip = 0; ip < model.nplist(); ip++) { + for (int ix = 0; ix < model.nx_solver; ix++) + sx.at(ix, ip) = -model.get_dxdotdp().at(ix, ip); solveLinearSystem(sx[ip]); } } } -/* ------------------------------------------------------------------------- */ -/* - Dense linear solver --------------------------------------------------- */ -/* ------------------------------------------------------------------------- */ - -/* Derived class for dense linear solver */ -NewtonSolverDense::NewtonSolverDense(realtype *t, AmiVector *x, Model *model) - : NewtonSolver(t, x, model), Jtmp_(model->nx_solver, model->nx_solver), - linsol_(SUNLinSol_Dense(x->getNVector(), Jtmp_.get())) { - int status = SUNLinSolInitialize_Dense(linsol_); - if(status != AMICI_SUCCESS) +NewtonSolverDense::NewtonSolverDense(Model const& model) + : NewtonSolver(model) + , Jtmp_(model.nx_solver, model.nx_solver) + , linsol_(SUNLinSol_Dense(x_.getNVector(), Jtmp_.get())) { + auto status = SUNLinSolInitialize_Dense(linsol_); + if (status != SUNLS_SUCCESS) throw NewtonFailure(status, "SUNLinSolInitialize_Dense"); } -/* ------------------------------------------------------------------------- */ - -void NewtonSolverDense::prepareLinearSystem(int /*ntry*/, int /*nnewt*/) { - model_->fJ(*t_, 0.0, *x_, dx_, xdot_, Jtmp_.get()); +void NewtonSolverDense::prepareLinearSystem( + Model& model, SimulationState const& state +) { + model.fJ(state.t, 0.0, state.x, state.dx, xdot_, Jtmp_.get()); Jtmp_.refresh(); - int status = SUNLinSolSetup_Dense(linsol_, Jtmp_.get()); - if(status != AMICI_SUCCESS) + auto status = SUNLinSolSetup_Dense(linsol_, Jtmp_.get()); + if (status != SUNLS_SUCCESS) throw NewtonFailure(status, "SUNLinSolSetup_Dense"); } -/* ------------------------------------------------------------------------- */ - -void NewtonSolverDense::prepareLinearSystemB(int /*ntry*/, int /*nnewt*/) { - model_->fJB(*t_, 0.0, *x_, dx_, xB_, dxB_, xdot_, Jtmp_.get()); +void NewtonSolverDense::prepareLinearSystemB( + Model& model, SimulationState const& state +) { + model.fJB(state.t, 0.0, state.x, state.dx, xB_, dxB_, xdot_, Jtmp_.get()); Jtmp_.refresh(); - int status = SUNLinSolSetup_Dense(linsol_, Jtmp_.get()); - if(status != AMICI_SUCCESS) + auto status = SUNLinSolSetup_Dense(linsol_, Jtmp_.get()); + if (status != SUNLS_SUCCESS) throw NewtonFailure(status, "SUNLinSolSetup_Dense"); } - -/* ------------------------------------------------------------------------- */ - -void NewtonSolverDense::solveLinearSystem(AmiVector &rhs) { - int status = SUNLinSolSolve_Dense(linsol_, Jtmp_.get(), - rhs.getNVector(), rhs.getNVector(), - 0.0); +void NewtonSolverDense::solveLinearSystem(AmiVector& rhs) { + auto status = SUNLinSolSolve_Dense( + linsol_, Jtmp_.get(), rhs.getNVector(), rhs.getNVector(), 0.0 + ); Jtmp_.refresh(); // last argument is tolerance and does not have any influence on result - if(status != AMICI_SUCCESS) + if (status != SUNLS_SUCCESS) throw NewtonFailure(status, "SUNLinSolSolve_Dense"); } -/* ------------------------------------------------------------------------- */ +void NewtonSolverDense::reinitialize(){ + /* dense solver does not need reinitialization */ +}; + +bool NewtonSolverDense::is_singular(Model& model, SimulationState const& state) + const { + // dense solver doesn't have any implementation for rcond/condest, so use + // sparse solver interface, not the most efficient solution, but who is + // concerned about speed and used the dense solver anyways ¯\_(ツ)_/¯ + NewtonSolverSparse sparse_solver(model); + sparse_solver.prepareLinearSystem(model, state); + return sparse_solver.is_singular(model, state); +} NewtonSolverDense::~NewtonSolverDense() { - if(linsol_) + if (linsol_) SUNLinSolFree_Dense(linsol_); } -/* ------------------------------------------------------------------------- */ -/* - Sparse linear solver -------------------------------------------------- */ -/* ------------------------------------------------------------------------- */ - -/* Derived class for sparse linear solver */ -NewtonSolverSparse::NewtonSolverSparse(realtype *t, AmiVector *x, Model *model) - : NewtonSolver(t, x, model), - Jtmp_(model->nx_solver, model->nx_solver, model->nnz, CSC_MAT), - linsol_(SUNKLU(x->getNVector(), Jtmp_.get())) { - int status = SUNLinSolInitialize_KLU(linsol_); - if(status != AMICI_SUCCESS) +NewtonSolverSparse::NewtonSolverSparse(Model const& model) + : NewtonSolver(model) + , Jtmp_(model.nx_solver, model.nx_solver, model.nnz, CSC_MAT) + , linsol_(SUNKLU(x_.getNVector(), Jtmp_.get())) { + auto status = SUNLinSolInitialize_KLU(linsol_); + if (status != SUNLS_SUCCESS) throw NewtonFailure(status, "SUNLinSolInitialize_KLU"); } -/* ------------------------------------------------------------------------- */ - -void NewtonSolverSparse::prepareLinearSystem(int /*ntry*/, int /*nnewt*/) { +void NewtonSolverSparse::prepareLinearSystem( + Model& model, SimulationState const& state +) { /* Get sparse Jacobian */ - model_->fJSparse(*t_, 0.0, *x_, dx_, xdot_, Jtmp_.get()); + model.fJSparse(state.t, 0.0, state.x, state.dx, xdot_, Jtmp_.get()); Jtmp_.refresh(); - int status = SUNLinSolSetup_KLU(linsol_, Jtmp_.get()); - if(status != AMICI_SUCCESS) + auto status = SUNLinSolSetup_KLU(linsol_, Jtmp_.get()); + if (status != SUNLS_SUCCESS) throw NewtonFailure(status, "SUNLinSolSetup_KLU"); } -/* ------------------------------------------------------------------------- */ - -void NewtonSolverSparse::prepareLinearSystemB(int /*ntry*/, int /*nnewt*/) { +void NewtonSolverSparse::prepareLinearSystemB( + Model& model, SimulationState const& state +) { /* Get sparse Jacobian */ - model_->fJSparseB(*t_, 0.0, *x_, dx_, xB_, dxB_, xdot_, Jtmp_.get()); + model.fJSparseB( + state.t, 0.0, state.x, state.dx, xB_, dxB_, xdot_, Jtmp_.get() + ); Jtmp_.refresh(); - int status = SUNLinSolSetup_KLU(linsol_, Jtmp_.get()); - if(status != AMICI_SUCCESS) + auto status = SUNLinSolSetup_KLU(linsol_, Jtmp_.get()); + if (status != SUNLS_SUCCESS) throw NewtonFailure(status, "SUNLinSolSetup_KLU"); } -/* ------------------------------------------------------------------------- */ - -void NewtonSolverSparse::solveLinearSystem(AmiVector &rhs) { +void NewtonSolverSparse::solveLinearSystem(AmiVector& rhs) { /* Pass pointer to the linear solver */ - int status = SUNLinSolSolve_KLU(linsol_, Jtmp_.get(), - rhs.getNVector(), rhs.getNVector(), 0.0); + auto status = SUNLinSolSolve_KLU( + linsol_, Jtmp_.get(), rhs.getNVector(), rhs.getNVector(), 0.0 + ); // last argument is tolerance and does not have any influence on result - if(status != AMICI_SUCCESS) + if (status != SUNLS_SUCCESS) throw NewtonFailure(status, "SUNLinSolSolve_KLU"); } -/* ------------------------------------------------------------------------- */ - -NewtonSolverSparse::~NewtonSolverSparse() { - if(linsol_) - SUNLinSolFree_KLU(linsol_); -} - -/* ------------------------------------------------------------------------- */ -/* - Iterative linear solver------------------------------------------------ */ -/* ------------------------------------------------------------------------- */ - -NewtonSolverIterative::NewtonSolverIterative(realtype *t, AmiVector *x, - Model *model) - : NewtonSolver(t, x, model), ns_p_(model->nx_solver), - ns_h_(model->nx_solver), ns_t_(model->nx_solver), ns_s_(model->nx_solver), - ns_r_(model->nx_solver), ns_rt_(model->nx_solver), ns_v_(model->nx_solver), - ns_Jv_(model->nx_solver), ns_tmp_(model->nx_solver), - ns_Jdiag_(model->nx_solver), ns_J_(model->nx_solver, model->nx_solver) - { -} - -/* ------------------------------------------------------------------------- */ - -void NewtonSolverIterative::prepareLinearSystem(int ntry, int nnewt) { - newton_try_ = ntry; - i_newton_ = nnewt; - if (nnewt == -1) { - throw NewtonFailure(AMICI_NOT_IMPLEMENTED, - "Linear solver SPBCG does not support sensitivity " - "computation for steady state problems."); +void NewtonSolverSparse::reinitialize() { + /* partial reinitialization, don't need to reallocate Jtmp_ */ + auto status = SUNLinSol_KLUReInit( + linsol_, Jtmp_.get(), Jtmp_.capacity(), SUNKLU_REINIT_PARTIAL + ); + if (status != SUNLS_SUCCESS) + throw NewtonFailure(status, "SUNLinSol_KLUReInit"); +} + +bool NewtonSolverSparse:: + is_singular(Model& /*model*/, SimulationState const& /*state*/) const { + // adapted from SUNLinSolSetup_KLU in sunlinsol/klu/sunlinsol_klu.c + auto content = (SUNLinearSolverContent_KLU)(linsol_->content); + // first cheap check via rcond + auto status + = sun_klu_rcond(content->symbolic, content->numeric, &content->common); + if (status == 0) + throw NewtonFailure(content->last_flag, "sun_klu_rcond"); + + auto precision = std::numeric_limits::epsilon(); + + if (content->common.rcond < precision) { + // cheap check indicates singular, expensive check via condest + status = sun_klu_condest( + SM_INDEXPTRS_S(Jtmp_.get()), SM_DATA_S(Jtmp_.get()), + content->symbolic, content->numeric, &content->common + ); + if (status == 0) + throw NewtonFailure(content->last_flag, "sun_klu_rcond"); + return content->common.condest > 1.0 / precision; } - - // Get the Jacobian and its diagonal for preconditioning - model_->fJ(*t_, 0.0, *x_, dx_, xdot_, ns_J_.get()); - ns_J_.refresh(); - model_->fJDiag(*t_, ns_Jdiag_, 0.0, *x_, dx_); - - // Ensure positivity of entries in ns_Jdiag - ns_p_.set(1.0); - ns_Jdiag_.abs(); - N_VCompare(1e-15, ns_Jdiag_.getNVector(), ns_tmp_.getNVector()); - linearSum(-1.0, ns_tmp_, 1.0, ns_p_, ns_tmp_); - linearSum(1.0, ns_Jdiag_, 1.0, ns_tmp_, ns_Jdiag_); + return false; } -/* ------------------------------------------------------------------------- */ - -void NewtonSolverIterative::prepareLinearSystemB(int ntry, int nnewt) { - newton_try_ = ntry; - i_newton_ = nnewt; - if (nnewt == -1) { - throw AmiException("Linear solver SPBCG does not support sensitivity " - "computation for steady state problems."); - } - - // Get the Jacobian and its diagonal for preconditioning - model_->fJB(*t_, 0.0, *x_, dx_, xB_, dxB_, xdot_, ns_J_.get()); - ns_J_.refresh(); - // Get the diagonal and ensure negativity of entries is ns_J. Note that diag(JB) = -diag(J). - model_->fJDiag(*t_, ns_Jdiag_, 0.0, *x_, dx_); - - ns_p_.set(1.0); - ns_Jdiag_.abs(); - N_VCompare(1e-15, ns_Jdiag_.getNVector(), ns_tmp_.getNVector()); - linearSum(-1.0, ns_tmp_, 1.0, ns_p_, ns_tmp_); - linearSum(1.0, ns_Jdiag_, 1.0, ns_tmp_, ns_Jdiag_); - ns_Jdiag_.minus(); -} - -/* ------------------------------------------------------------------------- */ - -void NewtonSolverIterative::solveLinearSystem(AmiVector &rhs) { - linsolveSPBCG(newton_try_, i_newton_, rhs); - rhs.minus(); -} - -/* ------------------------------------------------------------------------- */ - -void NewtonSolverIterative::linsolveSPBCG(int /*ntry*/, int nnewt, - AmiVector &ns_delta) { - xdot_ = ns_delta; - xdot_.minus(); - - // Initialize for linear solve - ns_p_.zero(); - ns_v_.zero(); - ns_delta.zero(); - ns_tmp_.zero(); - double rho = 1.0; - double omega = 1.0; - double alpha = 1.0; - - ns_J_.multiply(ns_Jv_, ns_delta); - - // ns_r = xdot - ns_Jv; - linearSum(-1.0, ns_Jv_, 1.0, xdot_, ns_r_); - ns_r_ /= ns_Jdiag_; - ns_rt_ = ns_r_; - - for (int i_linstep = 0; i_linstep < max_lin_steps_; i_linstep++) { - // Compute factors - double rho1 = rho; - rho = dotProd(ns_rt_, ns_r_); - double beta = rho * alpha / (rho1 * omega); - - // ns_p = ns_r + beta * (ns_p - omega * ns_v); - linearSum(1.0, ns_p_, -omega, ns_v_, ns_p_); - linearSum(1.0, ns_r_, beta, ns_p_, ns_p_); - - // ns_v = J * ns_p - ns_v_.zero(); - ns_J_.multiply(ns_v_, ns_p_); - ns_v_ /= ns_Jdiag_; - - // Compute factor - alpha = rho / dotProd(ns_rt_, ns_v_); - - // ns_h = ns_delta + alpha * ns_p; - linearSum(1.0, ns_delta, alpha, ns_p_, ns_h_); - // ns_s = ns_r - alpha * ns_v; - linearSum(1.0, ns_r_, -alpha, ns_v_, ns_s_); - - // ns_t = J * ns_s - ns_t_.zero(); - ns_J_.multiply(ns_t_, ns_s_); - ns_t_ /= ns_Jdiag_; - - // Compute factor - omega = dotProd(ns_t_, ns_s_) / dotProd(ns_t_, ns_t_); - - // ns_delta = ns_h + omega * ns_s; - linearSum(1.0, ns_h_, omega, ns_s_, ns_delta); - // ns_r = ns_s - omega * ns_t; - linearSum(1.0, ns_s_, -omega, ns_t_, ns_r_); - - // Compute the (unscaled) residual - ns_r_ *= ns_Jdiag_; - double res = sqrt(dotProd(ns_r_, ns_r_)); - - // Test convergence - if (res < atol_) { - // Write number of steps needed - num_lin_steps_.at(nnewt) = i_linstep + 1; - - // Return success - return; - } - - // Scale back - ns_r_ /= ns_Jdiag_; - } - throw NewtonFailure(AMICI_CONV_FAILURE, "linsolveSPBCG"); +NewtonSolverSparse::~NewtonSolverSparse() { + if (linsol_) + SUNLinSolFree_KLU(linsol_); } - } // namespace amici diff --git a/deps/AMICI/src/rdata.cpp b/deps/AMICI/src/rdata.cpp index d0fe49067..56fc0023c 100644 --- a/deps/AMICI/src/rdata.cpp +++ b/deps/AMICI/src/rdata.cpp @@ -2,7 +2,6 @@ #include "amici/backwardproblem.h" #include "amici/edata.h" -#include "amici/exception.h" #include "amici/forwardproblem.h" #include "amici/misc.h" #include "amici/model.h" @@ -10,39 +9,49 @@ #include "amici/steadystateproblem.h" #include "amici/symbolic_functions.h" -#include #include +#include namespace amici { -ReturnData::ReturnData(Solver const &solver, const Model &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()) {} - -ReturnData::ReturnData(std::vector ts, - ModelDimensions const& model_dimensions, - int nplist, int nmaxevent, - int nt, int newton_maxsteps, - std::vector pscale, - SecondOrderMode o2mode, SensitivityOrder sensi, - SensitivityMethod sensi_meth, RDataReporting rdrm, - bool quadratic_llh, bool sigma_res, - realtype sigma_offset) - : ModelDimensions(model_dimensions), ts(std::move(ts)), nx(nx_rdata), - nxtrue(nxtrue_rdata), nplist(nplist), - nmaxevent(nmaxevent), nt(nt), newton_maxsteps(newton_maxsteps), - pscale(std::move(pscale)), o2mode(o2mode), sensi(sensi), - sensi_meth(sensi_meth), rdata_reporting(rdrm), sigma_res(sigma_res), - sigma_offset(sigma_offset), x_solver_(nx_solver), - sx_solver_(nx_solver, nplist), x_rdata_(nx), sx_rdata_(nx, nplist), - nroots_(ne) { +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() + ) {} + +ReturnData::ReturnData( + std::vector ts, ModelDimensions const& model_dimensions, + int nplist, int nmaxevent, int nt, int newton_maxsteps, + std::vector pscale, SecondOrderMode o2mode, + SensitivityOrder sensi, SensitivityMethod sensi_meth, RDataReporting rdrm, + bool quadratic_llh, bool sigma_res, realtype sigma_offset +) + : ModelDimensions(model_dimensions) + , ts(std::move(ts)) + , nx(nx_rdata) + , nxtrue(nxtrue_rdata) + , nplist(nplist) + , nmaxevent(nmaxevent) + , nt(nt) + , newton_maxsteps(newton_maxsteps) + , pscale(std::move(pscale)) + , o2mode(o2mode) + , sensi(sensi) + , sensi_meth(sensi_meth) + , rdata_reporting(rdrm) + , sigma_res(sigma_res) + , sigma_offset(sigma_offset) + , x_solver_(nx_solver) + , sx_solver_(nx_solver, nplist) + , x_rdata_(nx) + , sx_rdata_(nx, nplist) + , nroots_(ne) { switch (rdata_reporting) { case RDataReporting::full: initializeFullReporting(quadratic_llh); @@ -66,8 +75,9 @@ void ReturnData::initializeLikelihoodReporting(bool enable_fim) { if (sensi >= SensitivityOrder::second) s2llh.resize(nplist * (nJ - 1), getNaN()); - if ((sensi_meth == SensitivityMethod::forward || - sensi >= SensitivityOrder::second) && enable_fim) + if ((sensi_meth == SensitivityMethod::forward + || sensi >= SensitivityOrder::second) + && enable_fim) FIM.resize(nplist * nplist, 0.0); } } @@ -78,8 +88,8 @@ void ReturnData::initializeResidualReporting(bool enable_res) { if (enable_res) res.resize((sigma_res ? 2 : 1) * nt * nytrue, 0.0); - if ((sensi_meth == SensitivityMethod::forward && - sensi >= SensitivityOrder::first) + if ((sensi_meth == SensitivityMethod::forward + && sensi >= SensitivityOrder::first) || sensi >= SensitivityOrder::second) { sy.resize(nt * ny * nplist, 0.0); @@ -118,8 +128,8 @@ void ReturnData::initializeFullReporting(bool quadratic_llh) { numnonlinsolvconvfails.resize(nt, 0); order.resize(nt, 0); - if (sensi_meth == SensitivityMethod::adjoint && - sensi >= SensitivityOrder::first) { + if (sensi_meth == SensitivityMethod::adjoint + && sensi >= SensitivityOrder::first) { numstepsB.resize(nt, 0); numrhsevalsB.resize(nt, 0); numerrtestfailsB.resize(nt, 0); @@ -134,28 +144,26 @@ void ReturnData::initializeFullReporting(bool quadratic_llh) { sx0.resize(nx * nplist, getNaN()); sx_ss.resize(nx * nplist, getNaN()); - if (sensi_meth == SensitivityMethod::forward || - sensi >= SensitivityOrder::second) { + if (sensi_meth == SensitivityMethod::forward + || sensi >= SensitivityOrder::second) { // for second order we can fill in from the augmented states sx.resize(nt * nx * nplist, 0.0); sz.resize(nmaxevent * nz * nplist, 0.0); srz.resize(nmaxevent * nz * nplist, 0.0); } - ssigmay.resize(nt * ny * nplist, 0.0); ssigmaz.resize(nmaxevent * nz * nplist, 0.0); - if (sensi >= SensitivityOrder::second && - sensi_meth == SensitivityMethod::forward) + if (sensi >= SensitivityOrder::second + && sensi_meth == SensitivityMethod::forward) s2rz.resize(nmaxevent * nztrue * nplist * nplist, 0.0); } } -void ReturnData::processSimulationObjects(SteadystateProblem const *preeq, - ForwardProblem const *fwd, - BackwardProblem const *bwd, - SteadystateProblem const *posteq, - Model &model, Solver const &solver, - ExpData const *edata) { +void ReturnData::processSimulationObjects( + SteadystateProblem const* preeq, ForwardProblem const* fwd, + BackwardProblem const* bwd, SteadystateProblem const* posteq, Model& model, + Solver const& solver, ExpData const* edata +) { ModelContext mc(&model); processSolver(solver); @@ -184,8 +192,9 @@ void ReturnData::processSimulationObjects(SteadystateProblem const *preeq, applyChainRuleFactorToSimulationResults(model); } -void ReturnData::processPreEquilibration(SteadystateProblem const &preeq, - Model &model) { +void ReturnData::processPreEquilibration( + SteadystateProblem const& preeq, Model& model +) { readSimulationState(preeq.getFinalSimulationState(), model); if (!x_ss.empty()) { @@ -193,11 +202,11 @@ void ReturnData::processPreEquilibration(SteadystateProblem const &preeq, writeSlice(x_rdata_, x_ss); } if (!sx_ss.empty() && sensi >= SensitivityOrder::first) { - model.fsx_rdata(sx_rdata_, sx_solver_); + model.fsx_rdata(sx_rdata_, sx_solver_, x_solver_); for (int ip = 0; ip < nplist; ip++) writeSlice(sx_rdata_[ip], slice(sx_ss, ip, nx)); } - /* Get cpu time for Newton solve in milliseconds */ + /* Get cpu time for pre-equilibration in milliseconds */ preeq_cpu_time = preeq.getCPUTime(); preeq_cpu_timeB = preeq.getCPUTimeB(); preeq_numstepsB = preeq.getNumStepsB(); @@ -207,14 +216,11 @@ void ReturnData::processPreEquilibration(SteadystateProblem const &preeq, preeq_t = preeq.getSteadyStateTime(); if (!preeq_numsteps.empty()) writeSlice(preeq.getNumSteps(), preeq_numsteps); - if (!preeq.getNumLinSteps().empty() && !preeq_numlinsteps.empty()) { - preeq_numlinsteps.resize(newton_maxsteps * 2, 0); - writeSlice(preeq.getNumLinSteps(), preeq_numlinsteps); - } } -void ReturnData::processPostEquilibration(SteadystateProblem const &posteq, - Model &model, ExpData const *edata) { +void ReturnData::processPostEquilibration( + SteadystateProblem const& posteq, Model& model, ExpData const* edata +) { for (int it = 0; it < nt; it++) { auto t = model.getTimepoint(it); if (std::isinf(t)) { @@ -232,14 +238,11 @@ void ReturnData::processPostEquilibration(SteadystateProblem const &posteq, posteq_t = posteq.getSteadyStateTime(); if (!posteq_numsteps.empty()) writeSlice(posteq.getNumSteps(), posteq_numsteps); - if (!posteq.getNumLinSteps().empty() && !posteq_numlinsteps.empty()) { - posteq_numlinsteps.resize(newton_maxsteps * 2, 0); - writeSlice(posteq.getNumLinSteps(), posteq_numlinsteps); - } } -void ReturnData::processForwardProblem(ForwardProblem const &fwd, Model &model, - ExpData const *edata) { +void ReturnData::processForwardProblem( + ForwardProblem const& fwd, Model& model, ExpData const* edata +) { if (edata) initializeObjectiveFunction(model.hasQuadraticLLH()); @@ -254,7 +257,7 @@ void ReturnData::processForwardProblem(ForwardProblem const &fwd, Model &model, } if (!sx0.empty()) { - model.fsx_rdata(sx_rdata_, sx_solver_); + model.fsx_rdata(sx_rdata_, sx_solver_, x_solver_); for (int ip = 0; ip < nplist; ip++) writeSlice(sx_rdata_[ip], slice(sx0, ip, nx)); } @@ -282,7 +285,7 @@ void ReturnData::processForwardProblem(ForwardProblem const &fwd, Model &model, } } -void ReturnData::getDataOutput(int it, Model &model, ExpData const *edata) { +void ReturnData::getDataOutput(int it, Model& model, ExpData const* edata) { if (!x.empty()) { model.fx_rdata(x_rdata_, x_solver_); writeSlice(x_rdata_, slice(x, it, nx)); @@ -303,44 +306,49 @@ void ReturnData::getDataOutput(int it, Model &model, ExpData const *edata) { if (sensi >= SensitivityOrder::first && nplist > 0) { - if (!ssigmay.empty()) - model.getObservableSigmaSensitivity(slice(ssigmay, it, nplist * ny), - it, edata); - if (sensi_meth == SensitivityMethod::forward) { getDataSensisFSA(it, model, edata); } else if (edata && !sllh.empty()) { model.addPartialObservableObjectiveSensitivity( - sllh, s2llh, it, x_solver_, *edata); + sllh, s2llh, it, x_solver_, *edata + ); } + + if (!ssigmay.empty()) + model.getObservableSigmaSensitivity( + slice(ssigmay, it, nplist * ny), slice(sy, it, nplist * ny), it, + edata + ); } } -void ReturnData::getDataSensisFSA(int it, Model &model, ExpData const *edata) { +void ReturnData::getDataSensisFSA(int it, Model& model, ExpData const* edata) { if (!sx.empty()) { - model.fsx_rdata(sx_rdata_, sx_solver_); + model.fsx_rdata(sx_rdata_, sx_solver_, x_solver_); for (int ip = 0; ip < nplist; ip++) { - writeSlice(sx_rdata_[ip], - slice(sx, it * nplist + ip, nx)); + writeSlice(sx_rdata_[ip], slice(sx, it * nplist + ip, nx)); } } if (!sy.empty()) { - model.getObservableSensitivity(slice(sy, it, nplist * ny), ts[it], - x_solver_, sx_solver_); + model.getObservableSensitivity( + slice(sy, it, nplist * ny), ts[it], x_solver_, sx_solver_ + ); } if (edata) { if (!sllh.empty()) - model.addObservableObjectiveSensitivity(sllh, s2llh, it, x_solver_, - sx_solver_, *edata); + model.addObservableObjectiveSensitivity( + sllh, s2llh, it, x_solver_, sx_solver_, *edata + ); fsres(it, model, *edata); fFIM(it, model, *edata); } } -void ReturnData::getEventOutput(realtype t, std::vector rootidx, - Model &model, ExpData const *edata) { +void ReturnData::getEventOutput( + realtype t, std::vector rootidx, Model& model, ExpData const* edata +) { for (int ie = 0; ie < ne; ie++) { if (rootidx.at(ie) != 1 || nroots_.at(ie) >= nmaxevent) @@ -353,22 +361,27 @@ void ReturnData::getEventOutput(realtype t, std::vector rootidx, then also get the root function value */ if (t == model.getTimepoint(nt - 1)) if (!rz.empty()) - model.getEventRegularization(slice(rz, nroots_.at(ie), nz), ie, - t, x_solver_); + model.getEventRegularization( + slice(rz, nroots_.at(ie), nz), ie, t, x_solver_ + ); if (edata) { if (!sigmaz.empty()) - model.getEventSigma(slice(sigmaz, nroots_.at(ie), nz), ie, - nroots_.at(ie), t, edata); + model.getEventSigma( + slice(sigmaz, nroots_.at(ie), nz), ie, nroots_.at(ie), t, + edata + ); if (!isNaN(llh)) - model.addEventObjective(llh, ie, nroots_.at(ie), t, x_solver_, - *edata); + model.addEventObjective( + llh, ie, nroots_.at(ie), t, x_solver_, *edata + ); /* if called from fillEvent at last timepoint, add regularization based on rz */ if (t == model.getTimepoint(nt - 1) && !isNaN(llh)) { model.addEventObjectiveRegularization( - llh, ie, nroots_.at(ie), t, x_solver_, *edata); + llh, ie, nroots_.at(ie), t, x_solver_, *edata + ); } } @@ -377,39 +390,45 @@ void ReturnData::getEventOutput(realtype t, std::vector rootidx, getEventSensisFSA(ie, t, model, edata); } else if (edata && !sllh.empty()) { model.addPartialEventObjectiveSensitivity( - sllh, s2llh, ie, nroots_.at(ie), t, x_solver_, *edata); + sllh, s2llh, ie, nroots_.at(ie), t, x_solver_, *edata + ); } } nroots_.at(ie)++; } } -void ReturnData::getEventSensisFSA(int ie, realtype t, Model &model, - ExpData const *edata) { +void ReturnData::getEventSensisFSA( + int ie, realtype t, Model& model, ExpData const* edata +) { if (t == model.getTimepoint(nt - 1)) { // call from fillEvent at last timepoint if (!sz.empty()) model.getUnobservedEventSensitivity( - slice(sz, nroots_.at(ie), nz * nplist), ie); + slice(sz, nroots_.at(ie), nz * nplist), ie + ); if (!srz.empty()) model.getEventRegularizationSensitivity( slice(srz, nroots_.at(ie), nz * nplist), ie, t, x_solver_, - sx_solver_); + sx_solver_ + ); } else if (!sz.empty()) { - model.getEventSensitivity(slice(sz, nroots_.at(ie), nz * nplist), ie, - t, x_solver_, sx_solver_); + model.getEventSensitivity( + slice(sz, nroots_.at(ie), nz * nplist), ie, t, x_solver_, sx_solver_ + ); } if (edata && !sllh.empty()) { - model.addEventObjectiveSensitivity(sllh, s2llh, ie, nroots_.at(ie), - t, x_solver_, sx_solver_, *edata); + model.addEventObjectiveSensitivity( + sllh, s2llh, ie, nroots_.at(ie), t, x_solver_, sx_solver_, *edata + ); } } -void ReturnData::processBackwardProblem(ForwardProblem const &fwd, - BackwardProblem const &bwd, - SteadystateProblem const *preeq, - Model &model) { +void ReturnData::processBackwardProblem( + ForwardProblem const& fwd, BackwardProblem const& bwd, + SteadystateProblem const* preeq, Model& model +) { if (sllh.empty()) return; readSimulationState(fwd.getInitialSimulationState(), model); @@ -429,17 +448,18 @@ void ReturnData::processBackwardProblem(ForwardProblem const &fwd, if (iJ == 0) { sllh.at(ip) -= llhS0[ip] + xQB[ip * model.nJ]; } else { - s2llh.at(iJ - 1 + ip * (model.nJ - 1)) -= - llhS0[ip + iJ * model.nplist()] + xQB[iJ + ip * model.nJ]; + s2llh.at(iJ - 1 + ip * (model.nJ - 1)) + -= llhS0[ip + iJ * model.nplist()] + + xQB[iJ + ip * model.nJ]; } } } } -void ReturnData::handleSx0Backward(const Model &model, - SteadystateProblem const &preeq, - std::vector &llhS0, - AmiVector &xQB) const { +void ReturnData::handleSx0Backward( + Model const& model, SteadystateProblem const& preeq, + std::vector& llhS0, AmiVector& xQB +) const { /* If preequilibration is run in adjoint mode, the scalar product of sx0 with its adjoint counterpart (see handleSx0Forward()) is not necessary: the actual simulation is "extended" by the preequilibration time. @@ -447,13 +467,13 @@ void ReturnData::handleSx0Backward(const Model &model, and so is the scalar product. Instead of the scalar product, the quadratures xQB from preequilibration contribute to the gradient (see example notebook on equilibration for further documentation). */ - const auto &xQBpreeq = preeq.getAdjointQuadrature(); + auto const& xQBpreeq = preeq.getAdjointQuadrature(); for (int ip = 0; ip < model.nplist(); ++ip) xQB[ip] += xQBpreeq.at(ip); /* We really need references here, as sx0 can be large... */ - const auto& sx0preeq = preeq.getStateSensitivity(); - const auto& xBpreeq = preeq.getAdjointState(); + auto const& sx0preeq = preeq.getStateSensitivity(); + auto const& xBpreeq = preeq.getAdjointState(); /* Add the contribution for sx0 from preequilibration. If backward * preequilibration was done by simulation due to a singular Jacobian, @@ -466,9 +486,9 @@ void ReturnData::handleSx0Backward(const Model &model, } } -void ReturnData::handleSx0Forward(const Model &model, - std::vector &llhS0, - AmiVector &xB) const { +void ReturnData::handleSx0Forward( + Model const& model, std::vector& llhS0, AmiVector& xB +) const { /* If preequilibration is run in forward mode or is not needed, then adjoint sensitivity analysis still needs the state sensitivities at t=0 (sx0), to compute the gradient. For each parameter, the scalar product of sx0 @@ -485,20 +505,24 @@ void ReturnData::handleSx0Forward(const Model &model, for (int ip = 0; ip < model.nplist(); ++ip) { llhS0[ip + iJ * model.nplist()] = 0.0; for (int ix = 0; ix < model.nxtrue_solver; ++ix) { - llhS0[ip + iJ * model.nplist()] += - xB[ix + iJ * model.nxtrue_solver] * sx_solver_.at(ix, ip) + - xB[ix] * sx_solver_.at(ix + iJ * model.nxtrue_solver, ip); + llhS0[ip + iJ * model.nplist()] + += xB[ix + iJ * model.nxtrue_solver] + * sx_solver_.at(ix, ip) + + xB[ix] + * sx_solver_.at( + ix + iJ * model.nxtrue_solver, ip + ); } } } } } -void ReturnData::processSolver(Solver const &solver) { +void ReturnData::processSolver(Solver const& solver) { cpu_time = solver.getCpuTime(); - const std::vector *tmp; + std::vector const* tmp; if (!numsteps.empty()) { tmp = &solver.getNumSteps(); @@ -546,13 +570,15 @@ void ReturnData::processSolver(Solver const &solver) { if (!numnonlinsolvconvfailsB.empty()) { tmp = &solver.getNumNonlinSolvConvFailsB(); - std::copy_n(tmp->cbegin(), tmp->size(), - numnonlinsolvconvfailsB.begin()); + std::copy_n( + tmp->cbegin(), tmp->size(), numnonlinsolvconvfailsB.begin() + ); } } -void ReturnData::readSimulationState(SimulationState const &state, - Model &model) { +void ReturnData::readSimulationState( + SimulationState const& state, Model& model +) { x_solver_ = state.x; dx_solver_ = state.dx; if (computingFSA() || state.t == model.t0()) @@ -561,7 +587,7 @@ void ReturnData::readSimulationState(SimulationState const &state, model.setModelState(state.state); } -void ReturnData::invalidate(const int it_start) { +void ReturnData::invalidate(int const it_start) { if (it_start >= nt) return; @@ -593,14 +619,15 @@ void ReturnData::invalidateSLLH() { } } -void ReturnData::applyChainRuleFactorToSimulationResults(const Model &model) { +void ReturnData::applyChainRuleFactorToSimulationResults(Model const& model) { // chain-rule factor: multiplier for am_p std::vector coefficient(nplist, 1.0); std::vector pcoefficient(nplist, 1.0); std::vector unscaledParameters = model.getParameters(); - unscaleParameters(unscaledParameters, model.getParameterScale(), - unscaledParameters); + unscaleParameters( + unscaledParameters, model.getParameterScale(), unscaledParameters + ); std::vector augcoefficient(np, 1.0); @@ -623,8 +650,8 @@ void ReturnData::applyChainRuleFactorToSimulationResults(const Model &model) { switch (pscale[model.plist(ip)]) { case ParameterScaling::log10: coefficient.at(ip) = log(10.0); - pcoefficient.at(ip) = - unscaledParameters.at(model.plist(ip)) * log(10); + pcoefficient.at(ip) + = unscaledParameters.at(model.plist(ip)) * log(10); break; case ParameterScaling::ln: pcoefficient.at(ip) = unscaledParameters.at(model.plist(ip)); @@ -637,56 +664,52 @@ void ReturnData::applyChainRuleFactorToSimulationResults(const Model &model) { if (sensi >= SensitivityOrder::first) { // recover first order sensitivities from states for adjoint sensitivity // analysis - if (sensi == SensitivityOrder::second - && o2mode == SecondOrderMode::full + if (sensi == SensitivityOrder::second && o2mode == SecondOrderMode::full && sensi_meth == SensitivityMethod::adjoint) { if (!sx.empty() && !x.empty()) for (int ip = 0; ip < nplist; ++ip) for (int ix = 0; ix < nxtrue; ++ix) for (int it = 0; it < nt; ++it) - sx.at(ix + nxtrue * (ip + it * nplist)) = - x.at(it * nx + nxtrue + ip * nxtrue + ix); + sx.at(ix + nxtrue * (ip + it * nplist)) + = x.at(it * nx + nxtrue + ip * nxtrue + ix); if (!sy.empty() && !y.empty()) for (int ip = 0; ip < nplist; ++ip) for (int iy = 0; iy < nytrue; ++iy) for (int it = 0; it < nt; ++it) - sy.at(iy + nytrue * (ip + it * nplist)) = - y.at(it * ny + nytrue + ip * nytrue + iy); + sy.at(iy + nytrue * (ip + it * nplist)) + = y.at(it * ny + nytrue + ip * nytrue + iy); if (!sz.empty() && !z.empty()) for (int ip = 0; ip < nplist; ++ip) for (int iz = 0; iz < nztrue; ++iz) for (int it = 0; it < nt; ++it) - sz.at(iz + nztrue * (ip + it * nplist)) = - z.at(it * nz + nztrue + ip * nztrue + iz); - + sz.at(iz + nztrue * (ip + it * nplist)) + = z.at(it * nz + nztrue + ip * nztrue + iz); } if (!sllh.empty()) for (int ip = 0; ip < nplist; ++ip) sllh.at(ip) *= pcoefficient.at(ip); - if (!sres.empty()) - for (int ires = 0; ires < static_cast(res.size()); ++ires) + for (int ires = 0; ires < gsl::narrow(res.size()); ++ires) for (int ip = 0; ip < nplist; ++ip) sres.at((ires * nplist + ip)) *= pcoefficient.at(ip); - - if(!FIM.empty()) + if (!FIM.empty()) for (int ip = 0; ip < nplist; ++ip) for (int jp = 0; jp < nplist; ++jp) - FIM.at(jp + ip * nplist) *= - pcoefficient.at(ip)*pcoefficient.at(jp); + FIM.at(jp + ip * nplist) + *= pcoefficient.at(ip) * pcoefficient.at(jp); #define chainRule(QUANT, IND1, N1T, N1, IND2, N2) \ if (!s##QUANT.empty()) \ 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)) *= \ - pcoefficient.at(ip); \ + s##QUANT.at(((IND2)*nplist + ip) * (N1) + (IND1)) \ + *= pcoefficient.at(ip); \ } chainRule(x, ix, nxtrue, nx, it, nt); @@ -702,11 +725,11 @@ void ReturnData::applyChainRuleFactorToSimulationResults(const Model &model) { if (!s2llh.empty() && !sllh.empty()) { for (int ip = 0; ip < nplist; ++ip) { for (int iJ = 1; iJ < nJ; ++iJ) { - s2llh[ip * nplist + (iJ - 1)] *= - pcoefficient.at(ip) * augcoefficient[iJ - 1]; + s2llh[ip * nplist + (iJ - 1)] + *= pcoefficient.at(ip) * augcoefficient[iJ - 1]; if (model.plist(ip) == iJ - 1) - s2llh[ip * nplist + (iJ - 1)] += - sllh.at(ip) * coefficient.at(ip); + s2llh[ip * nplist + (iJ - 1)] + += sllh.at(ip) * coefficient.at(ip); } } } @@ -717,15 +740,14 @@ void ReturnData::applyChainRuleFactorToSimulationResults(const Model &model) { for (int iJ = 1; iJ < nJ; ++iJ) \ for (int IND1 = 0; IND1 < N1T; ++IND1) \ for (int IND2 = 0; IND2 < N2; ++IND2) { \ - s##QUANT.at((IND2 * nplist + ip) * N1 + IND1 + \ - iJ * N1T) *= \ - pcoefficient.at(ip) * augcoefficient[iJ - 1]; \ + s##QUANT.at( \ + (IND2 * nplist + ip) * N1 + IND1 + iJ * N1T \ + ) *= pcoefficient.at(ip) * augcoefficient[iJ - 1]; \ if (model.plist(ip) == iJ - 1) \ - s##QUANT.at((IND2 * nplist + ip) * N1 + IND1 + \ - iJ * N1T) += \ - s##QUANT.at((IND2 * nplist + ip) * N1 + \ - IND1) * \ - coefficient[ip]; \ + s##QUANT.at( \ + (IND2 * nplist + ip) * N1 + IND1 + iJ * N1T \ + ) += s##QUANT.at((IND2 * nplist + ip) * N1 + IND1) \ + * coefficient[ip]; \ } s2ChainRule(x, ix, nxtrue, nx, it, nt); @@ -739,8 +761,8 @@ void ReturnData::applyChainRuleFactorToSimulationResults(const Model &model) { if (o2mode == SecondOrderMode::directional) { // directional for (int ip = 0; ip < nplist; ++ip) { s2llh.at(ip) *= pcoefficient.at(ip); - s2llh.at(ip) += model.k()[nk - nplist + ip] * sllh.at(ip) / - unscaledParameters[model.plist(ip)]; + s2llh.at(ip) += model.k()[nk - nplist + ip] * sllh.at(ip) + / unscaledParameters[model.plist(ip)]; } #define s2vecChainRule(QUANT, IND1, N1T, N1, IND2, N2) \ @@ -748,12 +770,12 @@ void ReturnData::applyChainRuleFactorToSimulationResults(const Model &model) { for (int ip = 0; ip < nplist; ++ip) \ for (int IND1 = 0; IND1 < N1T; ++IND1) \ for (int IND2 = 0; IND2 < N2; ++IND2) { \ - s##QUANT.at((IND2 * nplist + ip) * N1 + IND1 + N1T) *= \ - pcoefficient.at(ip); \ - s##QUANT.at((IND2 * nplist + ip) * N1 + IND1 + N1T) += \ - model.k()[nk - nplist + ip] * \ - s##QUANT.at((IND2 * nplist + ip) * N1 + IND1) / \ - unscaledParameters[model.plist(ip)]; \ + s##QUANT.at((IND2 * nplist + ip) * N1 + IND1 + N1T) \ + *= pcoefficient.at(ip); \ + s##QUANT.at((IND2 * nplist + ip) * N1 + IND1 + N1T) \ + += model.k()[nk - nplist + ip] \ + * s##QUANT.at((IND2 * nplist + ip) * N1 + IND1) \ + / unscaledParameters[model.plist(ip)]; \ } s2vecChainRule(x, ix, nxtrue, nx, it, nt); @@ -766,36 +788,37 @@ void ReturnData::applyChainRuleFactorToSimulationResults(const Model &model) { } void ReturnData::initializeObjectiveFunction(bool enable_chi2) { - if (rdata_reporting == RDataReporting::likelihood || - rdata_reporting == RDataReporting::full) { + if (rdata_reporting == RDataReporting::likelihood + || rdata_reporting == RDataReporting::full) { llh = 0.0; std::fill(sllh.begin(), sllh.end(), 0.0); std::fill(s2llh.begin(), s2llh.end(), 0.0); } - if ((rdata_reporting == RDataReporting::residuals || - rdata_reporting == RDataReporting::full) && enable_chi2) + if ((rdata_reporting == RDataReporting::residuals + || rdata_reporting == RDataReporting::full) + && enable_chi2) chi2 = 0.0; } -static realtype fres(realtype y, realtype my, realtype sigma_y, - ObservableScaling scale) { +static realtype +fres(realtype y, realtype my, realtype sigma_y, ObservableScaling scale) { switch (scale) { - case amici::ObservableScaling::lin: - return (y - my) / sigma_y; - case amici::ObservableScaling::log: - return (std::log(y) - std::log(my)) / sigma_y; - case amici::ObservableScaling::log10: - return (std::log10(y) - std::log10(my)) / sigma_y; - default: - throw std::invalid_argument("only lin, log, log10 allowed."); + case amici::ObservableScaling::lin: + return (y - my) / sigma_y; + case amici::ObservableScaling::log: + return (std::log(y) - std::log(my)) / sigma_y; + case amici::ObservableScaling::log10: + return (std::log10(y) - std::log10(my)) / sigma_y; + default: + throw std::invalid_argument("only lin, log, log10 allowed."); } } static realtype fres_error(realtype sigma_y, realtype sigma_offset) { - return sqrt(2*log(sigma_y) + sigma_offset); + return sqrt(2 * log(sigma_y) + sigma_offset); } -void ReturnData::fres(const int it, Model &model, const ExpData &edata) { +void ReturnData::fres(int const it, Model& model, ExpData const& edata) { if (res.empty()) return; @@ -811,17 +834,18 @@ void ReturnData::fres(const int it, Model &model, const ExpData &edata) { if (!edata.isSetObservedData(it, iy)) continue; - res.at(iyt) = amici::fres(y_it.at(iy), observedData[iy], - sigmay_it.at(iy), - model.getObservableScaling(iy)); + res.at(iyt) = amici::fres( + y_it.at(iy), observedData[iy], sigmay_it.at(iy), + model.getObservableScaling(iy) + ); if (sigma_res) - res.at(iyt + nt * nytrue) = fres_error(sigmay_it.at(iy), - sigma_offset); + res.at(iyt + nt * nytrue) + = fres_error(sigmay_it.at(iy), sigma_offset); } } -void ReturnData::fchi2(const int it, const ExpData &edata) { +void ReturnData::fchi2(int const it, ExpData const& edata) { if (res.empty() || isNaN(chi2)) return; @@ -833,28 +857,28 @@ void ReturnData::fchi2(const int it, const ExpData &edata) { } } -static realtype fsres(realtype y, realtype sy, realtype my, - realtype sigma_y, realtype ssigma_y, - ObservableScaling scale) { +static realtype fsres( + realtype y, realtype sy, realtype my, realtype sigma_y, realtype ssigma_y, + ObservableScaling scale +) { auto res = fres(y, my, sigma_y, scale); switch (scale) { - case amici::ObservableScaling::lin: - return (sy - ssigma_y * res) / sigma_y; - case amici::ObservableScaling::log: - return (sy / y - ssigma_y * res) / sigma_y; - case amici::ObservableScaling::log10: - return (sy / (y * std::log(10)) - ssigma_y * res) / sigma_y; - default: - throw std::invalid_argument("only lin, log, log10 allowed."); + case amici::ObservableScaling::lin: + return (sy - ssigma_y * res) / sigma_y; + case amici::ObservableScaling::log: + return (sy / y - ssigma_y * res) / sigma_y; + case amici::ObservableScaling::log10: + return (sy / (y * std::log(10)) - ssigma_y * res) / sigma_y; + default: + throw std::invalid_argument("only lin, log, log10 allowed."); } } -static realtype fsres_error(realtype sigma_y, realtype ssigma_y, - realtype sigma_offset) { - return ssigma_y / ( fres_error(sigma_y, sigma_offset) * sigma_y); +static realtype +fsres_error(realtype sigma_y, realtype ssigma_y, realtype sigma_offset) { + return ssigma_y / (fres_error(sigma_y, sigma_offset) * sigma_y); } - -void ReturnData::fsres(const int it, Model &model, const ExpData &edata) { +void ReturnData::fsres(int const it, Model& model, ExpData const& edata) { if (sres.empty()) return; @@ -866,7 +890,7 @@ void ReturnData::fsres(const int it, Model &model, const ExpData &edata) { std::vector sigmay_it(ny, 0.0); model.getObservableSigma(sigmay_it, it, &edata); std::vector ssigmay_it(ny * nplist, 0.0); - model.getObservableSigmaSensitivity(ssigmay_it, it, &edata); + model.getObservableSigmaSensitivity(ssigmay_it, sy_it, it, &edata); auto observedData = edata.getObservedDataPtr(it); for (int iy = 0; iy < nytrue; ++iy) { @@ -875,24 +899,26 @@ void ReturnData::fsres(const int it, Model &model, const ExpData &edata) { for (int ip = 0; ip < nplist; ++ip) { int idx = (iy + it * edata.nytrue()) * nplist + ip; - sres.at(idx) = amici::fsres(y_it.at(iy), sy_it.at(iy + ny * ip), - observedData[iy], sigmay_it.at(iy), - ssigmay_it.at(iy + ny * ip), - model.getObservableScaling(iy)); + sres.at(idx) = amici::fsres( + y_it.at(iy), sy_it.at(iy + ny * ip), observedData[iy], + sigmay_it.at(iy), ssigmay_it.at(iy + ny * ip), + model.getObservableScaling(iy) + ); if (sigma_res) { - int idx_res = - (iy + it * edata.nytrue() + edata.nytrue() * edata.nt()) * - nplist + ip; - sres.at(idx_res) = amici::fsres_error(sigmay_it.at(iy), - ssigmay_it.at(iy + ny * ip), - sigma_offset); + int idx_res + = (iy + it * edata.nytrue() + edata.nytrue() * edata.nt()) + * nplist + + ip; + sres.at(idx_res) = amici::fsres_error( + sigmay_it.at(iy), ssigmay_it.at(iy + ny * ip), sigma_offset + ); } } } } -void ReturnData::fFIM(int it, Model &model, const ExpData &edata) { +void ReturnData::fFIM(int it, Model& model, ExpData const& edata) { if (FIM.empty()) return; @@ -904,7 +930,7 @@ void ReturnData::fFIM(int it, Model &model, const ExpData &edata) { std::vector sigmay_it(ny, 0.0); model.getObservableSigma(sigmay_it, it, &edata); std::vector ssigmay_it(ny * nplist, 0.0); - model.getObservableSigmaSensitivity(ssigmay_it, it, &edata); + model.getObservableSigmaSensitivity(ssigmay_it, sy_it, it, &edata); /* * https://www.wolframalpha.com/input/?i=d%2Fdu+d%2Fdv+log%28s%28u%2Cv%29%29+%2B+0.5+*+%28r%28u%2Cv%29%2Fs%28u%2Cv%29%29%5E2 @@ -980,7 +1006,7 @@ void ReturnData::fFIM(int it, Model &model, const ExpData &edata) { auto dy_j = sy_it.at(iy + ny * jp); auto ds_j = ssigmay_it.at(iy + ny * jp); auto sr_j = amici::fsres(y, dy_j, m, s, ds_j, os); - FIM.at(ip + nplist * jp) += sr_i*sr_j; + FIM.at(ip + nplist * jp) += sr_i * sr_j; if (sigma_res) { auto sre_j = amici::fsres_error(s, ds_j, sigma_offset); FIM.at(ip + nplist * jp) += sre_i * sre_j; @@ -991,8 +1017,9 @@ void ReturnData::fFIM(int it, Model &model, const ExpData &edata) { } } -ModelContext::ModelContext(Model *model) - : model_(model), original_state_(model->getModelState()) {} +ModelContext::ModelContext(Model* model) + : model_(model) + , original_state_(model->getModelState()) {} ModelContext::~ModelContext() { restore(); } diff --git a/deps/AMICI/src/returndata_matlab.cpp b/deps/AMICI/src/returndata_matlab.cpp index 0a1d79961..87dca5889 100644 --- a/deps/AMICI/src/returndata_matlab.cpp +++ b/deps/AMICI/src/returndata_matlab.cpp @@ -1,43 +1,26 @@ #include "amici/returndata_matlab.h" -#include "amici/exception.h" #include "amici/defines.h" +#include "amici/exception.h" namespace amici { -mxArray *getReturnDataMatlabFromAmiciCall(ReturnData const *rdata) { - mxArray *matlabSolutionStruct = initMatlabReturnFields(rdata); +mxArray* getReturnDataMatlabFromAmiciCall(ReturnData const* rdata) { + mxArray* matlabSolutionStruct = initMatlabReturnFields(rdata); return matlabSolutionStruct; } -mxArray *initMatlabReturnFields(ReturnData const *rdata) { - const int numFields = 22; - const char *field_names_sol[numFields] = {"status", - "llh", - "sllh", - "s2llh", - "chi2", - "t", - "x", - "sx", - "y", - "sy", - "sigmay", - "ssigmay", - "z", - "sz", - "sigmaz", - "ssigmaz", - "rz", - "srz", - "s2rz", - "x0", - "sx0", - "diagnosis"}; - - checkFieldNames(field_names_sol,numFields); - - mxArray *matlabSolutionStruct = - mxCreateStructMatrix(1, 1, numFields, field_names_sol); +mxArray* initMatlabReturnFields(ReturnData const* rdata) { + int const numFields = 22; + char const* field_names_sol[numFields] + = {"status", "llh", "sllh", "s2llh", "chi2", "t", + "x", "sx", "y", "sy", "sigmay", "ssigmay", + "z", "sz", "sigmaz", "ssigmaz", "rz", "srz", + "s2rz", "x0", "sx0", "diagnosis"}; + + checkFieldNames(field_names_sol, numFields); + + mxArray* matlabSolutionStruct + = mxCreateStructMatrix(1, 1, numFields, field_names_sol); std::vector perm0 = {1, 0}; std::vector perm1 = {0, 1}; @@ -46,94 +29,143 @@ mxArray *initMatlabReturnFields(ReturnData const *rdata) { writeMatlabField0(matlabSolutionStruct, "status", rdata->status); - writeMatlabField1(matlabSolutionStruct, "t", gsl::make_span(rdata->ts), rdata->nt); + writeMatlabField1( + matlabSolutionStruct, "t", gsl::make_span(rdata->ts), rdata->nt + ); writeMatlabField0(matlabSolutionStruct, "llh", rdata->llh); writeMatlabField0(matlabSolutionStruct, "chi2", rdata->chi2); if ((rdata->nz > 0) & (rdata->ne > 0)) { - writeMatlabField2(matlabSolutionStruct, "z", rdata->z, rdata->nmaxevent, rdata->nz, perm1); - writeMatlabField2(matlabSolutionStruct, "rz", rdata->rz, rdata->nmaxevent, rdata->nz, perm1); - writeMatlabField2(matlabSolutionStruct, "sigmaz", rdata->sigmaz, rdata->nmaxevent, rdata->nz, perm1); + writeMatlabField2( + matlabSolutionStruct, "z", rdata->z, rdata->nmaxevent, rdata->nz, + perm1 + ); + writeMatlabField2( + matlabSolutionStruct, "rz", rdata->rz, rdata->nmaxevent, rdata->nz, + perm1 + ); + writeMatlabField2( + matlabSolutionStruct, "sigmaz", rdata->sigmaz, rdata->nmaxevent, + rdata->nz, perm1 + ); } if (rdata->nx > 0) { - writeMatlabField2(matlabSolutionStruct, "x", rdata->x, rdata->nt, rdata->nx, perm1); - writeMatlabField2(matlabSolutionStruct, "x0", rdata->x0, rdata->nx, 1, perm1); + writeMatlabField2( + matlabSolutionStruct, "x", rdata->x, rdata->nt, rdata->nx, perm1 + ); + writeMatlabField2( + matlabSolutionStruct, "x0", rdata->x0, rdata->nx, 1, perm1 + ); } if (rdata->ny > 0) { - writeMatlabField2(matlabSolutionStruct, "y", rdata->y, rdata->nt, rdata->ny, perm1); - writeMatlabField2(matlabSolutionStruct, "sigmay", rdata->sigmay, rdata->nt, rdata->ny, perm1); + writeMatlabField2( + matlabSolutionStruct, "y", rdata->y, rdata->nt, rdata->ny, perm1 + ); + writeMatlabField2( + matlabSolutionStruct, "sigmay", rdata->sigmay, rdata->nt, rdata->ny, + perm1 + ); } if (rdata->sensi >= SensitivityOrder::first) { - writeMatlabField1(matlabSolutionStruct, "sllh", gsl::make_span(rdata->sllh), rdata->nplist); - writeMatlabField2(matlabSolutionStruct, "sx0", rdata->sx0, rdata->nplist, rdata->nx, perm0); + writeMatlabField1( + matlabSolutionStruct, "sllh", gsl::make_span(rdata->sllh), + rdata->nplist + ); + writeMatlabField2( + matlabSolutionStruct, "sx0", rdata->sx0, rdata->nplist, rdata->nx, + perm0 + ); if (rdata->sensi_meth == SensitivityMethod::forward) { - writeMatlabField3(matlabSolutionStruct, "sx", rdata->sx, rdata->nt, rdata->nplist, rdata->nx, perm2); + writeMatlabField3( + matlabSolutionStruct, "sx", rdata->sx, rdata->nt, rdata->nplist, + rdata->nx, perm2 + ); if (rdata->ny > 0) { - writeMatlabField3(matlabSolutionStruct, "sy", rdata->sy, rdata->nt, rdata->nplist, rdata->ny, perm2); + writeMatlabField3( + matlabSolutionStruct, "sy", rdata->sy, rdata->nt, + rdata->nplist, rdata->ny, perm2 + ); } if ((rdata->nz > 0) & (rdata->ne > 0)) { - writeMatlabField3(matlabSolutionStruct, "srz", rdata->srz, rdata->nmaxevent, rdata->nplist, rdata->nz, perm2); + writeMatlabField3( + matlabSolutionStruct, "srz", rdata->srz, rdata->nmaxevent, + rdata->nplist, rdata->nz, perm2 + ); if (rdata->sensi >= SensitivityOrder::second) { - writeMatlabField4(matlabSolutionStruct, "s2rz", rdata->s2rz, rdata->nmaxevent, rdata->nplist, rdata->nztrue, - rdata->nplist, perm3); + writeMatlabField4( + matlabSolutionStruct, "s2rz", rdata->s2rz, + rdata->nmaxevent, rdata->nplist, rdata->nztrue, + rdata->nplist, perm3 + ); } - writeMatlabField3(matlabSolutionStruct, "sz", rdata->sz, rdata->nmaxevent, rdata->nplist, rdata->nz, perm2); + writeMatlabField3( + matlabSolutionStruct, "sz", rdata->sz, rdata->nmaxevent, + rdata->nplist, rdata->nz, perm2 + ); } } - if (rdata->ny > 0) { - writeMatlabField3(matlabSolutionStruct, "ssigmay", rdata->ssigmay, rdata->nt, rdata->nplist, rdata->ny, perm2); + if (!rdata->ssigmay.empty()) { + writeMatlabField3( + matlabSolutionStruct, "ssigmay", rdata->ssigmay, rdata->nt, + rdata->nplist, rdata->ny, perm2 + ); } if ((rdata->nz > 0) & (rdata->ne > 0)) { - writeMatlabField3(matlabSolutionStruct, "ssigmaz", rdata->ssigmaz, rdata->nmaxevent, rdata->nplist, rdata->nz, perm2); + writeMatlabField3( + matlabSolutionStruct, "ssigmaz", rdata->ssigmaz, + rdata->nmaxevent, rdata->nplist, rdata->nz, perm2 + ); } if (rdata->sensi >= SensitivityOrder::second) { - writeMatlabField2(matlabSolutionStruct, "s2llh", rdata->s2llh, rdata->nplist, rdata->nJ - 1, perm1); + writeMatlabField2( + matlabSolutionStruct, "s2llh", rdata->s2llh, rdata->nplist, + rdata->nJ - 1, perm1 + ); } } - mxArray *diagnosis = initMatlabDiagnosisFields(rdata); + mxArray* diagnosis = initMatlabDiagnosisFields(rdata); mxSetField(matlabSolutionStruct, 0, "diagnosis", diagnosis); - return(matlabSolutionStruct); + return (matlabSolutionStruct); } -mxArray *initMatlabDiagnosisFields(ReturnData const *rdata) { - const int numFields = 27; - const char *field_names_sol[numFields] = {"xdot", - "J", - "numsteps", - "numrhsevals", - "numerrtestfails", - "numnonlinsolvconvfails", - "order", - "numstepsB", - "numrhsevalsB", - "numerrtestfailsB", - "numnonlinsolvconvfailsB", - "preeq_status", - "preeq_numsteps", - "preeq_numstepsB", - "preeq_numlinsteps", - "preeq_cpu_time", - "preeq_cpu_timeB", - "preeq_t", - "preeq_wrms", - "posteq_status", - "posteq_numsteps", - "posteq_numstepsB", - "posteq_numlinsteps", - "posteq_cpu_time", - "posteq_cpu_timeB", - "posteq_t", - "posteq_wrms"}; - - checkFieldNames(field_names_sol,numFields); - - mxArray *matlabDiagnosisStruct = - mxCreateStructMatrix(1, 1, numFields, field_names_sol); +mxArray* initMatlabDiagnosisFields(ReturnData const* rdata) { + int const numFields = 25; + char const* field_names_sol[numFields] + = {"xdot", + "J", + "numsteps", + "numrhsevals", + "numerrtestfails", + "numnonlinsolvconvfails", + "order", + "numstepsB", + "numrhsevalsB", + "numerrtestfailsB", + "numnonlinsolvconvfailsB", + "preeq_status", + "preeq_numsteps", + "preeq_numstepsB", + "preeq_cpu_time", + "preeq_cpu_timeB", + "preeq_t", + "preeq_wrms", + "posteq_status", + "posteq_numsteps", + "posteq_numstepsB", + "posteq_cpu_time", + "posteq_cpu_timeB", + "posteq_t", + "posteq_wrms"}; + + checkFieldNames(field_names_sol, numFields); + + mxArray* matlabDiagnosisStruct + = mxCreateStructMatrix(1, 1, numFields, field_names_sol); std::vector perm1 = {0, 1}; int finite_nt = 0; @@ -143,219 +175,295 @@ mxArray *initMatlabDiagnosisFields(ReturnData const *rdata) { writeMatlabField1( matlabDiagnosisStruct, "numsteps", - gsl::make_span(rdata->numsteps).subspan(0, finite_nt), - finite_nt); + gsl::make_span(rdata->numsteps).subspan(0, finite_nt), finite_nt + ); writeMatlabField1( matlabDiagnosisStruct, "numrhsevals", - gsl::make_span(rdata->numrhsevals).subspan(0, finite_nt), - finite_nt); + gsl::make_span(rdata->numrhsevals).subspan(0, finite_nt), finite_nt + ); writeMatlabField1( matlabDiagnosisStruct, "numerrtestfails", - gsl::make_span(rdata->numerrtestfails).subspan(0, finite_nt), - finite_nt); + gsl::make_span(rdata->numerrtestfails).subspan(0, finite_nt), finite_nt + ); writeMatlabField1( matlabDiagnosisStruct, "numnonlinsolvconvfails", gsl::make_span(rdata->numnonlinsolvconvfails).subspan(0, finite_nt), - finite_nt); + finite_nt + ); writeMatlabField1( matlabDiagnosisStruct, "order", - gsl::make_span(rdata->order).subspan(0, finite_nt), - finite_nt); + gsl::make_span(rdata->order).subspan(0, finite_nt), finite_nt + ); if (rdata->nx > 0) { - writeMatlabField1(matlabDiagnosisStruct, "xdot", gsl::make_span(rdata->xdot), rdata->nx_solver); - writeMatlabField2(matlabDiagnosisStruct, "J", rdata->J, rdata->nx_solver, rdata->nx_solver, perm1); - - writeMatlabField1(matlabDiagnosisStruct, "preeq_status", gsl::make_span(rdata->preeq_status), 3); - writeMatlabField1(matlabDiagnosisStruct, "preeq_numsteps", gsl::make_span(rdata->preeq_numsteps), 3); - writeMatlabField2(matlabDiagnosisStruct, "preeq_numlinsteps", - rdata->preeq_numlinsteps, - rdata->preeq_numlinsteps.size() > 0 - ? rdata->newton_maxsteps : 0, 2, perm1); - writeMatlabField0(matlabDiagnosisStruct, "preeq_numstepsB", rdata->preeq_numstepsB); - writeMatlabField0(matlabDiagnosisStruct, "preeq_cpu_time", rdata->preeq_cpu_time); - writeMatlabField0(matlabDiagnosisStruct, "preeq_cpu_timeB", rdata->preeq_cpu_timeB); + writeMatlabField1( + matlabDiagnosisStruct, "xdot", gsl::make_span(rdata->xdot), + rdata->nx_solver + ); + writeMatlabField2( + matlabDiagnosisStruct, "J", rdata->J, rdata->nx_solver, + rdata->nx_solver, perm1 + ); + + writeMatlabField1( + matlabDiagnosisStruct, "preeq_status", + gsl::make_span(rdata->preeq_status), 3 + ); + writeMatlabField1( + matlabDiagnosisStruct, "preeq_numsteps", + gsl::make_span(rdata->preeq_numsteps), 3 + ); + writeMatlabField0( + matlabDiagnosisStruct, "preeq_numstepsB", rdata->preeq_numstepsB + ); + writeMatlabField0( + matlabDiagnosisStruct, "preeq_cpu_time", rdata->preeq_cpu_time + ); + writeMatlabField0( + matlabDiagnosisStruct, "preeq_cpu_timeB", rdata->preeq_cpu_timeB + ); writeMatlabField0(matlabDiagnosisStruct, "preeq_t", rdata->preeq_t); - writeMatlabField0(matlabDiagnosisStruct, "preeq_wrms", rdata->preeq_wrms); - - writeMatlabField1(matlabDiagnosisStruct, "posteq_status", gsl::make_span(rdata->posteq_status), 3); - writeMatlabField1(matlabDiagnosisStruct, "posteq_numsteps", gsl::make_span(rdata->posteq_numsteps), 3); - writeMatlabField2(matlabDiagnosisStruct, "posteq_numlinsteps", - rdata->posteq_numlinsteps, - rdata->posteq_numlinsteps.size() > 0 - ? rdata->newton_maxsteps : 0, 2, perm1); - writeMatlabField0(matlabDiagnosisStruct, "posteq_numstepsB", rdata->posteq_numstepsB); - writeMatlabField0(matlabDiagnosisStruct, "posteq_cpu_time", rdata->posteq_cpu_time); - writeMatlabField0(matlabDiagnosisStruct, "posteq_cpu_timeB", rdata->posteq_cpu_timeB); + writeMatlabField0( + matlabDiagnosisStruct, "preeq_wrms", rdata->preeq_wrms + ); + + writeMatlabField1( + matlabDiagnosisStruct, "posteq_status", + gsl::make_span(rdata->posteq_status), 3 + ); + writeMatlabField1( + matlabDiagnosisStruct, "posteq_numsteps", + gsl::make_span(rdata->posteq_numsteps), 3 + ); + writeMatlabField0( + matlabDiagnosisStruct, "posteq_numstepsB", rdata->posteq_numstepsB + ); + writeMatlabField0( + matlabDiagnosisStruct, "posteq_cpu_time", rdata->posteq_cpu_time + ); + writeMatlabField0( + matlabDiagnosisStruct, "posteq_cpu_timeB", rdata->posteq_cpu_timeB + ); writeMatlabField0(matlabDiagnosisStruct, "posteq_t", rdata->posteq_t); - writeMatlabField0(matlabDiagnosisStruct, "posteq_wrms", rdata->posteq_wrms); + writeMatlabField0( + matlabDiagnosisStruct, "posteq_wrms", rdata->posteq_wrms + ); } if (rdata->sensi >= SensitivityOrder::first) { if (rdata->sensi_meth == SensitivityMethod::adjoint) { writeMatlabField1( matlabDiagnosisStruct, "numstepsB", gsl::make_span(rdata->numstepsB).subspan(0, finite_nt), - finite_nt); + finite_nt + ); writeMatlabField1( matlabDiagnosisStruct, "numrhsevalsB", gsl::make_span(rdata->numrhsevalsB).subspan(0, finite_nt), - finite_nt); + finite_nt + ); writeMatlabField1( matlabDiagnosisStruct, "numerrtestfailsB", gsl::make_span(rdata->numerrtestfailsB).subspan(0, finite_nt), - finite_nt); + finite_nt + ); writeMatlabField1( matlabDiagnosisStruct, "numnonlinsolvconvfailsB", - gsl::make_span(rdata->numnonlinsolvconvfailsB - ).subspan(0, finite_nt), - finite_nt); + gsl::make_span(rdata->numnonlinsolvconvfailsB) + .subspan(0, finite_nt), + finite_nt + ); } } - return(matlabDiagnosisStruct); + return (matlabDiagnosisStruct); } - -template -void writeMatlabField0(mxArray *matlabStruct, const char *fieldName, - T fieldData) { +template +void writeMatlabField0( + mxArray* matlabStruct, char const* fieldName, T fieldData +) { std::vector dim = {(mwSize)(1), (mwSize)(1)}; - double *array = initAndAttachArray(matlabStruct, fieldName, dim); + double* array = initAndAttachArray(matlabStruct, fieldName, dim); array[0] = static_cast(fieldData); } -template -void writeMatlabField1(mxArray *matlabStruct, const char *fieldName, - gsl::span const& fieldData, int dim0) { - if(fieldData.size() != dim0) - throw AmiException("Dimension mismatch when writing rdata->%s to " - "matlab results (expected %d, got %d)", - fieldName, dim0, static_cast(fieldData.size())); +template +void writeMatlabField1( + mxArray* matlabStruct, char const* fieldName, + gsl::span const& fieldData, mwSize dim0 +) { + if (fieldData.size() != dim0) + throw AmiException( + "Dimension mismatch when writing rdata->%s to " + "matlab results (expected %d, got %d)", + fieldName, dim0, static_cast(fieldData.size()) + ); std::vector dim = {(mwSize)(dim0), (mwSize)(1)}; - double *array = initAndAttachArray(matlabStruct, fieldName, dim); + double* array = initAndAttachArray(matlabStruct, fieldName, dim); auto data_ptr = fieldData.data(); - for(int i = 0; i < dim0; i++) + for (mwSize i = 0; i < dim0; i++) array[i] = static_cast(data_ptr[i]); } -template -void writeMatlabField2(mxArray *matlabStruct, const char *fieldName, - std::vector const& fieldData, int dim0, int dim1, - std::vector perm) { - if(fieldData.size() != dim0*dim1) - throw AmiException("Dimension mismatch when writing rdata->%s to " - "matlab results (expected: %d, actual: %d)", - fieldName, dim0 * dim1, - static_cast(fieldData.size())); - - if(perm.size() != 2) +template +void writeMatlabField2( + mxArray* matlabStruct, char const* fieldName, + std::vector const& fieldData, mwSize dim0, mwSize dim1, + std::vector perm +) { + if (fieldData.size() != dim0 * dim1) + throw AmiException( + "Dimension mismatch when writing rdata->%s to " + "matlab results (expected: %d, actual: %d)", + fieldName, dim0 * dim1, static_cast(fieldData.size()) + ); + + if (perm.size() != 2) throw AmiException("Dimension mismatch when applying permutation!"); - std::vector dim = {(mwSize)(dim0), (mwSize)(dim1)}; + std::vector dim = {dim0, dim1}; - double *array = initAndAttachArray(matlabStruct, fieldName, reorder(dim,perm)); + double* array + = initAndAttachArray(matlabStruct, fieldName, reorder(dim, perm)); - std::vector index = {0,0}; + std::vector index = {0, 0}; /* transform rowmajor (c++) to colmajor (matlab) and apply permutation */ for (index[0] = 0; index[0] < dim[0]; index[0]++) { for (index[1] = 0; index[1] < dim[1]; index[1]++) { - array[index[perm[0]] + index[perm[1]]*dim[perm[0]]] = - static_cast(fieldData[index[0]*dim[1] + index[1]]); + array[index[perm[0]] + index[perm[1]] * dim[perm[0]]] + = static_cast(fieldData[index[0] * dim[1] + index[1]]); } } } -template -void writeMatlabField3(mxArray *matlabStruct, const char *fieldName, - std::vector const& fieldData, int dim0, int dim1, - int dim2, std::vector perm) { - if(fieldData.size() != dim0*dim1*dim2) - throw AmiException("Dimension mismatch when writing rdata->%s to matlab results",fieldName); - - if(perm.size() != 3) +template +void writeMatlabField3( + mxArray* matlabStruct, char const* fieldName, + std::vector const& fieldData, mwSize dim0, mwSize dim1, mwSize dim2, + std::vector perm +) { + if (fieldData.size() != dim0 * dim1 * dim2) + throw AmiException( + "Dimension mismatch when writing rdata->%s to matlab results", + fieldName + ); + + if (perm.size() != 3) throw AmiException("Dimension mismatch when applying permutation!"); std::vector dim = {(mwSize)(dim0), (mwSize)(dim1), (mwSize)(dim2)}; - double *array = initAndAttachArray(matlabStruct, fieldName, reorder(dim,perm)); + double* array + = initAndAttachArray(matlabStruct, fieldName, reorder(dim, perm)); - std::vector index = {0,0,0}; + std::vector index = {0, 0, 0}; /* transform rowmajor (c++) to colmajor (matlab) and apply permutation */ for (index[0] = 0; index[0] < dim[0]; index[0]++) { for (index[1] = 0; index[1] < dim[1]; index[1]++) { for (index[2] = 0; index[2] < dim[2]; index[2]++) { - array[index[perm[0]] + (index[perm[1]] + index[perm[2]]*dim[perm[1]])*dim[perm[0]]] = - static_cast(fieldData[(index[0]*dim[1] + index[1])*dim[2] + index[2]]); + array + [index[perm[0]] + + (index[perm[1]] + index[perm[2]] * dim[perm[1]]) + * dim[perm[0]]] + = static_cast( + fieldData + [(index[0] * dim[1] + index[1]) * dim[2] + index[2]] + ); } } } } -template -void writeMatlabField4(mxArray *matlabStruct, const char *fieldName, - std::vector const& fieldData, int dim0, int dim1, - int dim2, int dim3, std::vector perm) { - if(fieldData.size() != dim0*dim1*dim2*dim3) - throw AmiException("Dimension mismatch when writing rdata->%s to matlab results!",fieldName); - - if(perm.size() != 4) +template +void writeMatlabField4( + mxArray* matlabStruct, char const* fieldName, + std::vector const& fieldData, mwSize dim0, mwSize dim1, mwSize dim2, + mwSize dim3, std::vector perm +) { + if (fieldData.size() != dim0 * dim1 * dim2 * dim3) + throw AmiException( + "Dimension mismatch when writing rdata->%s to matlab results!", + fieldName + ); + + if (perm.size() != 4) throw AmiException("Dimension mismatch when applying permutation!"); - std::vector dim = {(mwSize)(dim0), (mwSize)(dim1), (mwSize)(dim2), (mwSize)(dim3)}; + std::vector dim + = {(mwSize)(dim0), (mwSize)(dim1), (mwSize)(dim2), (mwSize)(dim3)}; - double *array = initAndAttachArray(matlabStruct, fieldName, reorder(dim,perm)); + double* array + = initAndAttachArray(matlabStruct, fieldName, reorder(dim, perm)); - std::vector index = {0,0,0,0}; + std::vector index = {0, 0, 0, 0}; /* transform rowmajor (c++) to colmajor (matlab) and apply permutation */ for (index[0] = 0; index[0] < dim[0]; index[0]++) { for (index[1] = 0; index[1] < dim[1]; index[1]++) { for (index[2] = 0; index[2] < dim[2]; index[2]++) { for (index[3] = 0; index[3] < dim[3]; index[3]++) { - array[index[perm[0]] + (index[perm[1]] + (index[perm[2]] + index[perm[3]]*dim[perm[2]])*dim[perm[1]])*dim[perm[0]]] = - static_cast(fieldData[((index[0]*dim[1] + index[1])*dim[2] + index[2])*dim[3] + index[3]]); + array + [index[perm[0]] + + (index[perm[1]] + + (index[perm[2]] + index[perm[3]] * dim[perm[2]]) + * dim[perm[1]]) + * dim[perm[0]]] + = static_cast( + fieldData + [((index[0] * dim[1] + index[1]) * dim[2] + + index[2]) + * dim[3] + + index[3]] + ); } } } } } -double *initAndAttachArray(mxArray *matlabStruct, const char *fieldName, std::vector dim) { - if(!mxIsStruct(matlabStruct)) - throw AmiException("Passing non-struct mxArray to initAndAttachArray!",fieldName); +double* initAndAttachArray( + mxArray* matlabStruct, char const* fieldName, std::vector dim +) { + if (!mxIsStruct(matlabStruct)) + throw AmiException( + "Passing non-struct mxArray to initAndAttachArray!", fieldName + ); int fieldNumber = mxGetFieldNumber(matlabStruct, fieldName); - if(fieldNumber<0) - throw AmiException("Trying to access non-existent field '%s'!",fieldName); + if (fieldNumber < 0) + throw AmiException( + "Trying to access non-existent field '%s'!", fieldName + ); - mxArray *array = mxCreateNumericArray(dim.size(), dim.data(), mxDOUBLE_CLASS, mxREAL); + mxArray* array + = mxCreateNumericArray(dim.size(), dim.data(), mxDOUBLE_CLASS, mxREAL); mxSetFieldByNumber(matlabStruct, 0, fieldNumber, array); - return(mxGetPr(array)); + return (mxGetPr(array)); } -void checkFieldNames(const char **fieldNames,const int fieldCount) { - for (int ifield = 0; ifield -std::vector reorder(std::vector const& input, - std::vector const& order) { - if(order.size() != input.size()) +template +std::vector +reorder(std::vector const& input, std::vector const& order) { + if (order.size() != input.size()) throw AmiException("Input dimension mismatch!"); std::vector reordered; reordered.resize(input.size()); - for(int i = 0; i < input.size(); i++) + for (std::vector::size_type i = 0; i < input.size(); i++) reordered[i] = input[order[i]]; - return(reordered); + return reordered; } - } // namespace amici diff --git a/deps/AMICI/src/simulation_parameters.cpp b/deps/AMICI/src/simulation_parameters.cpp index 824f5e254..fd2e60744 100644 --- a/deps/AMICI/src/simulation_parameters.cpp +++ b/deps/AMICI/src/simulation_parameters.cpp @@ -1,43 +1,55 @@ #include "amici/simulation_parameters.h" +#include "amici/misc.h" #include namespace amici { -bool operator==(const SimulationParameters &a, const SimulationParameters &b) { - return (a.fixedParameters == b.fixedParameters) && - (a.fixedParametersPreequilibration == b.fixedParametersPreequilibration) && - (a.fixedParametersPresimulation == b.fixedParametersPresimulation) && - (a.parameters == b.parameters) && - (a.plist == b.plist) && - (a.pscale == b.pscale) && - (a.reinitializeFixedParameterInitialStates == b.reinitializeFixedParameterInitialStates) && - (a.sx0 == b.sx0) && - (a.t_presim == b.t_presim) && - (a.tstart_ == b.tstart_) && - (a.ts_ == b.ts_); +bool operator==(SimulationParameters const& a, SimulationParameters const& b) { + return is_equal(a.fixedParameters, b.fixedParameters) + && is_equal( + a.fixedParametersPreequilibration, + b.fixedParametersPreequilibration + ) + && is_equal( + a.fixedParametersPresimulation, b.fixedParametersPresimulation + ) + && is_equal(a.parameters, b.parameters) && (a.plist == b.plist) + && (a.pscale == b.pscale) + && (a.reinitializeFixedParameterInitialStates + == b.reinitializeFixedParameterInitialStates) + && is_equal(a.sx0, b.sx0) && (a.t_presim == b.t_presim) + && (a.tstart_ == b.tstart_) && (a.ts_ == b.ts_); } -void SimulationParameters::reinitializeAllFixedParameterDependentInitialStatesForPresimulation(int nx_rdata) -{ +void SimulationParameters:: + reinitializeAllFixedParameterDependentInitialStatesForPresimulation( + int nx_rdata + ) { reinitialization_state_idxs_presim.resize(nx_rdata); - std::iota(reinitialization_state_idxs_presim.begin(), - reinitialization_state_idxs_presim.end(), 0); - + std::iota( + reinitialization_state_idxs_presim.begin(), + reinitialization_state_idxs_presim.end(), 0 + ); } -void SimulationParameters::reinitializeAllFixedParameterDependentInitialStatesForSimulation(int nx_rdata) -{ +void SimulationParameters:: + reinitializeAllFixedParameterDependentInitialStatesForSimulation( + int nx_rdata + ) { reinitialization_state_idxs_sim.resize(nx_rdata); - std::iota(reinitialization_state_idxs_sim.begin(), - reinitialization_state_idxs_sim.end(), 0); + std::iota( + reinitialization_state_idxs_sim.begin(), + reinitialization_state_idxs_sim.end(), 0 + ); } -void SimulationParameters::reinitializeAllFixedParameterDependentInitialStates(int nx_rdata) -{ - reinitializeAllFixedParameterDependentInitialStatesForPresimulation(nx_rdata); +void SimulationParameters::reinitializeAllFixedParameterDependentInitialStates( + int nx_rdata +) { + reinitializeAllFixedParameterDependentInitialStatesForPresimulation(nx_rdata + ); reinitializeAllFixedParameterDependentInitialStatesForSimulation(nx_rdata); } - } // namespace amici diff --git a/deps/AMICI/src/solver.cpp b/deps/AMICI/src/solver.cpp index c77461566..56bed2a1a 100644 --- a/deps/AMICI/src/solver.cpp +++ b/deps/AMICI/src/solver.cpp @@ -1,9 +1,8 @@ #include "amici/solver.h" #include "amici/exception.h" -#include "amici/misc.h" #include "amici/model.h" -#include "amici/rdata.h" +#include "amici/symbolic_functions.h" #include #include @@ -12,52 +11,73 @@ namespace amici { -Solver::Solver(AmiciApplication *app) : app(app) -{ - -} - -Solver::Solver(const Solver &other) - : ism_(other.ism_), lmm_(other.lmm_), iter_(other.iter_), - interp_type_(other.interp_type_), maxsteps_(other.maxsteps_), - maxtime_(other.maxtime_), starttime_(other.starttime_), - sensi_meth_(other.sensi_meth_), sensi_meth_preeq_(other.sensi_meth_preeq_), - stldet_(other.stldet_), ordering_(other.ordering_), - newton_maxsteps_(other.newton_maxsteps_), - newton_maxlinsteps_(other.newton_maxlinsteps_), - newton_damping_factor_mode_(other.newton_damping_factor_mode_), - newton_damping_factor_lower_bound_(other.newton_damping_factor_lower_bound_), - requires_preequilibration_(other.requires_preequilibration_), - linsol_(other.linsol_), atol_(other.atol_), rtol_(other.rtol_), - atol_fsa_(other.atol_fsa_), rtol_fsa_(other.rtol_fsa_), - atolB_(other.atolB_), rtolB_(other.rtolB_), quad_atol_(other.quad_atol_), - quad_rtol_(other.quad_rtol_), ss_atol_(other.ss_atol_), - ss_rtol_(other.ss_rtol_), ss_atol_sensi_(other.ss_atol_sensi_), - ss_rtol_sensi_(other.ss_rtol_sensi_), rdata_mode_(other.rdata_mode_), - maxstepsB_(other.maxstepsB_), sensi_(other.sensi_) -{} +Solver::Solver(Solver const& other) + : ism_(other.ism_) + , lmm_(other.lmm_) + , iter_(other.iter_) + , interp_type_(other.interp_type_) + , maxsteps_(other.maxsteps_) + , maxtime_(other.maxtime_) + , simulation_timer_(other.simulation_timer_) + , sensi_meth_(other.sensi_meth_) + , sensi_meth_preeq_(other.sensi_meth_preeq_) + , stldet_(other.stldet_) + , ordering_(other.ordering_) + , newton_maxsteps_(other.newton_maxsteps_) + , newton_damping_factor_mode_(other.newton_damping_factor_mode_) + , newton_damping_factor_lower_bound_( + other.newton_damping_factor_lower_bound_ + ) + , linsol_(other.linsol_) + , atol_(other.atol_) + , rtol_(other.rtol_) + , atol_fsa_(other.atol_fsa_) + , rtol_fsa_(other.rtol_fsa_) + , atolB_(other.atolB_) + , rtolB_(other.rtolB_) + , quad_atol_(other.quad_atol_) + , quad_rtol_(other.quad_rtol_) + , ss_tol_factor_(other.ss_tol_factor_) + , ss_atol_(other.ss_atol_) + , ss_rtol_(other.ss_rtol_) + , ss_tol_sensi_factor_(other.ss_tol_sensi_factor_) + , ss_atol_sensi_(other.ss_atol_sensi_) + , ss_rtol_sensi_(other.ss_rtol_sensi_) + , rdata_mode_(other.rdata_mode_) + , newton_step_steadystate_conv_(other.newton_step_steadystate_conv_) + , check_sensi_steadystate_conv_(other.check_sensi_steadystate_conv_) + , maxstepsB_(other.maxstepsB_) + , sensi_(other.sensi_) {} void Solver::apply_max_num_steps() const { - // set remaining steps, setMaxNumSteps only applies to a single call of solve + // set remaining steps, setMaxNumSteps only applies to a single call of + // solve long int cursteps; getNumSteps(solver_memory_.get(), &cursteps); if (maxsteps_ <= cursteps) - throw AmiException("Reached maximum number of steps %ld before reaching " - "tout at t=%g.", maxsteps_, t_); + throw AmiException( + "Reached maximum number of steps %ld before reaching " + "tout at t=%g.", + maxsteps_, t_ + ); setMaxNumSteps(maxsteps_ - cursteps); } void Solver::apply_max_num_steps_B() const { - // set remaining steps, setMaxNumSteps only applies to a single call of solve + // set remaining steps, setMaxNumSteps only applies to a single call of + // solve long int curstepsB; auto maxstepsB = (maxstepsB_ == 0) ? maxsteps_ * 100 : maxstepsB_; for (int i_mem_b = 0; i_mem_b < (int)solver_memory_B_.size(); ++i_mem_b) { if (solver_memory_B_.at(i_mem_b)) { getNumSteps(solver_memory_B_.at(i_mem_b).get(), &curstepsB); if (maxstepsB <= curstepsB) - throw AmiException("Reached maximum number of steps %ld before " - "reaching tout at t=%g in backward " - "problem %i.", maxstepsB_, t_, i_mem_b); + throw AmiException( + "Reached maximum number of steps %ld before " + "reaching tout at t=%g in backward " + "problem %i.", + maxstepsB_, t_, i_mem_b + ); setMaxNumStepsB(i_mem_b, maxstepsB - curstepsB); } } @@ -65,7 +85,7 @@ void Solver::apply_max_num_steps_B() const { int Solver::run(const realtype tout) const { setStopTime(tout); - clock_t starttime = clock(); + CpuTimer cpu_timer; int status = AMICI_SUCCESS; apply_max_num_steps(); @@ -78,7 +98,7 @@ int Solver::run(const realtype tout) const { } else { t_ = tout; } - cpu_time_ += (realtype)((clock() - starttime) * 1000) / CLOCKS_PER_SEC; + cpu_time_ += cpu_timer.elapsed_milliseconds(); return status; } @@ -99,23 +119,25 @@ int Solver::step(const realtype tout) const { } void Solver::runB(const realtype tout) const { - clock_t starttime = clock(); + CpuTimer cpu_timer; apply_max_num_steps_B(); if (nx() > 0) { solveB(tout, AMICI_NORMAL); } - cpu_timeB_ += (realtype)((clock() - starttime) * 1000) / CLOCKS_PER_SEC; + cpu_timeB_ += cpu_timer.elapsed_milliseconds(); t_ = tout; } -void Solver::setup(const realtype t0, Model *model, const AmiVector &x0, - const AmiVector &dx0, const AmiVectorArray &sx0, - const AmiVectorArray &sdx0) const { - if (nx() != model->nx_solver || nplist() != model->nplist() || - nquad() != model->nJ * model->nplist()) { - resetMutableMemory(model->nx_solver, model->nplist(), - model->nJ * model->nplist()); +void Solver::setup( + const realtype t0, Model* model, AmiVector const& x0, AmiVector const& dx0, + AmiVectorArray const& sx0, AmiVectorArray const& sdx0 +) const { + if (nx() != model->nx_solver || nplist() != model->nplist() + || nquad() != model->nJ * model->nplist()) { + resetMutableMemory( + model->nx_solver, model->nplist(), model->nJ * model->nplist() + ); } /* Create solver memory object if necessary */ allocateSolver(); @@ -147,8 +169,8 @@ void Solver::setup(const realtype t0, Model *model, const AmiVector &x0, initializeLinearSolver(model); initializeNonLinearSolver(); - if (sensi_ >= SensitivityOrder::first && - sensi_meth_ > SensitivityMethod::none && model->nx_solver > 0) { + if (sensi_ >= SensitivityOrder::first + && sensi_meth_ > SensitivityMethod::none && model->nx_solver > 0) { auto plist = model->getParameterList(); sensInit1(sx0, sdx0); if (sensi_meth_ == SensitivityMethod::forward && !plist.empty()) { @@ -170,14 +192,18 @@ void Solver::setup(const realtype t0, Model *model, const AmiVector &x0, /* calculate consistent DAE initial conditions (no effect for ODE) */ if (model->nt() > 1) calcIC(model->getTimepoint(1)); + + cpu_time_ = 0.0; + cpu_timeB_ = 0.0; } -void Solver::setupB(int *which, const realtype tf, Model *model, - const AmiVector &xB0, const AmiVector &dxB0, - const AmiVector &xQB0) const { +void Solver::setupB( + int* which, const realtype tf, Model* model, AmiVector const& xB0, + AmiVector const& dxB0, AmiVector const& xQB0 +) const { if (!solver_memory_) - throw AmiException( - "Solver for the forward problem must be setup first"); + throw AmiException("Solver for the forward problem must be setup first" + ); /* allocate memory for the backward problem */ allocateSolverB(which); @@ -203,9 +229,10 @@ void Solver::setupB(int *which, const realtype tf, Model *model, setStabLimDetB(*which, stldet_); } -void Solver::setupSteadystate(const realtype t0, Model *model, const AmiVector &x0, - const AmiVector &dx0, const AmiVector &xB0, - const AmiVector &dxB0, const AmiVector &xQ0) const { +void Solver::setupSteadystate( + const realtype 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 */ initSteadystate(t0, x0, dx0); @@ -218,20 +245,21 @@ void Solver::setupSteadystate(const realtype t0, Model *model, const AmiVector & /* Check linear solver (works only with KLU atm) */ if (linsol_ != LinearSolver::KLU) throw AmiException("Backward steady state computation via integration " - "is currently only implemented for KLU linear solver"); + "is currently only implemented for KLU linear solver" + ); /* Set Jacobian function and initialize values */ setSparseJacFn_ss(); model->writeSteadystateJB(t0, 0, x0, dx0, xB0, dxB0, xB0); } -void Solver::updateAndReinitStatesAndSensitivities(Model *model) { +void Solver::updateAndReinitStatesAndSensitivities(Model* model) const { model->fx0_fixedParameters(x_); reInit(t_, x_, dx_); if (getSensitivityOrder() >= SensitivityOrder::first) { - model->fsx0_fixedParameters(sx_, x_); - if (getSensitivityMethod() == SensitivityMethod::forward) - sensReInit(sx_, sdx_); + model->fsx0_fixedParameters(sx_, x_); + if (getSensitivityMethod() == SensitivityMethod::forward) + sensReInit(sx_, sdx_); } } @@ -260,23 +288,23 @@ void Solver::storeDiagnosis() const { long int lnumber; getNumSteps(solver_memory_.get(), &lnumber); - ns_.push_back(static_cast(lnumber)); + ns_.push_back(gsl::narrow(lnumber)); getNumRhsEvals(solver_memory_.get(), &lnumber); - nrhs_.push_back(static_cast(lnumber)); + nrhs_.push_back(gsl::narrow(lnumber)); getNumErrTestFails(solver_memory_.get(), &lnumber); - netf_.push_back(static_cast(lnumber)); + netf_.push_back(gsl::narrow(lnumber)); getNumNonlinSolvConvFails(solver_memory_.get(), &lnumber); - nnlscf_.push_back(static_cast(lnumber)); + nnlscf_.push_back(gsl::narrow(lnumber)); int number; getLastOrder(solver_memory_.get(), &number); order_.push_back(number); } -void Solver::storeDiagnosisB(const int which) const { +void Solver::storeDiagnosisB(int const which) const { if (!solver_was_called_B_ || !solver_memory_B_.at(which)) { nsB_.push_back(0); nrhsB_.push_back(0); @@ -287,19 +315,19 @@ void Solver::storeDiagnosisB(const int which) const { long int number; getNumSteps(solver_memory_B_.at(which).get(), &number); - nsB_.push_back(static_cast(number)); + nsB_.push_back(gsl::narrow(number)); getNumRhsEvals(solver_memory_B_.at(which).get(), &number); - nrhsB_.push_back(static_cast(number)); + nrhsB_.push_back(gsl::narrow(number)); getNumErrTestFails(solver_memory_B_.at(which).get(), &number); - netfB_.push_back(static_cast(number)); + netfB_.push_back(gsl::narrow(number)); getNumNonlinSolvConvFails(solver_memory_B_.at(which).get(), &number); - nnlscfB_.push_back(static_cast(number)); + nnlscfB_.push_back(gsl::narrow(number)); } -void Solver::initializeLinearSolver(const Model *model) const { +void Solver::initializeLinearSolver(Model const* model) const { switch (linsol_) { /* DIRECT SOLVERS */ @@ -311,8 +339,8 @@ void Solver::initializeLinearSolver(const Model *model) const { break; case LinearSolver::band: - linear_solver_ = - std::make_unique(x_, model->ubw, model->lbw); + linear_solver_ + = std::make_unique(x_, model->ubw, model->lbw); setLinearSolver(); setBandJacFn(); break; @@ -353,7 +381,8 @@ void Solver::initializeLinearSolver(const Model *model) const { case LinearSolver::KLU: linear_solver_ = std::make_unique( x_, model->nnz, CSC_MAT, - static_cast(getStateOrdering())); + static_cast(getStateOrdering()) + ); setLinearSolver(); setSparseJacFn(); break; @@ -363,37 +392,41 @@ void Solver::initializeLinearSolver(const Model *model) const { // TODO state ordering linearSolver = std::make_unique( *x, model->nnz, CSC_MAT, - static_cast(getStateOrdering())); + static_cast(getStateOrdering()) + ); setLinearSolver(); setSparseJacFn(); break; #endif default: - throw AmiException("Invalid choice of solver: %d", - static_cast(linsol_)); + throw AmiException( + "Invalid choice of solver: %d", static_cast(linsol_) + ); } } void Solver::initializeNonLinearSolver() const { switch (iter_) { case NonlinearSolverIteration::newton: - non_linear_solver_ = std::make_unique(x_.getNVector()); + non_linear_solver_ + = std::make_unique(x_.getNVector()); break; case NonlinearSolverIteration::fixedpoint: - non_linear_solver_ = - std::make_unique(x_.getNVector()); + non_linear_solver_ + = std::make_unique(x_.getNVector()); break; default: - throw AmiException("Invalid non-linear solver specified (%d).", - static_cast(iter_)); + throw AmiException( + "Invalid non-linear solver specified (%d).", static_cast(iter_) + ); } setNonLinearSolver(); } -void Solver::initializeLinearSolverB(const Model *model, - const int which) const { +void Solver::initializeLinearSolverB(Model const* model, int const which) + const { switch (linsol_) { /* DIRECT SOLVERS */ case LinearSolver::dense: @@ -403,8 +436,8 @@ void Solver::initializeLinearSolverB(const Model *model, break; case LinearSolver::band: - linear_solver_B_ = - std::make_unique(xB_, model->ubw, model->lbw); + linear_solver_B_ + = std::make_unique(xB_, model->ubw, model->lbw); setLinearSolverB(which); setBandJacFnB(which); break; @@ -445,7 +478,8 @@ void Solver::initializeLinearSolverB(const Model *model, case LinearSolver::KLU: linear_solver_B_ = std::make_unique( xB_, model->nnz, CSC_MAT, - static_cast(getStateOrdering())); + static_cast(getStateOrdering()) + ); setLinearSolverB(which); setSparseJacFnB(which); break; @@ -453,67 +487,74 @@ void Solver::initializeLinearSolverB(const Model *model, case LinearSolver::SuperLUMT: linearSolverB = std::make_unique( *xB, model->nnz, CSC_MAT, - static_cast(getStateOrdering())); + static_cast(getStateOrdering()) + ); setLinearSolverB(which); setSparseJacFnB(which); break; #endif default: - throw AmiException("Invalid choice of solver: %d", - static_cast(linsol_)); + throw AmiException( + "Invalid choice of solver: %d", static_cast(linsol_) + ); } } -void Solver::initializeNonLinearSolverB(const int which) const { +void Solver::initializeNonLinearSolverB(int const which) const { switch (iter_) { case NonlinearSolverIteration::newton: - non_linear_solver_B_ = - std::make_unique(xB_.getNVector()); + non_linear_solver_B_ + = std::make_unique(xB_.getNVector()); break; case NonlinearSolverIteration::fixedpoint: - non_linear_solver_B_ = - std::make_unique(xB_.getNVector()); + non_linear_solver_B_ + = std::make_unique(xB_.getNVector()); break; default: - throw AmiException("Invalid non-linear solver specified (%d).", - static_cast(iter_)); + throw AmiException( + "Invalid non-linear solver specified (%d).", static_cast(iter_) + ); } setNonLinearSolverB(which); } -bool operator==(const Solver &a, const Solver &b) { +bool operator==(Solver const& a, Solver const& b) { if (typeid(a) != typeid(b)) return false; - return (a.interp_type_ == b.interp_type_) && (a.lmm_ == b.lmm_) && - (a.iter_ == b.iter_) && (a.stldet_ == b.stldet_) && - (a.ordering_ == b.ordering_) && - (a.newton_maxsteps_ == b.newton_maxsteps_) && - (a.newton_maxlinsteps_ == b.newton_maxlinsteps_) && - (a.newton_damping_factor_mode_ == b.newton_damping_factor_mode_) && - (a.newton_damping_factor_lower_bound_ == b.newton_damping_factor_lower_bound_) && - (a.requires_preequilibration_ == b.requires_preequilibration_) && (a.ism_ == b.ism_) && - (a.linsol_ == b.linsol_) && (a.atol_ == b.atol_) && (a.rtol_ == b.rtol_) && - (a.maxsteps_ == b.maxsteps_) && (a.maxstepsB_ == b.maxstepsB_) && - (a.quad_atol_ == b.quad_atol_) && (a.quad_rtol_ == b.quad_rtol_) && - (a.maxtime_ == b.maxtime_) && - (a.getAbsoluteToleranceSteadyState() == - b.getAbsoluteToleranceSteadyState()) && - (a.getRelativeToleranceSteadyState() == - b.getRelativeToleranceSteadyState()) && - (a.getAbsoluteToleranceSteadyStateSensi() == - b.getAbsoluteToleranceSteadyStateSensi()) && - (a.getRelativeToleranceSteadyStateSensi() == - b.getRelativeToleranceSteadyStateSensi()) && - (a.rtol_fsa_ == b.rtol_fsa_ || - (isNaN(a.rtol_fsa_) && isNaN(b.rtol_fsa_))) && - (a.atol_fsa_ == b.atol_fsa_ || - (isNaN(a.atol_fsa_) && isNaN(b.atol_fsa_))) && - (a.rtolB_ == b.rtolB_ || (isNaN(a.rtolB_) && isNaN(b.rtolB_))) && - (a.atolB_ == b.atolB_ || (isNaN(a.atolB_) && isNaN(b.atolB_))) && - (a.sensi_ == b.sensi_) && (a.sensi_meth_ == b.sensi_meth_) && - (a.rdata_mode_ == b.rdata_mode_); + return (a.interp_type_ == b.interp_type_) && (a.lmm_ == b.lmm_) + && (a.iter_ == b.iter_) && (a.stldet_ == b.stldet_) + && (a.ordering_ == b.ordering_) + && (a.newton_maxsteps_ == b.newton_maxsteps_) + && (a.newton_damping_factor_mode_ == b.newton_damping_factor_mode_) + && (a.newton_damping_factor_lower_bound_ + == b.newton_damping_factor_lower_bound_) + && (a.ism_ == b.ism_) && (a.linsol_ == b.linsol_) + && (a.atol_ == b.atol_) && (a.rtol_ == b.rtol_) + && (a.maxsteps_ == b.maxsteps_) && (a.maxstepsB_ == b.maxstepsB_) + && (a.quad_atol_ == b.quad_atol_) && (a.quad_rtol_ == b.quad_rtol_) + && (a.maxtime_ == b.maxtime_) + && (a.getAbsoluteToleranceSteadyState() + == b.getAbsoluteToleranceSteadyState()) + && (a.getRelativeToleranceSteadyState() + == b.getRelativeToleranceSteadyState()) + && (a.getAbsoluteToleranceSteadyStateSensi() + == b.getAbsoluteToleranceSteadyStateSensi()) + && (a.getRelativeToleranceSteadyStateSensi() + == b.getRelativeToleranceSteadyStateSensi()) + && (a.rtol_fsa_ == b.rtol_fsa_ + || (isNaN(a.rtol_fsa_) && isNaN(b.rtol_fsa_))) + && (a.atol_fsa_ == b.atol_fsa_ + || (isNaN(a.atol_fsa_) && isNaN(b.atol_fsa_))) + && (a.rtolB_ == b.rtolB_ || (isNaN(a.rtolB_) && isNaN(b.rtolB_))) + && (a.atolB_ == b.atolB_ || (isNaN(a.atolB_) && isNaN(b.atolB_))) + && (a.sensi_ == b.sensi_) && (a.sensi_meth_ == b.sensi_meth_) + && (a.newton_step_steadystate_conv_ + == b.newton_step_steadystate_conv_) + && (a.check_sensi_steadystate_conv_ + == b.check_sensi_steadystate_conv_) + && (a.rdata_mode_ == b.rdata_mode_); } void Solver::applyTolerances() const { @@ -539,7 +580,7 @@ void Solver::applyTolerancesFSA() const { } } -void Solver::applyTolerancesASA(const int which) const { +void Solver::applyTolerancesASA(int const which) const { if (!getAdjInitDone()) throw AmiException("Adjoint solver instance was not yet set up, the " "tolerances cannot be applied yet!"); @@ -551,7 +592,7 @@ void Solver::applyTolerancesASA(const int which) const { setSStolerancesB(which, getRelativeToleranceB(), getAbsoluteToleranceB()); } -void Solver::applyQuadTolerancesASA(const int which) const { +void Solver::applyQuadTolerancesASA(int const which) const { if (!getAdjInitDone()) throw AmiException("Adjoint solver instance was not yet set up, the " "tolerances cannot be applied yet!"); @@ -599,22 +640,27 @@ void Solver::applySensitivityTolerances() const { SensitivityMethod Solver::getSensitivityMethod() const { return sensi_meth_; } -SensitivityMethod Solver::getSensitivityMethodPreequilibration() const { return sensi_meth_preeq_; } +SensitivityMethod Solver::getSensitivityMethodPreequilibration() const { + return sensi_meth_preeq_; +} void Solver::setSensitivityMethod(const SensitivityMethod sensi_meth) { checkSensitivityMethod(sensi_meth, false); this->sensi_meth_ = sensi_meth; } -void Solver::setSensitivityMethodPreequilibration(const SensitivityMethod sensi_meth_preeq) { +void Solver::setSensitivityMethodPreequilibration( + const SensitivityMethod sensi_meth_preeq +) { checkSensitivityMethod(sensi_meth_preeq, true); sensi_meth_preeq_ = sensi_meth_preeq; } -void Solver::checkSensitivityMethod(const SensitivityMethod sensi_meth, - bool preequilibration) const { - if (rdata_mode_ == RDataReporting::residuals && - sensi_meth == SensitivityMethod::adjoint) +void Solver::checkSensitivityMethod( + const SensitivityMethod sensi_meth, bool preequilibration +) const { + if (rdata_mode_ == RDataReporting::residuals + && sensi_meth == SensitivityMethod::adjoint) throw AmiException("Adjoint Sensitivity Analysis is not compatible with" " only reporting residuals!"); if (!preequilibration && sensi_meth != sensi_meth_) @@ -623,36 +669,28 @@ void Solver::checkSensitivityMethod(const SensitivityMethod sensi_meth, int Solver::getNewtonMaxSteps() const { return newton_maxsteps_; } -void Solver::setNewtonMaxSteps(const int newton_maxsteps) { +void Solver::setNewtonMaxSteps(int const newton_maxsteps) { if (newton_maxsteps < 0) throw AmiException("newton_maxsteps must be a non-negative number"); newton_maxsteps_ = newton_maxsteps; } -bool Solver::getPreequilibration() const { return requires_preequilibration_; } - -void Solver::setPreequilibration(const bool require_preequilibration) { - requires_preequilibration_ = require_preequilibration; +NewtonDampingFactorMode Solver::getNewtonDampingFactorMode() const { + return newton_damping_factor_mode_; } -int Solver::getNewtonMaxLinearSteps() const { return newton_maxlinsteps_; } - -void Solver::setNewtonMaxLinearSteps(const int newton_maxlinsteps) { - if (newton_maxlinsteps < 0) - throw AmiException("newton_maxlinsteps must be a non-negative number"); - newton_maxlinsteps_ = newton_maxlinsteps; +void Solver::setNewtonDampingFactorMode( + NewtonDampingFactorMode dampingFactorMode +) { + newton_damping_factor_mode_ = dampingFactorMode; } -NewtonDampingFactorMode Solver::getNewtonDampingFactorMode() const { return newton_damping_factor_mode_; } - -void Solver::setNewtonDampingFactorMode(NewtonDampingFactorMode dampingFactorMode) { - newton_damping_factor_mode_ = dampingFactorMode; +double Solver::getNewtonDampingFactorLowerBound() const { + return newton_damping_factor_lower_bound_; } -double Solver::getNewtonDampingFactorLowerBound() const { return newton_damping_factor_lower_bound_; } - void Solver::setNewtonDampingFactorLowerBound(double dampingFactorLowerBound) { - newton_damping_factor_lower_bound_ = dampingFactorLowerBound; + newton_damping_factor_lower_bound_ = dampingFactorLowerBound; } SensitivityOrder Solver::getSensitivityOrder() const { return sensi_; } @@ -670,7 +708,7 @@ double Solver::getRelativeTolerance() const { return static_cast(rtol_); } -void Solver::setRelativeTolerance(const double rtol) { +void Solver::setRelativeTolerance(double const rtol) { if (rtol < 0) throw AmiException("rtol must be a non-negative number"); @@ -702,7 +740,7 @@ double Solver::getRelativeToleranceFSA() const { return static_cast(isNaN(rtol_fsa_) ? rtol_ : rtol_fsa_); } -void Solver::setRelativeToleranceFSA(const double rtol) { +void Solver::setRelativeToleranceFSA(double const rtol) { if (rtol < 0) throw AmiException("rtol must be a non-negative number"); @@ -717,7 +755,7 @@ double Solver::getAbsoluteToleranceFSA() const { return static_cast(isNaN(atol_fsa_) ? atol_ : atol_fsa_); } -void Solver::setAbsoluteToleranceFSA(const double atol) { +void Solver::setAbsoluteToleranceFSA(double const atol) { if (atol < 0) throw AmiException("atol must be a non-negative number"); @@ -732,7 +770,7 @@ double Solver::getRelativeToleranceB() const { return static_cast(isNaN(rtolB_) ? rtol_ : rtolB_); } -void Solver::setRelativeToleranceB(const double rtol) { +void Solver::setRelativeToleranceB(double const rtol) { if (rtol < 0) throw AmiException("rtol must be a non-negative number"); @@ -747,7 +785,7 @@ double Solver::getAbsoluteToleranceB() const { return static_cast(isNaN(atolB_) ? atol_ : atolB_); } -void Solver::setAbsoluteToleranceB(const double atol) { +void Solver::setAbsoluteToleranceB(double const atol) { if (atol < 0) throw AmiException("atol must be a non-negative number"); @@ -762,7 +800,7 @@ double Solver::getRelativeToleranceQuadratures() const { return static_cast(quad_rtol_); } -void Solver::setRelativeToleranceQuadratures(const double rtol) { +void Solver::setRelativeToleranceQuadratures(double const rtol) { if (rtol < 0) throw AmiException("rtol must be a non-negative number"); @@ -780,7 +818,7 @@ double Solver::getAbsoluteToleranceQuadratures() const { return static_cast(quad_atol_); } -void Solver::setAbsoluteToleranceQuadratures(const double atol) { +void Solver::setAbsoluteToleranceQuadratures(double const atol) { if (atol < 0) throw AmiException("atol must be a non-negative number"); @@ -794,11 +832,24 @@ void Solver::setAbsoluteToleranceQuadratures(const double atol) { applyTolerancesASA(iMem); } +double Solver::getSteadyStateToleranceFactor() const { + return static_cast(ss_tol_factor_); +} + +void Solver::setSteadyStateToleranceFactor(double const ss_tol_factor) { + if (ss_tol_factor < 0) + throw AmiException("ss_tol_factor must be a non-negative number"); + + ss_tol_factor_ = static_cast(ss_tol_factor); +} + double Solver::getRelativeToleranceSteadyState() const { - return static_cast(isNaN(ss_rtol_) ? rtol_ : ss_rtol_); + return static_cast( + isNaN(ss_rtol_) ? rtol_ * ss_tol_factor_ : ss_rtol_ + ); } -void Solver::setRelativeToleranceSteadyState(const double rtol) { +void Solver::setRelativeToleranceSteadyState(double const rtol) { if (rtol < 0) throw AmiException("rtol must be a non-negative number"); @@ -806,21 +857,37 @@ void Solver::setRelativeToleranceSteadyState(const double rtol) { } double Solver::getAbsoluteToleranceSteadyState() const { - return static_cast(isNaN(ss_atol_) ? atol_ : ss_atol_); + return static_cast( + isNaN(ss_atol_) ? atol_ * ss_tol_factor_ : ss_atol_ + ); } -void Solver::setAbsoluteToleranceSteadyState(const double atol) { +void Solver::setAbsoluteToleranceSteadyState(double const atol) { if (atol < 0) throw AmiException("atol must be a non-negative number"); ss_atol_ = static_cast(atol); } +double Solver::getSteadyStateSensiToleranceFactor() const { + return static_cast(ss_tol_sensi_factor_); +} + +void Solver::setSteadyStateSensiToleranceFactor(double const ss_tol_sensi_factor +) { + if (ss_tol_sensi_factor < 0) + throw AmiException("ss_tol_sensi_factor must be a non-negative number"); + + ss_tol_sensi_factor_ = static_cast(ss_tol_sensi_factor); +} + double Solver::getRelativeToleranceSteadyStateSensi() const { - return static_cast(isNaN(ss_rtol_sensi_) ? rtol_ : ss_rtol_sensi_); + return static_cast( + isNaN(ss_rtol_sensi_) ? rtol_ * ss_tol_sensi_factor_ : ss_rtol_sensi_ + ); } -void Solver::setRelativeToleranceSteadyStateSensi(const double rtol) { +void Solver::setRelativeToleranceSteadyStateSensi(double const rtol) { if (rtol < 0) throw AmiException("rtol must be a non-negative number"); @@ -828,10 +895,12 @@ void Solver::setRelativeToleranceSteadyStateSensi(const double rtol) { } double Solver::getAbsoluteToleranceSteadyStateSensi() const { - return static_cast(isNaN(ss_atol_sensi_) ? atol_ : ss_atol_sensi_); + return static_cast( + isNaN(ss_atol_sensi_) ? atol_ * ss_tol_sensi_factor_ : ss_atol_sensi_ + ); } -void Solver::setAbsoluteToleranceSteadyStateSensi(const double atol) { +void Solver::setAbsoluteToleranceSteadyStateSensi(double const atol) { if (atol < 0) throw AmiException("atol must be a non-negative number"); @@ -842,22 +911,28 @@ long int Solver::getMaxSteps() const { return maxsteps_; } double Solver::getMaxTime() const { return maxtime_.count(); } -void Solver::setMaxTime(double maxtime) -{ +void Solver::setMaxTime(double maxtime) { maxtime_ = std::chrono::duration(maxtime); } -void Solver::startTimer() const -{ - starttime_ = std::chrono::system_clock::now(); -} +void Solver::startTimer() const { simulation_timer_.reset(); } + +bool Solver::timeExceeded(int interval) const { + static int eval_counter = 0; + + // 0 means infinite time + if (maxtime_.count() == 0) + return false; + + if (++eval_counter % interval) + return false; -bool Solver::timeExceeded() const -{ - return std::chrono::system_clock::now() - starttime_ > maxtime_; + eval_counter = 0; + auto elapsed_s = simulation_timer_.elapsed_seconds(); + return std::chrono::duration(elapsed_s) > maxtime_; } -void Solver::setMaxSteps(const long int maxsteps) { +void Solver::setMaxSteps(long int const maxsteps) { if (maxsteps <= 0) throw AmiException("maxsteps must be a positive number"); @@ -868,7 +943,7 @@ void Solver::setMaxSteps(const long int maxsteps) { long int Solver::getMaxStepsBackwardProblem() const { return maxstepsB_; } -void Solver::setMaxStepsBackwardProblem(const long int maxsteps) { +void Solver::setMaxStepsBackwardProblem(long int const maxsteps) { if (maxsteps < 0) throw AmiException("maxsteps must be a non-negative number"); @@ -906,26 +981,26 @@ int Solver::getStateOrdering() const { return ordering_; } void Solver::setStateOrdering(int ordering) { ordering_ = ordering; if (solver_memory_ && linsol_ == LinearSolver::KLU) { - auto klu = dynamic_cast(linear_solver_.get()); + auto klu = dynamic_cast(linear_solver_.get()); klu->setOrdering(static_cast(ordering)); - klu = dynamic_cast(linear_solver_B_.get()); + klu = dynamic_cast(linear_solver_B_.get()); klu->setOrdering(static_cast(ordering)); } #ifdef SUNDIALS_SUPERLUMT if (solverMemory && linsol == LinearSolver::SuperLUMT) { - auto klu = dynamic_cast(linearSolver.get()); - klu->setOrdering( - static_cast(ordering)); - klu = dynamic_cast(linearSolverB.get()); - klu->setOrdering( - static_cast(ordering)); + auto klu = dynamic_cast(linearSolver.get()); + klu->setOrdering(static_cast(ordering + )); + klu = dynamic_cast(linearSolverB.get()); + klu->setOrdering(static_cast(ordering + )); } #endif } bool Solver::getStabilityLimitFlag() const { return stldet_; } -void Solver::setStabilityLimitFlag(const bool stldet) { +void Solver::setStabilityLimitFlag(bool const stldet) { stldet_ = stldet; if (solver_memory_) { setStabLimDet(stldet); @@ -958,29 +1033,31 @@ RDataReporting Solver::getReturnDataReportingMode() const { }; void Solver::setReturnDataReportingMode(RDataReporting rdrm) { - if (rdrm == RDataReporting::residuals && - sensi_meth_ == SensitivityMethod::adjoint) + if (rdrm == RDataReporting::residuals + && sensi_meth_ == SensitivityMethod::adjoint) throw AmiException("Adjoint Sensitivity Analysis cannot report " "residuals!"); rdata_mode_ = rdrm; } -void Solver::initializeNonLinearSolverSens(const Model *model) const { +void Solver::initializeNonLinearSolverSens(Model const* model) const { switch (iter_) { case NonlinearSolverIteration::newton: switch (ism_) { case InternalSensitivityMethod::staggered: case InternalSensitivityMethod::simultaneous: non_linear_solver_sens_ = std::make_unique( - 1 + model->nplist(), x_.getNVector()); + 1 + model->nplist(), x_.getNVector() + ); break; case InternalSensitivityMethod::staggered1: - non_linear_solver_sens_ = - std::make_unique(x_.getNVector()); + non_linear_solver_sens_ + = std::make_unique(x_.getNVector()); break; default: throw AmiException( - "Unsupported internal sensitivity method selected: %d", ism_); + "Unsupported internal sensitivity method selected: %d", ism_ + ); } break; case NonlinearSolverIteration::fixedpoint: @@ -988,20 +1065,23 @@ void Solver::initializeNonLinearSolverSens(const Model *model) const { case InternalSensitivityMethod::staggered: case InternalSensitivityMethod::simultaneous: non_linear_solver_sens_ = std::make_unique( - 1 + model->nplist(), x_.getNVector()); + 1 + model->nplist(), x_.getNVector() + ); break; case InternalSensitivityMethod::staggered1: - non_linear_solver_sens_ = - std::make_unique(x_.getNVector()); + non_linear_solver_sens_ + = std::make_unique(x_.getNVector()); break; default: throw AmiException( - "Unsupported internal sensitivity method selected: %d", ism_); + "Unsupported internal sensitivity method selected: %d", ism_ + ); } break; default: - throw AmiException("Invalid non-linear solver specified (%d).", - static_cast(iter_)); + throw AmiException( + "Invalid non-linear solver specified (%d).", static_cast(iter_) + ); } setNonLinearSolverSens(); @@ -1019,14 +1099,14 @@ bool Solver::getSensInitDone() const { return sens_initialized_; } bool Solver::getAdjInitDone() const { return adj_initialized_; } -bool Solver::getInitDoneB(const int which) const { - return static_cast(initializedB_.size()) > which && - initializedB_.at(which); +bool Solver::getInitDoneB(int const which) const { + return static_cast(initializedB_.size()) > which + && initializedB_.at(which); } -bool Solver::getQuadInitDoneB(const int which) const { - return static_cast(initializedQB_.size()) > which && - initializedQB_.at(which); +bool Solver::getQuadInitDoneB(int const which) const { + return static_cast(initializedQB_.size()) > which + && initializedQB_.at(which); } bool Solver::getQuadInitDone() const { return quad_initialized_; } @@ -1035,17 +1115,15 @@ void Solver::setInitDone() const { initialized_ = true; }; void Solver::setSensInitDone() const { sens_initialized_ = true; } -void Solver::setSensInitOff() const { sens_initialized_ = false; } - void Solver::setAdjInitDone() const { adj_initialized_ = true; } -void Solver::setInitDoneB(const int which) const { +void Solver::setInitDoneB(int const which) const { if (which >= static_cast(initializedB_.size())) initializedB_.resize(which + 1, false); initializedB_.at(which) = true; } -void Solver::setQuadInitDoneB(const int which) const { +void Solver::setQuadInitDoneB(int const which) const { if (which >= static_cast(initializedQB_.size())) initializedQB_.resize(which + 1, false); initializedQB_.at(which) = true; @@ -1053,21 +1131,14 @@ void Solver::setQuadInitDoneB(const int which) const { void Solver::setQuadInitDone() const { quad_initialized_ = true; } -void Solver::switchForwardSensisOff() const { - sensToggleOff(); - setSensInitOff(); -} +void Solver::switchForwardSensisOff() const { sensToggleOff(); } -realtype Solver::getCpuTime() const { - return cpu_time_; -} +realtype Solver::getCpuTime() const { return cpu_time_; } -realtype Solver::getCpuTimeB() const { - return cpu_timeB_; -} +realtype Solver::getCpuTimeB() const { return cpu_timeB_; } -void Solver::resetMutableMemory(const int nx, const int nplist, - const int nquad) const { +void Solver::resetMutableMemory(int const nx, int const nplist, int const nquad) + const { solver_memory_ = nullptr; initialized_ = false; adj_initialized_ = false; @@ -1091,8 +1162,9 @@ void Solver::resetMutableMemory(const int nx, const int nplist, initializedQB_.clear(); } -void Solver::writeSolution(realtype *t, AmiVector &x, AmiVector &dx, - AmiVectorArray &sx, AmiVector &xQ) const { +void Solver::writeSolution( + realtype* t, AmiVector& x, AmiVector& dx, AmiVectorArray& sx, AmiVector& xQ +) const { *t = gett(); if (quad_initialized_) xQ.copy(getQuadrature(*t)); @@ -1102,15 +1174,16 @@ void Solver::writeSolution(realtype *t, AmiVector &x, AmiVector &dx, dx.copy(getDerivativeState(*t)); } -void Solver::writeSolutionB(realtype *t, AmiVector &xB, AmiVector &dxB, - AmiVector &xQB, const int which) const { +void Solver::writeSolutionB( + realtype* t, AmiVector& xB, AmiVector& dxB, AmiVector& xQB, int const which +) const { *t = gett(); xB.copy(getAdjointState(which, *t)); dxB.copy(getAdjointDerivativeState(which, *t)); xQB.copy(getAdjointQuadrature(which, *t)); } -const AmiVector &Solver::getState(const realtype t) const { +AmiVector const& Solver::getState(const realtype t) const { if (t == t_) return x_; @@ -1120,7 +1193,7 @@ const AmiVector &Solver::getState(const realtype t) const { return dky_; } -const AmiVector &Solver::getDerivativeState(const realtype t) const { +AmiVector const& Solver::getDerivativeState(const realtype t) const { if (t == t_) return dx_; @@ -1130,7 +1203,7 @@ const AmiVector &Solver::getDerivativeState(const realtype t) const { return dky_; } -const AmiVectorArray &Solver::getStateSensitivity(const realtype t) const { +AmiVectorArray const& Solver::getStateSensitivity(const realtype t) const { if (sens_initialized_ && solver_was_called_F_) { if (t == t_) { getSens(); @@ -1141,8 +1214,8 @@ const AmiVectorArray &Solver::getStateSensitivity(const realtype t) const { return sx_; } -const AmiVector &Solver::getAdjointState(const int which, - const realtype t) const { +AmiVector const& +Solver::getAdjointState(int const which, const realtype t) const { if (adj_initialized_) { if (solver_was_called_B_) { if (t == t_) { @@ -1157,8 +1230,8 @@ const AmiVector &Solver::getAdjointState(const int which, return dky_; } -const AmiVector &Solver::getAdjointDerivativeState(const int which, - const realtype t) const { +AmiVector const& +Solver::getAdjointDerivativeState(int const which, const realtype t) const { if (adj_initialized_) { if (solver_was_called_B_) { if (t == t_) { @@ -1173,8 +1246,8 @@ const AmiVector &Solver::getAdjointDerivativeState(const int which, return dky_; } -const AmiVector &Solver::getAdjointQuadrature(const int which, - const realtype t) const { +AmiVector const& +Solver::getAdjointQuadrature(int const which, const realtype t) const { if (adj_initialized_) { if (solver_was_called_B_) { if (t == t_) { @@ -1189,7 +1262,7 @@ const AmiVector &Solver::getAdjointQuadrature(const int which, return xQB_; } -const AmiVector &Solver::getQuadrature(realtype t) const { +AmiVector const& Solver::getQuadrature(realtype t) const { if (quad_initialized_) { if (solver_was_called_F_) { if (t == t_) { @@ -1204,48 +1277,59 @@ const AmiVector &Solver::getQuadrature(realtype t) const { return xQ_; } - realtype Solver::gett() const { return t_; } -void wrapErrHandlerFn(int error_code, const char *module, - const char *function, char *msg, void * eh_data) { +void wrapErrHandlerFn( + int error_code, char const* module, char const* function, char* msg, + void* eh_data +) { constexpr int BUF_SIZE = 250; char buffer[BUF_SIZE]; char buffid[BUF_SIZE]; - snprintf(buffer, BUF_SIZE, "AMICI ERROR: in module %s in function %s : %s ", module, - function, msg); + snprintf( + buffer, BUF_SIZE, "AMICI ERROR: in module %s in function %s : %s ", + module, function, msg + ); switch (error_code) { case 99: - snprintf(buffid, BUF_SIZE, "AMICI:%s:%s:WARNING", module, function); + snprintf(buffid, BUF_SIZE, "%s:%s:WARNING", module, function); break; - case -1: - snprintf(buffid, BUF_SIZE, "AMICI:%s:%s:TOO_MUCH_WORK", module, function); + case AMICI_TOO_MUCH_WORK: + snprintf(buffid, BUF_SIZE, "%s:%s:TOO_MUCH_WORK", module, function); break; - case -2: - snprintf(buffid, BUF_SIZE, "AMICI:%s:%s:TOO_MUCH_ACC", module, function); + case AMICI_TOO_MUCH_ACC: + snprintf(buffid, BUF_SIZE, "%s:%s:TOO_MUCH_ACC", module, function); break; - case -3: - snprintf(buffid, BUF_SIZE, "AMICI:%s:%s:ERR_FAILURE", module, function); + case AMICI_ERR_FAILURE: + snprintf(buffid, BUF_SIZE, "%s:%s:ERR_FAILURE", module, function); break; - case -4: - snprintf(buffid, BUF_SIZE, "AMICI:%s:%s:CONV_FAILURE", module, function); + case AMICI_CONV_FAILURE: + snprintf(buffid, BUF_SIZE, "%s:%s:CONV_FAILURE", module, function); + break; + + case AMICI_RHSFUNC_FAIL: + snprintf(buffid, BUF_SIZE, "%s:%s:RHSFUNC_FAIL", module, function); + break; + + case AMICI_FIRST_RHSFUNC_ERR: + snprintf(buffid, BUF_SIZE, "%s:%s:FIRST_RHSFUNC_ERR", module, function); break; default: - snprintf(buffid, BUF_SIZE, "AMICI:%s:%s:OTHER", module, function); + snprintf(buffid, BUF_SIZE, "%s:%s:OTHER", module, function); break; } - - if(!eh_data) { + if (!eh_data) { throw std::runtime_error("eh_data unset"); } auto solver = static_cast(eh_data); - solver->app->warning(buffid, buffer); + if (solver->logger) + solver->logger->log(LogSeverity::debug, buffid, buffer); } } // namespace amici diff --git a/deps/AMICI/src/solver_cvodes.cpp b/deps/AMICI/src/solver_cvodes.cpp index fb0c8fd0f..7157302c9 100644 --- a/deps/AMICI/src/solver_cvodes.cpp +++ b/deps/AMICI/src/solver_cvodes.cpp @@ -1,7 +1,6 @@ #include "amici/solver_cvodes.h" #include "amici/exception.h" -#include "amici/misc.h" #include "amici/model_ode.h" #include "amici/sundials_linsol_wrapper.h" @@ -21,8 +20,9 @@ namespace amici { // Ensure AMICI options are in sync with Sundials options -static_assert((int)InternalSensitivityMethod::simultaneous == CV_SIMULTANEOUS, - ""); +static_assert( + (int)InternalSensitivityMethod::simultaneous == CV_SIMULTANEOUS, "" +); static_assert((int)InternalSensitivityMethod::staggered == CV_STAGGERED, ""); static_assert((int)InternalSensitivityMethod::staggered1 == CV_STAGGERED1, ""); @@ -34,68 +34,76 @@ static_assert((int)LinearMultistepMethod::BDF == CV_BDF, ""); static_assert(AMICI_ROOT_RETURN == CV_ROOT_RETURN, ""); - /* * The following static members are callback function to CVODES. * Their signatures must not be changes. */ -static int fxdot(realtype t, N_Vector x, N_Vector xdot, void *user_data); - -static int fJSparse(realtype t, N_Vector x, N_Vector xdot, SUNMatrix J, - void *user_data, N_Vector tmp1, N_Vector tmp2, - N_Vector tmp3); +static int fxdot(realtype t, N_Vector x, N_Vector xdot, void* user_data); -static int fJ(realtype t, N_Vector x, N_Vector xdot, SUNMatrix J, - void *user_data, N_Vector tmp1, N_Vector tmp2, N_Vector tmp3); +static int fJSparse( + realtype t, N_Vector x, N_Vector xdot, SUNMatrix J, void* user_data, + N_Vector tmp1, N_Vector tmp2, N_Vector tmp3 +); -static int fJB(realtype t, N_Vector x, N_Vector xB, N_Vector xBdot, - SUNMatrix JB, void *user_data, N_Vector tmp1B, - N_Vector tmp2B, N_Vector tmp3B); +static int +fJ(realtype t, N_Vector x, N_Vector xdot, SUNMatrix J, void* user_data, + N_Vector tmp1, N_Vector tmp2, N_Vector tmp3); -static int fJSparseB(realtype t, N_Vector x, N_Vector xB, N_Vector xBdot, - SUNMatrix JB, void *user_data, N_Vector tmp1B, - N_Vector tmp2B, N_Vector tmp3B); +static int +fJB(realtype t, N_Vector x, N_Vector xB, N_Vector xBdot, SUNMatrix JB, + void* user_data, N_Vector tmp1B, N_Vector tmp2B, N_Vector tmp3B); -static int fJBand(realtype t, N_Vector x, N_Vector xdot, SUNMatrix J, - void *user_data, N_Vector tmp1, N_Vector tmp2, - N_Vector tmp3); +static int fJSparseB( + realtype t, N_Vector x, N_Vector xB, N_Vector xBdot, SUNMatrix JB, + void* user_data, N_Vector tmp1B, N_Vector tmp2B, N_Vector tmp3B +); -static int fJBandB(realtype t, N_Vector x, N_Vector xB, N_Vector xBdot, - SUNMatrix JB, void *user_data, N_Vector tmp1B, - N_Vector tmp2B, N_Vector tmp3B); +static int fJBand( + realtype t, N_Vector x, N_Vector xdot, SUNMatrix J, void* user_data, + N_Vector tmp1, N_Vector tmp2, N_Vector tmp3 +); -static int fJv(N_Vector v, N_Vector Jv, realtype t, N_Vector x, - N_Vector xdot, void *user_data, N_Vector tmp); +static int fJBandB( + realtype t, N_Vector x, N_Vector xB, N_Vector xBdot, SUNMatrix JB, + void* user_data, N_Vector tmp1B, N_Vector tmp2B, N_Vector tmp3B +); -static int fJvB(N_Vector vB, N_Vector JvB, realtype t, N_Vector x, - N_Vector xB, N_Vector xBdot, void *user_data, - N_Vector tmpB); +static int +fJv(N_Vector v, N_Vector Jv, realtype t, N_Vector x, N_Vector xdot, + void* user_data, N_Vector tmp); -static int froot(realtype t, N_Vector x, realtype *root, void *user_data); +static int fJvB( + N_Vector vB, N_Vector JvB, realtype t, N_Vector x, N_Vector xB, + N_Vector xBdot, void* user_data, N_Vector tmpB +); -static int fxBdot(realtype t, N_Vector x, N_Vector xB, N_Vector xBdot, - void *user_data); +static int froot(realtype t, N_Vector x, realtype* root, void* user_data); -static int fqBdot(realtype t, N_Vector x, N_Vector xB, N_Vector qBdot, - void *user_data); +static int +fxBdot(realtype t, N_Vector x, N_Vector xB, N_Vector xBdot, void* user_data); -static int fxBdot_ss(realtype t, N_Vector xB, N_Vector xBdot, void *user_data); +static int +fqBdot(realtype t, N_Vector x, N_Vector xB, N_Vector qBdot, void* user_data); -static int fqBdot_ss(realtype t, N_Vector xB, N_Vector qBdot, void *user_data); +static int fxBdot_ss(realtype t, N_Vector xB, N_Vector xBdot, void* user_data); -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); +static int fqBdot_ss(realtype t, N_Vector xB, N_Vector qBdot, void* user_data); -static int fsxdot(int Ns, realtype t, N_Vector x, N_Vector xdot, int ip, - N_Vector sx, N_Vector sxdot, void *user_data, - N_Vector tmp1, N_Vector tmp2); +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 +); +static int fsxdot( + int Ns, realtype t, N_Vector x, N_Vector xdot, int ip, N_Vector sx, + N_Vector sxdot, void* user_data, N_Vector tmp1, N_Vector tmp2 +); /* Function implementations */ -void CVodeSolver::init(const realtype t0, const AmiVector &x0, - const AmiVector & /*dx0*/) const { +void CVodeSolver:: + init(const realtype t0, AmiVector const& x0, AmiVector const& /*dx0*/) + const { solver_was_called_F_ = false; force_reinit_postprocess_F_ = false; t_ = t0; @@ -111,17 +119,23 @@ void CVodeSolver::init(const realtype t0, const AmiVector &x0, throw CvodeException(status, "CVodeInit"); } -void CVodeSolver::initSteadystate(const realtype /*t0*/, const AmiVector &/*x0*/, - const AmiVector &/*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, - as re-calling init would unset solver settings. */ +void CVodeSolver::initSteadystate( + const realtype /*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, + // as re-calling init would unset solver settings. auto cv_mem = static_cast(solver_memory_.get()); cv_mem->cv_f = fxBdot_ss; + + // Since SUNDIALS v5.8.0, we also need to update the NlsRhs function, + // otherwise the old value of `cv_mem->cv_f` would still be used there and + // lead to incorrect simulation results. + CVodeSetNlsRhsFn(solver_memory_.get(), fxBdot_ss); } -void CVodeSolver::sensInit1(const AmiVectorArray &sx0, - const AmiVectorArray & /*sdx0*/) const { +void CVodeSolver:: + sensInit1(AmiVectorArray const& sx0, AmiVectorArray const& /*sdx0*/) const { int status = CV_SUCCESS; sx_ = sx0; if (getSensitivityMethod() == SensitivityMethod::forward && nplist() > 0) { @@ -129,12 +143,14 @@ void CVodeSolver::sensInit1(const AmiVectorArray &sx0, status = CVodeSensReInit( solver_memory_.get(), static_cast(getInternalSensitivityMethod()), - sx_.getNVectorArray()); + sx_.getNVectorArray() + ); } else { - status = - CVodeSensInit1(solver_memory_.get(), nplist(), - static_cast(getInternalSensitivityMethod()), - fsxdot, sx_.getNVectorArray()); + status = CVodeSensInit1( + solver_memory_.get(), nplist(), + static_cast(getInternalSensitivityMethod()), fsxdot, + sx_.getNVectorArray() + ); setSensInitDone(); } } @@ -142,32 +158,37 @@ void CVodeSolver::sensInit1(const AmiVectorArray &sx0, throw CvodeException(status, "CVodeSensInit1"); } -void CVodeSolver::binit(const int which, const realtype tf, - const AmiVector &xB0, - const AmiVector & /*dxB0*/) const { +void CVodeSolver::binit( + int const which, const realtype tf, AmiVector const& xB0, + AmiVector const& /*dxB0*/ +) const { solver_was_called_B_ = false; force_reinit_postprocess_B_ = false; xB_ = xB0; int status; if (getInitDoneB(which)) { - status = CVodeReInitB(solver_memory_.get(), which, tf, xB_.getNVector()); + status + = CVodeReInitB(solver_memory_.get(), which, tf, xB_.getNVector()); } else { - status = - CVodeInitB(solver_memory_.get(), which, fxBdot, tf, xB_.getNVector()); + status = CVodeInitB( + solver_memory_.get(), which, fxBdot, tf, xB_.getNVector() + ); setInitDoneB(which); } if (status != CV_SUCCESS) throw CvodeException(status, "CVodeInitB"); } -void CVodeSolver::qbinit(const int which, const AmiVector &xQB0) const { +void CVodeSolver::qbinit(int const which, AmiVector const& xQB0) const { xQB_ = xQB0; int status; if (getQuadInitDoneB(which)) { - status = CVodeQuadReInitB(solver_memory_.get(), which, xQB_.getNVector()); + status + = CVodeQuadReInitB(solver_memory_.get(), which, xQB_.getNVector()); } else { - status = - CVodeQuadInitB(solver_memory_.get(), which, fqBdot, xQB_.getNVector()); + status = CVodeQuadInitB( + solver_memory_.get(), which, fqBdot, xQB_.getNVector() + ); setQuadInitDoneB(which); } if (status != CV_SUCCESS) @@ -234,71 +255,76 @@ void CVodeSolver::setSparseJacFn_ss() const { throw CvodeException(status, "CVodeSetJacFn"); } -Solver *CVodeSolver::clone() const { return new CVodeSolver(*this); } +Solver* CVodeSolver::clone() const { return new CVodeSolver(*this); } void CVodeSolver::allocateSolver() const { if (!solver_memory_) - solver_memory_ = std::unique_ptr>( + solver_memory_ = std::unique_ptr>( CVodeCreate(static_cast(lmm_)), - [](void *ptr) { CVodeFree(&ptr); }); + [](void* ptr) { CVodeFree(&ptr); } + ); } -void CVodeSolver::setSStolerances(const double rtol, const double atol) const { +void CVodeSolver::setSStolerances(double const rtol, double const atol) const { int status = CVodeSStolerances(solver_memory_.get(), rtol, atol); if (status != CV_SUCCESS) throw CvodeException(status, "CVodeSStolerances"); } -void CVodeSolver::setSensSStolerances(const double rtol, - const double *atol) const { - int status = CVodeSensSStolerances(solver_memory_.get(), rtol, - const_cast(atol)); +void CVodeSolver::setSensSStolerances(double const rtol, double const* atol) + const { + int status = CVodeSensSStolerances( + solver_memory_.get(), rtol, const_cast(atol) + ); if (status != CV_SUCCESS) - throw CvodeException(status, "CVodeSensEEtolerances"); + throw CvodeException(status, "CVodeSensSStolerances"); } -void CVodeSolver::setSensErrCon(const bool error_corr) const { +void CVodeSolver::setSensErrCon(bool const error_corr) const { int status = CVodeSetSensErrCon(solver_memory_.get(), error_corr); if (status != CV_SUCCESS) throw CvodeException(status, "CVodeSetSensErrCon"); } -void CVodeSolver::setQuadErrConB(const int which, const bool flag) const { +void CVodeSolver::setQuadErrConB(int const which, bool const flag) const { int status = CVodeSetQuadErrConB(solver_memory_.get(), which, flag); if (status != CV_SUCCESS) throw CvodeException(status, "CVodeSetQuadErrConB"); } -void CVodeSolver::setQuadErrCon(const bool flag) const { +void CVodeSolver::setQuadErrCon(bool const flag) const { int status = CVodeSetQuadErrCon(solver_memory_.get(), flag); if (status != CV_SUCCESS) throw CvodeException(status, "CVodeSetQuadErrCon"); } -void CVodeSolver::getRootInfo(int *rootsfound) const { +void CVodeSolver::getRootInfo(int* rootsfound) const { int status = CVodeGetRootInfo(solver_memory_.get(), rootsfound); if (status != CV_SUCCESS) throw CvodeException(status, "CVodeGetRootInfo"); } void CVodeSolver::setLinearSolver() const { - int status = CVodeSetLinearSolver(solver_memory_.get(), linear_solver_->get(), - linear_solver_->getMatrix()); + int status = CVodeSetLinearSolver( + solver_memory_.get(), linear_solver_->get(), linear_solver_->getMatrix() + ); if (status != CV_SUCCESS) throw CvodeException(status, "setLinearSolver"); } void CVodeSolver::setLinearSolverB(int which) const { - int status = - CVodeSetLinearSolverB(solver_memory_.get(), which, linear_solver_B_->get(), - linear_solver_B_->getMatrix()); + int status = CVodeSetLinearSolverB( + solver_memory_.get(), which, linear_solver_B_->get(), + linear_solver_B_->getMatrix() + ); if (status != CV_SUCCESS) throw CvodeException(status, "setLinearSolverB"); } void CVodeSolver::setNonLinearSolver() const { - int status = - CVodeSetNonlinearSolver(solver_memory_.get(), non_linear_solver_->get()); + int status = CVodeSetNonlinearSolver( + solver_memory_.get(), non_linear_solver_->get() + ); if (status != CV_SUCCESS) throw CvodeException(status, "CVodeSetNonlinearSolver"); } @@ -313,80 +339,82 @@ void CVodeSolver::setNonLinearSolverSens() const { switch (ism_) { case InternalSensitivityMethod::staggered: - status = CVodeSetNonlinearSolverSensStg(solver_memory_.get(), - non_linear_solver_sens_->get()); + status = CVodeSetNonlinearSolverSensStg( + solver_memory_.get(), non_linear_solver_sens_->get() + ); break; case InternalSensitivityMethod::simultaneous: - status = CVodeSetNonlinearSolverSensSim(solver_memory_.get(), - non_linear_solver_sens_->get()); + status = CVodeSetNonlinearSolverSensSim( + solver_memory_.get(), non_linear_solver_sens_->get() + ); break; case InternalSensitivityMethod::staggered1: - status = CVodeSetNonlinearSolverSensStg1(solver_memory_.get(), - non_linear_solver_sens_->get()); + status = CVodeSetNonlinearSolverSensStg1( + solver_memory_.get(), non_linear_solver_sens_->get() + ); break; default: throw AmiException( - "Unsupported internal sensitivity method selected: %d", ism_); + "Unsupported internal sensitivity method selected: %d", ism_ + ); } if (status != CV_SUCCESS) throw CvodeException(status, "CVodeSolver::setNonLinearSolverSens"); } -void CVodeSolver::setNonLinearSolverB(const int which) const { - int status = CVodeSetNonlinearSolverB(solver_memory_.get(), which, - non_linear_solver_B_->get()); +void CVodeSolver::setNonLinearSolverB(int const which) const { + int status = CVodeSetNonlinearSolverB( + solver_memory_.get(), which, non_linear_solver_B_->get() + ); if (status != CV_SUCCESS) throw CvodeException(status, "CVodeSetNonlinearSolverB"); } void CVodeSolver::setErrHandlerFn() const { - int status = - CVodeSetErrHandlerFn(solver_memory_.get(), wrapErrHandlerFn, - reinterpret_cast( - const_cast(this))); + int status = CVodeSetErrHandlerFn( + solver_memory_.get(), wrapErrHandlerFn, + reinterpret_cast(const_cast(this)) + ); if (status != CV_SUCCESS) throw CvodeException(status, "CVodeSetErrHandlerFn"); } void CVodeSolver::setUserData() const { - int status = CVodeSetUserData( - solver_memory_.get(), - &user_data - ); + int status = CVodeSetUserData(solver_memory_.get(), &user_data); if (status != CV_SUCCESS) throw CvodeException(status, "CVodeSetUserData"); } -void CVodeSolver::setUserDataB(const int which) const { +void CVodeSolver::setUserDataB(int const which) const { int status = CVodeSetUserDataB(solver_memory_.get(), which, &user_data); if (status != CV_SUCCESS) throw CvodeException(status, "CVodeSetUserDataB"); } -void CVodeSolver::setMaxNumSteps(const long mxsteps) const { +void CVodeSolver::setMaxNumSteps(long const mxsteps) const { int status = CVodeSetMaxNumSteps(solver_memory_.get(), mxsteps); if (status != CV_SUCCESS) throw CvodeException(status, "CVodeSetMaxNumSteps"); } -void CVodeSolver::setStabLimDet(const int stldet) const { +void CVodeSolver::setStabLimDet(int const stldet) const { int status = CVodeSetStabLimDet(solver_memory_.get(), stldet); if (status != CV_SUCCESS) throw CvodeException(status, "CVodeSetStabLimDet"); } -void CVodeSolver::setStabLimDetB(const int which, const int stldet) const { +void CVodeSolver::setStabLimDetB(int const which, int const stldet) const { int status = CVodeSetStabLimDetB(solver_memory_.get(), which, stldet); if (status != CV_SUCCESS) throw CvodeException(status, "CVodeSetStabLimDetB"); } -void CVodeSolver::setId(const Model * /*model*/) const {} +void CVodeSolver::setId(Model const* /*model*/) const {} -void CVodeSolver::setSuppressAlg(const bool /*flag*/) const {} +void CVodeSolver::setSuppressAlg(bool const /*flag*/) const {} -void CVodeSolver::resetState(void *ami_mem, const_N_Vector y0) const { +void CVodeSolver::resetState(void* ami_mem, const_N_Vector y0) const { auto cv_mem = static_cast(ami_mem); /* here we force the order in the next step to zero, and update the @@ -437,16 +465,18 @@ void CVodeSolver::reInitPostProcessB(const realtype tnext) const { // store current backward problem in ca_mem to make it accessible in // adjoint rhs wrapper functions ca_mem->ca_bckpbCrt = cvB_mem; - reInitPostProcess(static_cast(cvB_mem->cv_mem), &tBret, &xB_, - tnext); + reInitPostProcess( + static_cast(cvB_mem->cv_mem), &tBret, &xB_, tnext + ); cvB_mem->cv_tout = tBret; cvB_mem = cvB_mem->cv_next; } force_reinit_postprocess_B_ = false; } -void CVodeSolver::reInitPostProcess(void *ami_mem, realtype *t, AmiVector *yout, - const realtype tout) const { +void CVodeSolver::reInitPostProcess( + void* ami_mem, realtype* t, AmiVector* yout, const realtype tout +) const { auto cv_mem = static_cast(ami_mem); auto nst_tmp = cv_mem->cv_nst; cv_mem->cv_nst = 0; @@ -458,10 +488,13 @@ void CVodeSolver::reInitPostProcess(void *ami_mem, realtype *t, AmiVector *yout, status = CVode(ami_mem, tout, yout->getNVector(), t, CV_ONE_STEP); if (status == CV_ROOT_RETURN) - throw CvodeException(status, "CVode returned a root after " + 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 " - "and relative tolerances!"); + "and relative tolerances!" + ); if (status != CV_SUCCESS) throw CvodeException(status, "reInitPostProcess"); @@ -492,8 +525,9 @@ void CVodeSolver::reInitPostProcess(void *ami_mem, realtype *t, AmiVector *yout, } } -void CVodeSolver::reInit(const realtype t0, const AmiVector &yy0, - const AmiVector & /*yp0*/) const { +void CVodeSolver:: + reInit(const realtype t0, AmiVector const& yy0, AmiVector const& /*yp0*/) + const { auto cv_mem = static_cast(solver_memory_.get()); cv_mem->cv_tn = t0; if (solver_was_called_F_) @@ -502,8 +536,9 @@ void CVodeSolver::reInit(const realtype t0, const AmiVector &yy0, resetState(cv_mem, x_.getNVector()); } -void CVodeSolver::sensReInit(const AmiVectorArray &yyS0, - const AmiVectorArray & /*ypS0*/) const { +void CVodeSolver:: + sensReInit(AmiVectorArray const& yyS0, AmiVectorArray const& /*ypS0*/) + const { auto cv_mem = static_cast(solver_memory_.get()); /* Initialize znS[0] in the history array */ for (int is = 0; is < nplist(); is++) @@ -511,17 +546,20 @@ void CVodeSolver::sensReInit(const AmiVectorArray &yyS0, if (solver_was_called_F_) force_reinit_postprocess_F_ = true; sx_.copy(yyS0); - int status = N_VScaleVectorArray(nplist(), cv_mem->cv_cvals, - sx_.getNVectorArray(), cv_mem->cv_znS[0]); + int status = N_VScaleVectorArray( + nplist(), cv_mem->cv_cvals, sx_.getNVectorArray(), cv_mem->cv_znS[0] + ); if (status != CV_SUCCESS) throw CvodeException(CV_VECTOROP_ERR, "CVodeSensReInit"); } -void CVodeSolver::reInitB(const int which, const realtype tB0, - const AmiVector &yyB0, - const AmiVector & /*ypB0*/) const { - auto cv_memB = - static_cast(CVodeGetAdjCVodeBmem(solver_memory_.get(), which)); +void CVodeSolver::reInitB( + int const which, const realtype tB0, AmiVector const& yyB0, + AmiVector const& /*ypB0*/ +) const { + auto cv_memB = static_cast( + CVodeGetAdjCVodeBmem(solver_memory_.get(), which) + ); if (solver_was_called_B_) force_reinit_postprocess_B_ = true; cv_memB->cv_tn = tB0; @@ -533,22 +571,28 @@ void CVodeSolver::sensToggleOff() const { auto status = CVodeSensToggleOff(solver_memory_.get()); if (status != CV_SUCCESS) throw CvodeException(status, "CVodeSensToggleOff"); + /* need to deallocate sensi memory, otherwise can't reenable */ + CVodeSensFree(solver_memory_.get()); + sens_initialized_ = false; } -void CVodeSolver::quadReInitB(int which, const AmiVector &yQB0) const { - auto cv_memB = - static_cast(CVodeGetAdjCVodeBmem(solver_memory_.get(), which)); +void CVodeSolver::quadReInitB(int which, AmiVector const& yQB0) const { + auto cv_memB = static_cast( + CVodeGetAdjCVodeBmem(solver_memory_.get(), which) + ); if (solver_was_called_B_) force_reinit_postprocess_B_ = true; xQB_.copy(yQB0); N_VScale(ONE, xQB_.getNVector(), cv_memB->cv_znQ[0]); } -void CVodeSolver::setSensParams(const realtype *p, const realtype *pbar, - const int *plist) const { +void CVodeSolver::setSensParams( + realtype const* p, realtype const* pbar, int const* plist +) const { int status = CVodeSetSensParams( - solver_memory_.get(), const_cast(p), - const_cast(pbar), const_cast(plist)); + solver_memory_.get(), const_cast(p), + const_cast(pbar), const_cast(plist) + ); if (status != CV_SUCCESS) throw CvodeException(status, "CVodeSetSensParams"); } @@ -561,52 +605,55 @@ void CVodeSolver::getDky(realtype t, int k) const { void CVodeSolver::getSens() const { realtype tDummy = 0; - int status = - CVodeGetSens(solver_memory_.get(), &tDummy, sx_.getNVectorArray()); + int status + = CVodeGetSens(solver_memory_.get(), &tDummy, sx_.getNVectorArray()); if (status != CV_SUCCESS) throw CvodeException(status, "CVodeGetSens"); } -void CVodeSolver::getSensDky(const realtype t, const int k) const { - int status = - CVodeGetSensDky(solver_memory_.get(), t, k, sx_.getNVectorArray()); +void CVodeSolver::getSensDky(const realtype 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, const int k, - const int which) const { - int status = CVodeGetDky(CVodeGetAdjCVodeBmem(solver_memory_.get(), which), t, - k, dky_.getNVector()); +void CVodeSolver::getDkyB(const realtype t, int const k, int const which) + const { + int status = CVodeGetDky( + CVodeGetAdjCVodeBmem(solver_memory_.get(), which), t, k, + dky_.getNVector() + ); if (status != CV_SUCCESS) throw CvodeException(status, "CVodeGetDkyB"); } void CVodeSolver::getQuadB(int which) const { realtype tDummy = 0; - int status = - CVodeGetQuadB(solver_memory_.get(), which, &tDummy, xQB_.getNVector()); + int status = CVodeGetQuadB( + solver_memory_.get(), which, &tDummy, xQB_.getNVector() + ); if (status != CV_SUCCESS) throw CvodeException(status, "CVodeGetQuadB"); } -void CVodeSolver::getQuad(realtype &t) const { +void CVodeSolver::getQuad(realtype& t) const { int status = CVodeGetQuad(solver_memory_.get(), &t, xQ_.getNVector()); if (status != CV_SUCCESS) throw CvodeException(status, "CVodeGetQuad"); } -void CVodeSolver::getQuadDkyB(const realtype t, const int k, int which) const { - int status = - CVodeGetQuadDky(CVodeGetAdjCVodeBmem(solver_memory_.get(), which), t, k, - xQB_.getNVector()); +void CVodeSolver::getQuadDkyB(const realtype t, int const k, int which) const { + int status = CVodeGetQuadDky( + CVodeGetAdjCVodeBmem(solver_memory_.get(), which), t, k, + xQB_.getNVector() + ); if (status != CV_SUCCESS) throw CvodeException(status, "CVodeGetQuadDkyB"); } -void CVodeSolver::getQuadDky(const realtype t, const int k) const { - int status = - CVodeGetQuadDky(solver_memory_.get(), t, k, xQ_.getNVector()); +void CVodeSolver::getQuadDky(const realtype t, int const k) const { + int status = CVodeGetQuadDky(solver_memory_.get(), t, k, xQ_.getNVector()); if (status != CV_SUCCESS) throw CvodeException(status, "CVodeGetQuadDky"); } @@ -616,75 +663,86 @@ void CVodeSolver::adjInit() const { if (getAdjInitDone()) { status = CVodeAdjReInit(solver_memory_.get()); } else { - status = CVodeAdjInit(solver_memory_.get(), static_cast(maxsteps_), - static_cast(interp_type_)); + status = CVodeAdjInit( + solver_memory_.get(), static_cast(maxsteps_), + static_cast(interp_type_) + ); setAdjInitDone(); } if (status != CV_SUCCESS) throw CvodeException(status, "CVodeAdjInit"); } -void CVodeSolver::quadInit(const AmiVector &xQ0) const { +void CVodeSolver::quadInit(AmiVector const& xQ0) const { int status; xQ_.copy(xQ0); if (getQuadInitDone()) { - status = CVodeQuadReInit(solver_memory_.get(), - const_cast(xQ0.getNVector())); + status = CVodeQuadReInit( + solver_memory_.get(), const_cast(xQ0.getNVector()) + ); } else { - status = CVodeQuadInit(solver_memory_.get(), fqBdot_ss, xQ_.getNVector()); + status + = CVodeQuadInit(solver_memory_.get(), fqBdot_ss, xQ_.getNVector()); setQuadInitDone(); } if (status != CV_SUCCESS) throw CvodeException(status, "CVodeQuadInit"); } -void CVodeSolver::allocateSolverB(int *which) const { +void CVodeSolver::allocateSolverB(int* which) const { if (!solver_memory_B_.empty()) { *which = 0; return; } - int status = CVodeCreateB(solver_memory_.get(), static_cast(lmm_), which); + int status + = CVodeCreateB(solver_memory_.get(), static_cast(lmm_), which); if (*which + 1 > static_cast(solver_memory_B_.size())) solver_memory_B_.resize(*which + 1); - solver_memory_B_.at(*which) = - std::unique_ptr>( - getAdjBmem(solver_memory_.get(), *which), [](void * /*ptr*/) {}); + solver_memory_B_.at(*which) + = std::unique_ptr>( + getAdjBmem(solver_memory_.get(), *which), [](void* /*ptr*/) {} + ); if (status != CV_SUCCESS) throw CvodeException(status, "CVodeCreateB"); } -void CVodeSolver::setSStolerancesB(const int which, const realtype relTolB, - const realtype absTolB) const { - int status = - CVodeSStolerancesB(solver_memory_.get(), which, relTolB, absTolB); +void CVodeSolver::setSStolerancesB( + int const which, const realtype relTolB, const realtype absTolB +) const { + int status + = CVodeSStolerancesB(solver_memory_.get(), which, relTolB, absTolB); if (status != CV_SUCCESS) throw CvodeException(status, "CVodeSStolerancesB"); } -void CVodeSolver::quadSStolerancesB(const int which, const realtype reltolQB, - const realtype abstolQB) const { - int status = - CVodeQuadSStolerancesB(solver_memory_.get(), which, reltolQB, abstolQB); +void CVodeSolver::quadSStolerancesB( + int const which, const realtype reltolQB, const realtype abstolQB +) const { + int status = CVodeQuadSStolerancesB( + solver_memory_.get(), which, reltolQB, abstolQB + ); if (status != CV_SUCCESS) throw CvodeException(status, "CVodeQuadSStolerancesB"); } -void CVodeSolver::quadSStolerances(const realtype reltolQB, - const realtype abstolQB) const { - int status = - CVodeQuadSStolerances(solver_memory_.get(), reltolQB, abstolQB); +void CVodeSolver::quadSStolerances( + const realtype reltolQB, const realtype abstolQB +) const { + int status + = CVodeQuadSStolerances(solver_memory_.get(), reltolQB, abstolQB); if (status != CV_SUCCESS) throw CvodeException(status, "CVodeQuadSStolerances"); } -void CVodeSolver::getB(const int which) const { +void CVodeSolver::getB(int const which) const { realtype tDummy = 0; - int status = CVodeGetB(solver_memory_.get(), which, &tDummy, xB_.getNVector()); + int status + = CVodeGetB(solver_memory_.get(), which, &tDummy, xB_.getNVector()); if (status != CV_SUCCESS) throw CvodeException(status, "CVodeGetB"); } -int CVodeSolver::solve(const realtype tout, const int itask) const { +int CVodeSolver::solve(const realtype tout, int const itask) const { if (force_reinit_postprocess_F_) reInitPostProcessF(tout); int status = CVode(solver_memory_.get(), tout, x_.getNVector(), &t_, itask); @@ -694,19 +752,20 @@ int CVodeSolver::solve(const realtype tout, const int itask) const { return status; } -int CVodeSolver::solveF(const realtype tout, const int itask, - int *ncheckPtr) const { +int CVodeSolver::solveF(const realtype tout, int const itask, int* ncheckPtr) + const { if (force_reinit_postprocess_F_) reInitPostProcessF(tout); - int status = - CVodeF(solver_memory_.get(), tout, x_.getNVector(), &t_, itask, ncheckPtr); + int status = CVodeF( + solver_memory_.get(), tout, x_.getNVector(), &t_, itask, ncheckPtr + ); if (status < 0) // status > 0 is okay and is used for e.g. root return throw IntegrationFailure(status, t_); solver_was_called_F_ = true; return status; } -void CVodeSolver::solveB(const realtype tBout, const int itaskB) const { +void CVodeSolver::solveB(const realtype tBout, int const itaskB) const { if (force_reinit_postprocess_B_) reInitPostProcessB(tBout); int status = CVodeB(solver_memory_.get(), tBout, itaskB); @@ -715,7 +774,7 @@ void CVodeSolver::solveB(const realtype tBout, const int itaskB) const { solver_was_called_B_ = true; } -void CVodeSolver::setMaxNumStepsB(const int which, const long mxstepsB) const { +void CVodeSolver::setMaxNumStepsB(int const which, long const mxstepsB) const { int status = CVodeSetMaxNumStepsB(solver_memory_.get(), which, mxstepsB); if (status != CV_SUCCESS) throw CvodeException(status, "CVodeSetMaxNumStepsB"); @@ -727,54 +786,58 @@ void CVodeSolver::diag() const { throw CvodeException(status, "CVDiag"); } -void CVodeSolver::diagB(const int which) const { +void CVodeSolver::diagB(int const which) const { int status = CVDiagB(solver_memory_.get(), which); if (status != CV_SUCCESS) throw CvodeException(status, "CVDiagB"); } -void CVodeSolver::getNumSteps(const void *ami_mem, long int *numsteps) const { - int status = CVodeGetNumSteps(const_cast(ami_mem), numsteps); +void CVodeSolver::getNumSteps(void const* ami_mem, long int* numsteps) const { + int status = CVodeGetNumSteps(const_cast(ami_mem), numsteps); if (status != CV_SUCCESS) throw CvodeException(status, "CVodeGetNumSteps"); } -void CVodeSolver::getNumRhsEvals(const void *ami_mem, - long int *numrhsevals) const { - int status = CVodeGetNumRhsEvals(const_cast(ami_mem), numrhsevals); +void CVodeSolver::getNumRhsEvals(void const* ami_mem, long int* numrhsevals) + const { + int status = CVodeGetNumRhsEvals(const_cast(ami_mem), numrhsevals); if (status != CV_SUCCESS) throw CvodeException(status, "CVodeGetNumRhsEvals"); } -void CVodeSolver::getNumErrTestFails(const void *ami_mem, - long int *numerrtestfails) const { - int status = - CVodeGetNumErrTestFails(const_cast(ami_mem), numerrtestfails); +void CVodeSolver::getNumErrTestFails( + void const* ami_mem, long int* numerrtestfails +) const { + int status + = CVodeGetNumErrTestFails(const_cast(ami_mem), numerrtestfails); if (status != CV_SUCCESS) throw CvodeException(status, "CVodeGetNumErrTestFails"); } void CVodeSolver::getNumNonlinSolvConvFails( - const void *ami_mem, long int *numnonlinsolvconvfails) const { - int status = CVodeGetNumNonlinSolvConvFails(const_cast(ami_mem), - numnonlinsolvconvfails); + void const* ami_mem, long int* numnonlinsolvconvfails +) const { + int status = CVodeGetNumNonlinSolvConvFails( + const_cast(ami_mem), numnonlinsolvconvfails + ); if (status != CV_SUCCESS) throw CvodeException(status, "CVodeGetNumNonlinSolvConvFails"); } -void CVodeSolver::getLastOrder(const void *ami_mem, int *order) const { - int status = CVodeGetLastOrder(const_cast(ami_mem), order); +void CVodeSolver::getLastOrder(void const* ami_mem, int* order) const { + int status = CVodeGetLastOrder(const_cast(ami_mem), order); if (status != CV_SUCCESS) throw CvodeException(status, "CVodeGetLastOrder"); } -void *CVodeSolver::getAdjBmem(void *ami_mem, int which) const { +void* CVodeSolver::getAdjBmem(void* ami_mem, int which) const { return CVodeGetAdjCVodeBmem(ami_mem, which); } void CVodeSolver::calcIC(const realtype /*tout1*/) const {}; -void CVodeSolver::calcICB(const int /*which*/, const realtype /*tout1*/) const {}; +void CVodeSolver::calcICB(int const /*which*/, const realtype /*tout1*/) + const {}; void CVodeSolver::setStopTime(const realtype tstop) const { int status = CVodeSetStopTime(solver_memory_.get(), tstop); @@ -782,29 +845,25 @@ void CVodeSolver::setStopTime(const realtype tstop) const { throw CvodeException(status, "CVodeSetStopTime"); } - void CVodeSolver::turnOffRootFinding() const { int status = CVodeRootInit(solver_memory_.get(), 0, nullptr); if (status != CV_SUCCESS) throw CvodeException(status, "CVodeRootInit"); } - -const Model *CVodeSolver::getModel() const { +Model const* CVodeSolver::getModel() const { if (!solver_memory_) throw AmiException("Solver has not been allocated, information is not " "available"); auto cv_mem = static_cast(solver_memory_.get()); - auto typed_udata = static_cast(cv_mem->cv_user_data); + auto typed_udata = static_cast(cv_mem->cv_user_data); Expects(typed_udata); return typed_udata->first; } - /** * @brief Jacobian of xdot with respect to states x - * @param N number of state variables * @param t timepoint * @param x Vector with the states * @param xdot Vector with the right hand side @@ -815,22 +874,20 @@ const Model *CVodeSolver::getModel() const { * @param tmp3 temporary storage vector * @return status flag indicating successful execution **/ -static int fJ(realtype t, N_Vector x, N_Vector xdot, SUNMatrix J, - void *user_data, N_Vector /*tmp1*/, N_Vector /*tmp2*/, - N_Vector /*tmp3*/) { - auto typed_udata = static_cast(user_data); +static int +fJ(realtype t, N_Vector x, N_Vector xdot, SUNMatrix J, void* user_data, + N_Vector /*tmp1*/, N_Vector /*tmp2*/, N_Vector /*tmp3*/) { + auto typed_udata = static_cast(user_data); Expects(typed_udata); - auto model = dynamic_cast(typed_udata->first); + auto model = dynamic_cast(typed_udata->first); Expects(model); model->fJ(t, x, xdot, J); - return model->checkFinite(gsl::make_span(J), "Jacobian"); + return model->checkFinite(J, ModelQuantity::J, t); } - /** * @brief Jacobian of xBdot with respect to adjoint state xB - * @param NeqBdot number of adjoint state variables * @param t timepoint * @param x Vector with the states * @param xB Vector with the adjoint states @@ -842,19 +899,19 @@ static int fJ(realtype t, N_Vector x, N_Vector xdot, SUNMatrix J, * @param tmp3B temporary storage vector * @return status flag indicating successful execution **/ -static int fJB(realtype t, N_Vector x, N_Vector xB, N_Vector xBdot, - SUNMatrix JB, void *user_data, N_Vector /*tmp1B*/, - N_Vector /*tmp2B*/, N_Vector /*tmp3B*/) { - auto typed_udata = static_cast(user_data); +static int +fJB(realtype t, N_Vector x, N_Vector xB, N_Vector xBdot, SUNMatrix JB, + void* user_data, N_Vector /*tmp1B*/, N_Vector /*tmp2B*/, + N_Vector /*tmp3B*/) { + auto typed_udata = static_cast(user_data); Expects(typed_udata); - auto model = dynamic_cast(typed_udata->first); + auto model = dynamic_cast(typed_udata->first); Expects(model); model->fJB(t, x, xB, xBdot, JB); - return model->checkFinite(gsl::make_span(JB), "Jacobian"); + return model->checkFinite(gsl::make_span(JB), ModelQuantity::JB); } - /** * @brief J in sparse form (for sparse solvers from the SuiteSparse Package) * @param t timepoint @@ -867,19 +924,19 @@ static int fJB(realtype t, N_Vector x, N_Vector xB, N_Vector xBdot, * @param tmp3 temporary storage vector * @return status flag indicating successful execution */ -static int fJSparse(realtype t, N_Vector x, N_Vector /*xdot*/, - SUNMatrix J, void *user_data, N_Vector /*tmp1*/, - N_Vector /*tmp2*/, N_Vector /*tmp3*/) { - auto typed_udata = static_cast(user_data); +static int fJSparse( + realtype t, N_Vector x, N_Vector /*xdot*/, SUNMatrix J, void* user_data, + N_Vector /*tmp1*/, N_Vector /*tmp2*/, N_Vector /*tmp3*/ +) { + auto typed_udata = static_cast(user_data); Expects(typed_udata); - auto model = dynamic_cast(typed_udata->first); + auto model = dynamic_cast(typed_udata->first); Expects(model); model->fJSparse(t, x, J); - return model->checkFinite(gsl::make_span(J), "Jacobian"); + return model->checkFinite(J, ModelQuantity::J, t); } - /** * @brief JB in sparse form (for sparse solvers from the SuiteSparse Package) * @param t timepoint @@ -893,19 +950,19 @@ static int fJSparse(realtype t, N_Vector x, N_Vector /*xdot*/, * @param tmp3B temporary storage vector * @return status flag indicating successful execution */ -static int fJSparseB(realtype t, N_Vector x, N_Vector xB, N_Vector xBdot, - SUNMatrix JB, void *user_data, N_Vector /*tmp1B*/, - N_Vector /*tmp2B*/, N_Vector /*tmp3B*/) { - auto typed_udata = static_cast(user_data); +static int fJSparseB( + realtype t, N_Vector x, N_Vector xB, N_Vector xBdot, SUNMatrix JB, + void* user_data, N_Vector /*tmp1B*/, N_Vector /*tmp2B*/, N_Vector /*tmp3B*/ +) { + auto typed_udata = static_cast(user_data); Expects(typed_udata); - auto model = dynamic_cast(typed_udata->first); + auto model = dynamic_cast(typed_udata->first); Expects(model); model->fJSparseB(t, x, xB, xBdot, JB); - return model->checkFinite(gsl::make_span(JB), "Jacobian"); + return model->checkFinite(gsl::make_span(JB), ModelQuantity::JB); } - /** * @brief J in banded form (for banded solvers) * @param t timepoint @@ -918,12 +975,13 @@ static int fJSparseB(realtype t, N_Vector x, N_Vector xB, N_Vector xBdot, * @param tmp3 temporary storage vector * @return status flag indicating successful execution */ -static int fJBand(realtype t, N_Vector x, N_Vector xdot, SUNMatrix J, - void *user_data, N_Vector tmp1, N_Vector tmp2, N_Vector tmp3) { +static int fJBand( + realtype t, N_Vector x, N_Vector xdot, SUNMatrix J, void* user_data, + N_Vector tmp1, N_Vector tmp2, N_Vector tmp3 +) { return fJ(t, x, xdot, J, user_data, tmp1, tmp2, tmp3); } - /** * @brief JB in banded form (for banded solvers) * @param t timepoint @@ -937,13 +995,13 @@ static int fJBand(realtype t, N_Vector x, N_Vector xdot, SUNMatrix J, * @param tmp3B temporary storage vector * @return status flag indicating successful execution */ -static int fJBandB(realtype t, N_Vector x, N_Vector xB, N_Vector xBdot, - SUNMatrix JB, void *user_data, N_Vector tmp1B, - N_Vector tmp2B, N_Vector tmp3B) { +static int fJBandB( + realtype t, N_Vector x, N_Vector xB, N_Vector xBdot, SUNMatrix JB, + void* user_data, N_Vector tmp1B, N_Vector tmp2B, N_Vector tmp3B +) { return fJB(t, x, xB, xBdot, JB, user_data, tmp1B, tmp2B, tmp3B); } - /** * @brief Matrix vector product of J with a vector v (for iterative solvers) * @param t timepoint @@ -956,18 +1014,18 @@ static int fJBandB(realtype t, N_Vector x, N_Vector xB, N_Vector xBdot, * @param tmp temporary storage vector * @return status flag indicating successful execution **/ -static int fJv(N_Vector v, N_Vector Jv, realtype t, N_Vector x, - N_Vector /*xdot*/, void *user_data, N_Vector /*tmp*/) { - auto typed_udata = static_cast(user_data); +static int +fJv(N_Vector v, N_Vector Jv, realtype t, N_Vector x, N_Vector /*xdot*/, + void* user_data, N_Vector /*tmp*/) { + auto typed_udata = static_cast(user_data); Expects(typed_udata); - auto model = dynamic_cast(typed_udata->first); + auto model = dynamic_cast(typed_udata->first); Expects(model); model->fJv(v, Jv, t, x); - return model->checkFinite(gsl::make_span(Jv), "Jacobian"); + return model->checkFinite(gsl::make_span(Jv), ModelQuantity::Jv); } - /** * @brief Matrix vector product of JB with a vector v (for iterative solvers) * @param t timepoint @@ -981,19 +1039,19 @@ static int fJv(N_Vector v, N_Vector Jv, realtype t, N_Vector x, * @param tmpB temporary storage vector * @return status flag indicating successful execution **/ -static int fJvB(N_Vector vB, N_Vector JvB, realtype t, N_Vector x, - N_Vector xB, N_Vector /*xBdot*/, void *user_data, - N_Vector /*tmpB*/) { - auto typed_udata = static_cast(user_data); +static int fJvB( + N_Vector vB, N_Vector JvB, realtype t, N_Vector x, N_Vector xB, + N_Vector /*xBdot*/, void* user_data, N_Vector /*tmpB*/ +) { + auto typed_udata = static_cast(user_data); Expects(typed_udata); - auto model = dynamic_cast(typed_udata->first); + auto model = dynamic_cast(typed_udata->first); Expects(model); model->fJvB(vB, JvB, t, x, xB); - return model->checkFinite(gsl::make_span(JvB), "Jacobian"); + return model->checkFinite(gsl::make_span(JvB), ModelQuantity::JvB); } - /** * @brief Event trigger function for events * @param t timepoint @@ -1002,19 +1060,18 @@ static int fJvB(N_Vector vB, N_Vector JvB, realtype t, N_Vector x, * @param user_data object with user input * @return status flag indicating successful execution */ -static int froot(realtype t, N_Vector x, realtype *root, - void *user_data) { - auto typed_udata = static_cast(user_data); +static int froot(realtype t, N_Vector x, realtype* root, void* user_data) { + auto typed_udata = static_cast(user_data); Expects(typed_udata); - auto model = dynamic_cast(typed_udata->first); + auto model = dynamic_cast(typed_udata->first); Expects(model); model->froot(t, x, gsl::make_span(root, model->ne)); - return model->checkFinite(gsl::make_span(root, model->ne), - "root function"); + return model->checkFinite( + gsl::make_span(root, model->ne), ModelQuantity::root + ); } - /** * @brief residual function of the ODE * @param t timepoint @@ -1023,19 +1080,20 @@ static int froot(realtype t, N_Vector x, realtype *root, * @param user_data object with user input * @return status flag indicating successful execution */ -static int fxdot(realtype t, N_Vector x, N_Vector xdot, void *user_data) { - auto typed_udata = static_cast(user_data); +static int fxdot(realtype t, N_Vector x, N_Vector xdot, void* user_data) { + auto typed_udata = static_cast(user_data); Expects(typed_udata); - auto model = dynamic_cast(typed_udata->first); + auto model = dynamic_cast(typed_udata->first); Expects(model); - auto solver = dynamic_cast(typed_udata->second); + auto solver = dynamic_cast(typed_udata->second); Expects(model); - if(solver->timeExceeded()) { + if (solver->timeExceeded(500)) { return AMICI_MAX_TIME_EXCEEDED; } - if (t > 1e200 && !model->checkFinite(gsl::make_span(x), "fxdot")) { + if (t > 1e200 + && !model->checkFinite(gsl::make_span(x), ModelQuantity::xdot)) { /* 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 @@ -1044,10 +1102,9 @@ 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), "fxdot"); + return model->checkFinite(gsl::make_span(xdot), ModelQuantity::xdot); } - /** * @brief Right hand side of differential equation for adjoint state xB * @param t timepoint @@ -1057,24 +1114,23 @@ static int fxdot(realtype t, N_Vector x, N_Vector xdot, void *user_data) { * @param user_data object with user input * @return status flag indicating successful execution */ -static int fxBdot(realtype t, N_Vector x, N_Vector xB, N_Vector xBdot, - void *user_data) { - auto typed_udata = static_cast(user_data); +static int +fxBdot(realtype t, N_Vector x, N_Vector xB, N_Vector xBdot, void* user_data) { + auto typed_udata = static_cast(user_data); Expects(typed_udata); - auto model = dynamic_cast(typed_udata->first); + auto model = dynamic_cast(typed_udata->first); Expects(model); auto solver = dynamic_cast(typed_udata->second); Expects(model); - if(solver->timeExceeded()) { + if (solver->timeExceeded(500)) { return AMICI_MAX_TIME_EXCEEDED; } model->fxBdot(t, x, xB, xBdot); - return model->checkFinite(gsl::make_span(xBdot), "fxBdot"); + return model->checkFinite(gsl::make_span(xBdot), ModelQuantity::xBdot); } - /** * @brief Right hand side of integral equation for quadrature states qB * @param t timepoint @@ -1084,18 +1140,17 @@ static int fxBdot(realtype t, N_Vector x, N_Vector xB, N_Vector xBdot, * @param user_data pointer to temp data object * @return status flag indicating successful execution */ -static int fqBdot(realtype t, N_Vector x, N_Vector xB, N_Vector qBdot, - void *user_data) { - auto typed_udata = static_cast(user_data); +static int +fqBdot(realtype t, N_Vector x, N_Vector xB, N_Vector qBdot, void* user_data) { + auto typed_udata = static_cast(user_data); Expects(typed_udata); - auto model = dynamic_cast(typed_udata->first); + auto model = dynamic_cast(typed_udata->first); Expects(model); model->fqBdot(t, x, xB, qBdot); - return model->checkFinite(gsl::make_span(qBdot), "qBdot"); + return model->checkFinite(gsl::make_span(qBdot), ModelQuantity::qBdot); } - /** * @brief Right hand side of differential equation for adjoint state xB * when simulating in steadystate mode @@ -1105,18 +1160,16 @@ static int fqBdot(realtype t, N_Vector x, N_Vector xB, N_Vector qBdot, * @param user_data object with user input * @return status flag indicating successful execution */ -static int fxBdot_ss(realtype t, N_Vector xB, N_Vector xBdot, - void *user_data) { - auto typed_udata = static_cast(user_data); +static int fxBdot_ss(realtype t, N_Vector xB, N_Vector xBdot, void* user_data) { + auto typed_udata = static_cast(user_data); Expects(typed_udata); - auto model = dynamic_cast(typed_udata->first); + auto model = dynamic_cast(typed_udata->first); Expects(model); model->fxBdot_ss(t, xB, xBdot); - return model->checkFinite(gsl::make_span(xBdot), "fxBdot_ss"); + return model->checkFinite(gsl::make_span(xBdot), ModelQuantity::xBdot_ss); } - /** * @brief Right hand side of integral equation for quadrature states qB * when simulating in steadystate mode @@ -1126,15 +1179,14 @@ static int fxBdot_ss(realtype t, N_Vector xB, N_Vector xBdot, * @param user_data pointer to temp data object * @return status flag indicating successful execution */ -static int fqBdot_ss(realtype t, N_Vector xB, N_Vector qBdot, - void *user_data) { - auto typed_udata = static_cast(user_data); +static int fqBdot_ss(realtype t, N_Vector xB, N_Vector qBdot, void* user_data) { + auto typed_udata = static_cast(user_data); Expects(typed_udata); - auto model = dynamic_cast(typed_udata->first); + auto model = dynamic_cast(typed_udata->first); Expects(model); model->fqBdot_ss(t, xB, qBdot); - return model->checkFinite(gsl::make_span(qBdot), "qBdot_ss"); + return model->checkFinite(gsl::make_span(qBdot), ModelQuantity::qBdot_ss); } /** @@ -1149,19 +1201,21 @@ static int fqBdot_ss(realtype t, N_Vector xB, N_Vector qBdot, * @param tmp3B temporary storage vector * @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*/) { - auto typed_udata = static_cast(user_data); +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*/ +) { + auto typed_udata = static_cast(user_data); Expects(typed_udata); - auto model = dynamic_cast(typed_udata->first); + auto model = dynamic_cast(typed_udata->first); Expects(model); model->fJSparseB_ss(JB); - return model->checkFinite(gsl::make_span(xBdot), "JSparseB_ss"); + return model->checkFinite( + gsl::make_span(xBdot), ModelQuantity::JSparseB_ss + ); } - /** * @brief Right hand side of differential equation for state sensitivities sx * @param Ns number of parameters @@ -1177,20 +1231,21 @@ static int fJSparseB_ss(realtype /*t*/, N_Vector /*x*/, N_Vector xBdot, * @param tmp3 temporary storage vector * @return status flag indicating successful execution */ -static int fsxdot(int /*Ns*/, realtype t, N_Vector x, N_Vector /*xdot*/, - int ip, N_Vector sx, N_Vector sxdot, void *user_data, - N_Vector /*tmp1*/, N_Vector /*tmp2*/) { - auto typed_udata = static_cast(user_data); +static int fsxdot( + int /*Ns*/, realtype t, N_Vector x, N_Vector /*xdot*/, int ip, N_Vector sx, + N_Vector sxdot, void* user_data, N_Vector /*tmp1*/, N_Vector /*tmp2*/ +) { + auto typed_udata = static_cast(user_data); Expects(typed_udata); - auto model = dynamic_cast(typed_udata->first); + auto model = dynamic_cast(typed_udata->first); Expects(model); model->fsxdot(t, x, ip, sx, sxdot); - return model->checkFinite(gsl::make_span(sxdot), "sxdot"); + return model->checkFinite(gsl::make_span(sxdot), ModelQuantity::sxdot); } -bool operator==(const CVodeSolver &a, const CVodeSolver &b) { - return static_cast(a) == static_cast(b); +bool operator==(CVodeSolver const& a, CVodeSolver const& b) { + return static_cast(a) == static_cast(b); } } // namespace amici diff --git a/deps/AMICI/src/solver_idas.cpp b/deps/AMICI/src/solver_idas.cpp index 18033f42c..63f65b184 100644 --- a/deps/AMICI/src/solver_idas.cpp +++ b/deps/AMICI/src/solver_idas.cpp @@ -1,7 +1,6 @@ #include "amici/solver_idas.h" #include "amici/exception.h" -#include "amici/misc.h" #include "amici/model_dae.h" #include "amici/sundials_linsol_wrapper.h" @@ -22,93 +21,109 @@ namespace amici { * Their signatures must not be changes. */ -static int fxdot(realtype t, N_Vector x, N_Vector dx, N_Vector xdot, - void *user_data); - -static int fJ(realtype t, realtype cj, N_Vector x, N_Vector dx, - N_Vector xdot, SUNMatrix J, void *user_data, N_Vector tmp1, - N_Vector tmp2, N_Vector tmp3); - -static int fJSparse(realtype t, realtype cj, N_Vector x, N_Vector dx, - N_Vector xdot, SUNMatrix J, void *user_data, - N_Vector tmp1, N_Vector tmp2, N_Vector tmp3); - -static int fJB(realtype t, realtype cj, N_Vector x, N_Vector dx, - N_Vector xB, N_Vector dxB, N_Vector xBdot, SUNMatrix JB, - void *user_data, N_Vector tmp1B, N_Vector tmp2B, - N_Vector tmp3B); - -static int fJSparseB(realtype t, realtype cj, N_Vector x, N_Vector dx, - N_Vector xB, N_Vector dxB, N_Vector xBdot, - SUNMatrix JB, void *user_data, N_Vector tmp1B, - N_Vector tmp2B, N_Vector tmp3B); - -static int fJBand(realtype t, realtype cj, N_Vector x, N_Vector dx, - N_Vector xdot, SUNMatrix J, void *user_data, - N_Vector tmp1, N_Vector tmp2, N_Vector tmp3); - -static int fJBandB(realtype t, realtype cj, N_Vector x, N_Vector dx, - N_Vector xB, N_Vector dxB, N_Vector xBdot, SUNMatrix JB, - void *user_data, N_Vector tmp1B, N_Vector tmp2B, - N_Vector tmp3B); - -static int fJv(realtype t, N_Vector x, N_Vector dx, N_Vector xdot, - N_Vector v, N_Vector Jv, realtype cj, void *user_data, - N_Vector tmp1, N_Vector tmp2); - -static int fJvB(realtype t, N_Vector x, N_Vector dx, N_Vector xB, - N_Vector dxB, N_Vector xBdot, N_Vector vB, N_Vector JvB, - realtype cj, void *user_data, N_Vector tmpB1, - N_Vector tmpB2); - -static int froot(realtype t, N_Vector x, N_Vector dx, realtype *root, - void *user_data); - -static int fxBdot(realtype t, N_Vector x, N_Vector dx, N_Vector xB, - N_Vector dxB, N_Vector xBdot, void *user_data); - -static int fqBdot(realtype t, N_Vector x, N_Vector dx, N_Vector xB, - N_Vector dxB, N_Vector qBdot, void *user_data); - -static int fxBdot_ss(realtype t, N_Vector xB, N_Vector dxB, N_Vector xBdot, - void *user_data); - -static int fqBdot_ss(realtype t, N_Vector xB, N_Vector dxB, N_Vector qBdot, - void *user_data); - -static int fJSparseB_ss(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); - -static int fsxdot(int Ns, realtype t, N_Vector x, N_Vector dx, - N_Vector xdot, N_Vector *sx, N_Vector *sdx, - N_Vector *sxdot, void *user_data, N_Vector tmp1, - N_Vector tmp2, N_Vector tmp3); - +static int +fxdot(realtype t, N_Vector x, N_Vector dx, N_Vector xdot, void* user_data); + +static int +fJ(realtype t, realtype cj, N_Vector x, N_Vector dx, N_Vector xdot, SUNMatrix J, + void* user_data, N_Vector tmp1, N_Vector tmp2, N_Vector tmp3); + +static int fJSparse( + realtype t, realtype cj, N_Vector x, N_Vector dx, N_Vector xdot, + SUNMatrix J, void* user_data, N_Vector tmp1, N_Vector tmp2, N_Vector tmp3 +); + +static int +fJB(realtype t, realtype cj, N_Vector x, N_Vector dx, N_Vector xB, N_Vector dxB, + N_Vector xBdot, SUNMatrix JB, void* user_data, N_Vector tmp1B, + N_Vector tmp2B, N_Vector tmp3B); + +static int fJSparseB( + realtype t, realtype cj, N_Vector x, N_Vector dx, N_Vector xB, N_Vector dxB, + N_Vector xBdot, SUNMatrix JB, void* user_data, N_Vector tmp1B, + N_Vector tmp2B, N_Vector tmp3B +); + +static int fJBand( + realtype t, realtype cj, N_Vector x, N_Vector dx, N_Vector xdot, + SUNMatrix J, void* user_data, N_Vector tmp1, N_Vector tmp2, N_Vector tmp3 +); + +static int fJBandB( + realtype t, realtype cj, N_Vector x, N_Vector dx, N_Vector xB, N_Vector dxB, + N_Vector xBdot, SUNMatrix JB, void* user_data, N_Vector tmp1B, + N_Vector tmp2B, N_Vector tmp3B +); + +static int +fJv(realtype t, N_Vector x, N_Vector dx, N_Vector xdot, N_Vector v, N_Vector Jv, + realtype cj, void* user_data, N_Vector tmp1, N_Vector tmp2); + +static int fJvB( + realtype t, N_Vector x, N_Vector dx, N_Vector xB, N_Vector dxB, + N_Vector xBdot, N_Vector vB, N_Vector JvB, realtype cj, void* user_data, + N_Vector tmpB1, N_Vector tmpB2 +); + +static int +froot(realtype t, N_Vector x, N_Vector dx, realtype* root, void* user_data); + +static int fxBdot( + realtype t, N_Vector x, N_Vector dx, N_Vector xB, N_Vector dxB, + N_Vector xBdot, void* user_data +); + +static int fqBdot( + realtype t, N_Vector x, N_Vector dx, N_Vector xB, N_Vector dxB, + N_Vector qBdot, void* user_data +); + +static int fxBdot_ss( + realtype t, N_Vector xB, N_Vector dxB, N_Vector xBdot, void* user_data +); + +static int fqBdot_ss( + realtype t, N_Vector xB, N_Vector dxB, N_Vector qBdot, void* user_data +); + +static int fJSparseB_ss( + 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 +); + +static int fsxdot( + int Ns, realtype t, N_Vector x, N_Vector dx, N_Vector xdot, N_Vector* sx, + N_Vector* sdx, N_Vector* sxdot, void* user_data, N_Vector tmp1, + N_Vector tmp2, N_Vector tmp3 +); /* Function implementations */ -void IDASolver::init(const realtype t0, const AmiVector &x0, - const AmiVector &dx0) const { +void IDASolver::init( + const realtype t0, AmiVector const& x0, AmiVector const& dx0 +) const { int status; solver_was_called_F_ = false; t_ = t0; x_ = x0; dx_ = dx0; if (getInitDone()) { - status = - IDAReInit(solver_memory_.get(), t_, x_.getNVector(), dx_.getNVector()); + status = IDAReInit( + solver_memory_.get(), t_, x_.getNVector(), dx_.getNVector() + ); } else { - status = IDAInit(solver_memory_.get(), fxdot, t_, x_.getNVector(), - dx_.getNVector()); + status = IDAInit( + solver_memory_.get(), fxdot, t_, x_.getNVector(), dx_.getNVector() + ); setInitDone(); } if (status != IDA_SUCCESS) throw IDAException(status, "IDAInit"); } -void IDASolver::initSteadystate(const realtype /*t0*/, const AmiVector &/*x0*/, - const AmiVector &/*dx0*/) const { +void IDASolver::initSteadystate( + const realtype /*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, as re-calling init would unset solver settings. */ @@ -116,22 +131,24 @@ void IDASolver::initSteadystate(const realtype /*t0*/, const AmiVector &/*x0*/, ida_mem->ida_res = fxBdot_ss; } -void IDASolver::sensInit1(const AmiVectorArray &sx0, - const AmiVectorArray &sdx0) const { +void IDASolver::sensInit1(AmiVectorArray const& sx0, AmiVectorArray const& sdx0) + const { int status = IDA_SUCCESS; sx_ = sx0; sdx_ = sdx0; if (getSensitivityMethod() == SensitivityMethod::forward && nplist() > 0) { if (getSensInitDone()) { - status = - IDASensReInit(solver_memory_.get(), - static_cast(getInternalSensitivityMethod()), - sx_.getNVectorArray(), sdx_.getNVectorArray()); + status = IDASensReInit( + solver_memory_.get(), + static_cast(getInternalSensitivityMethod()), + sx_.getNVectorArray(), sdx_.getNVectorArray() + ); } else { status = IDASensInit( solver_memory_.get(), nplist(), static_cast(getInternalSensitivityMethod()), fsxdot, - sx_.getNVectorArray(), sdx_.getNVectorArray()); + sx_.getNVectorArray(), sdx_.getNVectorArray() + ); setSensInitDone(); } } @@ -139,32 +156,38 @@ void IDASolver::sensInit1(const AmiVectorArray &sx0, throw IDAException(status, "IDASensInit"); } -void IDASolver::binit(const int which, const realtype tf, const AmiVector &xB0, - const AmiVector &dxB0) const { +void IDASolver::binit( + int const which, const realtype tf, AmiVector const& xB0, + AmiVector const& dxB0 +) const { int status; xB_ = xB0; dxB_ = dxB0; if (getInitDoneB(which)) - status = IDAReInitB(solver_memory_.get(), which, tf, xB_.getNVector(), - dxB_.getNVector()); + status = IDAReInitB( + solver_memory_.get(), which, tf, xB_.getNVector(), dxB_.getNVector() + ); else { - status = IDAInitB(solver_memory_.get(), which, fxBdot, tf, - xB_.getNVector(), dxB_.getNVector()); + status = IDAInitB( + solver_memory_.get(), which, fxBdot, tf, xB_.getNVector(), + dxB_.getNVector() + ); setInitDoneB(which); } if (status != IDA_SUCCESS) throw IDAException(status, "IDAInitB"); } -void IDASolver::qbinit(const int which, const AmiVector &xQB0) const { +void IDASolver::qbinit(int const which, AmiVector const& xQB0) const { int status; xQB_.copy(xQB0); if (getQuadInitDoneB(which)) status = IDAQuadReInitB(solver_memory_.get(), which, xQB_.getNVector()); else { - status = - IDAQuadInitB(solver_memory_.get(), which, fqBdot, xQB_.getNVector()); + status = IDAQuadInitB( + solver_memory_.get(), which, fqBdot, xQB_.getNVector() + ); setQuadInitDoneB(which); } if (status != IDA_SUCCESS) @@ -201,25 +224,25 @@ void IDASolver::setJacTimesVecFn() const { throw IDAException(status, "IDASpilsSetJacTimesVecFn"); } -void IDASolver::setDenseJacFnB(const int which) const { +void IDASolver::setDenseJacFnB(int const which) const { int status = IDASetJacFnB(solver_memory_.get(), which, fJB); if (status != IDA_SUCCESS) throw IDAException(status, "IDADlsSetDenseJacFnB"); } -void IDASolver::setSparseJacFnB(const int which) const { +void IDASolver::setSparseJacFnB(int const which) const { int status = IDASetJacFnB(solver_memory_.get(), which, fJSparseB); if (status != IDA_SUCCESS) throw IDAException(status, "IDASlsSetSparseJacFnB"); } -void IDASolver::setBandJacFnB(const int which) const { +void IDASolver::setBandJacFnB(int const which) const { int status = IDASetJacFnB(solver_memory_.get(), which, fJBandB); if (status != IDA_SUCCESS) throw IDAException(status, "IDADlsSetBandJacFnB"); } -void IDASolver::setJacTimesVecFnB(const int which) const { +void IDASolver::setJacTimesVecFnB(int const which) const { int status = IDASetJacTimesB(solver_memory_.get(), which, nullptr, fJvB); if (status != IDA_SUCCESS) throw IDAException(status, "IDASpilsSetJacTimesVecFnB"); @@ -231,57 +254,59 @@ void IDASolver::setSparseJacFn_ss() const { throw IDAException(status, "IDASetJacFn"); } -Solver *IDASolver::clone() const { return new IDASolver(*this); } +Solver* IDASolver::clone() const { return new IDASolver(*this); } void IDASolver::allocateSolver() const { if (!solver_memory_) - solver_memory_ = std::unique_ptr>( - IDACreate(), [](void *ptr) { IDAFree(&ptr); }); + solver_memory_ = std::unique_ptr>( + IDACreate(), [](void* ptr) { IDAFree(&ptr); } + ); } -void IDASolver::setSStolerances(const realtype rtol, - const realtype atol) const { +void IDASolver::setSStolerances(const realtype rtol, const realtype atol) + const { int status = IDASStolerances(solver_memory_.get(), rtol, atol); if (status != IDA_SUCCESS) throw IDAException(status, "IDASStolerances"); } -void IDASolver::setSensSStolerances(const realtype rtol, - const realtype *atol) const { - int status = IDASensSStolerances(solver_memory_.get(), rtol, - const_cast(atol)); +void IDASolver::setSensSStolerances(const realtype rtol, realtype const* atol) + const { + int status = IDASensSStolerances( + solver_memory_.get(), rtol, const_cast(atol) + ); if (status != IDA_SUCCESS) throw IDAException(status, "IDASensEEtolerances"); } -void IDASolver::setSensErrCon(const bool error_corr) const { +void IDASolver::setSensErrCon(bool const error_corr) const { int status = IDASetSensErrCon(solver_memory_.get(), error_corr); if (status != IDA_SUCCESS) throw IDAException(status, "IDASetSensErrCon"); } -void IDASolver::setQuadErrConB(const int which, const bool flag) const { +void IDASolver::setQuadErrConB(int const which, bool const flag) const { int status = IDASetQuadErrConB(solver_memory_.get(), which, flag); if (status != IDA_SUCCESS) throw IDAException(status, "IDASetQuadErrConB"); } -void IDASolver::setQuadErrCon(const bool flag) const { +void IDASolver::setQuadErrCon(bool const flag) const { int status = IDASetQuadErrCon(solver_memory_.get(), flag); if (status != IDA_SUCCESS) throw IDAException(status, "IDASetQuadErrCon"); } -void IDASolver::getRootInfo(int *rootsfound) const { +void IDASolver::getRootInfo(int* rootsfound) const { int status = IDAGetRootInfo(solver_memory_.get(), rootsfound); if (status != IDA_SUCCESS) throw IDAException(status, "IDAGetRootInfo"); } void IDASolver::setErrHandlerFn() const { - int status = - IDASetErrHandlerFn(solver_memory_.get(), wrapErrHandlerFn, - reinterpret_cast( - const_cast(this))); + int status = IDASetErrHandlerFn( + solver_memory_.get(), wrapErrHandlerFn, + reinterpret_cast(const_cast(this)) + ); if (status != IDA_SUCCESS) throw IDAException(status, "IDASetErrHandlerFn"); } @@ -298,21 +323,22 @@ void IDASolver::setUserDataB(int which) const { throw IDAException(status, "IDASetUserDataB"); } -void IDASolver::setMaxNumSteps(const long int mxsteps) const { +void IDASolver::setMaxNumSteps(long int const mxsteps) const { int status = IDASetMaxNumSteps(solver_memory_.get(), mxsteps); if (status != IDA_SUCCESS) throw IDAException(status, "IDASetMaxNumSteps"); } -void IDASolver::setStabLimDet(const int /*stldet*/) const {} +void IDASolver::setStabLimDet(int const /*stldet*/) const {} -void IDASolver::setStabLimDetB(const int /*which*/, - const int /*stldet*/) const {} +void IDASolver::setStabLimDetB(int const /*which*/, int const /*stldet*/) + const {} -void IDASolver::setId(const Model *model) const { +void IDASolver::setId(Model const* model) const { - N_Vector id = N_VMake_Serial(model->nx_solver, - const_cast(model->idlist.data())); + N_Vector id = N_VMake_Serial( + model->nx_solver, const_cast(model->idlist.data()) + ); int status = IDASetId(solver_memory_.get(), id); if (status != IDA_SUCCESS) @@ -321,14 +347,15 @@ void IDASolver::setId(const Model *model) const { N_VDestroy_Serial(id); } -void IDASolver::setSuppressAlg(const bool flag) const { +void IDASolver::setSuppressAlg(bool const flag) const { int status = IDASetSuppressAlg(solver_memory_.get(), flag); if (status != IDA_SUCCESS) throw IDAException(status, "IDASetSuppressAlg"); } -void IDASolver::resetState(void *ami_mem, const_N_Vector yy0, - const_N_Vector yp0) const { +void IDASolver::resetState( + void* ami_mem, const_N_Vector yy0, const_N_Vector yp0 +) const { auto ida_mem = static_cast(ami_mem); /* here we force the order in the next step to zero, and update the @@ -344,7 +371,7 @@ void IDASolver::resetState(void *ami_mem, const_N_Vector yy0, /* Set step parameters */ /* current order */ - ida_mem->ida_kk = 0; + ida_mem->ida_kk = 0; } void IDASolver::reInitPostProcessF(const realtype tnext) const { @@ -361,32 +388,34 @@ void IDASolver::reInitPostProcessB(const realtype tnext) const { // store current backward problem in ca_mem to make it accessible in // adjoint rhs wrapper functions idaadj_mem->ia_bckpbCrt = idaB_mem; - reInitPostProcess(static_cast(idaB_mem->IDA_mem), &tBret, &xB_, - &dxB_, tnext); + reInitPostProcess( + static_cast(idaB_mem->IDA_mem), &tBret, &xB_, &dxB_, tnext + ); // idaB_mem->ida_tout = tBret; idaB_mem = idaB_mem->ida_next; } force_reinit_postprocess_B_ = false; } -void IDASolver::reInitPostProcess(void *ami_mem, realtype *t, - AmiVector *yout, AmiVector *ypout, - realtype tout) const { +void IDASolver::reInitPostProcess( + void* ami_mem, realtype* t, AmiVector* yout, AmiVector* ypout, realtype tout +) const { auto ida_mem = static_cast(ami_mem); auto nst_tmp = ida_mem->ida_nst; ida_mem->ida_nst = 0; auto status = IDASetStopTime(ida_mem, tout); - if(status != IDA_SUCCESS) + if (status != IDA_SUCCESS) throw IDAException(status, "CVodeSetStopTime"); - status = IDASolve(ami_mem, tout, t, yout->getNVector(), ypout->getNVector(), - IDA_ONE_STEP); + status = IDASolve( + ami_mem, tout, t, yout->getNVector(), ypout->getNVector(), IDA_ONE_STEP + ); - if(status != IDA_SUCCESS) + if (status != IDA_SUCCESS) throw IDAException(status, "reInitPostProcess"); - ida_mem->ida_nst = nst_tmp+1; + ida_mem->ida_nst = nst_tmp + 1; if (ida_mem->ida_adjMallocDone == SUNTRUE) { /* add new step to history array, this is copied from CVodeF */ auto ia_mem = ida_mem->ida_adj_mem; @@ -401,8 +430,9 @@ void IDASolver::reInitPostProcess(void *ami_mem, realtype *t, /* Load next point in dt_mem */ dt_mem[ida_mem->ida_nst % ia_mem->ia_nsteps]->t = *t; - ia_mem->ia_storePnt(ida_mem, - dt_mem[ida_mem->ida_nst % ia_mem->ia_nsteps]); + ia_mem->ia_storePnt( + ida_mem, dt_mem[ida_mem->ida_nst % ia_mem->ia_nsteps] + ); /* Set t1 field of the current ckeck point structure for the case in which there will be no future @@ -414,8 +444,9 @@ void IDASolver::reInitPostProcess(void *ami_mem, realtype *t, } } -void IDASolver::reInit(const realtype t0, const AmiVector &yy0, - const AmiVector &yp0) const { +void IDASolver::reInit( + const realtype t0, AmiVector const& yy0, AmiVector const& yp0 +) const { auto ida_mem = static_cast(solver_memory_.get()); ida_mem->ida_tn = t0; @@ -426,8 +457,9 @@ void IDASolver::reInit(const realtype t0, const AmiVector &yy0, resetState(ida_mem, x_.getNVector(), xB_.getNVector()); } -void IDASolver::sensReInit(const AmiVectorArray &yyS0, - const AmiVectorArray &ypS0) const { +void IDASolver::sensReInit( + AmiVectorArray const& yyS0, AmiVectorArray const& ypS0 +) const { auto ida_mem = static_cast(solver_memory_.get()); /* Initialize znS[0] in the history array */ for (int is = 0; is < nplist(); is++) @@ -436,13 +468,16 @@ void IDASolver::sensReInit(const AmiVectorArray &yyS0, force_reinit_postprocess_F_ = true; sx_.copy(yyS0); sdx_.copy(ypS0); - auto status = - N_VScaleVectorArray(nplist(), ida_mem->ida_cvals, sx_.getNVectorArray(), - ida_mem->ida_phiS[0]); + auto status = N_VScaleVectorArray( + nplist(), ida_mem->ida_cvals, sx_.getNVectorArray(), + ida_mem->ida_phiS[0] + ); if (status != IDA_SUCCESS) throw IDAException(IDA_VECTOROP_ERR, "IDASensReInit"); - status = N_VScaleVectorArray(nplist(), ida_mem->ida_cvals, - sdx_.getNVectorArray(), ida_mem->ida_phiS[1]); + status = N_VScaleVectorArray( + nplist(), ida_mem->ida_cvals, sdx_.getNVectorArray(), + ida_mem->ida_phiS[1] + ); if (status != IDA_SUCCESS) throw IDAException(IDA_VECTOROP_ERR, "IDASensReInit"); } @@ -451,13 +486,18 @@ void IDASolver::sensToggleOff() const { auto status = IDASensToggleOff(solver_memory_.get()); if (status != IDA_SUCCESS) throw IDAException(status, "IDASensToggleOff"); + IDASensFree(solver_memory_.get()); + /* need to deallocate sensi memory, otherwise can't reenable */ + sens_initialized_ = false; } -void IDASolver::reInitB(const int which, const realtype tB0, - const AmiVector &yyB0, const AmiVector &ypB0) const { +void IDASolver::reInitB( + int const which, const realtype tB0, AmiVector const& yyB0, + AmiVector const& ypB0 +) const { - auto ida_memB = - static_cast(IDAGetAdjIDABmem(solver_memory_.get(), which)); + auto ida_memB + = static_cast(IDAGetAdjIDABmem(solver_memory_.get(), which)); if (solver_was_called_B_) force_reinit_postprocess_B_ = true; ida_memB->ida_tn = tB0; @@ -466,25 +506,27 @@ void IDASolver::reInitB(const int which, const realtype tB0, resetState(ida_memB, xB_.getNVector(), dxB_.getNVector()); } -void IDASolver::quadReInitB(const int which, const AmiVector &yQB0) const { - auto ida_memB = - static_cast(IDAGetAdjIDABmem(solver_memory_.get(), which)); +void IDASolver::quadReInitB(int const which, AmiVector const& yQB0) const { + auto ida_memB + = static_cast(IDAGetAdjIDABmem(solver_memory_.get(), which)); if (solver_was_called_B_) force_reinit_postprocess_B_ = true; xQB_.copy(yQB0); N_VScale(ONE, xQB_.getNVector(), ida_memB->ida_phiQ[0]); } -void IDASolver::setSensParams(const realtype *p, const realtype *pbar, - const int *plist) const { - int status = IDASetSensParams(solver_memory_.get(), const_cast(p), - const_cast(pbar), - const_cast(plist)); +void IDASolver::setSensParams( + realtype const* p, realtype const* pbar, int const* plist +) const { + int status = IDASetSensParams( + solver_memory_.get(), const_cast(p), + const_cast(pbar), const_cast(plist) + ); if (status != IDA_SUCCESS) throw IDAException(status, "IDASetSensParams"); } -void IDASolver::getDky(const realtype t, const int k) const { +void IDASolver::getDky(const realtype t, int const k) const { int status = IDAGetDky(solver_memory_.get(), t, k, dky_.getNVector()); if (status != IDA_SUCCESS) throw IDAException(status, "IDAGetDky"); @@ -492,53 +534,59 @@ void IDASolver::getDky(const realtype t, const int k) const { void IDASolver::getSens() const { realtype tDummy = 0; - int status = IDAGetSens(solver_memory_.get(), &tDummy, sx_.getNVectorArray()); + int status + = IDAGetSens(solver_memory_.get(), &tDummy, sx_.getNVectorArray()); if (status != IDA_SUCCESS) throw IDAException(status, "IDAGetSens"); } -void IDASolver::getSensDky(const realtype t, const int k) const { - int status = IDAGetSensDky(solver_memory_.get(), t, k, sx_.getNVectorArray()); +void IDASolver::getSensDky(const realtype t, int const k) const { + int status + = IDAGetSensDky(solver_memory_.get(), t, k, sx_.getNVectorArray()); if (status != IDA_SUCCESS) throw IDAException(status, "IDAGetSens"); } -void IDASolver::getB(const int which) const { +void IDASolver::getB(int const which) const { realtype tDummy = 0; - int status = IDAGetB(solver_memory_.get(), which, &tDummy, xB_.getNVector(), - dxB_.getNVector()); + int status = IDAGetB( + solver_memory_.get(), which, &tDummy, xB_.getNVector(), + dxB_.getNVector() + ); if (status != IDA_SUCCESS) throw IDAException(status, "IDAGetB"); } -void IDASolver::getDkyB(const realtype t, int k, const int which) const { - int status = IDAGetDky(IDAGetAdjIDABmem(solver_memory_.get(), which), t, k, - dky_.getNVector()); +void IDASolver::getDkyB(const realtype t, int k, int const which) const { + int status = IDAGetDky( + IDAGetAdjIDABmem(solver_memory_.get(), which), t, k, dky_.getNVector() + ); if (status != IDA_SUCCESS) throw IDAException(status, "IDAGetB"); } void IDASolver::getQuadB(int which) const { realtype tDummy = 0; - int status = - IDAGetQuadB(solver_memory_.get(), which, &tDummy, xQB_.getNVector()); + int status + = IDAGetQuadB(solver_memory_.get(), which, &tDummy, xQB_.getNVector()); if (status != IDA_SUCCESS) throw IDAException(status, "IDAGetQuadB"); } -void IDASolver::getQuad(realtype &t) const { +void IDASolver::getQuad(realtype& t) const { int status = IDAGetQuad(solver_memory_.get(), &t, xQ_.getNVector()); if (status != IDA_SUCCESS) throw IDAException(status, "IDAGetQuad"); } -void IDASolver::getQuadDkyB(const realtype t, int k, const int which) const { - int status = IDAGetQuadDky(IDAGetAdjIDABmem(solver_memory_.get(), which), t, - k, xQB_.getNVector()); +void IDASolver::getQuadDkyB(const realtype t, int k, int const which) const { + int status = IDAGetQuadDky( + IDAGetAdjIDABmem(solver_memory_.get(), which), t, k, xQB_.getNVector() + ); if (status != IDA_SUCCESS) throw IDAException(status, "IDAGetB"); } -void IDASolver::getQuadDky(const realtype t, const int k) const { +void IDASolver::getQuadDky(const realtype t, int const k) const { int status = IDAGetQuadDky(solver_memory_.get(), t, k, xQ_.getNVector()); if (status != IDA_SUCCESS) throw IDAException(status, "IDAGetQuadDky"); @@ -549,20 +597,23 @@ void IDASolver::adjInit() const { if (getAdjInitDone()) { status = IDAAdjReInit(solver_memory_.get()); } else { - status = IDAAdjInit(solver_memory_.get(), static_cast(maxsteps_), - static_cast(interp_type_)); + status = IDAAdjInit( + solver_memory_.get(), static_cast(maxsteps_), + static_cast(interp_type_) + ); setAdjInitDone(); } if (status != IDA_SUCCESS) throw IDAException(status, "IDAAdjInit"); } -void IDASolver::quadInit(const AmiVector &xQ0) const { +void IDASolver::quadInit(AmiVector const& xQ0) const { int status; xQ_.copy(xQ0); if (getQuadInitDone()) { - status = IDAQuadReInit(solver_memory_.get(), - const_cast(xQ0.getNVector())); + status = IDAQuadReInit( + solver_memory_.get(), const_cast(xQ0.getNVector()) + ); } else { status = IDAQuadInit(solver_memory_.get(), fqBdot_ss, xQ_.getNVector()); setQuadInitDone(); @@ -571,7 +622,7 @@ void IDASolver::quadInit(const AmiVector &xQ0) const { throw IDAException(status, "IDAQuadInit"); } -void IDASolver::allocateSolverB(int *which) const { +void IDASolver::allocateSolverB(int* which) const { if (!solver_memory_B_.empty()) { *which = 0; return; @@ -579,60 +630,68 @@ void IDASolver::allocateSolverB(int *which) const { int status = IDACreateB(solver_memory_.get(), which); if (*which + 1 > static_cast(solver_memory_B_.size())) solver_memory_B_.resize(*which + 1); - solver_memory_B_.at(*which) = - std::unique_ptr>( - getAdjBmem(solver_memory_.get(), *which), [](void * /*ptr*/) {}); + solver_memory_B_.at(*which) + = std::unique_ptr>( + getAdjBmem(solver_memory_.get(), *which), [](void* /*ptr*/) {} + ); if (status != IDA_SUCCESS) throw IDAException(status, "IDACreateB"); } -void IDASolver::setSStolerancesB(const int which, const realtype relTolB, - const realtype absTolB) const { - int status = IDASStolerancesB(solver_memory_.get(), which, relTolB, absTolB); +void IDASolver::setSStolerancesB( + int const which, const realtype relTolB, const realtype absTolB +) const { + int status + = IDASStolerancesB(solver_memory_.get(), which, relTolB, absTolB); if (status != IDA_SUCCESS) throw IDAException(status, "IDASStolerancesB"); } -void IDASolver::quadSStolerancesB(const int which, const realtype reltolQB, - const realtype abstolQB) const { - int status = - IDAQuadSStolerancesB(solver_memory_.get(), which, reltolQB, abstolQB); +void IDASolver::quadSStolerancesB( + int const which, const realtype reltolQB, const realtype abstolQB +) const { + int status + = IDAQuadSStolerancesB(solver_memory_.get(), which, reltolQB, abstolQB); if (status != IDA_SUCCESS) throw IDAException(status, "IDAQuadSStolerancesB"); } -void IDASolver::quadSStolerances(const realtype reltolQB, - const realtype abstolQB) const { +void IDASolver::quadSStolerances( + const realtype reltolQB, const realtype abstolQB +) const { int status = IDAQuadSStolerances(solver_memory_.get(), reltolQB, abstolQB); if (status != IDA_SUCCESS) throw IDAException(status, "IDAQuadSStolerances"); } - -int IDASolver::solve(const realtype tout, const int itask) const { +int IDASolver::solve(const realtype tout, int const itask) const { if (force_reinit_postprocess_F_) reInitPostProcessF(tout); - int status = IDASolve(solver_memory_.get(), tout, &t_, x_.getNVector(), - dx_.getNVector(), itask); + int status = IDASolve( + solver_memory_.get(), tout, &t_, x_.getNVector(), dx_.getNVector(), + itask + ); solver_was_called_F_ = true; if (status < 0) // status > 0 is okay and is used for e.g. root return throw IntegrationFailure(status, t_); return status; } -int IDASolver::solveF(const realtype tout, const int itask, - int *ncheckPtr) const { +int IDASolver::solveF(const realtype tout, int const itask, int* ncheckPtr) + const { if (force_reinit_postprocess_F_) reInitPostProcessF(tout); - int status = IDASolveF(solver_memory_.get(), tout, &t_, x_.getNVector(), - xB_.getNVector(), itask, ncheckPtr); + int status = IDASolveF( + solver_memory_.get(), tout, &t_, x_.getNVector(), xB_.getNVector(), + itask, ncheckPtr + ); solver_was_called_F_ = true; if (status < 0) // status > 0 is okay and is used for e.g. root return throw IntegrationFailure(status, t_); return status; } -void IDASolver::solveB(const realtype tBout, const int itaskB) const { +void IDASolver::solveB(const realtype tBout, int const itaskB) const { if (force_reinit_postprocess_B_) reInitPostProcessB(tBout); int status = IDASolveB(solver_memory_.get(), tBout, itaskB); @@ -641,8 +700,8 @@ void IDASolver::solveB(const realtype tBout, const int itaskB) const { throw IntegrationFailure(status, tBout); } -void IDASolver::setMaxNumStepsB(const int which, - const long int mxstepsB) const { +void IDASolver::setMaxNumStepsB(int const which, long int const mxstepsB) + const { int status = IDASetMaxNumStepsB(solver_memory_.get(), which, mxstepsB); if (status != IDA_SUCCESS) throw IDAException(status, "IDASetMaxNumStepsB"); @@ -652,46 +711,49 @@ void IDASolver::diag() const { throw AmiException("Diag Solver was not implemented for DAEs"); } -void IDASolver::diagB(const int /*which*/) const { +void IDASolver::diagB(int const /*which*/) const { throw AmiException("Diag Solver was not implemented for DAEs"); } -void IDASolver::getNumSteps(const void *ami_mem, long int *numsteps) const { - int status = IDAGetNumSteps(const_cast(ami_mem), numsteps); +void IDASolver::getNumSteps(void const* ami_mem, long int* numsteps) const { + int status = IDAGetNumSteps(const_cast(ami_mem), numsteps); if (status != IDA_SUCCESS) throw IDAException(status, "IDAGetNumSteps"); } -void IDASolver::getNumRhsEvals(const void *ami_mem, - long int *numrhsevals) const { - int status = IDAGetNumResEvals(const_cast(ami_mem), numrhsevals); +void IDASolver::getNumRhsEvals(void const* ami_mem, long int* numrhsevals) + const { + int status = IDAGetNumResEvals(const_cast(ami_mem), numrhsevals); if (status != IDA_SUCCESS) throw IDAException(status, "IDAGetNumResEvals"); } -void IDASolver::getNumErrTestFails(const void *ami_mem, - long int *numerrtestfails) const { - int status = - IDAGetNumErrTestFails(const_cast(ami_mem), numerrtestfails); +void IDASolver::getNumErrTestFails( + void const* ami_mem, long int* numerrtestfails +) const { + int status + = IDAGetNumErrTestFails(const_cast(ami_mem), numerrtestfails); if (status != IDA_SUCCESS) throw IDAException(status, "IDAGetNumErrTestFails"); } void IDASolver::getNumNonlinSolvConvFails( - const void *ami_mem, long int *numnonlinsolvconvfails) const { - int status = IDAGetNumNonlinSolvConvFails(const_cast(ami_mem), - numnonlinsolvconvfails); + void const* ami_mem, long int* numnonlinsolvconvfails +) const { + int status = IDAGetNumNonlinSolvConvFails( + const_cast(ami_mem), numnonlinsolvconvfails + ); if (status != IDA_SUCCESS) throw IDAException(status, "IDAGetNumNonlinSolvConvFails"); } -void IDASolver::getLastOrder(const void *ami_mem, int *order) const { - int status = IDAGetLastOrder(const_cast(ami_mem), order); +void IDASolver::getLastOrder(void const* ami_mem, int* order) const { + int status = IDAGetLastOrder(const_cast(ami_mem), order); if (status != IDA_SUCCESS) throw IDAException(status, "IDAGetLastOrder"); } -void *IDASolver::getAdjBmem(void *ami_mem, int which) const { +void* IDASolver::getAdjBmem(void* ami_mem, int which) const { return IDAGetAdjIDABmem(ami_mem, which); } @@ -699,15 +761,17 @@ void IDASolver::calcIC(realtype tout1) const { int status = IDACalcIC(solver_memory_.get(), IDA_YA_YDP_INIT, tout1); if (status != IDA_SUCCESS) throw IDAException(status, "IDACalcIC"); - status = - IDAGetConsistentIC(solver_memory_.get(), x_.getNVector(), dx_.getNVector()); + status = IDAGetConsistentIC( + solver_memory_.get(), x_.getNVector(), dx_.getNVector() + ); if (status != IDA_SUCCESS) throw IDAException(status, "IDACalcIC"); } -void IDASolver::calcICB(const int which, const realtype tout1) const { - int status = IDACalcICB(solver_memory_.get(), which, tout1, xB_.getNVector(), - dxB_.getNVector()); +void IDASolver::calcICB(int const which, const realtype tout1) const { + int status = IDACalcICB( + solver_memory_.get(), which, tout1, xB_.getNVector(), dxB_.getNVector() + ); if (status != IDA_SUCCESS) throw IDAException(status, "IDACalcICB"); } @@ -724,35 +788,39 @@ void IDASolver::turnOffRootFinding() const { throw IDAException(status, "IDARootInit"); } -const Model *IDASolver::getModel() const { +Model const* IDASolver::getModel() const { if (!solver_memory_) throw AmiException( - "Solver has not been allocated, information is not available"); + "Solver has not been allocated, information is not available" + ); auto ida_mem = static_cast(solver_memory_.get()); - auto user_data = static_cast(ida_mem->ida_user_data); - if(user_data) + auto user_data = static_cast(ida_mem->ida_user_data); + if (user_data) return user_data->first; return nullptr; } void IDASolver::setLinearSolver() const { - int status = IDASetLinearSolver(solver_memory_.get(), linear_solver_->get(), - linear_solver_->getMatrix()); + int status = IDASetLinearSolver( + solver_memory_.get(), linear_solver_->get(), linear_solver_->getMatrix() + ); if (status != IDA_SUCCESS) throw IDAException(status, "setLinearSolver"); } -void IDASolver::setLinearSolverB(const int which) const { - int status = - IDASetLinearSolverB(solver_memory_B_[which].get(), which, - linear_solver_B_->get(), linear_solver_B_->getMatrix()); +void IDASolver::setLinearSolverB(int const which) const { + int status = IDASetLinearSolverB( + solver_memory_B_[which].get(), which, linear_solver_B_->get(), + linear_solver_B_->getMatrix() + ); if (status != IDA_SUCCESS) throw IDAException(status, "setLinearSolverB"); } void IDASolver::setNonLinearSolver() const { - int status = - IDASetNonlinearSolver(solver_memory_.get(), non_linear_solver_->get()); + int status = IDASetNonlinearSolver( + solver_memory_.get(), non_linear_solver_->get() + ); if (status != IDA_SUCCESS) throw CvodeException(status, "CVodeSetNonlinearSolver"); } @@ -767,17 +835,20 @@ void IDASolver::setNonLinearSolverSens() const { switch (ism_) { case InternalSensitivityMethod::staggered: - status = IDASetNonlinearSolverSensStg(solver_memory_.get(), - non_linear_solver_sens_->get()); + status = IDASetNonlinearSolverSensStg( + solver_memory_.get(), non_linear_solver_sens_->get() + ); break; case InternalSensitivityMethod::simultaneous: - status = IDASetNonlinearSolverSensSim(solver_memory_.get(), - non_linear_solver_sens_->get()); + status = IDASetNonlinearSolverSensSim( + solver_memory_.get(), non_linear_solver_sens_->get() + ); break; case InternalSensitivityMethod::staggered1: default: throw AmiException( - "Unsupported internal sensitivity method selected: %d", ism_); + "Unsupported internal sensitivity method selected: %d", ism_ + ); } if (status != IDA_SUCCESS) @@ -785,8 +856,9 @@ void IDASolver::setNonLinearSolverSens() const { } void IDASolver::setNonLinearSolverB(int which) const { - int status = IDASetNonlinearSolverB(solver_memory_.get(), which, - non_linear_solver_B_->get()); + int status = IDASetNonlinearSolverB( + solver_memory_.get(), which, non_linear_solver_B_->get() + ); if (status != IDA_SUCCESS) throw CvodeException(status, "CVodeSetNonlinearSolverB"); } @@ -806,15 +878,17 @@ void IDASolver::setNonLinearSolverB(int which) const { * @param tmp3 temporary storage vector * @return status flag indicating successful execution **/ -int fJ(realtype t, realtype cj, N_Vector x, N_Vector dx, - N_Vector xdot, SUNMatrix J, void *user_data, - N_Vector /*tmp1*/, N_Vector /*tmp2*/, N_Vector /*tmp3*/) { - auto typed_udata = static_cast(user_data); +int fJ( + realtype t, realtype cj, N_Vector x, N_Vector dx, N_Vector xdot, + SUNMatrix J, void* user_data, N_Vector /*tmp1*/, N_Vector /*tmp2*/, + N_Vector /*tmp3*/ +) { + auto typed_udata = static_cast(user_data); Expects(typed_udata); - auto model = dynamic_cast(typed_udata->first); + auto model = dynamic_cast(typed_udata->first); Expects(model); model->fJ(t, cj, x, dx, xdot, J); - return model->checkFinite(gsl::make_span(J), "Jacobian"); + return model->checkFinite(J, ModelQuantity::J, t); } /** @@ -833,17 +907,18 @@ int fJ(realtype t, realtype cj, N_Vector x, N_Vector dx, * @param tmp3B temporary storage vector * @return status flag indicating successful execution **/ -int fJB(realtype t, realtype cj, N_Vector x, N_Vector dx, - N_Vector xB, N_Vector dxB, N_Vector /*xBdot*/, SUNMatrix JB, - void *user_data, N_Vector /*tmp1B*/, N_Vector /*tmp2B*/, - N_Vector /*tmp3B*/) { - auto typed_udata = static_cast(user_data); +int fJB( + realtype t, realtype cj, N_Vector x, N_Vector dx, N_Vector xB, N_Vector dxB, + N_Vector /*xBdot*/, SUNMatrix JB, void* user_data, N_Vector /*tmp1B*/, + N_Vector /*tmp2B*/, N_Vector /*tmp3B*/ +) { + auto typed_udata = static_cast(user_data); Expects(typed_udata); - auto model = dynamic_cast(typed_udata->first); + auto model = dynamic_cast(typed_udata->first); Expects(model); model->fJB(t, cj, x, dx, xB, dxB, JB); - return model->checkFinite(gsl::make_span(JB), "Jacobian"); + return model->checkFinite(JB, ModelQuantity::JB, t); } /** @@ -860,17 +935,18 @@ int fJB(realtype t, realtype cj, N_Vector x, N_Vector dx, * @param tmp3 temporary storage vector * @return status flag indicating successful execution */ -int fJSparse(realtype t, realtype cj, N_Vector x, N_Vector dx, - N_Vector /*xdot*/, SUNMatrix J, void *user_data, - N_Vector /*tmp1*/, N_Vector /*tmp2*/, - N_Vector /*tmp3*/) { - auto typed_udata = static_cast(user_data); +int fJSparse( + realtype t, realtype cj, N_Vector x, N_Vector dx, N_Vector /*xdot*/, + SUNMatrix J, void* user_data, N_Vector /*tmp1*/, N_Vector /*tmp2*/, + N_Vector /*tmp3*/ +) { + auto typed_udata = static_cast(user_data); Expects(typed_udata); - auto model = dynamic_cast(typed_udata->first); + auto model = dynamic_cast(typed_udata->first); Expects(model); model->fJSparse(t, cj, x, dx, J); - return model->checkFinite(gsl::make_span(J), "Jacobian"); + return model->checkFinite(J, ModelQuantity::J, t); } /** @@ -889,17 +965,18 @@ int fJSparse(realtype t, realtype cj, N_Vector x, N_Vector dx, * @param tmp3B temporary storage vector * @return status flag indicating successful execution */ -int fJSparseB(realtype t, realtype cj, N_Vector x, N_Vector dx, - N_Vector xB, N_Vector dxB, N_Vector /*xBdot*/, - SUNMatrix JB, void *user_data, N_Vector /*tmp1B*/, - N_Vector /*tmp2B*/, N_Vector /*tmp3B*/) { - auto typed_udata = static_cast(user_data); +int fJSparseB( + realtype t, realtype cj, N_Vector x, N_Vector dx, N_Vector xB, N_Vector dxB, + N_Vector /*xBdot*/, SUNMatrix JB, void* user_data, N_Vector /*tmp1B*/, + N_Vector /*tmp2B*/, N_Vector /*tmp3B*/ +) { + auto typed_udata = static_cast(user_data); Expects(typed_udata); - auto model = dynamic_cast(typed_udata->first); + auto model = dynamic_cast(typed_udata->first); Expects(model); model->fJSparseB(t, cj, x, dx, xB, dxB, JB); - return model->checkFinite(gsl::make_span(JB), "Jacobian"); + return model->checkFinite(JB, ModelQuantity::JB, t); } /** @@ -916,9 +993,10 @@ int fJSparseB(realtype t, realtype cj, N_Vector x, N_Vector dx, * @param tmp3 temporary storage vector * @return status flag indicating successful execution */ -int fJBand(realtype t, realtype cj, N_Vector x, N_Vector dx, - N_Vector xdot, SUNMatrix J, void *user_data, - N_Vector tmp1, N_Vector tmp2, N_Vector tmp3) { +int fJBand( + realtype t, realtype cj, N_Vector x, N_Vector dx, N_Vector xdot, + SUNMatrix J, void* user_data, N_Vector tmp1, N_Vector tmp2, N_Vector tmp3 +) { return fJ(t, cj, x, dx, xdot, J, user_data, tmp1, tmp2, tmp3); } @@ -938,12 +1016,14 @@ int fJBand(realtype t, realtype cj, N_Vector x, N_Vector dx, * @param tmp3B temporary storage vector * @return status flag indicating successful execution */ -int fJBandB(realtype t, realtype cj, N_Vector x, N_Vector dx, - N_Vector xB, N_Vector dxB, N_Vector xBdot, SUNMatrix JB, - void *user_data, N_Vector tmp1B, N_Vector tmp2B, - N_Vector tmp3B) { - return fJB(t, cj, x, dx, xB, dxB, xBdot, JB, user_data, tmp1B, tmp2B, - tmp3B); +int fJBandB( + realtype t, realtype cj, N_Vector x, N_Vector dx, N_Vector xB, N_Vector dxB, + N_Vector xBdot, SUNMatrix JB, void* user_data, N_Vector tmp1B, + N_Vector tmp2B, N_Vector tmp3B +) { + return fJB( + t, cj, x, dx, xB, dxB, xBdot, JB, user_data, tmp1B, tmp2B, tmp3B + ); } /** @@ -960,17 +1040,19 @@ int fJBandB(realtype t, realtype cj, N_Vector x, N_Vector dx, * @param tmp2 temporary storage vector * @return status flag indicating successful execution **/ -int fJv(realtype t, N_Vector x, N_Vector dx, N_Vector /*xdot*/, - N_Vector v, N_Vector Jv, realtype cj, void *user_data, - N_Vector /*tmp1*/, N_Vector /*tmp2*/) { +int fJv( + realtype t, N_Vector x, N_Vector dx, N_Vector /*xdot*/, N_Vector v, + N_Vector Jv, realtype cj, void* user_data, N_Vector /*tmp1*/, + N_Vector /*tmp2*/ +) { - auto typed_udata = static_cast(user_data); + auto typed_udata = static_cast(user_data); Expects(typed_udata); - auto model = dynamic_cast(typed_udata->first); + auto model = dynamic_cast(typed_udata->first); Expects(model); model->fJv(t, x, dx, v, Jv, cj); - return model->checkFinite(gsl::make_span(Jv), "Jacobian"); + return model->checkFinite(gsl::make_span(Jv), ModelQuantity::Jv); } /** @@ -989,18 +1071,19 @@ int fJv(realtype t, N_Vector x, N_Vector dx, N_Vector /*xdot*/, * @param tmpB2 temporary storage vector * @return status flag indicating successful execution **/ -int fJvB(realtype t, N_Vector x, N_Vector dx, N_Vector xB, - N_Vector dxB, N_Vector /*xBdot*/, N_Vector vB, N_Vector JvB, - realtype cj, void *user_data, N_Vector /*tmpB1*/, - N_Vector /*tmpB2*/) { +int fJvB( + realtype t, N_Vector x, N_Vector dx, N_Vector xB, N_Vector dxB, + N_Vector /*xBdot*/, N_Vector vB, N_Vector JvB, realtype cj, void* user_data, + N_Vector /*tmpB1*/, N_Vector /*tmpB2*/ +) { - auto typed_udata = static_cast(user_data); + auto typed_udata = static_cast(user_data); Expects(typed_udata); - auto model = dynamic_cast(typed_udata->first); + auto model = dynamic_cast(typed_udata->first); Expects(model); model->fJvB(t, x, dx, xB, dxB, vB, JvB, cj); - return model->checkFinite(gsl::make_span(JvB), "Jacobian"); + return model->checkFinite(gsl::make_span(JvB), ModelQuantity::JvB); } /** @@ -1012,16 +1095,18 @@ int fJvB(realtype t, N_Vector x, N_Vector dx, N_Vector xB, * @param user_data object with user input * @return status flag indicating successful execution */ -int froot(realtype t, N_Vector x, N_Vector dx, realtype *root, - void *user_data) { - auto typed_udata = static_cast(user_data); +int froot( + realtype t, N_Vector x, N_Vector dx, realtype* root, void* user_data +) { + auto typed_udata = static_cast(user_data); Expects(typed_udata); - auto model = dynamic_cast(typed_udata->first); + auto model = dynamic_cast(typed_udata->first); Expects(model); model->froot(t, x, dx, gsl::make_span(root, model->ne)); - return model->checkFinite(gsl::make_span(root, model->ne), - "root function"); + return model->checkFinite( + gsl::make_span(root, model->ne), ModelQuantity::root + ); } /** @@ -1033,20 +1118,20 @@ int froot(realtype t, N_Vector x, N_Vector dx, realtype *root, * @param user_data object with user input * @return status flag indicating successful execution */ -int fxdot(realtype t, N_Vector x, N_Vector dx, N_Vector xdot, - void *user_data) { - auto typed_udata = static_cast(user_data); +int fxdot(realtype t, N_Vector x, N_Vector dx, N_Vector xdot, void* user_data) { + auto typed_udata = static_cast(user_data); Expects(typed_udata); - auto model = dynamic_cast(typed_udata->first); + auto model = dynamic_cast(typed_udata->first); Expects(model); auto solver = dynamic_cast(typed_udata->second); Expects(model); - if(solver->timeExceeded()) { + if (solver->timeExceeded(500)) { return AMICI_MAX_TIME_EXCEEDED; } - if (t > 1e200 && !model->app->checkFinite(gsl::make_span(x), "fxdot")) { + if (t > 1e200 + && !model->checkFinite(gsl::make_span(x), ModelQuantity::xdot)) { /* 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 @@ -1055,7 +1140,7 @@ int fxdot(realtype t, N_Vector x, N_Vector dx, N_Vector xdot, } model->fxdot(t, x, dx, xdot); - return model->checkFinite(gsl::make_span(xdot), "fxdot"); + return model->checkFinite(gsl::make_span(xdot), ModelQuantity::xdot); } /** @@ -1069,21 +1154,23 @@ int fxdot(realtype t, N_Vector x, N_Vector dx, N_Vector xdot, * @param user_data object with user input * @return status flag indicating successful execution */ -int fxBdot(realtype t, N_Vector x, N_Vector dx, N_Vector xB, - N_Vector dxB, N_Vector xBdot, void *user_data) { - auto typed_udata = static_cast(user_data); +int fxBdot( + realtype t, N_Vector x, N_Vector dx, N_Vector xB, N_Vector dxB, + N_Vector xBdot, void* user_data +) { + auto typed_udata = static_cast(user_data); Expects(typed_udata); - auto model = dynamic_cast(typed_udata->first); + auto model = dynamic_cast(typed_udata->first); Expects(model); auto solver = dynamic_cast(typed_udata->second); Expects(model); - if(solver->timeExceeded()) { + if (solver->timeExceeded(500)) { return AMICI_MAX_TIME_EXCEEDED; } model->fxBdot(t, x, dx, xB, dxB, xBdot); - return model->checkFinite(gsl::make_span(xBdot), "xBdot"); + return model->checkFinite(gsl::make_span(xBdot), ModelQuantity::xBdot); } /** @@ -1097,20 +1184,20 @@ int fxBdot(realtype t, N_Vector x, N_Vector dx, N_Vector xB, * @param user_data pointer to temp data object * @return status flag indicating successful execution */ -int fqBdot(realtype t, N_Vector x, N_Vector dx, N_Vector xB, - N_Vector dxB, N_Vector qBdot, void *user_data) { +int fqBdot( + realtype t, N_Vector x, N_Vector dx, N_Vector xB, N_Vector dxB, + N_Vector qBdot, void* user_data +) { - auto typed_udata = static_cast(user_data); + auto typed_udata = static_cast(user_data); Expects(typed_udata); - auto model = dynamic_cast(typed_udata->first); + auto model = dynamic_cast(typed_udata->first); Expects(model); model->fqBdot(t, x, dx, xB, dxB, qBdot); - return model->checkFinite(gsl::make_span(qBdot), "qBdot"); - + return model->checkFinite(gsl::make_span(qBdot), ModelQuantity::qBdot); } - /** * @brief Right hand side of differential equation for adjoint state xB * when simulating in steadystate mode @@ -1121,18 +1208,18 @@ int fqBdot(realtype t, N_Vector x, N_Vector dx, N_Vector xB, * @param user_data object with user input * @return status flag indicating successful execution */ -static int fxBdot_ss(realtype t, N_Vector xB, N_Vector dxB, N_Vector xBdot, - void *user_data) { - auto typed_udata = static_cast(user_data); +static int fxBdot_ss( + realtype t, N_Vector xB, N_Vector dxB, N_Vector xBdot, void* user_data +) { + auto typed_udata = static_cast(user_data); Expects(typed_udata); - auto model = dynamic_cast(typed_udata->first); + auto model = dynamic_cast(typed_udata->first); Expects(model); model->fxBdot_ss(t, xB, dxB, xBdot); - return model->checkFinite(gsl::make_span(xBdot), "xBdot_ss"); + return model->checkFinite(gsl::make_span(xBdot), ModelQuantity::xBdot_ss); } - /** * @brief Right hand side of integral equation for quadrature states qB * when simulating in steadystate mode @@ -1143,15 +1230,16 @@ static int fxBdot_ss(realtype t, N_Vector xB, N_Vector dxB, N_Vector xBdot, * @param user_data pointer to temp data object * @return status flag indicating successful execution */ -static int fqBdot_ss(realtype t, N_Vector xB, N_Vector dxB, N_Vector qBdot, - void *user_data) { - auto typed_udata = static_cast(user_data); +static int fqBdot_ss( + realtype t, N_Vector xB, N_Vector dxB, N_Vector qBdot, void* user_data +) { + auto typed_udata = static_cast(user_data); Expects(typed_udata); - auto model = dynamic_cast(typed_udata->first); + auto model = dynamic_cast(typed_udata->first); Expects(model); model->fqBdot_ss(t, xB, dxB, qBdot); - return model->checkFinite(gsl::make_span(qBdot), "qBdot_ss"); + return model->checkFinite(gsl::make_span(qBdot), ModelQuantity::qBdot_ss); } /** @@ -1168,17 +1256,20 @@ static int fqBdot_ss(realtype t, N_Vector xB, N_Vector dxB, N_Vector qBdot, * @param tmp3 temporary storage vector * @return status flag indicating successful execution */ - static int fJSparseB_ss(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*/) { - auto typed_udata = static_cast(user_data); +static int fJSparseB_ss( + 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*/ +) { + auto typed_udata = static_cast(user_data); Expects(typed_udata); - auto model = dynamic_cast(typed_udata->first); + auto model = dynamic_cast(typed_udata->first); Expects(model); model->fJSparseB_ss(JB); - return model->checkFinite(gsl::make_span(xBdot), "JSparseB_ss"); + return model->checkFinite( + gsl::make_span(xBdot), ModelQuantity::JSparseB_ss + ); } /** @@ -1197,20 +1288,21 @@ static int fqBdot_ss(realtype t, N_Vector xB, N_Vector dxB, N_Vector qBdot, * @param tmp3 temporary storage vector * @return status flag indicating successful execution */ -int fsxdot(int /*Ns*/, realtype t, N_Vector x, N_Vector dx, - N_Vector /*xdot*/, N_Vector *sx, N_Vector *sdx, - N_Vector *sxdot, void *user_data, N_Vector /*tmp1*/, - N_Vector /*tmp2*/, N_Vector /*tmp3*/) { +int fsxdot( + int /*Ns*/, realtype t, N_Vector x, N_Vector dx, N_Vector /*xdot*/, + N_Vector* sx, N_Vector* sdx, N_Vector* sxdot, void* user_data, + N_Vector /*tmp1*/, N_Vector /*tmp2*/, N_Vector /*tmp3*/ +) { - auto typed_udata = static_cast(user_data); + auto typed_udata = static_cast(user_data); Expects(typed_udata); - auto model = dynamic_cast(typed_udata->first); + auto model = dynamic_cast(typed_udata->first); Expects(model); 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]), "sxdot") - != AMICI_SUCCESS) + if (model->checkFinite(gsl::make_span(sxdot[ip]), ModelQuantity::sxdot) + != AMICI_SUCCESS) return AMICI_RECOVERABLE_ERROR; } diff --git a/deps/AMICI/src/spline.cpp b/deps/AMICI/src/spline.cpp index 4cbfd8245..4e7d74252 100644 --- a/deps/AMICI/src/spline.cpp +++ b/deps/AMICI/src/spline.cpp @@ -7,60 +7,62 @@ namespace amici { /************************************************/ +/* Legacy implementation of spline functions */ /* adapted from */ /* CMATH. Copyright (c) 1989 Design Software */ /* */ /************************************************/ - -int spline(int n, int end1, int end2, double slope1, double slope2, double x[], - double y[], double b[], double c[], double d[]) - /** - Evaluate the coefficients b[i], c[i], d[i], i = 0, 1, .. n-1 for - a cubic interpolating spline - - S(xx) = Y[i] + b[i] * w + c[i] * w**2 + d[i] * w**3 - where w = xx - x[i] - and x[i] <= xx <= x[i+1] - - The n supplied data points are x[i], y[i], i = 0 ... n-1. - - @param[in] n The number of data points or knots (n >= 2) - @param[in] end1 0: default condition 1: specify the slopes at x[0] - @param[in] end2 0: default condition 1: specify the slopes at x[n-1] - @param[in] slope1 slope at x[0] - @param[in] slope2 slope at x[n-1] - @param[in] x[] the abscissas of the knots in strictly increasing order - @param[in] y[] the ordinates of the knots - @param[out] b[] array of spline coefficients - @param[out] c[] array of spline coefficients - @param[out] d[] array of spline coefficients - - @retval 0 normal return - @retval 1 less than two data points; cannot interpolate - @retval 2 x[] are not in ascending order - - Notes - ----- - - The accompanying function seval() may be used to evaluate the - spline while deriv will provide the first derivative. - - Using p to denote differentiation - y[i] = S(X[i]) - b[i] = Sp(X[i]) - c[i] = Spp(X[i])/2 - d[i] = Sppp(X[i])/6 ( Derivative from the right ) - - Since the zero elements of the arrays ARE NOW used here, - all arrays to be passed from the main program should be - dimensioned at least [n]. These routines will use elements - [0 .. n-1]. - - Adapted from the text - Forsythe, G.E., Malcolm, M.A. and Moler, C.B. (1977) - "Computer Methods for Mathematical Computations" - Prentice Hall - - Note that although there are only n-1 polynomial segments, - n elements are requird in b, c, d. The elements b[n-1], - c[n-1] and d[n-1] are set to continue the last segment - past x[n-1]. +int spline( + int n, int end1, int end2, double slope1, double slope2, double x[], + double y[], double b[], double c[], double d[] +) +/** +Evaluate the coefficients b[i], c[i], d[i], i = 0, 1, .. n-1 for +a cubic interpolating spline + +S(xx) = Y[i] + b[i] * w + c[i] * w**2 + d[i] * w**3 +where w = xx - x[i] +and x[i] <= xx <= x[i+1] + +The n supplied data points are x[i], y[i], i = 0 ... n-1. + +@param[in] n The number of data points or knots (n >= 2) +@param[in] end1 0: default condition 1: specify the slopes at x[0] +@param[in] end2 0: default condition 1: specify the slopes at x[n-1] +@param[in] slope1 slope at x[0] +@param[in] slope2 slope at x[n-1] +@param[in] x[] the abscissas of the knots in strictly increasing order +@param[in] y[] the ordinates of the knots +@param[out] b[] array of spline coefficients +@param[out] c[] array of spline coefficients +@param[out] d[] array of spline coefficients + +@retval 0 normal return +@retval 1 less than two data points; cannot interpolate +@retval 2 x[] are not in ascending order + +Notes +----- + - The accompanying function seval() may be used to evaluate the + spline while deriv will provide the first derivative. + - Using p to denote differentiation + y[i] = S(X[i]) + b[i] = Sp(X[i]) + c[i] = Spp(X[i])/2 + d[i] = Sppp(X[i])/6 ( Derivative from the right ) + - Since the zero elements of the arrays ARE NOW used here, + all arrays to be passed from the main program should be + dimensioned at least [n]. These routines will use elements + [0 .. n-1]. + - Adapted from the text + Forsythe, G.E., Malcolm, M.A. and Moler, C.B. (1977) + "Computer Methods for Mathematical Computations" + Prentice Hall + - Note that although there are only n-1 polynomial segments, + n elements are requird in b, c, d. The elements b[n-1], + c[n-1] and d[n-1] are set to continue the last segment + past x[n-1]. */ { /* begin procedure spline() */ @@ -107,8 +109,8 @@ int spline(int n, int end1, int end2, double slope1, double slope2, double x[], c[nm1] = 0.0; if (n != 3) { c[0] = c[2] / (x[3] - x[1]) - c[1] / (x[2] - x[0]); - c[nm1] = c[n - 2] / (x[nm1] - x[n - 3]) - - c[n - 3] / (x[n - 2] - x[n - 4]); + c[nm1] = c[n - 2] / (x[nm1] - x[n - 3]) + - c[n - 3] / (x[n - 2] - x[n - 4]); c[0] = c[0] * d[0] * d[0] / (x[3] - x[0]); c[nm1] = -c[nm1] * d[n - 2] * d[n - 2] / (x[nm1] - x[n - 4]); } @@ -140,8 +142,8 @@ int spline(int n, int end1, int end2, double slope1, double slope2, double x[], /* c[i] is now the sigma[i] of the text */ /* Compute the polynomial coefficients */ - b[nm1] = (y[nm1] - y[n - 2]) / d[n - 2] + - d[n - 2] * (c[n - 2] + 2.0 * c[nm1]); + b[nm1] = (y[nm1] - y[n - 2]) / d[n - 2] + + d[n - 2] * (c[n - 2] + 2.0 * c[nm1]); for (i = 0; i < nm1; ++i) { b[i] = (y[i + 1] - y[i]) / d[i] - d[i] * (c[i + 1] + 2.0 * c[i]); d[i] = (c[i + 1] - c[i]) / d[i]; @@ -192,8 +194,9 @@ int spline(int n, int end1, int end2, double slope1, double slope2, double x[], */ -double seval(int n, double u, double x[], double y[], double b[], double c[], - double d[]) +double seval( + int n, double u, double x[], double y[], double b[], double c[], double d[] +) { /* begin function seval() */ @@ -253,8 +256,9 @@ double seval(int n, double u, double x[], double y[], double b[], double c[], */ -double sinteg(int n, double u, double x[], double y[], double b[], double c[], - double d[]) { /* begin function sinteg() */ +double sinteg( + int n, double u, double x[], double y[], double b[], double c[], double d[] +) { /* begin function sinteg() */ int i, j; double sum, dx; @@ -277,14 +281,15 @@ double sinteg(int n, double u, double x[], double y[], double b[], double c[], /* ---- Evaluate the integral for segments x < u ---- */ for (j = 0; j < i; ++j) { dx = x[j + 1] - x[j]; - sum += dx * (y[j] + - dx * (0.5 * b[j] + dx * (c[j] / 3.0 + dx * 0.25 * d[j]))); + sum += dx + * (y[j] + + dx * (0.5 * b[j] + dx * (c[j] / 3.0 + dx * 0.25 * d[j]))); } /* ---- Evaluate the integral fot this segment ---- */ dx = u - x[i]; - sum += - dx * (y[i] + dx * (0.5 * b[i] + dx * (c[i] / 3.0 + dx * 0.25 * d[i]))); + sum += dx + * (y[i] + dx * (0.5 * b[i] + dx * (c[i] / 3.0 + dx * 0.25 * d[i]))); return (sum); } diff --git a/deps/AMICI/src/splinefunctions.cpp b/deps/AMICI/src/splinefunctions.cpp new file mode 100644 index 000000000..ba9865a72 --- /dev/null +++ b/deps/AMICI/src/splinefunctions.cpp @@ -0,0 +1,1029 @@ +#include "amici/splinefunctions.h" +#include "amici/amici.h" +#include "amici/defines.h" +#include "amici/exception.h" +#include "amici/vector.h" + +#include // std::min +#include +#include + +namespace amici { + +static realtype +evaluate_polynomial(realtype const x, gsl::span coeff) { + /* Use Horner's method (https://en.wikipedia.org/wiki/Horner%27s_method) + * for numerical efficiency: + * + * spline(t) = a * t**3 + b * t**2 + c * t + d + * = d + t * (c + t * (b + t * a)) + * with coeff[0, 1, 2, 3] = [d, c, b, a] + */ + assert(coeff.size() >= 4); + auto coeff_p = coeff.data(); + return coeff_p[0] + x * (coeff_p[1] + x * (coeff_p[2] + x * coeff_p[3])); +} + +AbstractSpline::AbstractSpline( + std::vector nodes, std::vector node_values, + bool equidistant_spacing, bool logarithmic_parametrization +) + : nodes_(std::move(nodes)) + , node_values_(std::move(node_values)) + , equidistant_spacing_(equidistant_spacing) + , logarithmic_parametrization_(logarithmic_parametrization) { + + /* we want to set the number of nodes */ + auto n_nodes_ = static_cast(node_values_.size()); + + /* In case we have equidistant spacing, compute node locations */ + if (equidistant_spacing_) { + if (nodes_.size() != 2) + throw AmiException("Splines with equidistant spacing need a nodes " + "vector with two elements (first/last node)."); + realtype node_start = nodes_[0]; + realtype node_step = (nodes_[1] - nodes_[0]) / (n_nodes_ - 1); + nodes_.resize(n_nodes_); + nodes_[n_nodes_ - 1] = nodes_[1]; + for (int i_node = 0; i_node < n_nodes_ - 1; i_node++) + nodes_[i_node] = node_start + i_node * node_step; + } else if (nodes_.size() != node_values_.size()) { + throw std::invalid_argument( + "Number of nodes and number of node_values do not match." + ); + } +} + +realtype AbstractSpline::get_value(const realtype 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 { + 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 +) const { + auto s = get_sensitivity_scaled(t, ip); + return logarithmic_parametrization_ ? s * value : s; +} + +realtype AbstractSpline::get_node_value(int const i) const { + return node_values_[i]; +} + +realtype AbstractSpline::get_node_value_scaled(int const i) const { + // TODO It could be precomputed and stored in the object. + // Not sure if its worth the effort. + if (logarithmic_parametrization_) + return std::log(node_values_[i]); + else + return node_values_[i]; +} + +realtype AbstractSpline::get_final_value_scaled() const { + return final_value_scaled_; +} + +realtype AbstractSpline::get_final_value() const { + auto y = get_final_value_scaled(); + return logarithmic_parametrization_ ? std::exp(y) : y; +} + +void AbstractSpline::set_final_value_scaled(realtype finalValue) { + final_value_scaled_ = finalValue; +} + +realtype AbstractSpline::get_final_sensitivity_scaled(int const ip) const { + return final_sensitivity_scaled_[ip]; +} + +realtype AbstractSpline::get_final_sensitivity(int const ip) const { + auto s = get_final_sensitivity_scaled(ip); + if (logarithmic_parametrization_) { + auto v = get_final_value(); + if (std::isinf(v)) { + assert( + v > 0 + ); // logarithmic parameterization means positive values only + assert( + std::isnan(s) || s == 0 + ); // in the case the limit is +inf, sensitivity in log-scale will + // either be NaN or zero + return s; + } else { + return s * v; + } + } else { + return s; + } +} + +void AbstractSpline::set_final_sensitivity_scaled( + std::vector finalSensitivity +) { + final_sensitivity_scaled_ = std::move(finalSensitivity); +} + +bool AbstractSpline::get_equidistant_spacing() const { + return equidistant_spacing_; +} + +bool AbstractSpline::get_logarithmic_parametrization() const { + return logarithmic_parametrization_; +} + +HermiteSpline::HermiteSpline( + std::vector nodes, std::vector node_values, + std::vector node_values_derivative, + SplineBoundaryCondition firstNodeBC, SplineBoundaryCondition lastNodeBC, + SplineExtrapolation firstNodeExtrapol, SplineExtrapolation lastNodeExtrapol, + bool node_derivative_by_FD, bool equidistant_spacing, + bool logarithmic_parametrization +) + : AbstractSpline( + 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) + , first_node_ep_(firstNodeExtrapol) + , last_node_ep_(lastNodeExtrapol) + , node_derivative_by_FD_(node_derivative_by_FD) { + if (!node_derivative_by_FD_ + && node_values_derivative_.size() != nodes_.size()) { + throw std::invalid_argument( + "Size of node_values_derivative does not match number of nodes." + ); + } + + /* We may have to compute the derivatives at the nodes */ + handle_inner_derivatives(); + /* First and last node need to be handled separately */ + handle_boundary_conditions(); +} + +void HermiteSpline::handle_inner_derivatives() { + /* If values of the derivative at the nodes are to be computed by finite + * differences, we have to fill up node_values_derivative_ */ + if (node_derivative_by_FD_) { + node_values_derivative_.resize(n_nodes(), 0.0); + if (get_equidistant_spacing()) { + realtype hx2 = 2 * (nodes_[1] - nodes_[0]); + for (int i_node = 1; i_node < n_nodes() - 1; i_node++) + node_values_derivative_[i_node] + = (node_values_[i_node + 1] - node_values_[i_node - 1]) + / hx2; + } else { + for (int i_node = 1; i_node < n_nodes() - 1; i_node++) { + realtype dleft + = (node_values_[i_node] - node_values_[i_node - 1]) + / (nodes_[i_node] - nodes_[i_node - 1]); + realtype dright + = (node_values_[i_node + 1] - node_values_[i_node]) + / (nodes_[i_node + 1] - nodes_[i_node]); + node_values_derivative_[i_node] = (dleft + dright) / 2; + } + } + } +} + +void HermiteSpline::handle_boundary_conditions() { + int last = n_nodes() - 1; + + if ((first_node_bc_ == SplineBoundaryCondition::periodic + || last_node_bc_ == SplineBoundaryCondition::periodic) + && first_node_bc_ != last_node_bc_) + throw AmiException("If one of the boundary conditions is periodic, " + "the other one must be periodic too."); + + /* We have to take special care of the first node */ + switch (first_node_bc_) { + case SplineBoundaryCondition::given: + if (node_derivative_by_FD_) + /* 1-sided FD */ + node_values_derivative_[0] + = (node_values_[1] - node_values_[0]) / (nodes_[1] - nodes_[0]); + break; + + case SplineBoundaryCondition::zeroDerivative: + node_values_derivative_[0] = 0; + break; + + case SplineBoundaryCondition::natural: + node_values_derivative_[0] = -0.5 * node_values_derivative_[1] + + 1.5 * (node_values_[1] - node_values_[0]) + / (nodes_[1] - nodes_[0]); + break; + + case SplineBoundaryCondition::naturalZeroDerivative: + throw AmiException("Natural boundary condition with zero " + "derivative is not allowed for Hermite splines."); + + case SplineBoundaryCondition::periodic: + if (node_derivative_by_FD_) { + if (get_equidistant_spacing()) { + realtype hx2 = 2 * (nodes_[1] - nodes_[0]); + node_values_derivative_[0] + = (node_values_[1] - node_values_[last - 1]) / hx2; + } else { + realtype dleft = (node_values_[last] - node_values_[last - 1]) + / (nodes_[last] - nodes_[last - 1]); + realtype dright = (node_values_[1] - node_values_[0]) + / (nodes_[1] - nodes_[0]); + node_values_derivative_[0] = (dleft + dright) / 2; + } + } + break; + + default: + throw AmiException("Invalid value for boundary condition."); + } + + /* ...and the last node (1-sided FD). */ + switch (last_node_bc_) { + case SplineBoundaryCondition::given: + if (node_derivative_by_FD_) + /* 1-sided FD */ + node_values_derivative_[last] + = (node_values_[last] - node_values_[last - 1]) + / (nodes_[last] - nodes_[last - 1]); + break; + + case SplineBoundaryCondition::zeroDerivative: + node_values_derivative_[last] = 0; + break; + + case SplineBoundaryCondition::natural: + node_values_derivative_[last] + = -0.5 * node_values_derivative_[last - 1] + + 1.5 * (node_values_[last] - node_values_[last - 1]) + / (nodes_[last] - nodes_[last - 1]); + break; + + case SplineBoundaryCondition::naturalZeroDerivative: + throw AmiException("Natural boundary condition with zero " + "derivative is not allowed for Hermite splines."); + + case SplineBoundaryCondition::periodic: + if (node_derivative_by_FD_) + // if one bc is periodic, the other is periodic too + node_values_derivative_[last] = node_values_derivative_[0]; + break; + + default: + throw AmiException("Invalid value for boundary condition."); + } +} + +realtype HermiteSpline::get_node_derivative(int const i) const { + return node_values_derivative_[i]; +} + +realtype HermiteSpline::get_node_derivative_scaled(int const i) const { + // TODO It could be precomputed and stored in the object. + // Not sure if its worth the effort. + if (get_logarithmic_parametrization()) + return node_values_derivative_[i] / node_values_[i]; + else + return node_values_derivative_[i]; +} + +void HermiteSpline::compute_coefficients() { + /* Allocate space for the coefficients for Horner's method. + * They are stored in the vector as + * [d_0, c_0, b_0, a_0, d_1, c_1, ... , b_{n_nodes-1}, a_{n_nodes-1}] */ + coefficients.resize(4 * (n_nodes() - 1), 0.0); + + /* Compute the coefficients of the spline polynomials: + * spline(t) = a * t**3 + b * t**2 + c * t + d + * = d + t * (c + t * (b + t * a)) + * with coefficients[4 * i_node + (0, 1, 2, 3)] = (d, c, b, a) + */ + + for (int i_node = 0; i_node < n_nodes() - 1; i_node++) { + /* Get the length of the interval. Yes, we could save computation time + * by exploiting equidistant spacing, but we're talking about <1k FLOPs + * for sure, no matter what model. Screw it. */ + realtype len = nodes_[i_node + 1] - nodes_[i_node]; + + /* Coefficients for cubic Hermite polynomials */ + coefficients[4 * i_node] = get_node_value_scaled(i_node); + coefficients[4 * i_node + 1] = len * get_node_derivative_scaled(i_node); + coefficients[4 * i_node + 2] + = -3 * get_node_value_scaled(i_node) + - 2 * len * get_node_derivative_scaled(i_node) + + 3 * get_node_value_scaled(i_node + 1) + - len * get_node_derivative_scaled(i_node + 1); + coefficients[4 * i_node + 3] + = 2 * get_node_value_scaled(i_node) + + len * get_node_derivative_scaled(i_node) + - 2 * get_node_value_scaled(i_node + 1) + + len * get_node_derivative_scaled(i_node + 1); + } + + /* Take care of coefficients for extrapolation */ + compute_coefficients_extrapolation(); +} + +void HermiteSpline::compute_coefficients_extrapolation() { + /* Do we want to extrapolate at all? */ + bool needExtrapolationCoefficients + = first_node_ep_ == SplineExtrapolation::constant + || first_node_ep_ == SplineExtrapolation::linear + || last_node_ep_ == SplineExtrapolation::constant + || last_node_ep_ == SplineExtrapolation::linear; + if (!needExtrapolationCoefficients) + return; + + coefficients_extrapolate.resize(4, 0.0); + + int last = n_nodes() - 1; + + /* Beyond the spline nodes, we need to extrapolate using a * t + b. + * Those coefficients are stored as [b_first, a_first, b_last, a_last] */ + switch (first_node_ep_) { + case SplineExtrapolation::constant: + coefficients_extrapolate[0] = get_node_value_scaled(0); + coefficients_extrapolate[1] = 0; + break; + + case SplineExtrapolation::linear: + coefficients_extrapolate[0] + = get_node_value_scaled(0) + - nodes_[0] * get_node_derivative_scaled(0); + coefficients_extrapolate[1] = get_node_derivative_scaled(0); + break; + + default: + /* We don't need specific coefficients in the cases of: + * noExtrapolation, polynomial, periodic*/ + break; + } + switch (last_node_ep_) { + case SplineExtrapolation::constant: + coefficients_extrapolate[2] = get_node_value_scaled(last); + coefficients_extrapolate[3] = 0; + break; + + case SplineExtrapolation::linear: + coefficients_extrapolate[2] + = get_node_value_scaled(last) + - nodes_[last] * get_node_derivative_scaled(last); + coefficients_extrapolate[3] = get_node_derivative_scaled(last); + break; + + default: + /* We don't need specific coefficients in the cases of: + * noExtrapolation, polynomial, periodic*/ + break; + } +} + +#ifdef DVALUESDP +#error "Preprocessor macro DVALUESDP already defined?!" +#else +#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] +#endif + +void HermiteSpline::compute_coefficients_sensi( + int nplist, int spline_offset, gsl::span dvaluesdp, + gsl::span dslopesdp +) { + // If slopes are computed by finite differences, + // we need to autocompute the slope sensitivities + if (node_derivative_by_FD_) { + assert(dvaluesdp.size() == dslopesdp.size()); + for (int ip = 0; ip < nplist; ip++) + compute_slope_sensitivities_by_fd( + nplist, spline_offset, ip, dvaluesdp, dslopesdp + ); + } + + /* Ensure that dslopesdp satisfies the BC */ + if (first_node_bc_ == SplineBoundaryCondition::zeroDerivative) { + for (int ip = 0; ip < nplist; ip++) + dslopesdp[spline_offset + ip] = 0.0; + } + if (last_node_bc_ == SplineBoundaryCondition::zeroDerivative) { + int last = n_nodes() - 1; + for (int ip = 0; ip < nplist; ip++) + dslopesdp[spline_offset + ip + last * nplist] = 0.0; + } + + // If necessary, translate sensitivities to logarithmic parametrization + if (get_logarithmic_parametrization()) { + for (int i_node = 0; i_node < n_nodes(); i_node++) { + for (int ip = 0; ip < nplist; ip++) { + int node_offset = spline_offset + ip; + realtype value = get_node_value(i_node); + realtype slope = node_values_derivative_[i_node]; + realtype dvaluedp = DVALUESDP(i_node); + realtype dslopedp = DSLOPESDP(i_node); + DVALUESDP(i_node) = dvaluedp / value; + DSLOPESDP(i_node) + = (dslopedp - dvaluedp * slope / value) / value; + } + } + } + + /* + * Allocate space for the coefficients + * They are stored in the vector as + * [ D[d_0, p0], D[c_0, p0], D[b_0, p0], D[a_0, p0], D[d_1, p0], + * ... , + * D[b_{n_nodes-1}, p0], D[a_{n_nodes-1}, p0], + * D[d_0, p1], D[c_0, p1], ... + * ..., D[b_{n_nodes-1}, p{nplist-1}, D[a_{n_nodes-1}, p{nplist-1}] + * ] + */ + int n_spline_coefficients = 4 * (n_nodes() - 1); + coefficients_sensi.resize(n_spline_coefficients * nplist, 0.0); + + /* + * We're using short hand notation for some node values or slopes, based on + * the notation used on https://en.wikipedia.org/wiki/Cubic_Hermite_spline + * In brief: "p" denotes the current (k-th) spline node value, + * "m" its tangent or slope, "s" in front the sensitivity, "1" at the end + * means the following node (" + 1"), so "smk1" is the sensitivity of the + * slope at node k + 1, w.r.t. to the current parameter (looping index). + */ + + /* Parametric derivatives of splines are splines again. + * We compute the coefficients for those polynomials now. */ + for (int i_node = 0; i_node < n_nodes() - 1; i_node++) { + /* Get the length of the interval. */ + realtype len = nodes_[i_node + 1] - nodes_[i_node]; + + /* As computing the coefficient is a mess, it's in another function */ + for (int ip = 0; ip < nplist; ip++) + get_coeffs_sensi_lowlevel( + ip, i_node, nplist, n_spline_coefficients, spline_offset, len, + dvaluesdp, dslopesdp, coefficients_sensi + ); + } + + /* We need the coefficients for extrapolating beyond the spline domain */ + compute_coefficients_extrapolation_sensi( + nplist, spline_offset, dvaluesdp, dslopesdp + ); +} + +void HermiteSpline::compute_slope_sensitivities_by_fd( + int nplist, int spline_offset, int ip, gsl::span dvaluesdp, + gsl::span dslopesdp +) { + int last = n_nodes() - 1; + int node_offset = spline_offset + ip; + + // Left boundary (first node) + switch (first_node_bc_) { + case SplineBoundaryCondition::given: + DSLOPESDP(0) = (DVALUESDP(1) - DVALUESDP(0)) / (nodes_[1] - nodes_[0]); + break; + + case SplineBoundaryCondition::zeroDerivative: + DSLOPESDP(0) = 0; + break; + + case SplineBoundaryCondition::natural: + throw AmiException("Natural boundary condition for Hermite " + "splines is not implemented yet."); + + case SplineBoundaryCondition::periodic: + if (get_equidistant_spacing()) { + realtype hx2 = 2 * (nodes_[1] - nodes_[0]); + DSLOPESDP(0) = (DVALUESDP(1) - DVALUESDP(last - 1)) / hx2; + } else { + realtype dleft = (DVALUESDP(last) - DVALUESDP(last - 1)) + / (nodes_[last] - nodes_[last - 1]); + realtype dright + = (DVALUESDP(1) - DVALUESDP(0)) / (nodes_[1] - nodes_[0]); + DSLOPESDP(0) = (dleft + dright) / 2; + } + break; + + default: + throw AmiException("Unexpected value for boundary condition."); + } + + // Inner nodes + if (get_equidistant_spacing()) { + realtype hx2 = 2 * (nodes_[1] - nodes_[0]); + for (int i_node = 1; i_node < n_nodes() - 1; i_node++) + DSLOPESDP(i_node) + = (DVALUESDP(i_node + 1) - DVALUESDP(i_node - 1)) / hx2; + } else { + for (int i_node = 1; i_node < n_nodes() - 1; i_node++) { + realtype dleft = (DVALUESDP(i_node) - DVALUESDP(i_node - 1)) + / (nodes_[i_node] - nodes_[i_node - 1]); + realtype dright = (DVALUESDP(i_node + 1) - DVALUESDP(i_node)) + / (nodes_[i_node + 1] - nodes_[i_node]); + DSLOPESDP(i_node) = (dleft + dright) / 2; + } + } + + // Right boundary (last nodes) + switch (last_node_bc_) { + case SplineBoundaryCondition::given: + DSLOPESDP(last) = (DVALUESDP(last) - DVALUESDP(last - 1)) + / (nodes_[last] - nodes_[last - 1]); + break; + + case SplineBoundaryCondition::zeroDerivative: + DSLOPESDP(last) = 0; + break; + + case SplineBoundaryCondition::natural: + throw AmiException("Natural boundary condition for Hermite " + "splines is not implemented yet."); + + case SplineBoundaryCondition::naturalZeroDerivative: + throw AmiException("Natural boundary condition with zero " + "derivative is not allowed for Hermite splines."); + + case SplineBoundaryCondition::periodic: + // if one bc is periodic, the other is periodic too + DSLOPESDP(last) = DSLOPESDP(0); + break; + + default: + throw AmiException("Unexpected value for boundary condition."); + } +} + +#undef DVALUESDP +#undef DSLOPESDP + +void HermiteSpline::compute_coefficients_extrapolation_sensi( + int nplist, int spline_offset, gsl::span dvaluesdp, + gsl::span dslopesdp +) { + + /* Do we want to extrapolate at all? */ + bool needExtrapolationCoefficients + = first_node_ep_ == SplineExtrapolation::constant + || first_node_ep_ == SplineExtrapolation::linear + || last_node_ep_ == SplineExtrapolation::constant + || last_node_ep_ == SplineExtrapolation::linear; + if (!needExtrapolationCoefficients) + return; + + /* Beyond the spline nodes, we need to extrapolate using a * t + b. + * Those coefficients are stored as + * [ + * D[b_first, p0], D[a_first, p0], D[b_last, p0], D[a_last, p0], + * D[b_first, p1], ... D[a_last, p{nplist-1}] + * ] + */ + coefficients_extrapolate_sensi.resize(4 * nplist, 0.0); + + realtype sm0; + for (int ip = 0; ip < nplist; ip++) { + realtype sp0 = dvaluesdp[spline_offset + ip]; + switch (first_node_ep_) { + /* This whole switch-case-if-else-if-thing could be moved + * outside the loop, I know. Yet, it's at most some thousand + * if's done once in the program for saving many lines of code + * and getting a much clearer code structure. */ + case SplineExtrapolation::constant: + sm0 = 0; + break; + + case SplineExtrapolation::linear: + if (first_node_bc_ == SplineBoundaryCondition::zeroDerivative) { + sm0 = 0; + } 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) { + 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) { + sm0 = dslopesdp[spline_offset + ip]; + + } 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 { + throw AmiException( + "Some weird combination of spline boundary " + "condition, extrapolation and finite differences was " + "passed which should not be allowed." + ); + } + break; + + default: + /* We don't need specific coefficients in the cases of: + * noExtrapolation, polynomial, periodic + * NB the corresponding values in coefficients_extrapolate_sensi + * will never be accessed, so it's safe to leave them + * undefined. + */ + continue; + } + /* Write them to the vector */ + coefficients_extrapolate_sensi[4 * ip] = sp0 - sm0 * nodes_[0]; + coefficients_extrapolate_sensi[4 * ip + 1] = sm0; + } + + realtype sm_end; + for (int ip = 0; ip < nplist; ip++) { + realtype sp_end + = dvaluesdp[spline_offset + ip + (n_nodes() - 1) * nplist]; + switch (last_node_ep_) { + /* This whole switch-case-if-else-if-thing could be moved + * outside the loop, I know. Yet, it's at most some thousand + * if's done once in the program for saving many lines of code + * and getting a much clearer code structure. */ + case SplineExtrapolation::constant: + sm_end = 0; + break; + + case SplineExtrapolation::linear: + if (last_node_bc_ == SplineBoundaryCondition::zeroDerivative) { + sm_end = 0; + } 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) { + 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) { + sm_end + = dslopesdp[spline_offset + ip + (n_nodes() - 1) * nplist]; + + } 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 { + throw AmiException( + "Some weird combination of spline boundary " + "condition, extrapolation and finite differences was " + "passed which should not be allowed." + ); + } + break; + + default: + /* We don't need specific coefficients in the cases of: + * noExtrapolation, polynomial, periodic + * NB the corresponding values in coefficients_extrapolate_sensi + * will never be accessed, so it's safe to leave them + * undefined. + */ + continue; + } + /* Write them to the vector */ + coefficients_extrapolate_sensi[4 * ip + 2] + = sp_end - sm_end * nodes_[n_nodes() - 1]; + coefficients_extrapolate_sensi[4 * ip + 3] = sm_end; + } +} + +void HermiteSpline::get_coeffs_sensi_lowlevel( + int ip, int i_node, int nplist, int n_spline_coefficients, + int spline_offset, realtype len, gsl::span dnodesdp, + gsl::span dslopesdp, gsl::span coeffs +) const { + /* We're using the short hand notation for node values and slopes from + * compute_coefficients_sensi() here. See this function for documentation. + */ + int node_offset = spline_offset + ip; + realtype spk = dnodesdp[node_offset + i_node * nplist]; + realtype spk1 = dnodesdp[node_offset + (i_node + 1) * nplist]; + realtype smk = dslopesdp[node_offset + i_node * nplist]; + realtype smk1 = dslopesdp[node_offset + (i_node + 1) * nplist]; + + /* Compute the actual coefficients */ + coeffs[ip * n_spline_coefficients + 4 * i_node] = spk; + coeffs[ip * n_spline_coefficients + 4 * i_node + 1] = len * smk; + coeffs[ip * n_spline_coefficients + 4 * i_node + 2] + = 3 * (spk1 - spk) - len * (2 * smk + smk1); + coeffs[ip * n_spline_coefficients + 4 * i_node + 3] + = 2 * (spk - spk1) + len * (smk + smk1); +} + +void HermiteSpline::compute_final_value() { + /* We need to compute the final value of the spline, depending on its + * boundary condition and the extrapolation option. */ + realtype finalValue; + if (last_node_ep_ == SplineExtrapolation::constant) { + finalValue = coefficients_extrapolate[2]; + } else if (last_node_ep_ == SplineExtrapolation::linear) { + if (last_node_bc_ == SplineBoundaryCondition::zeroDerivative) { + finalValue = coefficients_extrapolate[2]; + } else if (coefficients_extrapolate[3] < 0) { + finalValue = -INFINITY; + } else if (coefficients_extrapolate[3] > 0) { + finalValue = INFINITY; + } else { + finalValue = coefficients_extrapolate[2]; + } + } else if (last_node_ep_ == SplineExtrapolation::polynomial) { + int last = 4 * (n_nodes() - 1) - 1; + if (coefficients[last] < 0) { + finalValue = -INFINITY; + } else if (coefficients[last] > 0) { + finalValue = INFINITY; + } else if (coefficients[last - 1] < 0) { + finalValue = -INFINITY; + } else if (coefficients[last - 1] > 0) { + finalValue = INFINITY; + } else if (coefficients[last - 2] < 0) { + finalValue = -INFINITY; + } else if (coefficients[last - 2] > 0) { + finalValue = INFINITY; + } else { + finalValue = coefficients[last - 3]; + } + } else { + /* Periodic: will not yield a steady state, unless the spline is the + * constant function */ + finalValue = get_node_value_scaled(0); + for (int i = 0; i < n_nodes(); i++) { + if (get_node_value_scaled(i) != finalValue + || get_node_derivative_scaled(i) != 0) { + finalValue = NAN; + break; + } + } + } + set_final_value_scaled(finalValue); +} + +void HermiteSpline::compute_final_sensitivity( + int nplist, int /*spline_offset*/, gsl::span /*dvaluesdp*/, + gsl::span /*dslopesdp*/ +) { + /* We need to compute the final value of the spline, depending on its + * boundary condition and the extrapolation option. */ + std::vector finalSensitivity(nplist, 0); + if ((last_node_ep_ == SplineExtrapolation::constant) + || (last_node_bc_ == SplineBoundaryCondition::zeroDerivative + && last_node_ep_ == SplineExtrapolation::linear)) { + for (int ip = 0; ip < nplist; ip++) + finalSensitivity[ip] = coefficients_extrapolate_sensi[4 * ip + 2]; + } else if (last_node_ep_ == SplineExtrapolation::linear) { + /* If steady state is infinity, sensitivity must be 0 + * (unless the derivative is zero and the final value will change + * abruptly from finite to +-inf) (if the derivative is constant zero in + * a neighbourhood, then the final value will not change, but this is + * impossible to determine just from the sensitivity of the derivative) + */ + int last = n_nodes() - 1; + if (get_node_derivative_scaled(last) == 0) + std::fill(finalSensitivity.begin(), finalSensitivity.end(), NAN); + } else if (last_node_ep_ == SplineExtrapolation::polynomial) { + /* Yes, that's not correct. But I don't see any good reason for + * implementing a case, which anybody with more than a dead fish + * between the ears will never use. */ + std::fill(finalSensitivity.begin(), finalSensitivity.end(), NAN); + } else { + /* Periodic: will not yield a steady state + * (unless the spline is the constant funtion, + * but even in that case sensitivity information is not able to tell us + * whether the steady state continues to exist in a neighbourhood of the + * current parameters + */ + std::fill(finalSensitivity.begin(), finalSensitivity.end(), NAN); + } + set_final_sensitivity_scaled(finalSensitivity); +} + +realtype HermiteSpline::get_value_scaled(const realtype t) const { + /* Is this a steady state computation? */ + if (std::isinf(t)) + return get_final_value_scaled(); + + /* Compute the spline value */ + int i_node; + realtype len; + + /* Are we past the last node? Extrapolate! */ + if (t > nodes_[n_nodes() - 1]) { + switch (last_node_ep_) { + case SplineExtrapolation::noExtrapolation: + throw AmiException( + "Trying to evaluate spline after last " + "spline node, but spline has been specified not to allow " + "extrapolation." + ); + + case SplineExtrapolation::constant: + return coefficients_extrapolate[2]; + + case SplineExtrapolation::linear: + return coefficients_extrapolate[2] + + t * coefficients_extrapolate[3]; + + case SplineExtrapolation::polynomial: + /* Evaluate last interpolation polynomial */ + i_node = n_nodes() - 2; + len = nodes_[i_node + 1] - nodes_[i_node]; + return evaluate_polynomial( + (t - nodes_[i_node]) / len, + gsl::make_span(coefficients).subspan(i_node * 4) + ); + + case SplineExtrapolation::periodic: + len = nodes_[n_nodes() - 1] - nodes_[0]; + return get_value(nodes_[0] + std::fmod(t - nodes_[0], len)); + + default: + throw AmiException("Unsupported SplineExtrapolation type"); + } + } + + /* Are we before the first node? Extrapolate! */ + if (t < nodes_[0]) { + switch (first_node_ep_) { + case SplineExtrapolation::noExtrapolation: + throw AmiException( + "Trying to evaluate spline before first " + "spline node, but spline has been specified not to allow " + "extrapolation." + ); + + case SplineExtrapolation::constant: + return coefficients_extrapolate[0]; + + case SplineExtrapolation::linear: + return coefficients_extrapolate[0] + + t * coefficients_extrapolate[1]; + + case SplineExtrapolation::polynomial: + /* Evaluate last interpolation polynomial */ + len = nodes_[1] - nodes_[0]; + return evaluate_polynomial((t - nodes_[0]) / len, coefficients); + + case SplineExtrapolation::periodic: + len = nodes_[n_nodes() - 1] - nodes_[0]; + return get_value( + nodes_[n_nodes() - 1] + std::fmod(t - nodes_[0], len) + ); + default: + throw AmiException("Unsupported SplineExtrapolation type"); + } + } + + /* Get the spline interval which we need */ + if (get_equidistant_spacing()) { + /* equidistant spacing: just compute the interval */ + len = nodes_[1] - nodes_[0]; + i_node = static_cast(std::trunc((t - nodes_[0]) / len)); + i_node = std::min(i_node, n_nodes() - 2); + } else { + /* no equidistant spacing: we need to iterate */ + i_node = 0; + while (nodes_[i_node + 1] < t) { + i_node++; + } + if (t == nodes_[i_node + 1]) + return get_node_value_scaled(i_node + 1); // make it exact on nodes + len = nodes_[i_node + 1] - nodes_[i_node]; + } + + /* Evaluate the interpolation polynomial */ + return evaluate_polynomial( + (t - nodes_[i_node]) / len, + gsl::make_span(coefficients).subspan(i_node * 4) + ); +} + +realtype +HermiteSpline::get_sensitivity_scaled(const realtype t, int const ip) const { + /* Is this a steady state computation? */ + if (std::isinf(t)) + return get_final_sensitivity_scaled(ip); + + /* Compute the parametric derivative of the spline value */ + int i_node; + realtype len; + + if (t > nodes_[n_nodes() - 1]) { + /* Are we past the last node? Extrapolate! */ + switch (last_node_ep_) { + case SplineExtrapolation::noExtrapolation: + throw AmiException( + "Trying to evaluate spline sensitivity " + "after last spline node, but spline has been specified " + "to not allow extrapolation." + ); + + case SplineExtrapolation::constant: + return coefficients_extrapolate_sensi[4 * ip + 2]; + + case SplineExtrapolation::linear: + return coefficients_extrapolate_sensi[4 * ip + 2] + + t * coefficients_extrapolate_sensi[4 * ip + 3]; + + case SplineExtrapolation::polynomial: + /* Evaluate last interpolation polynomial */ + i_node = n_nodes() - 2; + len = nodes_[i_node + 1] - nodes_[i_node]; + return evaluate_polynomial( + (t - nodes_[i_node]) / len, + gsl::make_span(coefficients_sensi) + .subspan(ip * (n_nodes() - 1) * 4 + i_node * 4) + ); + + case SplineExtrapolation::periodic: + len = nodes_[n_nodes() - 1] - nodes_[0]; + return get_sensitivity( + nodes_[0] + std::fmod(t - nodes_[0], len), ip + ); + default: + throw AmiException("Unsupported SplineExtrapolation type"); + } + } + + if (t < nodes_[0]) { + /* Are we before the first node? Extrapolate! */ + switch (first_node_ep_) { + case SplineExtrapolation::noExtrapolation: + throw AmiException( + "Trying to evaluate spline before first " + "spline node, but spline has been specified to not allow " + "extrapolation." + ); + + case SplineExtrapolation::constant: + return coefficients_extrapolate_sensi[4 * ip + 0]; + + case SplineExtrapolation::linear: + return coefficients_extrapolate_sensi[4 * ip + 0] + + t * coefficients_extrapolate_sensi[4 * ip + 1]; + + case SplineExtrapolation::polynomial: + /* Evaluate last interpolation polynomial */ + len = nodes_[1] - nodes_[0]; + return evaluate_polynomial( + (t - nodes_[0]) / len, gsl::make_span(coefficients_sensi) + .subspan(ip * (n_nodes() - 1) * 4) + ); + + case SplineExtrapolation::periodic: + len = nodes_[n_nodes() - 1] - nodes_[0]; + return get_sensitivity( + nodes_[n_nodes() - 1] + std::fmod(t - nodes_[0], len), ip + ); + default: + throw AmiException("Unsupported SplineExtrapolation type"); + } + } + + /* Get the spline interval which we need */ + if (get_equidistant_spacing()) { + /* equidistant spacing: just compute the interval */ + len = nodes_[1] - nodes_[0]; + i_node = static_cast(std::trunc((t - nodes_[0]) / len)); + i_node = std::min(i_node, n_nodes() - 2); + } else { + /* no equidistant spacing: we need to iterate */ + i_node = 0; + while (nodes_[i_node + 1] < t) { + i_node++; + } + len = nodes_[i_node + 1] - nodes_[i_node]; + } + + /* Evaluate the interpolation polynomial */ + return evaluate_polynomial( + (t - nodes_[i_node]) / len, + gsl::make_span(coefficients_sensi) + .subspan(ip * (n_nodes() - 1) * 4 + i_node * 4) + ); +} + +} // namespace amici diff --git a/deps/AMICI/src/steadystateproblem.cpp b/deps/AMICI/src/steadystateproblem.cpp index 9ae6d7bd4..c655b9b38 100644 --- a/deps/AMICI/src/steadystateproblem.cpp +++ b/deps/AMICI/src/steadystateproblem.cpp @@ -1,239 +1,307 @@ #include "amici/steadystateproblem.h" +#include "amici/backwardproblem.h" #include "amici/defines.h" -#include "amici/model.h" -#include "amici/solver.h" -#include "amici/solver_cvodes.h" #include "amici/edata.h" -#include "amici/forwardproblem.h" -#include "amici/backwardproblem.h" -#include "amici/newton_solver.h" #include "amici/misc.h" +#include "amici/model.h" +#include "amici/newton_solver.h" +#include "amici/solver.h" #include #include -#include -#include -#include #include +#include +#include -namespace amici { +constexpr realtype conv_thresh = 1.0; -SteadystateProblem::SteadystateProblem(const Solver &solver, const Model &model) - : delta_(model.nx_solver), ewt_(model.nx_solver), ewtQB_(model.nplist()), - rel_x_newton_(model.nx_solver), x_newton_(model.nx_solver), - x_(model.nx_solver), x_old_(model.nx_solver), dx_(model.nx_solver), - xdot_(model.nx_solver), xdot_old_(model.nx_solver), - sx_(model.nx_solver, model.nplist()), sdx_(model.nx_solver, model.nplist()), - xB_(model.nJ * model.nx_solver), xQ_(model.nJ * model.nx_solver), - xQB_(model.nplist()), xQBdot_(model.nplist()), - dJydx_(model.nJ * model.nx_solver * model.nt(), 0.0) { - /* maxSteps must be adapted if iterative linear solvers are used */ - if (solver.getLinearSolver() == LinearSolver::SPBCG) { - max_steps_ = solver.getNewtonMaxSteps(); - numlinsteps_.resize(2 * max_steps_, 0); - } - /* Check for compatibility of options */ - if (solver.getSensitivityMethod() == SensitivityMethod::forward && - solver.getSensitivityMethodPreequilibration() == SensitivityMethod::adjoint && - solver.getSensitivityOrder() > SensitivityOrder::none) - throw AmiException("Preequilibration using adjoint sensitivities " - "is not compatible with using forward " - "sensitivities during simulation"); - } - -void SteadystateProblem::workSteadyStateProblem(Solver *solver, Model *model, - int it) { +namespace amici { - /* process solver handling for pre- or postequilibration */ - if (it == -1) { - /* solver was not run before, set up everything */ - model->initialize(x_, dx_, sx_, sdx_, - solver->getSensitivityOrder() >= - SensitivityOrder::first); - t_ = model->t0(); - solver->setup(t_, model, x_, dx_, sx_, sdx_); - } else { - /* solver was run before, extract current state from solver */ - solver->writeSolution(&t_, x_, dx_, sx_, xQ_); - } +SteadystateProblem::SteadystateProblem(Solver const& solver, Model const& model) + : delta_(model.nx_solver) + , delta_old_(model.nx_solver) + , ewt_(model.nx_solver) + , ewtQB_(model.nplist()) + , x_old_(model.nx_solver) + , xdot_(model.nx_solver) + , sdx_(model.nx_solver, model.nplist()) + , xB_(model.nJ * model.nx_solver) + , xQ_(model.nJ * model.nx_solver) + , xQB_(model.nplist()) + , xQBdot_(model.nplist()) + , max_steps_(solver.getNewtonMaxSteps()) + , dJydx_(model.nJ * model.nx_solver * model.nt(), 0.0) + , state_( + {INFINITY, // t + AmiVector(model.nx_solver), // x + AmiVector(model.nx_solver), // dx + AmiVectorArray(model.nx_solver, model.nplist()), // sx + model.getModelState()} + ) + , // state + atol_(solver.getAbsoluteToleranceSteadyState()) + , rtol_(solver.getRelativeToleranceSteadyState()) + , atol_sensi_(solver.getAbsoluteToleranceSteadyStateSensi()) + , rtol_sensi_(solver.getRelativeToleranceSteadyStateSensi()) + , atol_quad_(solver.getAbsoluteToleranceQuadratures()) + , rtol_quad_(solver.getRelativeToleranceQuadratures()) + , newton_solver_(NewtonSolver::getSolver(solver, model)) + , damping_factor_mode_(solver.getNewtonDampingFactorMode()) + , damping_factor_lower_bound_(solver.getNewtonDampingFactorLowerBound()) + , newton_step_conv_(solver.getNewtonStepSteadyStateCheck()) + , check_sensi_conv_(solver.getSensiSteadyStateCheck()) { + /* Check for compatibility of options */ + if (solver.getSensitivityMethod() == SensitivityMethod::forward + && solver.getSensitivityMethodPreequilibration() + == SensitivityMethod::adjoint + && solver.getSensitivityOrder() > SensitivityOrder::none) + throw AmiException("Preequilibration using adjoint sensitivities " + "is not compatible with using forward " + "sensitivities during simulation"); + if (solver.getSensitivityMethod() == SensitivityMethod::forward + && model.getSteadyStateComputationMode() + == SteadyStateComputationMode::newtonOnly + && model.getSteadyStateSensitivityMode() + == SteadyStateSensitivityMode::integrationOnly) + throw AmiException("For forward sensitivity analysis steady-state " + "computation mode 'newtonOnly' and steady-state " + "sensitivity mode 'integrationOnly' are not " + "compatible as numerical integration of the model " + "ODEs and corresponding forward sensitivities ODEs " + "is coupled"); +} - /* create a Newton solver object */ - auto newtonSolver = NewtonSolver::getSolver(&t_, &x_, *solver, model); +void SteadystateProblem::workSteadyStateProblem( + Solver const& solver, Model& model, int it +) { + initializeForwardProblem(it, solver, model); - /* Compute steady state and get the computation time */ - clock_t starttime = clock(); - findSteadyState(solver, newtonSolver.get(), model, it); - cpu_time_ = (double)((clock() - starttime) * 1000) / CLOCKS_PER_SEC; + /* Compute steady state, track computation time */ + CpuTimer cpu_timer; + findSteadyState(solver, model, it); /* Check whether state sensis still need to be computed */ - if (getSensitivityFlag(model, solver, it, SteadyStateContext::newtonSensi)) - { + if (getSensitivityFlag( + model, solver, it, SteadyStateContext::newtonSensi + )) { try { /* this might still fail, if the Jacobian is singular and simulation did not find a steady state */ - newtonSolver->computeNewtonSensis(sx_); - } catch (NewtonFailure const &) { - /* No steady state could be inferred. Store simulation state */ - storeSimulationState(model, solver->getSensitivityOrder() >= - SensitivityOrder::first); - throw AmiException("Steady state sensitivity computation failed due " - "to unsuccessful factorization of RHS Jacobian"); + newton_solver_->computeNewtonSensis(state_.sx, model, state_); + } catch (NewtonFailure const&) { + throw AmiException( + "Steady state sensitivity computation failed due " + "to unsuccessful factorization of RHS Jacobian" + ); } } - - /* Get output of steady state solver, write it to x0 and reset time - if necessary */ - storeSimulationState(model, getSensitivityFlag(model, solver, it, - SteadyStateContext::sensiStorage)); + cpu_time_ = cpu_timer.elapsed_milliseconds(); } -void SteadystateProblem::workSteadyStateBackwardProblem(Solver *solver, - Model *model, - const BackwardProblem *bwd) { - /* initialize and check if there is something to be done */ +void SteadystateProblem::workSteadyStateBackwardProblem( + Solver const& solver, Model& model, BackwardProblem const* bwd +) { + if (!initializeBackwardProblem(solver, model, bwd)) return; - /* Get the Newton solver */ - auto newtonSolver = NewtonSolver::getSolver(&t_, &x_, *solver, model); - - /* get the run time */ - clock_t starttime = clock(); - computeSteadyStateQuadrature(newtonSolver.get(), solver, model); - cpu_timeB_ = (double)((clock() - starttime) * 1000) / CLOCKS_PER_SEC; + /* compute quadratures, track computation time */ + CpuTimer cpu_timer; + computeSteadyStateQuadrature(solver, model); + cpu_timeB_ = cpu_timer.elapsed_milliseconds(); } -void SteadystateProblem::findSteadyState(Solver *solver, - NewtonSolver *newtonSolver, - Model *model, int it) { - /* First, try to run the Newton solver */ +void SteadystateProblem::findSteadyState( + Solver const& solver, Model& model, int it +) { steady_state_status_.resize(3, SteadyStateStatus::not_run); - findSteadyStateByNewtonsMethod(newtonSolver, model, false); + /* Turn off Newton's method if 'integrationOnly' approach is chosen for + steady-state computation or newton_maxsteps is set to 0 or + if 'integrationOnly' approach is chosen for sensitivity computation + in combination with forward sensitivities approach. The latter is necessary + as numerical integration of the model ODEs and corresponding + forward sensitivities ODEs is coupled. If 'integrationOnly' approach is + chosen for sensitivity computation it is enforced that steady state is + computed only by numerical integration as well. */ + bool turnOffNewton + = model.getSteadyStateComputationMode() + == SteadyStateComputationMode::integrationOnly + || solver.getNewtonMaxSteps() == 0 + || (model.getSteadyStateSensitivityMode() + == SteadyStateSensitivityMode::integrationOnly + && ((it == -1 + && solver.getSensitivityMethodPreequilibration() + == SensitivityMethod::forward) + || solver.getSensitivityMethod() == SensitivityMethod::forward + )); + + bool turnOffSimulation = model.getSteadyStateComputationMode() + == SteadyStateComputationMode::newtonOnly; + + /* First, try to run the Newton solver */ + if (!turnOffNewton) + findSteadyStateByNewtonsMethod(model, false); /* Newton solver didn't work, so try to simulate to steady state */ - if (!checkSteadyStateSuccess()) + if (!turnOffSimulation && !checkSteadyStateSuccess()) findSteadyStateBySimulation(solver, model, it); /* Simulation didn't work, retry the Newton solver from last sim state. */ - if (!checkSteadyStateSuccess()) - findSteadyStateByNewtonsMethod(newtonSolver, model, true); + if (!turnOffNewton && !turnOffSimulation && !checkSteadyStateSuccess()) + findSteadyStateByNewtonsMethod(model, true); /* Nothing worked, throw an as informative error as possible */ if (!checkSteadyStateSuccess()) - handleSteadyStateFailure(solver, model); + handleSteadyStateFailure( + !turnOffNewton, !turnOffSimulation, + !turnOffNewton && !turnOffSimulation + ); } -void SteadystateProblem::findSteadyStateByNewtonsMethod(NewtonSolver *newtonSolver, - Model *model, - bool newton_retry) { +void SteadystateProblem::findSteadyStateByNewtonsMethod( + Model& model, bool newton_retry +) { int ind = newton_retry ? 2 : 0; try { - applyNewtonsMethod(model, newtonSolver, newton_retry); + applyNewtonsMethod(model, newton_retry); steady_state_status_[ind] = SteadyStateStatus::success; - } catch (NewtonFailure const &ex) { + } catch (NewtonFailure const& ex) { /* nothing to be done */ switch (ex.error_code) { - case AMICI_TOO_MUCH_WORK: - steady_state_status_[ind] = - SteadyStateStatus::failed_convergence; - break; - case AMICI_NO_STEADY_STATE: - steady_state_status_[ind] = - SteadyStateStatus::failed_too_long_simulation; - break; - case AMICI_SINGULAR_JACOBIAN: - steady_state_status_[ind] = - SteadyStateStatus::failed_factorization; - break; - case AMICI_DAMPING_FACTOR_ERROR: - steady_state_status_[ind] = SteadyStateStatus::failed_damping; - break; - default: - steady_state_status_[ind] = SteadyStateStatus::failed; - break; - } - } - - /* copy number of linear steps used */ - if (max_steps_ > 0) { - if (newton_retry) { - std::copy_n(newtonSolver->getNumLinSteps().begin(), - max_steps_, &numlinsteps_.at(max_steps_)); - } else { - std::copy_n(newtonSolver->getNumLinSteps().begin(), - max_steps_, numlinsteps_.begin()); + case AMICI_TOO_MUCH_WORK: + steady_state_status_[ind] = SteadyStateStatus::failed_convergence; + break; + case AMICI_NO_STEADY_STATE: + steady_state_status_[ind] + = SteadyStateStatus::failed_too_long_simulation; + break; + case AMICI_SINGULAR_JACOBIAN: + steady_state_status_[ind] = SteadyStateStatus::failed_factorization; + break; + case AMICI_DAMPING_FACTOR_ERROR: + steady_state_status_[ind] = SteadyStateStatus::failed_damping; + break; + default: + steady_state_status_[ind] = SteadyStateStatus::failed; + break; } } } -void SteadystateProblem::findSteadyStateBySimulation(const Solver *solver, - Model *model, - int it) { - /* set starting timepoint for the simulation solver */ - if (it < 1) /* No previous time point computed, set t = t0 */ - t_ = model->t0(); - else /* Carry on simulating from last point */ - t_ = model->getTimepoint(it - 1); - +void SteadystateProblem::findSteadyStateBySimulation( + Solver const& solver, Model& model, int it +) { try { if (it < 0) { - /* Preequilibration? -> Create a new CVode object for sim */ - bool integrateSensis = getSensitivityFlag(model, solver, it, - SteadyStateContext::solverCreation); - auto newtonSimSolver = createSteadystateSimSolver(solver, model, - integrateSensis, - false); - runSteadystateSimulation(newtonSimSolver.get(), model, false); + /* Preequilibration? -> Create a new solver instance for sim */ + bool integrateSensis = getSensitivityFlag( + model, solver, it, SteadyStateContext::solverCreation + ); + auto newtonSimSolver = createSteadystateSimSolver( + solver, model, integrateSensis, false + ); + runSteadystateSimulation(*newtonSimSolver, model, false); } else { /* Solver was already created, use this one */ runSteadystateSimulation(solver, model, false); } steady_state_status_[1] = SteadyStateStatus::success; - } catch (NewtonFailure const &ex) { + } catch (IntegrationFailure const& ex) { switch (ex.error_code) { - case AMICI_TOO_MUCH_WORK: - steady_state_status_[1] = SteadyStateStatus::failed_convergence; - break; - case AMICI_NO_STEADY_STATE: - steady_state_status_[1] = SteadyStateStatus::failed_too_long_simulation; - break; - default: - model->app->warningF("AMICI:newton", - "AMICI newton method failed: %s\n", - ex.what()); - steady_state_status_[1] = SteadyStateStatus::failed; + case AMICI_TOO_MUCH_WORK: + steady_state_status_[1] = SteadyStateStatus::failed_convergence; + if (model.logger) + model.logger->log( + LogSeverity::debug, "EQUILIBRATION_FAILURE", + "AMICI equilibration exceeded maximum number of" + " integration steps at t=%g.", + ex.time + ); + break; + case AMICI_RHSFUNC_FAIL: + steady_state_status_[1] + = SteadyStateStatus::failed_too_long_simulation; + if (model.logger) + model.logger->log( + LogSeverity::debug, "EQUILIBRATION_FAILURE", + "AMICI equilibration was stopped after exceedingly" + " long simulation time at t=%g.", + ex.time + ); + break; + default: + steady_state_status_[1] = SteadyStateStatus::failed; + if (model.logger) + model.logger->log( + LogSeverity::debug, "OTHER", + "AMICI equilibration failed at t=%g.", ex.time + ); } - } catch (AmiException const &ex) { - model->app->warningF("AMICI:equilibration", - "AMICI equilibration failed: %s\n", - ex.what()); + } catch (AmiException const& ex) { + if (model.logger) + model.logger->log( + LogSeverity::debug, "OTHER", "AMICI equilibration failed: %s", + ex.what() + ); steady_state_status_[1] = SteadyStateStatus::failed; } } -bool SteadystateProblem::initializeBackwardProblem(Solver *solver, - Model *model, - const BackwardProblem *bwd) { +void SteadystateProblem::initializeForwardProblem( + int it, Solver const& solver, Model& model +) { + newton_solver_->reinitialize(); + /* process solver handling for pre- or postequilibration */ + if (it == -1) { + /* solver was not run before, set up everything */ + auto roots_found = std::vector(model.ne, 0); + model.initialize( + state_.x, state_.dx, state_.sx, sdx_, + solver.getSensitivityOrder() >= SensitivityOrder::first, roots_found + ); + state_.t = model.t0(); + solver.setup(state_.t, &model, state_.x, state_.dx, state_.sx, sdx_); + } else { + /* solver was run before, extract current state from solver */ + solver.writeSolution(&state_.t, state_.x, state_.dx, state_.sx, xQ_); + } + + /* overwrite starting timepoint */ + if (it < 1) /* No previous time point computed, set t = t0 */ + state_.t = model.t0(); + else /* Carry on simulating from last point */ + state_.t = model.getTimepoint(it - 1); + + state_.state = model.getModelState(); + flagUpdatedState(); +} + +bool SteadystateProblem::initializeBackwardProblem( + Solver const& solver, Model& model, BackwardProblem const* bwd +) { + newton_solver_->reinitialize(); + /* note that state_ is still set from forward run */ if (bwd) { - /* If preequilibration but not adjoint mode, there's nothing to do */ - if (solver->getSensitivityMethodPreequilibration() != - SensitivityMethod::adjoint) - return false; + /* preequilibration */ + if (solver.getSensitivityMethodPreequilibration() + != SensitivityMethod::adjoint) + return false; /* if not adjoint mode, there's nothing to do */ /* If we need to reinitialize solver states, this won't work yet. */ - if (model->nx_reinit() > 0) - throw NewtonFailure(AMICI_NOT_IMPLEMENTED, + if (model.nx_reinit() > 0) + throw NewtonFailure( + AMICI_NOT_IMPLEMENTED, "Adjoint preequilibration with reinitialization of " - "non-constant states is not yet implemented. Stopping."); + "non-constant states is not yet implemented. Stopping." + ); - /* If we have a backward problem, we're in preequilibration. - Hence, quantities like t, x, and xB must be set. */ - solver->reInit(t_, x_, x_); - solver->updateAndReinitStatesAndSensitivities(model); + solver.reInit(state_.t, state_.x, state_.dx); + solver.updateAndReinitStatesAndSensitivities(&model); xB_.copy(bwd->getAdjointState()); } + /* postequilibration does not need a reInit */ - /* Will need to write quadratures: set to 0 */ + /* initialize quadratures */ xQ_.zero(); xQB_.zero(); xQBdot_.zero(); @@ -241,9 +309,9 @@ bool SteadystateProblem::initializeBackwardProblem(Solver *solver, return true; } -void SteadystateProblem::computeSteadyStateQuadrature(NewtonSolver *newtonSolver, - const Solver *solver, - Model *model) { +void SteadystateProblem::computeSteadyStateQuadrature( + Solver const& solver, Model& model +) { /* This routine computes the quadratures: xQB = Integral[ xB(x(t), t, p) * dxdot/dp(x(t), t, p) | dt ] As we're in steady state, we have x(t) = x_ss (x_steadystate), hence @@ -251,22 +319,31 @@ void SteadystateProblem::computeSteadyStateQuadrature(NewtonSolver *newtonSolver We therefore compute the integral over xB first and then do a matrix-vector multiplication */ - /* Try to compute the analytical solution for quadrature algebraically */ - getQuadratureByLinSolve(newtonSolver, model); + auto sensitivityMode = model.getSteadyStateSensitivityMode(); - /* Analytical solution didn't work, perform simulation instead */ - if (!hasQuadrature()) + /* Try to compute the analytical solution for quadrature algebraically */ + if (sensitivityMode == SteadyStateSensitivityMode::newtonOnly + || sensitivityMode + == SteadyStateSensitivityMode::integrateIfNewtonFails) + getQuadratureByLinSolve(model); + + /* Perform simulation */ + if (sensitivityMode == SteadyStateSensitivityMode::integrationOnly + || (sensitivityMode + == SteadyStateSensitivityMode::integrateIfNewtonFails + && !hasQuadrature())) getQuadratureBySimulation(solver, model); /* If analytic solution and integration did not work, throw an Exception */ if (!hasQuadrature()) - throw AmiException("Steady state backward computation failed: Linear " + throw AmiException( + "Steady state backward computation failed: Linear " "system could not be solved (possibly due to singular Jacobian), " - "and numerical integration did not equilibrate within maxsteps"); + "and numerical integration did not equilibrate within maxsteps" + ); } -void SteadystateProblem::getQuadratureByLinSolve(NewtonSolver *newtonSolver, - Model *model) { +void SteadystateProblem::getQuadratureByLinSolve(Model& model) { /* Computes the integral over the adjoint state xB: If the Jacobian has full rank, this has an analytical solution, since d/dt[ xB(t) ] = JB^T(x(t), p) xB(t) = JB^T(x_ss, p) xB(t) @@ -282,28 +359,29 @@ void SteadystateProblem::getQuadratureByLinSolve(NewtonSolver *newtonSolver, /* try to solve the linear system */ try { /* compute integral over xB and write to xQ */ - newtonSolver->prepareLinearSystemB(0, -1); - newtonSolver->solveLinearSystem(xQ_); - /* Compute the quadrature as the inner product xQ * dxotdp */ + newton_solver_->prepareLinearSystemB(model, state_); + newton_solver_->solveLinearSystem(xQ_); + /* Compute the quadrature as the inner product xQ * dxdotdp */ computeQBfromQ(model, xQ_, xQB_); /* set flag that quadratures is available (for processing in rdata) */ hasQuadrature_ = true; /* Finalize by setting adjoint state to zero (its steady state) */ xB_.zero(); - } catch (NewtonFailure const &) { + } catch (NewtonFailure const&) { hasQuadrature_ = false; } } -void SteadystateProblem::getQuadratureBySimulation(const Solver *solver, - Model *model) { +void SteadystateProblem::getQuadratureBySimulation( + Solver const& solver, Model& model +) { /* If the Jacobian is singular, the integral over xB must be computed by usual integration over time, but simplifications can be applied: x is not time dependent, no forward trajectory is needed. */ /* set starting timepoint for the simulation solver */ - t_ = model->t0(); + state_.t = model.t0(); /* xQ was written in getQuadratureByLinSolve() -> set to zero */ xQ_.zero(); @@ -312,59 +390,59 @@ void SteadystateProblem::getQuadratureBySimulation(const Solver *solver, /* perform integration and quadrature */ try { - runSteadystateSimulation(simSolver.get(), model, true); + runSteadystateSimulation(*simSolver, model, true); hasQuadrature_ = true; - } catch (NewtonFailure const &) { + } catch (NewtonFailure const&) { hasQuadrature_ = false; } } -[[noreturn]] void SteadystateProblem::handleSteadyStateFailure(const Solver *solver, - Model *model) { - /* No steady state could be inferred. Store simulation state */ - storeSimulationState(model, solver->getSensitivityOrder() >= - SensitivityOrder::first); - +[[noreturn]] void SteadystateProblem::handleSteadyStateFailure( + bool tried_newton_1, bool tried_simulation, bool tried_newton_2 +) { /* Throw error message according to error codes */ - std::string errorString = "Steady state computation failed. " - "First run of Newton solver failed"; - writeErrorString(&errorString, steady_state_status_[0]); - errorString.append(" Simulation to steady state failed"); - writeErrorString(&errorString, steady_state_status_[1]); - errorString.append(" Second run of Newton solver failed"); - writeErrorString(&errorString, steady_state_status_[2]); - + std::string errorString = "Steady state computation failed."; + if (tried_newton_1) { + errorString.append(" First run of Newton solver failed"); + writeErrorString(&errorString, steady_state_status_[0]); + } + if (tried_simulation) { + errorString.append(" Simulation to steady state failed"); + writeErrorString(&errorString, steady_state_status_[1]); + } + if (tried_newton_2) { + errorString.append(" Second run of Newton solver failed"); + writeErrorString(&errorString, steady_state_status_[2]); + } throw AmiException(errorString.c_str()); } -void SteadystateProblem::writeErrorString(std::string *errorString, - SteadyStateStatus status) const { +void SteadystateProblem::writeErrorString( + std::string* errorString, SteadyStateStatus status +) const { /* write error message according to steady state status */ switch (status) { - case SteadyStateStatus::failed_too_long_simulation: - (*errorString).append(": System could not be equilibrated via" - " simulating to a late time point."); - break; - case SteadyStateStatus::failed_damping: - (*errorString).append(": Damping factor reached lower bound."); - break; - case SteadyStateStatus::failed_factorization: - (*errorString).append(": RHS could not be factorized."); - break; - case SteadyStateStatus::failed_convergence: - (*errorString).append(": No convergence was achieved."); - break; - case SteadyStateStatus::failed: - (*errorString).append("."); - break; - default: - break; + case SteadyStateStatus::failed_too_long_simulation: + (*errorString).append(": System could not be equilibrated."); + break; + case SteadyStateStatus::failed_damping: + (*errorString).append(": Damping factor reached lower bound."); + break; + case SteadyStateStatus::failed_factorization: + (*errorString).append(": RHS could not be factorized."); + break; + case SteadyStateStatus::failed_convergence: + (*errorString).append(": No convergence was achieved."); + break; + default: + (*errorString).append("."); + break; } } -bool SteadystateProblem::getSensitivityFlag(const Model *model, - const Solver *solver, - int it, SteadyStateContext context) { +bool SteadystateProblem::getSensitivityFlag( + Model const& model, Solver const& solver, int it, SteadyStateContext context +) { /* We need to check whether we need to compute forward sensitivities. Depending on the situation (pre-/postequilibration) and the solver settings, the logic may be involved and is handled here. @@ -375,220 +453,246 @@ bool SteadystateProblem::getSensitivityFlag(const Model *model, bool preequilibration = (it == -1); /* Have we maybe already computed forward sensitivities? */ - bool forwardSensisAlreadyComputed = - solver->getSensitivityOrder() >= SensitivityOrder::first && - steady_state_status_[1] == SteadyStateStatus::success && - model->getSteadyStateSensitivityMode() == SteadyStateSensitivityMode::simulationFSA; - - bool simulationStartedInSteadystate = - steady_state_status_[0] == SteadyStateStatus::success && - numsteps_[0] == 0; + bool forwardSensisAlreadyComputed + = solver.getSensitivityOrder() >= SensitivityOrder::first + && steady_state_status_[1] == SteadyStateStatus::success + && (model.getSteadyStateSensitivityMode() + == SteadyStateSensitivityMode::integrationOnly + || model.getSteadyStateSensitivityMode() + == SteadyStateSensitivityMode::integrateIfNewtonFails); + + bool simulationStartedInSteadystate + = steady_state_status_[0] == SteadyStateStatus::success + && numsteps_[0] == 0; /* Do we need forward sensis for postequilibration? */ - bool needForwardSensisPosteq = !preequilibration && - !forwardSensisAlreadyComputed && - solver->getSensitivityOrder() >= SensitivityOrder::first && - solver->getSensitivityMethod() == SensitivityMethod::forward; + bool needForwardSensisPosteq + = !preequilibration && !forwardSensisAlreadyComputed + && solver.getSensitivityOrder() >= SensitivityOrder::first + && solver.getSensitivityMethod() == SensitivityMethod::forward; /* Do we need forward sensis for preequilibration? */ - bool needForwardSensisPreeq = preequilibration && - !forwardSensisAlreadyComputed && - solver->getSensitivityMethodPreequilibration() == SensitivityMethod::forward && - solver->getSensitivityOrder() >= SensitivityOrder::first; + bool needForwardSensisPreeq + = preequilibration && !forwardSensisAlreadyComputed + && solver.getSensitivityMethodPreequilibration() + == SensitivityMethod::forward + && solver.getSensitivityOrder() >= SensitivityOrder::first; /* Do we need to do the linear system solve to get forward sensitivities? */ - bool needForwardSensisNewton = - (needForwardSensisPreeq || needForwardSensisPosteq) && - !simulationStartedInSteadystate; + bool needForwardSensisNewton + = (needForwardSensisPreeq || needForwardSensisPosteq) + && !simulationStartedInSteadystate; /* When we're creating a new solver object */ - bool needForwardSensiAtCreation = needForwardSensisPreeq && - model->getSteadyStateSensitivityMode() == SteadyStateSensitivityMode::simulationFSA; + bool needForwardSensiAtCreation + = needForwardSensisPreeq + && (model.getSteadyStateSensitivityMode() + == SteadyStateSensitivityMode::integrationOnly + || model.getSteadyStateSensitivityMode() + == SteadyStateSensitivityMode::integrateIfNewtonFails); /* Check if we need to store sensis */ switch (context) { - case SteadyStateContext::newtonSensi: - return needForwardSensisNewton; + case SteadyStateContext::newtonSensi: + return needForwardSensisNewton; - case SteadyStateContext::sensiStorage: - return needForwardSensisNewton || - forwardSensisAlreadyComputed || - simulationStartedInSteadystate; + case SteadyStateContext::sensiStorage: + return needForwardSensisNewton || forwardSensisAlreadyComputed + || simulationStartedInSteadystate; - case SteadyStateContext::solverCreation: - return needForwardSensiAtCreation; + case SteadyStateContext::solverCreation: + return needForwardSensiAtCreation; - default: - throw AmiException("Requested invalid context in sensitivity " - "processing during steady state computation"); + default: + throw AmiException("Requested invalid context in sensitivity " + "processing during steady state computation"); } } -realtype SteadystateProblem::getWrmsNorm(const AmiVector &x, - const AmiVector &xdot, - realtype atol, - realtype rtol, - AmiVector &ewt) const { +realtype SteadystateProblem::getWrmsNorm( + AmiVector const& x, AmiVector const& xdot, 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 */ + /* ewt = x */ N_VAbs(const_cast(x.getNVector()), ewt.getNVector()); + /* ewt *= rtol */ N_VScale(rtol, ewt.getNVector(), ewt.getNVector()); + /* ewt += atol */ N_VAddConst(ewt.getNVector(), atol, ewt.getNVector()); + /* ewt = 1/ewt (ewt = 1/(rtol*x+atol)) */ N_VInv(ewt.getNVector(), ewt.getNVector()); - return N_VWrmsNorm(const_cast(xdot.getNVector()), - ewt.getNVector()); + /* wrms = sqrt(sum((xdot/ewt)**2)/n) where n = size of state vector */ + return N_VWrmsNorm( + const_cast(xdot.getNVector()), ewt.getNVector() + ); } -bool SteadystateProblem::checkConvergence(const Solver *solver, - Model *model, - SensitivityMethod checkSensitivities) { - if (checkSensitivities == SensitivityMethod::adjoint) { +realtype +SteadystateProblem::getWrms(Model& model, SensitivityMethod sensi_method) { + realtype wrms = INFINITY; + if (sensi_method == SensitivityMethod::adjoint) { /* In the adjoint case, only xQB contributes to the gradient, the exact steadystate is less important, as xB = xQdot may even not converge to zero at all. So we need xQBdot, hence compute xQB first. */ computeQBfromQ(model, xQ_, xQB_); computeQBfromQ(model, xB_, xQBdot_); - wrms_ = getWrmsNorm(xQB_, xQBdot_, solver->getAbsoluteToleranceQuadratures(), - solver->getRelativeToleranceQuadratures(), ewtQB_); + if (newton_step_conv_) + throw NewtonFailure( + AMICI_NOT_IMPLEMENTED, + "Newton type convergence check is not implemented for adjoint " + "steady state computations. Stopping." + ); + wrms = getWrmsNorm(xQB_, xQBdot_, atol_quad_, rtol_quad_, ewtQB_); } else { /* If we're doing a forward simulation (with or without sensitivities: Get RHS and compute weighted error norm */ - model->fxdot(t_, x_, dx_, xdot_); - wrms_ = getWrmsNorm(x_, xdot_, solver->getAbsoluteToleranceSteadyState(), - solver->getRelativeToleranceSteadyState(), ewt_); + if (newton_step_conv_) + getNewtonStep(model); + else + updateRightHandSide(model); + wrms = getWrmsNorm( + state_.x, newton_step_conv_ ? delta_ : xdot_, atol_, rtol_, ewt_ + ); } - bool converged = wrms_ < RCONST(1.0); - - if (checkSensitivities != SensitivityMethod::forward) - return converged; + return wrms; +} +realtype SteadystateProblem::getWrmsFSA(Model& model) { /* Forward sensitivities: Compute weighted error norm for their RHS */ - for (int ip = 0; ip < model->nplist(); ++ip) { - if (converged) { - sx_ = solver->getStateSensitivity(t_); - model->fsxdot(t_, x_, dx_, ip, sx_[ip], dx_, xdot_); - wrms_ = getWrmsNorm( - sx_[ip], xdot_, solver->getAbsoluteToleranceSteadyStateSensi(), - solver->getRelativeToleranceSteadyStateSensi(), ewt_); - converged = wrms_ < RCONST(1.0); - } + realtype wrms = 0.0; + + /* we don't need to call prepareLinearSystem in this function, since it was + already computed in the preceding getWrms call and both equations have the + same jacobian */ + + xdot_updated_ = false; + for (int ip = 0; ip < model.nplist(); ++ip) { + model.fsxdot( + state_.t, state_.x, state_.dx, ip, state_.sx[ip], state_.dx, xdot_ + ); + if (newton_step_conv_) + newton_solver_->solveLinearSystem(xdot_); + wrms + = getWrmsNorm(state_.sx[ip], xdot_, 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 + satisfied. */ + if (wrms > conv_thresh) + break; } - return converged; + /* just report the parameter for the last ip, value doesn't matter it's + only important that all of them satisfy the convergence threshold */ + return wrms; } bool SteadystateProblem::checkSteadyStateSuccess() const { /* Did one of the attempts yield s steady state? */ - if (std::any_of(steady_state_status_.begin(), steady_state_status_.end(), - [](SteadyStateStatus status) - {return status == SteadyStateStatus::success;})) { - return true; - } else { - return false; - } + return std::any_of( + steady_state_status_.begin(), steady_state_status_.end(), + [](SteadyStateStatus status) { + return status == SteadyStateStatus::success; + } + ); } -void SteadystateProblem::applyNewtonsMethod(Model *model, - NewtonSolver *newtonSolver, - bool newton_retry) { - int i_newtonstep = 0; - int ix = 0; - double gamma = 1.0; - bool compNewStep = true; +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) + if (model.nx_solver == 0) return; /* initialize output of linear solver for Newton step */ delta_.zero(); - - model->fxdot(t_, x_, dx_, xdot_); - - /* Check for relative error, but make sure not to divide by 0! - Ensure positivity of the state */ - x_newton_ = x_; - x_old_ = x_; - xdot_old_ = xdot_; - - wrms_ = getWrmsNorm(x_newton_, xdot_, newtonSolver->atol_, - newtonSolver->rtol_, ewt_); - bool converged = newton_retry ? false : wrms_ < RCONST(1.0); - while (!converged && i_newtonstep < newtonSolver->max_steps) { - - /* If Newton steps are necessary, compute the initial search direction */ - if (compNewStep) { - try { - delta_ = xdot_; - newtonSolver->getStep(newton_retry ? 2 : 1, i_newtonstep, delta_); - } catch (NewtonFailure const &) { - numsteps_.at(newton_retry ? 2 : 0) = i_newtonstep; - throw; - } + x_old_.copy(state_.x); + bool converged = false; + wrms_ = getWrms(model, SensitivityMethod::none); + converged = newton_retry ? false : wrms_ < conv_thresh; + while (!converged && i_newtonstep < max_steps_) { + + /* If Newton steps are necessary, compute the initial search + direction */ + if (update_direction) { + getNewtonStep(model); + /* we store delta_ here as later convergence checks may + update it */ + delta_old_.copy(delta_); } - /* Try a full, undamped Newton step */ - linearSum(1.0, x_old_, gamma, delta_, x_); + /* Try step with new gamma_/delta_ */ + linearSum( + 1.0, x_old_, gamma_, update_direction ? delta_ : delta_old_, + state_.x + ); + flagUpdatedState(); /* Compute new xdot and residuals */ - model->fxdot(t_, x_, dx_, xdot_); - realtype wrms_tmp = getWrmsNorm(x_newton_, xdot_, newtonSolver->atol_, - newtonSolver->rtol_, ewt_); + realtype wrms_tmp = getWrms(model, SensitivityMethod::none); - if (wrms_tmp < wrms_) { + step_successful = wrms_tmp < wrms_; + if (step_successful) { /* If new residuals are smaller than old ones, update state */ wrms_ = wrms_tmp; - x_old_ = x_; - xdot_old_ = xdot_; - /* New linear solve due to new state */ - compNewStep = true; - /* Check residuals vs tolerances */ - converged = wrms_ < RCONST(1.0); - + /* precheck convergence */ + converged = wrms_ < conv_thresh; if (converged) { - /* Ensure positivity of the found state and recheck if - the convergence still holds */ - bool recheck_convergence = false; - for (ix = 0; ix < model->nx_solver; ix++) { - if (x_[ix] < 0.0) { - x_[ix] = 0.0; - recheck_convergence = true; - } - } - if (recheck_convergence) { - model->fxdot(t_, x_, dx_, xdot_); - wrms_ = getWrmsNorm(x_newton_, xdot_, newtonSolver->atol_, - newtonSolver->rtol_, ewt_); - converged = wrms_ < RCONST(1.0); - } - } else if (newtonSolver->damping_factor_mode_==NewtonDampingFactorMode::on) { - /* increase dampening factor (superfluous, if converged) */ - gamma = fmin(1.0, 2.0 * gamma); + converged = makePositiveAndCheckConvergence(model); } - } else if (newtonSolver->damping_factor_mode_==NewtonDampingFactorMode::on) { - /* Reduce dampening factor and raise an error when becomes too small */ - gamma = gamma / 4.0; - if (gamma < newtonSolver->damping_factor_lower_bound) - throw NewtonFailure(AMICI_DAMPING_FACTOR_ERROR, - "Newton solver failed: the damping factor " - "reached its lower bound"); - - /* No new linear solve, only try new dampening */ - compNewStep = false; + /* update x_old_ _after_ positivity was enforced */ + x_old_.copy(state_.x); } + + update_direction = updateDampingFactor(step_successful); /* increase step counter */ i_newtonstep++; } - /* Set return values */ - numsteps_.at(newton_retry ? 2 : 0) = i_newtonstep; if (!converged) throw NewtonFailure(AMICI_TOO_MUCH_WORK, "applyNewtonsMethod"); } -void SteadystateProblem::runSteadystateSimulation(const Solver *solver, - Model *model, - bool backward) -{ - if (model->nx_solver == 0) +bool SteadystateProblem::makePositiveAndCheckConvergence(Model& model) { + /* Ensure positivity of the found state and recheck if + the convergence still holds */ + auto nonnegative = model.getStateIsNonNegative(); + for (int ix = 0; ix < model.nx_solver; ix++) { + if (state_.x[ix] < 0.0 && nonnegative[ix]) { + state_.x[ix] = 0.0; + flagUpdatedState(); + } + } + wrms_ = getWrms(model, SensitivityMethod::none); + return wrms_ < conv_thresh; +} + +bool SteadystateProblem::updateDampingFactor(bool step_successful) { + if (damping_factor_mode_ != NewtonDampingFactorMode::on) + return true; + + if (step_successful) + gamma_ = fmin(1.0, 2.0 * gamma_); + else + gamma_ = gamma_ / 4.0; + + if (gamma_ < damping_factor_lower_bound_) + throw NewtonFailure( + AMICI_DAMPING_FACTOR_ERROR, + "Newton solver failed: the damping factor " + "reached its lower bound" + ); + return step_successful; +} + +void SteadystateProblem::runSteadystateSimulation( + Solver const& solver, Model& model, bool backward +) { + if (model.nx_solver == 0) return; /* Loop over steps and check for convergence NB: This function is used for forward and backward simulation, and may @@ -598,23 +702,54 @@ void SteadystateProblem::runSteadystateSimulation(const Solver *solver, /* Do we also have to check for convergence of sensitivities? */ SensitivityMethod sensitivityFlag = SensitivityMethod::none; - if (solver->getSensitivityOrder() > SensitivityOrder::none && - solver->getSensitivityMethod() == SensitivityMethod::forward) + if (solver.getSensitivityOrder() > SensitivityOrder::none + && solver.getSensitivityMethod() == SensitivityMethod::forward) sensitivityFlag = SensitivityMethod::forward; /* If flag for forward sensitivity computation by simulation is not set, disable forward sensitivity integration. Sensitivities will be computed - by newtonSolver->computeNewtonSensis then */ - if (model->getSteadyStateSensitivityMode() == SteadyStateSensitivityMode::newtonOnly) { - solver->switchForwardSensisOff(); + by newtonsolver.computeNewtonSensis then */ + if (model.getSteadyStateSensitivityMode() + == SteadyStateSensitivityMode::newtonOnly) { + solver.switchForwardSensisOff(); sensitivityFlag = SensitivityMethod::none; } if (backward) sensitivityFlag = SensitivityMethod::adjoint; - bool converged = checkConvergence(solver, model, sensitivityFlag); - int sim_steps = 0; + int& sim_steps = backward ? numstepsB_ : numsteps_.at(1); + + int convergence_check_frequency = 1; + + if (newton_step_conv_) + convergence_check_frequency = 25; + + while (true) { + if (sim_steps % convergence_check_frequency == 0) { + // Check for convergence (already before simulation, since we might + // start in steady state) + wrms_ = getWrms(model, sensitivityFlag); + if (wrms_ < conv_thresh) { + if (check_sensi_conv_ + && sensitivityFlag == SensitivityMethod::forward) { + updateSensiSimulation(solver); + // getWrms needs to be called before getWrmsFSA + // such that the linear system is prepared for newton-type + // convergence check + if (getWrmsFSA(model) < conv_thresh) + break; // converged + } else { + break; // converged + } + } + } - while (!converged) { + /* check for maxsteps */ + if (sim_steps >= solver.getMaxSteps()) { + throw IntegrationFailure(AMICI_TOO_MUCH_WORK, state_.t); + } + + /* increase counter */ + sim_steps++; /* One step of ODE integration reason for tout specification: max with 1 ensures correct direction (any positive value would do) @@ -622,55 +757,38 @@ void SteadystateProblem::runSteadystateSimulation(const Solver *solver, stable computation value is not important for AMICI_ONE_STEP mode, only direction w.r.t. current t */ - solver->step(std::max(t_, 1.0) * 10); + solver.step(std::max(state_.t, 1.0) * 10); + if (backward) { - solver->writeSolution(&t_, xB_, dx_, sx_, xQ_); + solver.writeSolution(&state_.t, xB_, state_.dx, state_.sx, xQ_); } else { - solver->writeSolution(&t_, x_, dx_, sx_, xQ_); - } - - /* Check for convergence */ - converged = checkConvergence(solver, model, sensitivityFlag); - /* increase counter, check for maxsteps */ - sim_steps++; - if (sim_steps >= solver->getMaxSteps() && !converged) { - numsteps_.at(1) = sim_steps; - throw NewtonFailure(AMICI_TOO_MUCH_WORK, - "exceeded maximum number of steps"); - } - if (t_ >= 1e200 && !converged) { - numsteps_.at(1) = sim_steps; - throw NewtonFailure(AMICI_NO_STEADY_STATE, "simulated to late time" - " point without convergence of RHS"); + solver.writeSolution( + &state_.t, state_.x, state_.dx, state_.sx, xQ_ + ); + flagUpdatedState(); } } - /* store information about steps and sensitivities, if necessary */ - if (backward) { - numstepsB_ = sim_steps; - } else { - numsteps_.at(1) = sim_steps; - if (solver->getSensitivityOrder() > SensitivityOrder::none && - model->getSteadyStateSensitivityMode() == - SteadyStateSensitivityMode::simulationFSA) - sx_ = solver->getStateSensitivity(t_); - } + // if check_sensi_conv_ is deactivated, we still have to update sensis + if (sensitivityFlag == SensitivityMethod::forward) + updateSensiSimulation(solver); } std::unique_ptr SteadystateProblem::createSteadystateSimSolver( - const Solver *solver, Model *model, bool forwardSensis, bool backward) const -{ + Solver const& solver, Model& model, bool forwardSensis, bool backward +) const { /* Create new CVode solver object */ - auto sim_solver = std::unique_ptr(solver->clone()); + auto sim_solver = std::unique_ptr(solver.clone()); - switch (solver->getLinearSolver()) { - case LinearSolver::dense: - break; - case LinearSolver::KLU: - break; - default: - throw NewtonFailure(AMICI_NOT_IMPLEMENTED, - "invalid solver for steadystate simulation"); + sim_solver->logger = solver.logger; + + switch (solver.getLinearSolver()) { + case LinearSolver::dense: + break; + case LinearSolver::KLU: + break; + default: + throw AmiException("invalid solver for steadystate simulation"); } /* do we need sensitivities? */ if (forwardSensis) { @@ -682,56 +800,82 @@ std::unique_ptr SteadystateProblem::createSteadystateSimSolver( } /* use x and sx as dummies for dx and sdx (they wont get touched in a CVodeSolver) */ - sim_solver->setup(model->t0(), model, x_, x_, sx_, sx_); + sim_solver->setup(model.t0(), &model, state_.x, state_.dx, state_.sx, sdx_); if (backward) { - sim_solver->setup(model->t0(), model, xB_, xB_, sx_, sx_); - sim_solver->setupSteadystate(model->t0(), model, x_, x_, xB_, xB_, xQ_); + sim_solver->setup(model.t0(), &model, xB_, xB_, state_.sx, sdx_); + sim_solver->setupSteadystate( + model.t0(), &model, state_.x, state_.dx, xB_, xB_, xQ_ + ); } else { - sim_solver->setup(model->t0(), model, x_, x_, sx_, sx_); + sim_solver->setup( + model.t0(), &model, state_.x, state_.dx, state_.sx, sdx_ + ); } return sim_solver; } -void SteadystateProblem::computeQBfromQ(Model *model, const AmiVector &yQ, - AmiVector &yQB) const { - /* Compute the quadrature as the inner product: yQB = dxotdp * yQ */ +void SteadystateProblem::computeQBfromQ( + Model& model, AmiVector const& yQ, AmiVector& yQB +) const { + /* Compute the quadrature as the inner product: yQB = dxdotdp * yQ */ /* set to zero first, as multiplication adds to existing value */ yQB.zero(); /* multiply */ - if (model->pythonGenerated) { + if (model.pythonGenerated) { /* fill dxdotdp with current values */ - const auto& plist = model->getParameterList(); - model->fdxdotdp(t_, x_, x_); - model->get_dxdotdp_full().multiply(yQB.getNVector(), yQ.getNVector(), - plist, true); + auto const& plist = model.getParameterList(); + model.fdxdotdp(state_.t, state_.x, state_.dx); + model.get_dxdotdp_full().multiply( + yQB.getNVector(), yQ.getNVector(), plist, true + ); } else { - for (int ip=0; ipnplist(); ++ip) - yQB[ip] = dotProd(yQ, model->get_dxdotdp()[ip]); + for (int ip = 0; ip < model.nplist(); ++ip) + yQB[ip] = dotProd(yQ, model.get_dxdotdp()[ip]); } } -void SteadystateProblem::getAdjointUpdates(Model &model, - const ExpData &edata) { +void SteadystateProblem::getAdjointUpdates(Model& model, ExpData const& edata) { xB_.zero(); - for (int it=0; it < model.nt(); it++) { + for (int it = 0; it < model.nt(); it++) { if (std::isinf(model.getTimepoint(it))) { model.getAdjointStateObservableUpdate( - slice(dJydx_, it, model.nx_solver * model.nJ), it, x_, edata); + slice(dJydx_, it, model.nx_solver * model.nJ), it, state_.x, + edata + ); for (int ix = 0; ix < model.nxtrue_solver; ix++) xB_[ix] += dJydx_[ix + it * model.nx_solver]; } } } -void SteadystateProblem::storeSimulationState(Model *model, bool storesensi) { - state_.t = INFINITY; - state_.x = x_; - state_.dx = xdot_; - if (storesensi) - state_.sx = sx_; - state_.state = model->getModelState(); +void SteadystateProblem::flagUpdatedState() { + xdot_updated_ = false; + delta_updated_ = false; + sensis_updated_ = false; } +void SteadystateProblem::updateSensiSimulation(Solver const& solver) { + if (sensis_updated_) + return; + state_.sx = solver.getStateSensitivity(state_.t); + sensis_updated_ = true; +} + +void SteadystateProblem::updateRightHandSide(Model& model) { + if (xdot_updated_) + return; + model.fxdot(state_.t, state_.x, state_.dx, xdot_); + xdot_updated_ = true; +} + +void SteadystateProblem::getNewtonStep(Model& model) { + if (delta_updated_) + return; + updateRightHandSide(model); + delta_.copy(xdot_); + newton_solver_->getStep(delta_, model, state_); + delta_updated_ = true; +} } // namespace amici diff --git a/deps/AMICI/src/sundials_linsol_wrapper.cpp b/deps/AMICI/src/sundials_linsol_wrapper.cpp index fec65e718..de5d4f1d6 100644 --- a/deps/AMICI/src/sundials_linsol_wrapper.cpp +++ b/deps/AMICI/src/sundials_linsol_wrapper.cpp @@ -7,14 +7,15 @@ namespace amici { -SUNLinSolWrapper::SUNLinSolWrapper(SUNLinearSolver linsol) : solver_(linsol) {} +SUNLinSolWrapper::SUNLinSolWrapper(SUNLinearSolver linsol) + : solver_(linsol) {} SUNLinSolWrapper::~SUNLinSolWrapper() { if (solver_) SUNLinSolFree(solver_); } -SUNLinSolWrapper::SUNLinSolWrapper(SUNLinSolWrapper &&other) noexcept { +SUNLinSolWrapper::SUNLinSolWrapper(SUNLinSolWrapper&& other) noexcept { std::swap(solver_, other.solver_); } @@ -37,17 +38,20 @@ void SUNLinSolWrapper::setup(SUNMatrix A) const { throw AmiException("Solver setup failed with code %d", res); } -void SUNLinSolWrapper::setup(const SUNMatrixWrapper& A) const { return setup(A.get()); } +void SUNLinSolWrapper::setup(SUNMatrixWrapper const& A) const { + return setup(A.get()); +} -int SUNLinSolWrapper::Solve(SUNMatrix A, N_Vector x, N_Vector b, realtype tol) const { +int SUNLinSolWrapper::Solve(SUNMatrix A, N_Vector x, N_Vector b, realtype tol) + const { return SUNLinSolSolve(solver_, A, x, b, tol); } long SUNLinSolWrapper::getLastFlag() const { - return static_cast(SUNLinSolLastFlag(solver_)); + return gsl::narrow(SUNLinSolLastFlag(solver_)); } -int SUNLinSolWrapper::space(long *lenrwLS, long *leniwLS) const { +int SUNLinSolWrapper::space(long* lenrwLS, long* leniwLS) const { return SUNLinSolSpace(solver_, lenrwLS, leniwLS); } @@ -61,12 +65,12 @@ SUNNonLinSolWrapper::~SUNNonLinSolWrapper() { SUNNonlinSolFree(solver); } -SUNNonLinSolWrapper::SUNNonLinSolWrapper(SUNNonLinSolWrapper &&other) noexcept { +SUNNonLinSolWrapper::SUNNonLinSolWrapper(SUNNonLinSolWrapper&& other) noexcept { std::swap(solver, other.solver); } -SUNNonLinSolWrapper &SUNNonLinSolWrapper:: -operator=(SUNNonLinSolWrapper &&other) noexcept { +SUNNonLinSolWrapper& SUNNonLinSolWrapper::operator=(SUNNonLinSolWrapper&& other +) noexcept { std::swap(solver, other.solver); return *this; } @@ -77,15 +81,17 @@ SUNNonlinearSolver_Type SUNNonLinSolWrapper::getType() const { return SUNNonlinSolGetType(solver); } -int SUNNonLinSolWrapper::setup(N_Vector y, void *mem) { +int SUNNonLinSolWrapper::setup(N_Vector y, void* mem) { auto res = SUNNonlinSolSetup(solver, y, mem); if (res != SUN_NLS_SUCCESS) throw AmiException("Nonlinear solver setup failed with code %d", res); return res; } -int SUNNonLinSolWrapper::Solve(N_Vector y0, N_Vector y, N_Vector w, - realtype tol, bool callLSetup, void *mem) { +int SUNNonLinSolWrapper::Solve( + N_Vector y0, N_Vector y, N_Vector w, realtype tol, bool callLSetup, + void* mem +) { return SUNNonlinSolSolve(solver, y0, y, w, tol, callLSetup, mem); } @@ -101,8 +107,9 @@ int SUNNonLinSolWrapper::setLSolveFn(SUNNonlinSolLSolveFn SolveFn) { return SUNNonlinSolSetLSolveFn(solver, SolveFn); } -int SUNNonLinSolWrapper::setConvTestFn(SUNNonlinSolConvTestFn CTestFn, - void* ctest_data) { +int SUNNonLinSolWrapper::setConvTestFn( + SUNNonlinSolConvTestFn CTestFn, void* ctest_data +) { return SUNNonlinSolSetConvTestFn(solver, CTestFn, ctest_data); } @@ -132,8 +139,9 @@ long SUNNonLinSolWrapper::getNumConvFails() const { long int nconvfails = -1; auto res = SUNNonlinSolGetNumConvFails(solver, &nconvfails); if (res != SUN_NLS_SUCCESS) { - throw AmiException("SUNNonlinSolGetNumConvFails failed with code %d", - res); + throw AmiException( + "SUNNonlinSolGetNumConvFails failed with code %d", res + ); } return nconvfails; } @@ -142,7 +150,8 @@ void SUNNonLinSolWrapper::initialize() { int status = SUNNonlinSolInitialize(solver); if (status != SUN_NLS_SUCCESS) throw AmiException( - "Nonlinear solver initialization failed with code %d", status); + "Nonlinear solver initialization failed with code %d", status + ); } SUNLinSolBand::SUNLinSolBand(N_Vector x, SUNMatrix A) @@ -151,18 +160,17 @@ SUNLinSolBand::SUNLinSolBand(N_Vector x, SUNMatrix A) throw AmiException("Failed to create solver."); } -SUNLinSolBand::SUNLinSolBand(const AmiVector &x, int ubw, int lbw) : - A_(SUNMatrixWrapper(x.getLength(), ubw, lbw)) { +SUNLinSolBand::SUNLinSolBand(AmiVector const& x, int ubw, int lbw) + : A_(SUNMatrixWrapper(x.getLength(), ubw, lbw)) { solver_ = SUNLinSol_Band(const_cast(x.getNVector()), A_.get()); if (!solver_) throw AmiException("Failed to create solver."); - } SUNMatrix SUNLinSolBand::getMatrix() const { return A_.get(); } -SUNLinSolDense::SUNLinSolDense(const AmiVector &x) : - A_(SUNMatrixWrapper(x.getLength(), x.getLength())) { +SUNLinSolDense::SUNLinSolDense(AmiVector const& x) + : A_(SUNMatrixWrapper(x.getLength(), x.getLength())) { solver_ = SUNLinSol_Dense(const_cast(x.getNVector()), A_.get()); if (!solver_) throw AmiException("Failed to create solver."); @@ -176,9 +184,10 @@ SUNLinSolKLU::SUNLinSolKLU(N_Vector x, SUNMatrix A) throw AmiException("Failed to create solver."); } -SUNLinSolKLU::SUNLinSolKLU(const AmiVector &x, int nnz, int sparsetype, - StateOrdering ordering) : - A_(SUNMatrixWrapper(x.getLength(), x.getLength(), nnz, sparsetype)) { +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()); if (!solver_) throw AmiException("Failed to create solver."); @@ -206,12 +215,13 @@ SUNLinSolPCG::SUNLinSolPCG(N_Vector y, int pretype, int maxl) throw AmiException("Failed to create solver."); } -int SUNLinSolPCG::setATimes(void *A_data, ATimesFn ATimes) { +int SUNLinSolPCG::setATimes(void* A_data, ATimesFn ATimes) { return SUNLinSolSetATimes_PCG(solver_, A_data, ATimes); } -int SUNLinSolPCG::setPreconditioner(void *P_data, PSetupFn Pset, - PSolveFn Psol) { +int SUNLinSolPCG::setPreconditioner( + void* P_data, PSetupFn Pset, PSolveFn Psol +) { return SUNLinSolSetPreconditioner_PCG(solver_, P_data, Pset, Psol); } @@ -221,7 +231,9 @@ int SUNLinSolPCG::setScalingVectors(N_Vector s, N_Vector nul) { int SUNLinSolPCG::getNumIters() const { return SUNLinSolNumIters_PCG(solver_); } -realtype SUNLinSolPCG::getResNorm() const { return SUNLinSolResNorm_PCG(solver_); } +realtype SUNLinSolPCG::getResNorm() const { + return SUNLinSolResNorm_PCG(solver_); +} N_Vector SUNLinSolPCG::getResid() const { return SUNLinSolResid_PCG(solver_); } @@ -231,18 +243,20 @@ SUNLinSolSPBCGS::SUNLinSolSPBCGS(N_Vector x, int pretype, int maxl) throw AmiException("Failed to create solver."); } -SUNLinSolSPBCGS::SUNLinSolSPBCGS(const AmiVector &x, int pretype, int maxl) { - solver_ = SUNLinSol_SPBCGS(const_cast(x.getNVector()), pretype, maxl); +SUNLinSolSPBCGS::SUNLinSolSPBCGS(AmiVector const& x, int pretype, int maxl) { + solver_ + = SUNLinSol_SPBCGS(const_cast(x.getNVector()), pretype, maxl); if (!solver_) throw AmiException("Failed to create solver."); } -int SUNLinSolSPBCGS::setATimes(void *A_data, ATimesFn ATimes) { +int SUNLinSolSPBCGS::setATimes(void* A_data, ATimesFn ATimes) { return SUNLinSolSetATimes_SPBCGS(solver_, A_data, ATimes); } -int SUNLinSolSPBCGS::setPreconditioner(void *P_data, PSetupFn Pset, - PSolveFn Psol) { +int SUNLinSolSPBCGS::setPreconditioner( + void* P_data, PSetupFn Pset, PSolveFn Psol +) { return SUNLinSolSetPreconditioner_SPBCGS(solver_, P_data, Pset, Psol); } @@ -250,26 +264,33 @@ int SUNLinSolSPBCGS::setScalingVectors(N_Vector s, N_Vector nul) { return SUNLinSolSetScalingVectors_SPBCGS(solver_, s, nul); } -int SUNLinSolSPBCGS::getNumIters() const { return SUNLinSolNumIters_SPBCGS(solver_); } +int SUNLinSolSPBCGS::getNumIters() const { + return SUNLinSolNumIters_SPBCGS(solver_); +} realtype SUNLinSolSPBCGS::getResNorm() const { return SUNLinSolResNorm_SPBCGS(solver_); } -N_Vector SUNLinSolSPBCGS::getResid() const { return SUNLinSolResid_SPBCGS(solver_); } +N_Vector SUNLinSolSPBCGS::getResid() const { + return SUNLinSolResid_SPBCGS(solver_); +} -SUNLinSolSPFGMR::SUNLinSolSPFGMR(const AmiVector &x, int pretype, int maxl) - : SUNLinSolWrapper(SUNLinSol_SPFGMR(const_cast(x.getNVector()), pretype, maxl)) { +SUNLinSolSPFGMR::SUNLinSolSPFGMR(AmiVector const& x, int pretype, int maxl) + : SUNLinSolWrapper( + SUNLinSol_SPFGMR(const_cast(x.getNVector()), pretype, maxl) + ) { if (!solver_) throw AmiException("Failed to create solver."); } -int SUNLinSolSPFGMR::setATimes(void *A_data, ATimesFn ATimes) { +int SUNLinSolSPFGMR::setATimes(void* A_data, ATimesFn ATimes) { return SUNLinSolSetATimes_SPFGMR(solver_, A_data, ATimes); } -int SUNLinSolSPFGMR::setPreconditioner(void *P_data, PSetupFn Pset, - PSolveFn Psol) { +int SUNLinSolSPFGMR::setPreconditioner( + void* P_data, PSetupFn Pset, PSolveFn Psol +) { return SUNLinSolSetPreconditioner_SPFGMR(solver_, P_data, Pset, Psol); } @@ -277,27 +298,33 @@ int SUNLinSolSPFGMR::setScalingVectors(N_Vector s, N_Vector nul) { return SUNLinSolSetScalingVectors_SPFGMR(solver_, s, nul); } -int SUNLinSolSPFGMR::getNumIters() const { return SUNLinSolNumIters_SPFGMR(solver_); } +int SUNLinSolSPFGMR::getNumIters() const { + return SUNLinSolNumIters_SPFGMR(solver_); +} realtype SUNLinSolSPFGMR::getResNorm() const { return SUNLinSolResNorm_SPFGMR(solver_); } -N_Vector SUNLinSolSPFGMR::getResid() const { return SUNLinSolResid_SPFGMR(solver_); } +N_Vector SUNLinSolSPFGMR::getResid() const { + return SUNLinSolResid_SPFGMR(solver_); +} -SUNLinSolSPGMR::SUNLinSolSPGMR(const AmiVector &x, int pretype, int maxl) - : SUNLinSolWrapper(SUNLinSol_SPGMR(const_cast(x.getNVector()), - pretype, maxl)) { +SUNLinSolSPGMR::SUNLinSolSPGMR(AmiVector const& x, int pretype, int maxl) + : SUNLinSolWrapper( + SUNLinSol_SPGMR(const_cast(x.getNVector()), pretype, maxl) + ) { if (!solver_) throw AmiException("Failed to create solver."); } -int SUNLinSolSPGMR::setATimes(void *A_data, ATimesFn ATimes) { +int SUNLinSolSPGMR::setATimes(void* A_data, ATimesFn ATimes) { return SUNLinSolSetATimes_SPGMR(solver_, A_data, ATimes); } -int SUNLinSolSPGMR::setPreconditioner(void *P_data, PSetupFn Pset, - PSolveFn Psol) { +int SUNLinSolSPGMR::setPreconditioner( + void* P_data, PSetupFn Pset, PSolveFn Psol +) { return SUNLinSolSetPreconditioner_SPGMR(solver_, P_data, Pset, Psol); } @@ -305,11 +332,17 @@ int SUNLinSolSPGMR::setScalingVectors(N_Vector s, N_Vector nul) { return SUNLinSolSetScalingVectors_SPGMR(solver_, s, nul); } -int SUNLinSolSPGMR::getNumIters() const { return SUNLinSolNumIters_SPGMR(solver_); } +int SUNLinSolSPGMR::getNumIters() const { + return SUNLinSolNumIters_SPGMR(solver_); +} -realtype SUNLinSolSPGMR::getResNorm() const { return SUNLinSolResNorm_SPGMR(solver_); } +realtype SUNLinSolSPGMR::getResNorm() const { + return SUNLinSolResNorm_SPGMR(solver_); +} -N_Vector SUNLinSolSPGMR::getResid() const { return SUNLinSolResid_SPGMR(solver_); } +N_Vector SUNLinSolSPGMR::getResid() const { + return SUNLinSolResid_SPGMR(solver_); +} SUNLinSolSPTFQMR::SUNLinSolSPTFQMR(N_Vector x, int pretype, int maxl) : SUNLinSolWrapper(SUNLinSol_SPTFQMR(x, pretype, maxl)) { @@ -317,18 +350,21 @@ SUNLinSolSPTFQMR::SUNLinSolSPTFQMR(N_Vector x, int pretype, int maxl) throw AmiException("Failed to create solver."); } -SUNLinSolSPTFQMR::SUNLinSolSPTFQMR(const AmiVector &x, int pretype, int maxl) { - solver_ = SUNLinSol_SPTFQMR(const_cast(x.getNVector()), pretype, maxl); +SUNLinSolSPTFQMR::SUNLinSolSPTFQMR(AmiVector const& x, int pretype, int maxl) { + solver_ = SUNLinSol_SPTFQMR( + const_cast(x.getNVector()), pretype, maxl + ); if (!solver_) throw AmiException("Failed to create solver."); } -int SUNLinSolSPTFQMR::setATimes(void *A_data, ATimesFn ATimes) { +int SUNLinSolSPTFQMR::setATimes(void* A_data, ATimesFn ATimes) { return SUNLinSolSetATimes_SPTFQMR(solver_, A_data, ATimes); } -int SUNLinSolSPTFQMR::setPreconditioner(void *P_data, PSetupFn Pset, - PSolveFn Psol) { +int SUNLinSolSPTFQMR::setPreconditioner( + void* P_data, PSetupFn Pset, PSolveFn Psol +) { return SUNLinSolSetPreconditioner_SPTFQMR(solver_, P_data, Pset, Psol); } @@ -344,11 +380,12 @@ realtype SUNLinSolSPTFQMR::getResNorm() const { return SUNLinSolResNorm_SPTFQMR(solver_); } -N_Vector SUNLinSolSPTFQMR::getResid() const { return SUNLinSolResid_SPTFQMR(solver_); } +N_Vector SUNLinSolSPTFQMR::getResid() const { + return SUNLinSolResid_SPTFQMR(solver_); +} SUNNonLinSolNewton::SUNNonLinSolNewton(N_Vector x) - : SUNNonLinSolWrapper(SUNNonlinSol_Newton(x)) { -} + : SUNNonLinSolWrapper(SUNNonlinSol_Newton(x)) {} SUNNonLinSolNewton::SUNNonLinSolNewton(int count, N_Vector x) : SUNNonLinSolWrapper(SUNNonlinSol_NewtonSens(count, x)) { @@ -356,7 +393,7 @@ SUNNonLinSolNewton::SUNNonLinSolNewton(int count, N_Vector x) throw(AmiException("SUNNonlinSol_NewtonSens failed")); } -int SUNNonLinSolNewton::getSysFn(SUNNonlinSolSysFn *SysFn) const { +int SUNNonLinSolNewton::getSysFn(SUNNonlinSolSysFn* SysFn) const { return SUNNonlinSolGetSysFn_Newton(solver, SysFn); } @@ -364,31 +401,32 @@ SUNNonLinSolFixedPoint::SUNNonLinSolFixedPoint(const_N_Vector x, int m) : SUNNonLinSolWrapper(SUNNonlinSol_FixedPoint(const_cast(x), m)) { } -SUNNonLinSolFixedPoint::SUNNonLinSolFixedPoint(int count, const_N_Vector x, int m) +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 { +int SUNNonLinSolFixedPoint::getSysFn(SUNNonlinSolSysFn* SysFn) const { return SUNNonlinSolGetSysFn_FixedPoint(solver, SysFn); } #ifdef SUNDIALS_SUPERLUMT SUNLinSolSuperLUMT::SUNLinSolSuperLUMT(N_Vector x, SUNMatrix A, int numThreads) - : SUNLinSolWrapper(SUNLinSol_SuperLUMT(x, A, numThreads)) -{ + : SUNLinSolWrapper(SUNLinSol_SuperLUMT(x, A, numThreads)) { if (!solver) throw AmiException("Failed to create solver."); } SUNLinSolSuperLUMT::SUNLinSolSuperLUMT( - const AmiVector &x, int nnz, int sparsetype, - SUNLinSolSuperLUMT::StateOrdering ordering) - : A(SUNMatrixWrapper(x.getLength(), x.getLength(), nnz, sparsetype)) -{ + AmiVector const& x, int nnz, int sparsetype, + SUNLinSolSuperLUMT::StateOrdering ordering +) + : A(SUNMatrixWrapper(x.getLength(), x.getLength(), nnz, sparsetype)) { int numThreads = 1; - if(auto env = std::getenv("AMICI_SUPERLUMT_NUM_THREADS")) { + if (auto env = std::getenv("AMICI_SUPERLUMT_NUM_THREADS")) { numThreads = std::max(1, std::stoi(env)); } @@ -399,29 +437,27 @@ SUNLinSolSuperLUMT::SUNLinSolSuperLUMT( setOrdering(ordering); } -SUNLinSolSuperLUMT::SUNLinSolSuperLUMT(const AmiVector &x, int nnz, - int sparsetype, StateOrdering ordering, - int numThreads) - : A(SUNMatrixWrapper(x.getLength(), x.getLength(), nnz, sparsetype)) -{ - solver = SUNLinSol_SuperLUMT(x.getNVector(), A.get(), numThreads); - if (!solver) - throw AmiException("Failed to create solver."); - - setOrdering(ordering); -} +SUNLinSolSuperLUMT::SUNLinSolSuperLUMT( + AmiVector const& x, int nnz, int sparsetype, StateOrdering ordering, + int numThreads +) + : A(SUNMatrixWrapper(x.getLength(), x.getLength(), nnz, sparsetype)) { + solver = SUNLinSol_SuperLUMT(x.getNVector(), A.get(), numThreads); + if (!solver) + throw AmiException("Failed to create solver."); -SUNMatrix SUNLinSolSuperLUMT::getMatrix() const -{ - return A.get(); + setOrdering(ordering); } +SUNMatrix SUNLinSolSuperLUMT::getMatrix() const { return A.get(); } -void SUNLinSolSuperLUMT::setOrdering(StateOrdering ordering) -{ - auto status = SUNLinSol_SuperLUMTSetOrdering(solver, static_cast(ordering)); +void SUNLinSolSuperLUMT::setOrdering(StateOrdering ordering) { + auto status + = SUNLinSol_SuperLUMTSetOrdering(solver, static_cast(ordering)); if (status != SUNLS_SUCCESS) - throw AmiException("SUNLinSol_SuperLUMTSetOrdering failed with %d", status); + throw AmiException( + "SUNLinSol_SuperLUMTSetOrdering failed with %d", status + ); } #endif diff --git a/deps/AMICI/src/sundials_matrix_wrapper.cpp b/deps/AMICI/src/sundials_matrix_wrapper.cpp index bd3fda4fb..b5c730062 100644 --- a/deps/AMICI/src/sundials_matrix_wrapper.cpp +++ b/deps/AMICI/src/sundials_matrix_wrapper.cpp @@ -3,16 +3,18 @@ #include -#include // bad_alloc -#include +#include // bad_alloc #include // invalid_argument and domain_error +#include namespace amici { -SUNMatrixWrapper::SUNMatrixWrapper(sunindextype M, sunindextype N, - sunindextype NNZ, int sparsetype) - : matrix_(SUNSparseMatrix(M, N, NNZ, sparsetype)), id_(SUNMATRIX_SPARSE), - sparsetype_(sparsetype) { +SUNMatrixWrapper::SUNMatrixWrapper( + sunindextype M, sunindextype N, sunindextype NNZ, int sparsetype +) + : matrix_(SUNSparseMatrix(M, N, NNZ, sparsetype)) + , id_(SUNMATRIX_SPARSE) + , sparsetype_(sparsetype) { if (sparsetype != CSC_MAT && sparsetype != CSR_MAT) throw std::invalid_argument("Invalid sparsetype. Must be CSC_MAT or " @@ -29,7 +31,8 @@ SUNMatrixWrapper::SUNMatrixWrapper(sunindextype M, sunindextype N, } SUNMatrixWrapper::SUNMatrixWrapper(sunindextype M, sunindextype N) - : matrix_(SUNDenseMatrix(M, N)), id_(SUNMATRIX_DENSE) { + : matrix_(SUNDenseMatrix(M, N)) + , id_(SUNMATRIX_DENSE) { if (M && N && !matrix_) throw std::bad_alloc(); @@ -38,17 +41,21 @@ SUNMatrixWrapper::SUNMatrixWrapper(sunindextype M, sunindextype N) assert(N == columns() || !matrix_); } -SUNMatrixWrapper::SUNMatrixWrapper(sunindextype M, sunindextype ubw, - sunindextype lbw) - : matrix_(SUNBandMatrix(M, ubw, lbw)), id_(SUNMATRIX_BAND) { +SUNMatrixWrapper::SUNMatrixWrapper( + sunindextype M, sunindextype ubw, sunindextype lbw +) + : matrix_(SUNBandMatrix(M, ubw, lbw)) + , id_(SUNMATRIX_BAND) { if (M && !matrix_) throw std::bad_alloc(); finish_init(); } -SUNMatrixWrapper::SUNMatrixWrapper(const SUNMatrixWrapper &A, realtype droptol, - int sparsetype) - : id_(SUNMATRIX_SPARSE), sparsetype_(sparsetype) { +SUNMatrixWrapper::SUNMatrixWrapper( + SUNMatrixWrapper const& A, realtype droptol, int sparsetype +) + : id_(SUNMATRIX_SPARSE) + , sparsetype_(sparsetype) { if (sparsetype != CSC_MAT && sparsetype != CSR_MAT) throw std::invalid_argument("Invalid sparsetype. Must be CSC_MAT or " "CSR_MAT"); @@ -70,21 +77,23 @@ SUNMatrixWrapper::SUNMatrixWrapper(const SUNMatrixWrapper &A, realtype droptol, num_nonzeros_ = indexptrs_[num_indexptrs()]; } -static inline SUNMatrix_ID get_sparse_id_w_default(SUNMatrix mat) { +inline static SUNMatrix_ID get_sparse_id_w_default(SUNMatrix mat) { if (mat) return SUNMatGetID(mat); return SUNMATRIX_CUSTOM; } -static inline int get_sparse_type_w_default(SUNMatrix mat) { +inline static int get_sparse_type_w_default(SUNMatrix mat) { if (mat && SUNMatGetID(mat) == SUNMATRIX_SPARSE) return SM_SPARSETYPE_S(mat); return CSC_MAT; } SUNMatrixWrapper::SUNMatrixWrapper(SUNMatrix mat) - : matrix_(mat), id_(get_sparse_id_w_default(mat)), - sparsetype_(get_sparse_type_w_default(mat)), ownmat(false) { + : matrix_(mat) + , id_(get_sparse_id_w_default(mat)) + , sparsetype_(get_sparse_type_w_default(mat)) + , ownmat(false) { finish_init(); } @@ -93,9 +102,9 @@ SUNMatrixWrapper::~SUNMatrixWrapper() { SUNMatDestroy(matrix_); } -SUNMatrixWrapper::SUNMatrixWrapper(const SUNMatrixWrapper &other) - : id_(get_sparse_id_w_default(other.matrix_)), - sparsetype_(get_sparse_type_w_default(other.matrix_)) { +SUNMatrixWrapper::SUNMatrixWrapper(SUNMatrixWrapper const& other) + : id_(get_sparse_id_w_default(other.matrix_)) + , sparsetype_(get_sparse_type_w_default(other.matrix_)) { if (!other.matrix_) return; @@ -107,20 +116,20 @@ SUNMatrixWrapper::SUNMatrixWrapper(const SUNMatrixWrapper &other) finish_init(); } -SUNMatrixWrapper::SUNMatrixWrapper(SUNMatrixWrapper &&other) - : id_(get_sparse_id_w_default(other.matrix_)), - sparsetype_(get_sparse_type_w_default(other.matrix_)) { +SUNMatrixWrapper::SUNMatrixWrapper(SUNMatrixWrapper&& other) + : id_(get_sparse_id_w_default(other.matrix_)) + , sparsetype_(get_sparse_type_w_default(other.matrix_)) { std::swap(matrix_, other.matrix_); finish_init(); } -SUNMatrixWrapper &SUNMatrixWrapper::operator=(const SUNMatrixWrapper &other) { - if(&other == this) +SUNMatrixWrapper& SUNMatrixWrapper::operator=(SUNMatrixWrapper const& other) { + if (&other == this) return *this; return *this = SUNMatrixWrapper(other); } -SUNMatrixWrapper &SUNMatrixWrapper::operator=(SUNMatrixWrapper &&other) { +SUNMatrixWrapper& SUNMatrixWrapper::operator=(SUNMatrixWrapper&& other) { std::swap(matrix_, other.matrix_); id_ = other.id_; sparsetype_ = other.sparsetype_; @@ -134,8 +143,11 @@ void SUNMatrixWrapper::reallocate(sunindextype NNZ) { "CSR_MAT."); if (int ret = SUNSparseMatrix_Reallocate(matrix_, NNZ) != SUNMAT_SUCCESS) - throw std::runtime_error("SUNSparseMatrix_Reallocate failed with " - "error code " + std::to_string(ret) + "."); + throw std::runtime_error( + "SUNSparseMatrix_Reallocate failed with " + "error code " + + std::to_string(ret) + "." + ); update_ptrs(); capacity_ = NNZ; @@ -147,22 +159,24 @@ void SUNMatrixWrapper::realloc() { throw std::invalid_argument("Invalid sparsetype. Must be CSC_MAT or " "CSR_MAT."); if (int ret = SUNSparseMatrix_Realloc(matrix_) != SUNMAT_SUCCESS) - throw std::runtime_error("SUNSparseMatrix_Realloc failed with " - "error code " + std::to_string(ret) + "."); + throw std::runtime_error( + "SUNSparseMatrix_Realloc failed with " + "error code " + + std::to_string(ret) + "." + ); update_ptrs(); capacity_ = num_nonzeros_; assert(capacity() || !matrix_); } - - sunindextype SUNMatrixWrapper::num_indexptrs() const { assert(matrix_id() == SUNMATRIX_SPARSE); - assert(!matrix_ || - (sparsetype() == CSC_MAT ? - num_indexptrs_ == num_columns_ : - num_indexptrs_ == num_rows_)); + assert( + !matrix_ + || (sparsetype() == CSC_MAT ? num_indexptrs_ == num_columns_ + : num_indexptrs_ == num_rows_) + ); assert(!matrix_ || num_indexptrs_ == SM_NP_S(matrix_)); return num_indexptrs_; } @@ -175,18 +189,15 @@ sunindextype SUNMatrixWrapper::capacity() const { sunindextype SUNMatrixWrapper::num_nonzeros() const { assert(matrix_id() == SUNMATRIX_SPARSE); - assert(!matrix_ || - num_nonzeros_ == SM_INDEXPTRS_S(matrix_)[SM_NP_S(matrix_)]); + assert( + !matrix_ || num_nonzeros_ == SM_INDEXPTRS_S(matrix_)[SM_NP_S(matrix_)] + ); return num_nonzeros_; } -const realtype *SUNMatrixWrapper::data() const { - return data_; -} +realtype const* SUNMatrixWrapper::data() const { return data_; } -realtype *SUNMatrixWrapper::data() { - return data_; -} +realtype* SUNMatrixWrapper::data() { return data_; } int SUNMatrixWrapper::sparsetype() const { assert(matrix_); @@ -202,78 +213,80 @@ void SUNMatrixWrapper::scale(realtype a) { } } -void SUNMatrixWrapper::multiply(N_Vector c, const_N_Vector b, - const realtype alpha) const { - multiply(gsl::make_span(NV_DATA_S(c), NV_LENGTH_S(c)), - gsl::make_span(NV_DATA_S(b), NV_LENGTH_S(b)), - alpha); +void SUNMatrixWrapper::multiply( + N_Vector c, const_N_Vector b, const realtype alpha +) const { + multiply( + gsl::make_span(NV_DATA_S(c), NV_LENGTH_S(c)), + gsl::make_span(NV_DATA_S(b), NV_LENGTH_S(b)), alpha + ); } #ifndef NDEBUG -static inline void check_csc(const SUNMatrixWrapper *mat) { +inline static void check_csc(SUNMatrixWrapper const* mat) { assert(mat->matrix_id() == SUNMATRIX_SPARSE); assert(mat->sparsetype() == CSC_MAT); } #else // avoid "unused parameter" warning -static inline void check_csc(const SUNMatrixWrapper */*mat*/) {} +inline static void check_csc(SUNMatrixWrapper const* /*mat*/) {} #endif -void SUNMatrixWrapper::multiply(gsl::span c, - gsl::span b, - const realtype alpha) const { - +void SUNMatrixWrapper::multiply( + gsl::span c, gsl::span b, const realtype alpha +) const { if (!matrix_) return; - assert(rows() == static_cast(c.size())); - assert(columns() == static_cast(b.size())); + assert(rows() == gsl::narrow(c.size())); + assert(columns() == gsl::narrow(b.size())); switch (matrix_id()) { case SUNMATRIX_DENSE: - amici_dgemv(BLASLayout::colMajor, BLASTranspose::noTrans, - static_cast(rows()), static_cast(columns()), - alpha, data(), static_cast(rows()), - b.data(), 1, 1.0, c.data(), 1); + amici_dgemv( + BLASLayout::colMajor, BLASTranspose::noTrans, + gsl::narrow(rows()), gsl::narrow(columns()), alpha, + data(), gsl::narrow(rows()), b.data(), 1, 1.0, c.data(), 1 + ); break; case SUNMATRIX_SPARSE: - if(!num_nonzeros()) { + if (!num_nonzeros()) { return; } check_csc(this); for (sunindextype icol = 0; icol < columns(); ++icol) { - scatter(icol, b[icol] * alpha, nullptr, c, icol+1, nullptr, 0); + scatter(icol, b[icol] * alpha, nullptr, c, icol + 1, nullptr, 0); } break; default: throw std::domain_error("Not Implemented."); } - } -void SUNMatrixWrapper::multiply(N_Vector c, - const_N_Vector b, - gsl::span cols, - bool transpose) const { - multiply(gsl::make_span(NV_DATA_S(c), NV_LENGTH_S(c)), - gsl::make_span(NV_DATA_S(b), NV_LENGTH_S(b)), - cols, transpose); +void SUNMatrixWrapper::multiply( + N_Vector c, const_N_Vector b, gsl::span cols, bool transpose +) const { + multiply( + gsl::make_span(NV_DATA_S(c), NV_LENGTH_S(c)), + gsl::make_span(NV_DATA_S(b), NV_LENGTH_S(b)), cols, + transpose + ); } -void SUNMatrixWrapper::multiply(gsl::span c, - gsl::span b, - gsl::span cols, - bool transpose) const { +void SUNMatrixWrapper::multiply( + gsl::span c, gsl::span b, + gsl::span cols, bool transpose +) const { if (!matrix_) return; if (transpose) { assert(cols.size() == c.size()); - assert(rows() == static_cast(b.size())); + assert(rows() == gsl::narrow(b.size())); } else { - assert(rows() == static_cast(c.size())); - assert(columns() == static_cast(b.size())); + assert(rows() == gsl::narrow(c.size())); + assert(columns() == gsl::narrow(b.size())); } check_csc(this); @@ -294,13 +307,13 @@ void SUNMatrixWrapper::multiply(gsl::span c, auto idx_val = get_indexval(idx); assert(icols < c.size()); - assert(static_cast(idx_val) < b.size()); + assert(gsl::narrow(idx_val) < b.size()); c_ptr[icols] += get_data(idx) * b_ptr[idx_val]; } } } else { - auto num_cols = static_cast(columns()); + auto num_cols = gsl::narrow(columns()); for (std::size_t icols = 0; icols < num_cols; ++icols) { auto idx_next_col = get_indexptr(cols[icols] + 1); @@ -309,7 +322,7 @@ void SUNMatrixWrapper::multiply(gsl::span c, auto idx_val = get_indexval(idx); assert(icols < b.size()); - assert(static_cast(idx_val) < c.size()); + assert(gsl::narrow(idx_val) < c.size()); c_ptr[idx_val] += get_data(idx) * b_ptr[icols]; } @@ -317,9 +330,9 @@ void SUNMatrixWrapper::multiply(gsl::span c, } } - -void SUNMatrixWrapper::sparse_multiply(SUNMatrixWrapper &C, - const SUNMatrixWrapper &B) const { +void SUNMatrixWrapper::sparse_multiply( + SUNMatrixWrapper& C, SUNMatrixWrapper const& B +) const { if (!matrix_ || !B.matrix_ || !C.matrix_) return; @@ -327,9 +340,9 @@ void SUNMatrixWrapper::sparse_multiply(SUNMatrixWrapper &C, check_csc(&B); check_csc(&C); - assert(rows() == static_cast(C.rows())); + assert(rows() == gsl::narrow(C.rows())); assert(C.columns() == B.columns()); - assert(static_cast(B.rows()) == columns()); + assert(gsl::narrow(B.rows()) == columns()); C.zero(); @@ -339,8 +352,8 @@ void SUNMatrixWrapper::sparse_multiply(SUNMatrixWrapper &C, if (num_nonzeros() == 0 || B.num_nonzeros() == 0) return; // nothing to multiply - - /* see https://github.com/DrTimothyAldenDavis/SuiteSparse/blob/master/CSparse/Source/cs_multiply.c + /* see + * https://github.com/DrTimothyAldenDavis/SuiteSparse/blob/master/CSparse/Source/cs_multiply.c * modified such that we don't need to use CSparse memory structure and can * work with preallocated C. This should minimize number of necessary * reallocations as we can assume that C doesn't change size. @@ -352,29 +365,31 @@ void SUNMatrixWrapper::sparse_multiply(SUNMatrixWrapper &C, sunindextype cidx; auto w = std::vector(rows()); // sparsity of C(:,j) - auto x = std::vector(rows()); // entries in C(:,j) + auto x = std::vector(rows()); // entries in C(:,j) - for (bcol = 0; bcol < B.columns(); bcol++) // k in C(i,j) = sum_k A(i,k)*B(k,j) + for (bcol = 0; bcol < B.columns(); + bcol++) // k in C(i,j) = sum_k A(i,k)*B(k,j) { - C.set_indexptr(bcol, nnz); /* column j of C starts here */ - if ((B.get_indexptr(bcol+1) > B.get_indexptr(bcol)) - && (nnz + rows() > C.capacity())) - { + C.set_indexptr(bcol, nnz); /* column j of C starts here */ + if ((B.get_indexptr(bcol + 1) > B.get_indexptr(bcol)) + && (nnz + rows() > C.capacity())) { /* * if memory usage becomes a concern, remove the factor two here, * as it effectively trades memory efficiency against less * reallocations */ - C.reallocate(2*C.capacity() + rows()); + C.reallocate(2 * C.capacity() + rows()); } - for (bidx = B.get_indexptr(bcol); bidx < B.get_indexptr(bcol+1); bidx++) - { - nnz = scatter(B.get_indexval(bidx), B.get_data(bidx), - w.data(), gsl::make_span(x), bcol+1, &C, nnz); + for (bidx = B.get_indexptr(bcol); bidx < B.get_indexptr(bcol + 1); + bidx++) { + nnz = scatter( + B.get_indexval(bidx), B.get_data(bidx), w.data(), + gsl::make_span(x), bcol + 1, &C, nnz + ); assert(nnz - C.get_indexptr(bcol) <= rows()); } for (cidx = C.get_indexptr(bcol); cidx < nnz; cidx++) - C.set_data(cidx, x.at(C.get_indexval(cidx))); // copy data to C + C.set_data(cidx, x[C.get_indexval(cidx)]); // copy data to C } C.set_indexptr(C.num_indexptrs(), nnz); @@ -384,8 +399,10 @@ void SUNMatrixWrapper::sparse_multiply(SUNMatrixWrapper &C, */ } -void SUNMatrixWrapper::sparse_add(const SUNMatrixWrapper &A, realtype alpha, - const SUNMatrixWrapper &B, realtype beta) { +void SUNMatrixWrapper::sparse_add( + SUNMatrixWrapper const& A, realtype alpha, SUNMatrixWrapper const& B, + realtype beta +) { // matrix_ == nullptr is allowed on the first call if (!A.matrix_ || !B.matrix_) return; @@ -394,19 +411,19 @@ void SUNMatrixWrapper::sparse_add(const SUNMatrixWrapper &A, realtype alpha, check_csc(&A); check_csc(&B); - assert(rows() == static_cast(A.rows())); - assert(rows() == static_cast(B.rows())); - assert(columns() == static_cast(A.columns())); - assert(columns() == static_cast(B.columns())); + assert(rows() == gsl::narrow(A.rows())); + assert(rows() == gsl::narrow(B.rows())); + assert(columns() == gsl::narrow(A.columns())); + assert(columns() == gsl::narrow(B.columns())); zero(); - if (columns() == 0 || rows() == 0 || - (A.num_nonzeros() + B.num_nonzeros() == 0)) + if (columns() == 0 || rows() == 0 + || (A.num_nonzeros() + B.num_nonzeros() == 0)) return; // nothing to do - - /* see https://github.com/DrTimothyAldenDavis/SuiteSparse/blob/master/CSparse/Source/cs_add.c + /* see + * https://github.com/DrTimothyAldenDavis/SuiteSparse/blob/master/CSparse/Source/cs_add.c * modified such that we don't need to use CSparse memory structure and can * work with preallocated C. This should minimize number of necessary * reallocations as we can assume that C doesn't change size. @@ -418,43 +435,45 @@ void SUNMatrixWrapper::sparse_add(const SUNMatrixWrapper &A, realtype alpha, sunindextype cidx; // first call, make sure that matrix is initialized with no capacity - if(!capacity()) + if (!capacity()) reallocate(A.num_nonzeros() + B.num_nonzeros()); auto w = std::vector(rows()); auto x = std::vector(rows()); - for (ccol = 0; ccol < columns(); ccol++) - { - set_indexptr(ccol, nnz); /* column j of C starts here */ - nnz = A.scatter(ccol, alpha, w.data(), gsl::make_span(x), ccol+1, this, - nnz); - nnz = B.scatter(ccol, beta, w.data(), gsl::make_span(x), ccol+1, this, - nnz); + for (ccol = 0; ccol < columns(); ccol++) { + set_indexptr(ccol, nnz); /* column j of C starts here */ + nnz = A.scatter( + ccol, alpha, w.data(), gsl::make_span(x), ccol + 1, this, nnz + ); + nnz = B.scatter( + ccol, beta, w.data(), gsl::make_span(x), ccol + 1, this, nnz + ); // no reallocation should happen here for (cidx = get_indexptr(ccol); cidx < nnz; cidx++) { auto x_idx = get_indexval(cidx); - assert(x_idx >= 0 && static_cast(x_idx) < x.size()); + assert(x_idx >= 0 && gsl::narrow(x_idx) < x.size()); set_data(cidx, x[x_idx]); // copy data to C } } set_indexptr(num_indexptrs(), nnz); if (capacity() == A.num_nonzeros() + B.num_nonzeros()) - realloc(); // resize if necessary, will have correct size in future calls + realloc( + ); // resize if necessary, will have correct size in future calls } -void SUNMatrixWrapper::sparse_sum(const std::vector &mats) { +void SUNMatrixWrapper::sparse_sum(std::vector const& mats) { // matrix_ == nullptr is allowed on the first call - auto all_empty = std::all_of(mats.begin(), mats.end(), - [](const SUNMatrixWrapper &m){ - return !m.matrix_; - }); + auto all_empty + = std::all_of(mats.begin(), mats.end(), [](SUNMatrixWrapper const& m) { + return !m.matrix_; + }); if (all_empty) return; check_csc(this); int max_total_nonzero = 0; - for (auto & mat : mats) { + for (auto& mat : mats) { check_csc(&mat); assert(rows() == mat.rows()); assert(columns() == mat.columns()); @@ -466,7 +485,8 @@ void SUNMatrixWrapper::sparse_sum(const std::vector &mats) { if (columns() == 0 || rows() == 0 || max_total_nonzero == 0) return; // nothing to do - /* see https://github.com/DrTimothyAldenDavis/SuiteSparse/blob/master/CSparse/Source/cs_add.c + /* see + * https://github.com/DrTimothyAldenDavis/SuiteSparse/blob/master/CSparse/Source/cs_add.c * modified such that we don't need to use CSparse memory structure and can * work with preallocated C. This should minimize number of necessary * reallocations as we can assume that C doesn't change size. @@ -477,22 +497,22 @@ void SUNMatrixWrapper::sparse_sum(const std::vector &mats) { sunindextype acol; sunindextype aidx; // first call, make sure that matrix is initialized with no capacity - if(!capacity()) + if (!capacity()) reallocate(max_total_nonzero); auto w = std::vector(rows()); auto x = std::vector(rows()); - for (acol = 0; acol < columns(); acol++) - { + for (acol = 0; acol < columns(); acol++) { set_indexptr(acol, nnz); /* column j of A starts here */ - for (auto & mat : mats) - nnz = mat.scatter(acol, 1.0, w.data(), gsl::make_span(x), acol+1, - this, nnz); + for (auto& mat : mats) + nnz = mat.scatter( + acol, 1.0, w.data(), gsl::make_span(x), acol + 1, this, nnz + ); // no reallocation should happen here for (aidx = get_indexptr(acol); aidx < nnz; aidx++) { auto x_idx = get_indexval(aidx); - assert(x_idx >= 0 && static_cast(x_idx) < x.size()); + assert(x_idx >= 0 && gsl::narrow(x_idx) < x.size()); set_data(aidx, x[x_idx]); // copy data to C } } @@ -501,13 +521,11 @@ void SUNMatrixWrapper::sparse_sum(const std::vector &mats) { realloc(); // resize if necessary } -sunindextype SUNMatrixWrapper::scatter(const sunindextype acol, - const realtype beta, - sunindextype *w, - gsl::span x, - const sunindextype mark, - SUNMatrixWrapper *C, - sunindextype nnz) const { +sunindextype SUNMatrixWrapper::scatter( + const sunindextype acol, const realtype beta, sunindextype* w, + gsl::span x, const sunindextype mark, SUNMatrixWrapper* C, + sunindextype nnz +) const { if (!matrix_) return nnz; @@ -518,20 +536,26 @@ sunindextype SUNMatrixWrapper::scatter(const sunindextype acol, if (!num_nonzeros()) return nnz; - /* see https://github.com/DrTimothyAldenDavis/SuiteSparse/blob/master/CSparse/Source/cs_scatter.c */ + auto x_data = x.data(); + /* see + * https://github.com/DrTimothyAldenDavis/SuiteSparse/blob/master/CSparse/Source/cs_scatter.c + */ sunindextype aidx; - for (aidx = get_indexptr(acol); aidx < get_indexptr(acol+1); aidx++) - { - auto arow = get_indexval(aidx); /* A(arow,acol) is nonzero */ - assert(arow >= 0 && static_cast(arow) <= x.size()); + for (aidx = get_indexptr(acol); aidx < get_indexptr(acol + 1); aidx++) { + auto arow = get_indexval(aidx); /* A(arow,acol) is nonzero */ + assert(arow >= 0 && gsl::narrow(arow) <= x.size()); if (w && w[arow] < mark) { - w[arow] = mark; /* arow is new entry in C(:,*) */ + w[arow] = mark; /* arow is new entry in C(:,*) */ if (C) - C->set_indexval(nnz++, arow); /* add arow to pattern of C(:,*) */ - x[arow] = beta * get_data(aidx); /* x(arow) = beta*A(arow,acol) */ + C->set_indexval( + nnz++, arow + ); /* add arow to pattern of C(:,*) */ + x_data[arow] + = beta * get_data(aidx); /* x(arow) = beta*A(arow,acol) */ } else { - x[arow] += beta * get_data(aidx); /* arow exists in C(:,*) already */ + x_data[arow] + += beta * get_data(aidx); /* arow exists in C(:,*) already */ } } assert(!C || nnz <= C->capacity()); @@ -540,20 +564,20 @@ sunindextype SUNMatrixWrapper::scatter(const sunindextype acol, // https://github.com/DrTimothyAldenDavis/SuiteSparse/blob/master/CSparse/Source/cs_cumsum.c /* p [0..n] = cumulative sum of c[0..n-1], and then copy p [0..n-1] into c */ -static void cumsum(gsl::span p, std::vector &c) { +static void cumsum(gsl::span p, std::vector& c) { sunindextype nz = 0; assert(p.size() == c.size() + 1); - for (sunindextype i = 0; i < static_cast(c.size()); i++) - { + for (sunindextype i = 0; i < gsl::narrow(c.size()); i++) { p[i] = nz; nz += c[i]; - c[i] = p[i]; /* also copy p[0..n-1] back into c[0..n-1]*/ + c[i] = p[i]; /* also copy p[0..n-1] back into c[0..n-1]*/ } p[c.size()] = nz; } -void SUNMatrixWrapper::transpose(SUNMatrixWrapper &C, const realtype alpha, - sunindextype blocksize) const{ +void SUNMatrixWrapper::transpose( + SUNMatrixWrapper& C, const realtype alpha, sunindextype blocksize +) const { if (!matrix_ || !C.matrix_) return; @@ -578,7 +602,8 @@ void SUNMatrixWrapper::transpose(SUNMatrixWrapper &C, const realtype alpha, if (!num_nonzeros() || !columns() || !rows()) return; - // see https://github.com/DrTimothyAldenDavis/SuiteSparse/blob/master/CSparse/Source/cs_transpose.c + // see + // https://github.com/DrTimothyAldenDavis/SuiteSparse/blob/master/CSparse/Source/cs_transpose.c auto nrows = rows(); @@ -587,58 +612,59 @@ void SUNMatrixWrapper::transpose(SUNMatrixWrapper &C, const realtype alpha, auto w_data = w.data(); for (sunindextype acol = 0; acol < nrows; acol++) { /* row counts */ - auto next_indexptr = get_indexptr(acol+1); - auto widx_offset = (acol/blocksize)*blocksize; - for (sunindextype aidx = get_indexptr(acol); - aidx < next_indexptr; aidx++) { - sunindextype widx = widx_offset + get_indexval(aidx) % blocksize; + auto next_indexptr = get_indexptr(acol + 1); + auto widx_offset = (acol / blocksize) * blocksize; + for (sunindextype aidx = get_indexptr(acol); aidx < next_indexptr; + aidx++) { + sunindextype widx + = widx_offset + get_indexval(aidx) % blocksize; assert(widx >= 0 && widx < (sunindextype)w.size()); w_data[widx]++; assert(w_data[widx] <= nrows); } } /* row pointers */ - cumsum(gsl::make_span(C.indexptrs_, C.columns()+1), w); + cumsum(gsl::make_span(C.indexptrs_, C.columns() + 1), w); - for (sunindextype acol = 0; acol < nrows; acol++) - { - auto next_indexptr = get_indexptr(acol+1); - auto ccol_offset = (acol/blocksize)*blocksize; + for (sunindextype acol = 0; acol < nrows; acol++) { + auto next_indexptr = get_indexptr(acol + 1); + auto ccol_offset = (acol / blocksize) * blocksize; auto crow_offset = acol % blocksize; - for (sunindextype aidx = get_indexptr(acol); aidx < next_indexptr; aidx++) - { + for (sunindextype aidx = get_indexptr(acol); aidx < next_indexptr; + aidx++) { auto indexval_aidx = get_indexval(aidx); sunindextype ccol = ccol_offset + indexval_aidx % blocksize; - sunindextype crow = (indexval_aidx/blocksize)*blocksize + crow_offset; + sunindextype crow + = (indexval_aidx / blocksize) * blocksize + crow_offset; assert(crow < nrows); assert(ccol < columns()); assert(aidx < capacity()); assert(ccol >= 0 && ccol < (sunindextype)w.size()); sunindextype cidx = w_data[ccol]++; - C.set_indexval(cidx, crow); /* place A(i,j) as entry C(j,i) */ + C.set_indexval(cidx, crow); /* place A(i,j) as entry C(j,i) */ C.set_data(cidx, alpha * get_data(aidx)); } } } else { - for (sunindextype acol = 0; acol < nrows; acol++) - { - auto next_indexptr = get_indexptr(acol+1); + for (sunindextype acol = 0; acol < nrows; acol++) { + auto next_indexptr = get_indexptr(acol + 1); - for (sunindextype aidx = get_indexptr(acol); aidx < next_indexptr; aidx++) - { - sunindextype ccol = (acol/blocksize)*blocksize + get_indexval(aidx) % blocksize; - sunindextype crow = (get_indexval(aidx)/blocksize)*blocksize + acol % blocksize; + for (sunindextype aidx = get_indexptr(acol); aidx < next_indexptr; + aidx++) { + sunindextype ccol = (acol / blocksize) * blocksize + + get_indexval(aidx) % blocksize; + sunindextype crow = (get_indexval(aidx) / blocksize) * blocksize + + acol % blocksize; assert(crow < nrows); assert(ccol < columns()); C.set_data(crow, ccol, alpha * get_data(aidx)); } } } - } -void SUNMatrixWrapper::to_dense(SUNMatrixWrapper &D) const { +void SUNMatrixWrapper::to_dense(SUNMatrixWrapper& D) const { if (!matrix_ || !D.matrix_) return; check_csc(this); @@ -652,7 +678,7 @@ void SUNMatrixWrapper::to_dense(SUNMatrixWrapper &D) const { sunindextype icol; sunindextype idx; for (icol = 0; icol < columns(); ++icol) - for (idx = get_indexptr(icol); idx < get_indexptr(icol+1); ++idx) { + for (idx = get_indexptr(icol); idx < get_indexptr(icol + 1); ++idx) { D.set_data(get_indexval(idx), icol, get_data(idx)); } } @@ -671,19 +697,18 @@ void SUNMatrixWrapper::to_diag(N_Vector v) const { sunindextype icol; sunindextype idx; for (icol = 0; icol < columns(); ++icol) - for (idx = get_indexptr(icol); idx < get_indexptr(icol+1); ++idx) + for (idx = get_indexptr(icol); idx < get_indexptr(icol + 1); ++idx) if (get_indexval(idx) == icol) NV_Ith_S(v, icol) = get_data(idx); } - -void SUNMatrixWrapper::zero() -{ +void SUNMatrixWrapper::zero() { if (!matrix_) return; - if(int res = SUNMatZero(matrix_)) - throw std::runtime_error("SUNMatrixWrapper::zero() failed with " - + std::to_string(res) + "."); + if (int res = SUNMatZero(matrix_)) + throw std::runtime_error( + "SUNMatrixWrapper::zero() failed with " + std::to_string(res) + "." + ); } void SUNMatrixWrapper::finish_init() { @@ -747,5 +772,44 @@ void SUNMatrixWrapper::refresh() { SUNMatrix SUNMatrixWrapper::get() const { return matrix_; } -} // namespace amici +std::pair +unravel_index(sunindextype i, SUNMatrix m) { + gsl_ExpectsDebug(i >= 0); + auto mat_id = SUNMatGetID(m); + if (mat_id == SUNMATRIX_DENSE) { + gsl_ExpectsDebug(i < SM_COLUMNS_D(m) * SM_ROWS_D(m)); + + auto num_rows = SM_ROWS_D(m); + // col-major + sunindextype row = i % num_rows; + sunindextype col = i / num_rows; + + gsl_EnsuresDebug(row >= 0); + gsl_EnsuresDebug(row < SM_ROWS_D(m)); + gsl_EnsuresDebug(col >= 0); + gsl_EnsuresDebug(col < SM_COLUMNS_D(m)); + + return {row, col}; + } + 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; + } + } + + throw amici::AmiException("Unimplemented SUNMatrix type for unravel_index"); +} + +} // namespace amici diff --git a/deps/AMICI/src/symbolic_functions.cpp b/deps/AMICI/src/symbolic_functions.cpp index df8397644..6c18d851b 100644 --- a/deps/AMICI/src/symbolic_functions.cpp +++ b/deps/AMICI/src/symbolic_functions.cpp @@ -71,7 +71,7 @@ double sign(double x) { return 0.0; } -double max(double a, double b, double /*c*/) { +double max(double a, double b, double /*c*/) { int anan = isNaN(a), bnan = isNaN(b); if (anan || bnan) { if (anan && !bnan) @@ -83,11 +83,9 @@ double max(double a, double b, double /*c*/) { return (std::max(a, b)); } -double min(double a, double b, double c) { - return (-max(-a,-b,c)); -} +double min(double a, double b, double c) { return (-max(-a, -b, c)); } -double Dmax(int id, double a, double b, double /*c*/) { +double Dmax(int id, double a, double b, double /*c*/) { if (id == 1.0) { if (a > b) return 1.0; @@ -100,16 +98,15 @@ double Dmax(int id, double a, double b, double /*c*/) { } double Dmin(int id, double a, double b, double c) { - return Dmax(id,-a,-b,c); + return Dmax(id, -a, -b, c); } double pos_pow(double base, double exponent) { - // we do NOT want to propagate NaN values here, if base is nan, so should the output be - return pow(std::max(base, 0.0),exponent); + // we do NOT want to propagate NaN values here, if base is nan, so should + // the output be + return pow(std::max(base, 0.0), exponent); } - - // Legacy spline implementation in C (MATLAB only) double spline(double t, int num, ...) { @@ -119,12 +116,12 @@ double spline(double t, int num, ...) { double ss; double dudt; - auto *ts = (double *)alloca(num * sizeof(double)); - auto *us = (double *)alloca(num * sizeof(double)); + auto* ts = (double*)alloca(num * sizeof(double)); + auto* us = (double*)alloca(num * sizeof(double)); - auto *b = (double *)alloca(num * sizeof(double)); - auto *c = (double *)alloca(num * sizeof(double)); - auto *d = (double *)alloca(num * sizeof(double)); + auto* b = (double*)alloca(num * sizeof(double)); + auto* c = (double*)alloca(num * sizeof(double)); + auto* d = (double*)alloca(num * sizeof(double)); /* Variable list type macro */ /* initialize valist for num number of arguments */ @@ -155,13 +152,13 @@ double spline_pos(double t, int num, ...) { double ss; double dudt; - auto *ts = (double *)alloca(num * sizeof(double)); - auto *us = (double *)alloca(num * sizeof(double)); - auto *uslog = (double *)alloca(num * sizeof(double)); + auto* ts = (double*)alloca(num * sizeof(double)); + auto* us = (double*)alloca(num * sizeof(double)); + auto* uslog = (double*)alloca(num * sizeof(double)); - auto *b = (double *)alloca(num * sizeof(double)); - auto *c = (double *)alloca(num * sizeof(double)); - auto *d = (double *)alloca(num * sizeof(double)); + auto* b = (double*)alloca(num * sizeof(double)); + auto* c = (double*)alloca(num * sizeof(double)); + auto* d = (double*)alloca(num * sizeof(double)); /* initialize valist for num number of arguments */ va_start(valist, num); @@ -192,13 +189,13 @@ double Dspline(int id, double t, int num, ...) { double ss; double dudt; - double *ts = (double *)alloca(num * sizeof(double)); - double *us = (double *)alloca(num * sizeof(double)); - double *ps = (double *)alloca(num * sizeof(double)); + double* ts = (double*)alloca(num * sizeof(double)); + double* us = (double*)alloca(num * sizeof(double)); + double* ps = (double*)alloca(num * sizeof(double)); - double *b = (double *)alloca(num * sizeof(double)); - double *c = (double *)alloca(num * sizeof(double)); - double *d = (double *)alloca(num * sizeof(double)); + double* b = (double*)alloca(num * sizeof(double)); + double* c = (double*)alloca(num * sizeof(double)); + double* d = (double*)alloca(num * sizeof(double)); int did = id / 2 - 2; @@ -228,14 +225,14 @@ double Dspline_pos(int id, double t, int num, ...) { va_list valist; - auto *ts = (double *)alloca(num * sizeof(double)); - auto *us = (double *)alloca(num * sizeof(double)); - auto *sus = (double *)alloca(num * sizeof(double)); - auto *uslog = (double *)alloca(num * sizeof(double)); + auto* ts = (double*)alloca(num * sizeof(double)); + auto* us = (double*)alloca(num * sizeof(double)); + auto* sus = (double*)alloca(num * sizeof(double)); + auto* uslog = (double*)alloca(num * sizeof(double)); - auto *b = (double *)alloca(num * sizeof(double)); - auto *c = (double *)alloca(num * sizeof(double)); - auto *d = (double *)alloca(num * sizeof(double)); + auto* b = (double*)alloca(num * sizeof(double)); + auto* c = (double*)alloca(num * sizeof(double)); + auto* d = (double*)alloca(num * sizeof(double)); double uout; double ss; @@ -272,21 +269,23 @@ double Dspline_pos(int id, double t, int num, ...) { return uout; } -double DDspline(int /*id1*/, int /*id2*/, double /*t*/, int /*num*/, ...) { return 0.0; } +double DDspline(int /*id1*/, int /*id2*/, double /*t*/, int /*num*/, ...) { + return 0.0; +} double DDspline_pos(int id1, int id2, double t, int num, ...) { va_list valist; - auto *ts = (double *)alloca(num * sizeof(double)); - auto *us = (double *)alloca(num * sizeof(double)); - auto *sus1 = (double *)alloca(num * sizeof(double)); - auto *sus2 = (double *)alloca(num * sizeof(double)); - auto *uslog = (double *)alloca(num * sizeof(double)); + auto* ts = (double*)alloca(num * sizeof(double)); + auto* us = (double*)alloca(num * sizeof(double)); + auto* sus1 = (double*)alloca(num * sizeof(double)); + auto* sus2 = (double*)alloca(num * sizeof(double)); + auto* uslog = (double*)alloca(num * sizeof(double)); - auto *b = (double *)alloca(num * sizeof(double)); - auto *c = (double *)alloca(num * sizeof(double)); - auto *d = (double *)alloca(num * sizeof(double)); + auto* b = (double*)alloca(num * sizeof(double)); + auto* c = (double*)alloca(num * sizeof(double)); + auto* d = (double*)alloca(num * sizeof(double)); double uout; double ss; diff --git a/deps/AMICI/src/vector.cpp b/deps/AMICI/src/vector.cpp index 25502e788..78b66d795 100644 --- a/deps/AMICI/src/vector.cpp +++ b/deps/AMICI/src/vector.cpp @@ -1,54 +1,57 @@ #include "amici/vector.h" -#include #include +#include namespace amici { -AmiVector &AmiVector::operator=(AmiVector const &other) { +AmiVector& AmiVector::operator=(AmiVector const& other) { vec_ = other.vec_; synchroniseNVector(); return *this; } -realtype *AmiVector::data() { return vec_.data(); } +realtype* AmiVector::data() { return vec_.data(); } -const realtype *AmiVector::data() const { return vec_.data(); } +realtype const* AmiVector::data() const { return vec_.data(); } N_Vector AmiVector::getNVector() { return nvec_; } const_N_Vector AmiVector::getNVector() const { return nvec_; } -std::vector const &AmiVector::getVector() const { return vec_; } +std::vector const& AmiVector::getVector() const { return vec_; } -int AmiVector::getLength() const { return static_cast(vec_.size()); } +int AmiVector::getLength() const { return gsl::narrow(vec_.size()); } void AmiVector::zero() { set(0.0); } void AmiVector::minus() { - std::transform(vec_.begin(), vec_.end(), - vec_.begin(), std::negate()); + std::transform( + vec_.begin(), vec_.end(), vec_.begin(), std::negate() + ); } void AmiVector::set(realtype val) { std::fill(vec_.begin(), vec_.end(), val); } -realtype &AmiVector::operator[](int pos) { - return vec_.at(static_cast(pos)); +realtype& AmiVector::operator[](int pos) { + return vec_.at(gsl::narrow(pos)); } -realtype &AmiVector::at(int pos) { - return vec_.at(static_cast(pos)); +realtype& AmiVector::at(int pos) { + return vec_.at(gsl::narrow(pos)); } -const realtype &AmiVector::at(int pos) const { - return vec_.at(static_cast(pos)); +realtype const& AmiVector::at(int pos) const { + return vec_.at(gsl::narrow(pos)); } -void AmiVector::copy(const AmiVector &other) { - if(getLength() != other.getLength()) - throw AmiException("Dimension of AmiVector (%i) does not " - "match input dimension (%i)", - getLength(), other.getLength()); +void AmiVector::copy(AmiVector const& other) { + if (getLength() != other.getLength()) + throw AmiException( + "Dimension of AmiVector (%i) does not " + "match input dimension (%i)", + getLength(), other.getLength() + ); std::copy(other.vec_.begin(), other.vec_.end(), vec_.begin()); synchroniseNVector(); } @@ -56,7 +59,7 @@ void AmiVector::copy(const AmiVector &other) { void AmiVector::synchroniseNVector() { if (nvec_) N_VDestroy_Serial(nvec_); - nvec_ = N_VMake_Serial(static_cast(vec_.size()), vec_.data()); + nvec_ = N_VMake_Serial(gsl::narrow(vec_.size()), vec_.data()); } AmiVector::~AmiVector() { @@ -72,7 +75,7 @@ AmiVectorArray::AmiVectorArray(long int length_inner, long int length_outer) } } -AmiVectorArray &AmiVectorArray::operator=(AmiVectorArray const &other) { +AmiVectorArray& AmiVectorArray::operator=(AmiVectorArray const& other) { vec_array_ = other.vec_array_; nvec_array_.resize(other.getLength()); for (int idx = 0; idx < other.getLength(); idx++) { @@ -81,7 +84,7 @@ AmiVectorArray &AmiVectorArray::operator=(AmiVectorArray const &other) { return *this; } -AmiVectorArray::AmiVectorArray(const AmiVectorArray &vaold) +AmiVectorArray::AmiVectorArray(AmiVectorArray const& vaold) : vec_array_(vaold.vec_array_) { nvec_array_.resize(vaold.getLength()); for (int idx = 0; idx < vaold.getLength(); idx++) { @@ -89,51 +92,55 @@ AmiVectorArray::AmiVectorArray(const AmiVectorArray &vaold) } } -realtype *AmiVectorArray::data(int pos) { return vec_array_.at(pos).data(); } +realtype* AmiVectorArray::data(int pos) { return vec_array_.at(pos).data(); } -const realtype *AmiVectorArray::data(int pos) const { +realtype const* AmiVectorArray::data(int pos) const { return vec_array_.at(pos).data(); } -realtype &AmiVectorArray::at(int ipos, int jpos) { +realtype& AmiVectorArray::at(int ipos, int jpos) { return vec_array_.at(jpos).at(ipos); } -const realtype &AmiVectorArray::at(int ipos, int jpos) const { +realtype const& AmiVectorArray::at(int ipos, int jpos) const { return vec_array_.at(jpos).at(ipos); } -N_Vector *AmiVectorArray::getNVectorArray() { return nvec_array_.data(); } +N_Vector* AmiVectorArray::getNVectorArray() { return nvec_array_.data(); } N_Vector AmiVectorArray::getNVector(int pos) { return nvec_array_.at(pos); } -const_N_Vector AmiVectorArray::getNVector(int pos) const { return nvec_array_.at(pos); } +const_N_Vector AmiVectorArray::getNVector(int pos) const { + return nvec_array_.at(pos); +} -AmiVector &AmiVectorArray::operator[](int pos) { return vec_array_.at(pos); } +AmiVector& AmiVectorArray::operator[](int pos) { return vec_array_.at(pos); } -const AmiVector &AmiVectorArray::operator[](int pos) const { +AmiVector const& AmiVectorArray::operator[](int pos) const { return vec_array_.at(pos); } int AmiVectorArray::getLength() const { - return static_cast(vec_array_.size()); + return gsl::narrow(vec_array_.size()); } void AmiVectorArray::zero() { - for (auto &v : vec_array_) + for (auto& v : vec_array_) v.zero(); } -void AmiVectorArray::flatten_to_vector(std::vector &vec) const { - int n_outer = static_cast(vec_array_.size()); +void AmiVectorArray::flatten_to_vector(std::vector& vec) const { + int n_outer = gsl::narrow(vec_array_.size()); if (n_outer == 0) return; // nothing to do ... int n_inner = vec_array_.at(0).getLength(); - if (static_cast(vec.size()) != n_inner * n_outer) { - throw AmiException("Dimension of AmiVectorArray (%ix%i) does not " - "match target vector dimension (%u)", - n_inner, n_outer, vec.size()); + if (gsl::narrow(vec.size()) != n_inner * n_outer) { + throw AmiException( + "Dimension of AmiVectorArray (%ix%i) does not " + "match target vector dimension (%u)", + n_inner, n_outer, vec.size() + ); } for (int outer = 0; outer < n_outer; ++outer) { @@ -142,11 +149,13 @@ void AmiVectorArray::flatten_to_vector(std::vector &vec) const { } } -void AmiVectorArray::copy(const AmiVectorArray &other) { +void AmiVectorArray::copy(AmiVectorArray const& other) { if (getLength() != other.getLength()) - throw AmiException("Dimension of AmiVectorArray (%i) does not " - "match input dimension (%i)", - getLength(), other.getLength()); + throw AmiException( + "Dimension of AmiVectorArray (%i) does not " + "match input dimension (%i)", + getLength(), other.getLength() + ); for (int iv = 0; iv < getLength(); ++iv) { vec_array_.at(iv).copy(other.vec_array_.at(iv)); diff --git a/deps/AMICI/src/wrapfunctions.template.cpp b/deps/AMICI/src/wrapfunctions.template.cpp index bb363c0c9..e7ab71ddb 100644 --- a/deps/AMICI/src/wrapfunctions.template.cpp +++ b/deps/AMICI/src/wrapfunctions.template.cpp @@ -1,16 +1,16 @@ -#include "amici/model.h" #include "wrapfunctions.h" #include "TPL_MODELNAME.h" +#include "amici/model.h" namespace amici { namespace generic_model { std::unique_ptr getModel() { return std::unique_ptr( - new amici::model_TPL_MODELNAME::Model_TPL_MODELNAME()); + new amici::model_TPL_MODELNAME::Model_TPL_MODELNAME() + ); } - } // namespace generic_model } // namespace amici diff --git a/deps/AMICI/src/wrapfunctions.ODE_template.h b/deps/AMICI/src/wrapfunctions.template.h similarity index 100% rename from deps/AMICI/src/wrapfunctions.ODE_template.h rename to deps/AMICI/src/wrapfunctions.template.h diff --git a/deps/AMICI/swig/CMakeLists.txt b/deps/AMICI/swig/CMakeLists.txt index 3705441e4..87d607d33 100644 --- a/deps/AMICI/swig/CMakeLists.txt +++ b/deps/AMICI/swig/CMakeLists.txt @@ -3,17 +3,23 @@ # # Use most recent SWIG version available -SET(CMAKE_FIND_PACKAGE_SORT_DIRECTION DEC) +set(CMAKE_FIND_PACKAGE_SORT_DIRECTION DEC) find_package(SWIG REQUIRED) set(SWIG_VERSION_MIN "3.0") -if (${SWIG_VERSION} VERSION_LESS ${SWIG_VERSION_MIN}) - message(FATAL_ERROR "Requiring SWIG>=${SWIG_VERSION_MIN} " - "but found only ${SWIG_VERSION}.") +if(${SWIG_VERSION} VERSION_LESS ${SWIG_VERSION_MIN}) + message(FATAL_ERROR "Requiring SWIG>=${SWIG_VERSION_MIN} " + "but found only ${SWIG_VERSION}.") endif() -include(${SWIG_USE_FILE}) +if(DEFINED ENV{PYTHON_EXECUTABLE}) + set(Python3_EXECUTABLE $ENV{PYTHON_EXECUTABLE}) +endif() +find_package( + Python3 + COMPONENTS Interpreter Development NumPy + REQUIRED) set(AMICI_INTERFACE_LIST ${CMAKE_CURRENT_SOURCE_DIR}/amici.i ${CMAKE_CURRENT_SOURCE_DIR}/edata.i @@ -28,8 +34,97 @@ set(AMICI_INTERFACE_LIST ${CMAKE_CURRENT_SOURCE_DIR}/std_unique_ptr.i ${CMAKE_CURRENT_SOURCE_DIR}/hdf5.i ${CMAKE_CURRENT_SOURCE_DIR}/abstract_model.i - ${CMAKE_CURRENT_SOURCE_DIR}/stdvec2numpy.h -) + ${CMAKE_CURRENT_SOURCE_DIR}/stdvec2numpy.h) # Add target to show files in IDE -add_custom_target(swigInterface SOURCES ${AMICI_INTERFACE_LIST}) +add_custom_target( + swigInterface + SOURCES ${AMICI_INTERFACE_LIST} + COMMENT "Dummy target for SWIG files.") + +# https://cmake.org/cmake/help/latest/module/UseSWIG.html +include(${SWIG_USE_FILE}) + +set(UseSWIG_TARGET_NAME_PREFERENCE STANDARD) +set_property(SOURCE amici.i PROPERTY CPLUSPLUS ON) +set_property(SOURCE amici.i PROPERTY OUTPUT_NAME _amici) + +swig_add_library( + _amici + LANGUAGE Python + SOURCES amici.i + TYPE MODULE) + +set_target_properties( + _amici + PROPERTIES SWIG_USE_TARGET_INCLUDE_DIRECTORIES TRUE + PREFIX "" + BUILD_RPATH_USE_ORIGIN TRUE + INSTALL_RPATH ThirdParty/SuiteSparse/lib + BUILD_RPATH ThirdParty/SuiteSparse/lib) + +get_target_property(AMICI_HEADERS amici PUBLIC_HEADER) +file(GLOB SWIG_FILES *.i *.h) + +set_property(TARGET _amici PROPERTY SWIG_DEPENDS ${AMICI_HEADERS} ${SWIG_FILES}) + +# Python extension suffix +execute_process( + COMMAND ${Python3_EXECUTABLE} -c + "import sysconfig; print(sysconfig.get_config_var('EXT_SUFFIX'))" + OUTPUT_VARIABLE PY_EXT_SUFFIX + OUTPUT_STRIP_TRAILING_WHITESPACE) +if(NOT "${PY_EXT_SUFFIX}" STREQUAL "") + message(STATUS "Python extension suffix is ${PY_EXT_SUFFIX}") + set_target_properties(_amici PROPERTIES SUFFIX "${PY_EXT_SUFFIX}") +endif() + +target_include_directories( + _amici + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../include + ${Python3_NumPy_INCLUDE_DIRS}) + +set_property( + TARGET _amici + APPEND + PROPERTY SWIG_COMPILE_OPTIONS -threads -Wall) + +if(NOT HDF5_FOUND) + set_target_properties(_amici PROPERTIES SWIG_COMPILE_DEFINITIONS + AMICI_SWIG_WITHOUT_HDF5) +endif() + +if(${SWIG_VERSION} VERSION_GREATER_EQUAL 4.0.0) + set_property( + TARGET _amici + APPEND + PROPERTY SWIG_COMPILE_OPTIONS -doxygen) +else() + set_property( + TARGET _amici + APPEND + PROPERTY SWIG_COMPILE_OPTIONS -modern) +endif() +if(${SWIG_VERSION} VERSION_LESS 4.1.0) + set_property( + TARGET _amici + APPEND + 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. +target_link_libraries(_amici amici Python3::Python) +if(WIN32) + add_custom_command( + TARGET _amici + POST_BUILD + COMMAND dumpbin "/DEPENDENTS" "$" + COMMENT "Dumping extension dependencies.") + message("Dependencies: ${dump}") +endif() + +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/amici.py $ + DESTINATION .) diff --git a/deps/AMICI/swig/CMakeLists_model.cmake b/deps/AMICI/swig/CMakeLists_model.cmake index af0f64a76..eb9fe681c 100644 --- a/deps/AMICI/swig/CMakeLists_model.cmake +++ b/deps/AMICI/swig/CMakeLists_model.cmake @@ -1,28 +1,19 @@ -cmake_minimum_required(VERSION 3.8) # swig_add_library - -if(POLICY CMP0078) - cmake_policy(SET CMP0078 OLD) -endif(POLICY CMP0078) -if(POLICY CMP0074) - # Use package_ROOT environment variables - cmake_policy(SET CMP0074 NEW) -endif(POLICY CMP0074) -if(POLICY CMP0086) - cmake_policy(SET CMP0086 NEW) -endif(POLICY CMP0086) +cmake_minimum_required(VERSION 3.15) +if(DEFINED ENV{SWIG}) + set(SWIG_EXECUTABLE $ENV{SWIG}) +endif() find_package(SWIG REQUIRED) include(${SWIG_USE_FILE}) -if(${CMAKE_VERSION} VERSION_LESS "3.12.0") - find_package(PythonLibs REQUIRED) - include_directories(${PYTHON_INCLUDE_DIRS}) - set(Python3_LIBRARIES ${PYTHON_LIBRARIES}) -else() - find_package (Python3 COMPONENTS Development) - include_directories(${Python3_INCLUDE_DIRS}) +if(DEFINED ENV{PYTHON_EXECUTABLE}) + set(Python3_EXECUTABLE $ENV{PYTHON_EXECUTABLE}) endif() +# We don't need "Interpreter" here, but without that, FindPython3 will +# ignore the Python version selected via $Python3_EXECUTABLE +find_package(Python3 COMPONENTS Interpreter Development) +include_directories(${Python3_INCLUDE_DIRS}) set(SWIG_LIBRARY_NAME _${PROJECT_NAME}) set(CMAKE_SWIG_FLAGS "") @@ -30,17 +21,38 @@ set_source_files_properties(${PROJECT_NAME}.i PROPERTIES CPLUSPLUS ON) # swig does not use INTERFACE_INCLUDE_DIRS of linked libraries, so add manually get_target_property(AMICI_INCLUDE_DIRS Upstream::amici INTERFACE_INCLUDE_DIRECTORIES) -include_directories(${AMICI_INCLUDE_DIRS} ..) +include_directories(${AMICI_INCLUDE_DIRS} .. ${AMICI_INCLUDE_DIRS}/../swig) -swig_add_library(${PROJECT_NAME} +swig_add_library(${SWIG_LIBRARY_NAME} TYPE MODULE LANGUAGE python SOURCES ${PROJECT_NAME}.i) -swig_link_libraries(${PROJECT_NAME} + +set_target_properties(${SWIG_LIBRARY_NAME} + PROPERTIES + SWIG_USE_TARGET_INCLUDE_DIRECTORIES TRUE + PREFIX "" +) + +# Python extension suffix +execute_process( + COMMAND ${Python3_EXECUTABLE} -c + "import sysconfig; print(sysconfig.get_config_var('EXT_SUFFIX'))" + OUTPUT_VARIABLE PY_EXT_SUFFIX OUTPUT_STRIP_TRAILING_WHITESPACE) +if(NOT "${PY_EXT_SUFFIX}" STREQUAL "") + message(STATUS "Python extension suffix is ${PY_EXT_SUFFIX}") + set_target_properties(${SWIG_LIBRARY_NAME} PROPERTIES SUFFIX "${PY_EXT_SUFFIX}" ) +endif() + + +swig_link_libraries(${SWIG_LIBRARY_NAME} ${Python3_LIBRARIES} model) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.py + $ DESTINATION .) + # configure module setup script set(SETUP_PY_IN ${Amici_DIR}/model_setup.template.py) set(SETUP_PY_OUT ${CMAKE_CURRENT_BINARY_DIR}/setup.py) diff --git a/deps/AMICI/swig/abstract_model.i b/deps/AMICI/swig/abstract_model.i index 73cbe9139..47a0dfb99 100644 --- a/deps/AMICI/swig/abstract_model.i +++ b/deps/AMICI/swig/abstract_model.i @@ -62,5 +62,11 @@ %ignore fx0_fixedParameters; %ignore fsx0; %ignore fsx0_fixedParameters; - +%ignore fdx_rdatadtcl_colptrs; +%ignore fdx_rdatadtcl_rowvals; +%ignore fdx_rdatadx_solver_colptrs; +%ignore fdx_rdatadx_solver_rowvals; +%ignore fdtotal_cldx_rdata_colptrs; +%ignore fdtotal_cldx_rdata_rowvals; +%ignore fcreate_splines; %include "amici/abstract_model.h" diff --git a/deps/AMICI/swig/amici.i b/deps/AMICI/swig/amici.i index 09b74411b..9eac8e504 100644 --- a/deps/AMICI/swig/amici.i +++ b/deps/AMICI/swig/amici.i @@ -1,4 +1,56 @@ -%module amici +%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 +``amici.ExpData``. +Usage of functions and classes from the base ``amici`` package is +generally recommended as they often include convenience wrappers that avoid +common pitfalls when accessing C++ types from python and implement some +nonstandard type conversions. +""" +%enddef +%module (docstring=DOCSTRING) amici + +// typemaps for docstrings +%typemap(doctype) std::unique_ptr< amici::ExpData >::pointer "ExpData"; +%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"; +%typemap(doctype) std::vector< int,std::allocator< int > > "IntVector"; +%typemap(doctype) std::vector< amici::ParameterScaling,std::allocator< amici::ParameterScaling > > "ParameterScalingVector"; +%typemap(doctype) std::vector< std::string,std::allocator< std::string > > "StringVector"; +%typemap(doctype) std::vector< bool,std::allocator< bool > > "BoolVector"; +%typemap(doctype) std::map< std::string,amici::realtype,std::less< std::string >, std::allocator< std::pair< std::string const,amici::realtype > > > "StringDoubleMap"; +%typemap(doctype) std::vector< amici::ExpData *,std::allocator< amici::ExpData * > > "ExpDataPtrVector"; +%typemap(doctype) std::vector< std::unique_ptr< amici::ReturnData >,std::allocator< std::unique_ptr< amici::ReturnData > > > "Iterable[ReturnData]"; +%typemap(doctype) void "None"; +%typemap(doctype) std::unique_ptr< amici::Solver > "Solver"; +%typemap(doctype) amici::InternalSensitivityMethod "InternalSensitivityMethod"; +%typemap(doctype) amici::InterpolationType "InterpolationType"; +%typemap(doctype) amici::LinearMultistepMethod "LinearMultistepMethod"; +%typemap(doctype) amici::LinearSolver "LinearSolver"; +%typemap(doctype) amici::Model * "Model"; +%typemap(doctype) amici::Model const * "Model"; +%typemap(doctype) amici::NewtonDampingFactorMode "NewtonDampingFactorMode"; +%typemap(doctype) amici::NonlinearSolverIteration "NonlinearSolverIteration"; +%typemap(doctype) amici::RDataReporting "RDataReporting"; +%typemap(doctype) amici::SensitivityMethod "SensitivityMethod"; +%typemap(doctype) amici::SensitivityOrder "SensitivityOrder"; +%typemap(doctype) amici::Solver * "Solver"; +%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) std::string "str"; +%typemap(doctype) std::string const & "str"; +%typemap(doctype) std::unique_ptr< amici::ExpData > "ExpData"; +%typemap(doctype) std::unique_ptr< amici::ReturnData > "ReturnData"; +%typemap(doctype) size_t "int"; + %include %exception { @@ -11,6 +63,9 @@ } } +// Warning 503: Can't wrap 'operator ==' unless renamed to a valid identifier. +%rename("__eq__") operator ==; + %{ #define SWIG_FILE_WITH_INIT #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION @@ -27,10 +82,48 @@ import_array(); // Expose vectors %include %template(DoubleVector) std::vector; +%extend std::vector { +%pythoncode %{ +def __repr__(self): + return self.this.__repr__()[:-1] + '; ' + repr(np.asarray(self, dtype=np.float64)) + ' >' + +%} +}; %template(IntVector) std::vector; +%extend std::vector { +%pythoncode %{ +def __repr__(self): + return self.this.__repr__()[:-1] + '; ' + repr(np.asarray(self, dtype=np.int64)) + ' >' + +%} +}; %template(BoolVector) std::vector; +%extend std::vector { +%pythoncode %{ +def __repr__(self): + return self.this.__repr__()[:-1] + '; ' + repr(np.asarray(self, dtype=np.bool_)) + ' >' + +%} +}; %template(StringVector) std::vector; +%extend std::vector { +%pythoncode %{ +def __repr__(self): + return self.this.__repr__()[:-1] + '; ' + repr(list(self)) + ' >' + +%} +}; +%feature("docstring") std::map +"Swig-Generated class templating :class:`Dict` +[:class:`str`, :class:`float`] to facilitate interfacing with C++ bindings."; %template(StringDoubleMap) std::map; +%extend std::map { +%pythoncode %{ +def __repr__(self): + return self.this.__repr__()[:-1] + '; ' + repr(dict(self)) + ' >' + +%} +}; // Let numpy access std::vector %{ @@ -61,10 +154,15 @@ wrap_unique_ptr(ExpDataPtr, amici::ExpData) %naturalvar amici::SimulationParameters::reinitialization_state_idxs_sim; %naturalvar amici::SimulationParameters::reinitialization_state_idxs_presim; +// DO NOT IGNORE amici::SimulationParameters, amici::ModelDimensions, amici::CpuTimer %ignore amici::ModelContext; %ignore amici::ContextManager; %ignore amici::ModelState; %ignore amici::ModelStateDerived; +%ignore amici::unravel_index; +%ignore amici::backtraceString; +%ignore amici::Logger; +%ignore amici::SimulationState; // Include before any other header which uses enums defined there %include "amici/defines.h" @@ -82,6 +180,7 @@ wrap_unique_ptr(ExpDataPtr, amici::ExpData) %include model.i %include model_ode.i %include model_dae.i +%include logging.i %include rdata.i #ifndef AMICI_SWIG_WITHOUT_HDF5 @@ -98,9 +197,6 @@ wrap_unique_ptr(ExpDataPtr, amici::ExpData) %} // Add necessary symbols to generated header -// Ignore due to https://github.com/swig/swig/issues/1643 -%ignore amici::AmiciApplication::warningF; -%ignore amici::AmiciApplication::errorF; %{ #include "amici/amici.h" using namespace amici; @@ -109,17 +205,44 @@ using namespace amici; // Prevent using ValueWrapper, but don't expose unique_ptr vector %ignore std::vector>; %template(ReturnDataPtrVector) std::vector>; +%extend std::vector> { +%pythoncode %{ +def __repr__(self): + return self.this.__repr__()[:-1] + '; ' + repr(list(self)) + ' >' + +%} +}; // Process symbols in header %include "amici/amici.h" // Expose vectors %template(ExpDataPtrVector) std::vector; +%extend std::vector { +%pythoncode %{ +def __repr__(self): + return self.this.__repr__()[:-1] + '; ' + repr(list(self)) + ' >' + +%} +}; +%template(LogItemVector) std::vector; +%extend std::vector { +%pythoncode %{ +def __repr__(self): + return self.this.__repr__()[:-1] + '; ' + repr(list(self)) + ' >' + +%} +}; // Convert integer values to enum class // defeats the purpose of enum class, but didn't find a better way to allow for // vectors of enum class types in python +%feature("docstring") parameterScalingFromIntVector +"Swig-Generated class, which, in contrast to other Vector +classes, does not allow for simple interoperability with common +Python types, but must be created using +:func:`amici.amici.parameterScalingFromIntVector`"; %{ namespace amici { std::vector parameterScalingFromIntVector(std::vector const& intVec) { @@ -145,9 +268,18 @@ namespace amici { void Model::setParameterScale(std::vector const& intVec); } %template(ParameterScalingVector) std::vector; +%extend std::vector { +%pythoncode %{ +def __repr__(self): + return self.this.__repr__()[:-1] + '; ' + repr(list(self)) + ' >' + +%} +}; // Add function to check if amici was compiled with OpenMP +%feature("docstring") compiledWithOpenMP + "AMICI extension was compiled with OpenMP?"; %{ namespace amici { /** AMICI extension was compiled with OpenMP? */ @@ -180,6 +312,7 @@ InternalSensitivityMethod = enum('InternalSensitivityMethod') InterpolationType = enum('InterpolationType') LinearMultistepMethod = enum('LinearMultistepMethod') NonlinearSolverIteration = enum('NonlinearSolverIteration') +SteadyStateComputationMode = enum('SteadyStateComputationMode') SteadyStateSensitivityMode = enum('SteadyStateSensitivityMode') SteadyStateStatus = enum('SteadyStateStatus') NewtonDampingFactorMode = enum('NewtonDampingFactorMode') @@ -188,21 +321,30 @@ RDataReporting = enum('RDataReporting') %} %template(SteadyStateStatusVector) std::vector; +%extend std::vector { +%pythoncode %{ +def __repr__(self): + return self.this.__repr__()[:-1] + '; ' + repr(list(self)) + ' >' -// add module docstring and import additional types for typehints +%} +}; + +// Handle AMICI_DLL_DIRS environment variable %pythonbegin %{ -""" -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 -``amici.ExpData``. -Usage of functions and classes from the base ``amici`` package is -generally recommended as they often include convenience wrappers that avoid -common pitfalls when accessing C++ types from python and implement some -nonstandard type conversions. -""" +import sys +import os -from typing import Iterable +if sys.platform == 'win32' and (dll_dirs := os.environ.get('AMICI_DLL_DIRS')): + for dll_dir in dll_dirs.split(os.pathsep): + os.add_dll_directory(dll_dir) + +%} + +// import additional types for typehints +// also import np for use in __repr__ functions +%pythonbegin %{ +from typing import TYPE_CHECKING, Iterable, List, Tuple, Sequence +import numpy as np +if TYPE_CHECKING: + import numpy %} diff --git a/deps/AMICI/swig/edata.i b/deps/AMICI/swig/edata.i index ec6fe7d55..f2f7d0da8 100644 --- a/deps/AMICI/swig/edata.i +++ b/deps/AMICI/swig/edata.i @@ -8,5 +8,89 @@ using namespace amici; %ignore ConditionContext; +// ExpData.__repr__ +%pythoncode %{ +def _edata_repr(self: "ExpData"): + n_data_y = sum( + self.isSetObservedData(it, iy) + for it in range(self.nt()) for + iy in range(self.nytrue()) + ) + n_sigma_y = sum( + self.isSetObservedDataStdDev(it, iy) + for it in range(self.nt()) + for iy in range(self.nytrue()) + ) + n_data_z = sum( + self.isSetObservedEvents(ie, iz) + for ie in range(self.nmaxevent()) + for iz in range(self.nztrue()) + ) + n_sigma_z = sum( + self.isSetObservedEventsStdDev(ie, iz) + for ie in range(self.nmaxevent()) + for iz in range(self.nztrue()) + ) + + custom_simulation_settings = [] + if self.pscale: + custom_simulation_settings.append(f"parameter scales") + if self.fixedParameters: + custom_simulation_settings.append(f"constants") + if self.fixedParametersPreequilibration: + custom_simulation_settings.append(f"pre-equilibration condition") + if self.t_presim: + tmp = f"pre-simulation condition (t={self.t_presim})" + if self.fixedParametersPresimulation: + tmp += " with custom constants" + custom_simulation_settings.append(tmp) + if self.reinitializeFixedParameterInitialStates and self.reinitialization_state_idxs_sim: + custom_simulation_settings.append(f"{len(self.reinitialization_state_idxs_sim)} reinitialized states (simulation)") + if self.reinitializeFixedParameterInitialStates and self.reinitialization_state_idxs_presim: + custom_simulation_settings.append(f"{len(self.reinitialization_state_idxs_presim)} reinitialized states (presimulation)") + if self.parameters: + custom_simulation_settings.append(f"parameters") + if self.x0: + custom_simulation_settings.append(f"initial states") + if self.sx0: + custom_simulation_settings.append(f"initial state sensitivities") + + if custom_simulation_settings: + custom_simulation_settings = " with custom " + ", ".join(custom_simulation_settings) + else: + custom_simulation_settings = " without custom settings" + + return "\n".join([ + self.this.__repr__()[:-1], + f" condition '{self.id}' starting at t={self.tstart_}" + custom_simulation_settings, + f" {self.nt()}x{self.nytrue()} time-resolved datapoints", + f" ({n_data_y}/{self.nt()*self.nytrue()} measurements & {n_sigma_y}/{self.nt()*self.nytrue()} sigmas set)", + f" {self.nmaxevent()}x{self.nztrue()} event-resolved datapoints", + f" ({n_data_z}/{self.nmaxevent()*self.nztrue()} measurements & {n_sigma_z}/{self.nmaxevent()*self.nztrue()} sigmas set)", + ">" + ]) +%} +%extend amici::ExpData { +%pythoncode %{ +def __repr__(self): + return _edata_repr(self) + +def __eq__(self, other): + return other.__class__ == self.__class__ and __eq__(self, other) + +def __deepcopy__(self, memo): + # invoke copy constructor + return type(self)(self) + +%} +}; +%extend std::unique_ptr { +%pythoncode %{ +def __repr__(self): + return _edata_repr(self) +%} +}; + + // Process symbols in header %include "amici/edata.h" diff --git a/deps/AMICI/swig/logging.i b/deps/AMICI/swig/logging.i new file mode 100644 index 000000000..2c96882f9 --- /dev/null +++ b/deps/AMICI/swig/logging.i @@ -0,0 +1,11 @@ +%module logging + +%nodefaultctor amici::LogItem; + +// Add necessary symbols to generated header +%{ +#include "amici/logging.h" +%} + +// Process symbols in header +%include "amici/logging.h" diff --git a/deps/AMICI/swig/model.i b/deps/AMICI/swig/model.i index 81466a5c3..3063590c2 100644 --- a/deps/AMICI/swig/model.i +++ b/deps/AMICI/swig/model.i @@ -32,7 +32,7 @@ using namespace amici; %ignore getObservable; %ignore getObservableSensitivity; %ignore getExpression; -%ignore initHeaviside; +%ignore initEvents; %ignore initialize; %ignore initializeB; %ignore initializeStateSensitivities; @@ -84,6 +84,16 @@ using namespace amici; %ignore getObservableSigma; %ignore getObservableSigmaSensitivity; %ignore getUnobservedEventSensitivity; +%ignore fdsigmaydy; +%ignore fdspline_slopesdp; +%ignore fdspline_valuesdp; +%ignore fdtotal_cldp; +%ignore fdtotal_cldx_rdata; +%ignore fdx_rdatadp; +%ignore fdx_rdatadtcl; +%ignore fdx_rdatadx_solver; +%ignore fdsigmaydy; + diff --git a/deps/AMICI/swig/model_ode.i b/deps/AMICI/swig/model_ode.i index de342fef9..a372c1efa 100644 --- a/deps/AMICI/swig/model_ode.i +++ b/deps/AMICI/swig/model_ode.i @@ -15,5 +15,3 @@ using namespace amici; // Process symbols in header %include "amici/model_ode.h" - - diff --git a/deps/AMICI/swig/modelname.template.i b/deps/AMICI/swig/modelname.template.i index 3425d78a1..69015dc79 100644 --- a/deps/AMICI/swig/modelname.template.i +++ b/deps/AMICI/swig/modelname.template.i @@ -10,5 +10,13 @@ using namespace amici; %} +// Make model module accessible from the model +%feature("pythonappend") amici::generic_model::getModel %{ + if '.' in __name__: + import sys + val.module = sys.modules['.'.join(__name__.split('.')[:-1])] +%} + + // Process symbols in header %include "wrapfunctions.h" diff --git a/deps/AMICI/swig/solver.i b/deps/AMICI/swig/solver.i index 853edae9b..992842c40 100644 --- a/deps/AMICI/swig/solver.i +++ b/deps/AMICI/swig/solver.i @@ -39,6 +39,82 @@ using namespace amici; %ignore turnOffRootFinding; %ignore getRootInfo; %ignore updateAndReinitStatesAndSensitivities; +%ignore getCpuTime; +%ignore getCpuTimeB; +%ignore getLastOrder; +%ignore getNumErrTestFails; +%ignore getNumErrTestFailsB; +%ignore getNumNonlinSolvConvFails; +%ignore getNumNonlinSolvConvFailsB; +%ignore getNumRhsEvals; +%ignore getNumRhsEvalsB; +%ignore getNumSteps; +%ignore getNumStepsB; +%ignore gett; +%ignore startTimer; +%ignore switchForwardSensisOff; +%ignore timeExceeded; + +// Solver.__repr__ +%pythoncode %{ +def _solver_repr(self: "Solver"): + return "\n".join([ + self.this.__repr__()[:-1], + " reporting_mode: " + f"{RDataReporting(self.getReturnDataReportingMode())!r}", + f" sens_meth: {SensitivityMethod(self.getSensitivityMethod())!r}", + f" sens_order: {SensitivityOrder(self.getSensitivityOrder())!r}", + f" sens_meth_preeq: {SensitivityMethod(self.getSensitivityMethodPreequilibration())!r}", + f" maxsteps: {self.getMaxSteps()}", + f" maxtime: {self.getMaxTime()}s", + f" abs_tol: {self.getAbsoluteTolerance()}", + f" rel_tol: {self.getRelativeTolerance()}", + f" abs_tol_b: {self.getAbsoluteToleranceB()}", + f" rel_tol_b: {self.getRelativeToleranceB()}", + f" abs_tol_fsa: {self.getAbsoluteToleranceFSA()}", + f" rel_tol_fsa: {self.getRelativeToleranceFSA()}", + f" abs_tol_quad: {self.getAbsoluteToleranceQuadratures()}", + f" rel_tol_quad: {self.getRelativeToleranceQuadratures()}", + f" abs_tol_ss: {self.getAbsoluteToleranceSteadyState()}", + f" rel_tol_ss: {self.getRelativeToleranceSteadyState()}", + f" abs_tol_sss: {self.getAbsoluteToleranceSteadyStateSensi()}", + f" rel_tol_sss: {self.getRelativeToleranceSteadyStateSensi()}", + f" int_sens_meth: {InternalSensitivityMethod(self.getInternalSensitivityMethod())!r}", + f" int_type: {InterpolationType(self.getInterpolationType())!r}", + f" linsol: {LinearSolver(self.getLinearSolver())!r}", + f" lmm: {LinearMultistepMethod(self.getLinearMultistepMethod())!r}", + f" newton_damp_mode: {NewtonDampingFactorMode(self.getNewtonDampingFactorMode())!r}", + f" newton_damp_lb: {self.getNewtonDampingFactorLowerBound()}", + f" newton_maxsteps: {self.getNewtonMaxSteps()}", + f" newton_ss_check: {self.getNewtonStepSteadyStateCheck()}", + f" sens_ss_check: {self.getSensiSteadyStateCheck()}", + f" interpolation_type: {InterpolationType(self.getInterpolationType())!r}", + f" ism: {InternalSensitivityMethod(self.getInternalSensitivityMethod())!r}", + f" nlsol_iter: {NonlinearSolverIteration(self.getNonlinearSolverIteration())!r}", + f" stability_limit: {self.getStabilityLimitFlag()}", + f" state_ordering: {self.getStateOrdering()}", + ">" + ]) +%} +%extend amici::CVodeSolver { +%pythoncode %{ +def __repr__(self): + return _solver_repr(self) +%} +}; +%extend amici::IDASolver { +%pythoncode %{ +def __repr__(self): + return _solver_repr(self) +%} +}; + +%extend std::unique_ptr { +%pythoncode %{ +def __repr__(self): + return _solver_repr(self) +%} +}; %newobject amici::Solver::clone; diff --git a/deps/AMICI/tests/benchmark-models/benchmark_models.yaml b/deps/AMICI/tests/benchmark-models/benchmark_models.yaml index 49e509f42..8550c9ca3 100644 --- a/deps/AMICI/tests/benchmark-models/benchmark_models.yaml +++ b/deps/AMICI/tests/benchmark-models/benchmark_models.yaml @@ -1,27 +1,42 @@ Bachmann_MSB2011: llh: 418.40573341425295 + t_sim: 0.05 + t_fwd: 3.5 + t_adj: 5.0 note: benchmark collection reference value matches up to sign when applying log10-correction +sum(log(meas*log(10)) / 2 -Becker_Science2010: - llh: -364.118614198023 - Beer_MolBioSystems2014: llh: 58622.9145631413 + t_sim: 0.05 + t_fwd: 0.5 + t_adj: 8.5 note: benchmark collection reference parameters do not match petab, but reference llh has been confirmed for parameters reported there up to sign Boehm_JProteomeRes2014: llh: -138.22199693517703 + t_sim: 0.005 + t_fwd: 0.05 + t_adj: 0.05 note: benchmark collection reference ignores factor 1/2 Borghans_BiophysChem1997: llh: 83.3237191357272 + t_sim: 0.005 + t_fwd: 0.5 + t_adj: 0.2 note: benchmark collection reference value matches up to sign when applying log10-correction +sum(log(meas*log(10)) / 2 Brannmark_JBC2010: llh: -141.889113770537 + t_sim: 0.005 + t_fwd: 0.5 + t_adj: 0.5 Bruno_JExpBot2016: llh: 46.688176988431806 + t_sim: 0.005 + t_fwd: 0.05 + t_adj: 0.1 note: benchmark collection only reports chi2 value, but llh value can be derived Chen_MSB2009: @@ -30,56 +45,74 @@ Chen_MSB2009: Crauste_CellSystems2017: llh: -190.96521897435176 + t_sim: 0.005 + t_fwd: 0.05 + t_adj: 0.05 note: benchmark collection only reports chi2 value, but llh value can be derived Elowitz_Nature2000: llh: 63.20279991419332 + t_sim: 0.005 + t_fwd: 0.11 + t_adj: 0.11 note: benchmark collection reference value matches up to sign when applying log10-correction +sum(log(meas*log(10))) / 2 Fiedler_BMC2016: llh: 58.58390161681 + t_sim: 0.005 + t_fwd: 0.05 + t_adj: 0.15 Fujita_SciSignal2010: llh: 53.08377736998929 - -# Hass_PONE2017 None + t_sim: 0.01 + t_fwd: 0.5 + t_adj: 1.0 Isensee_JCB2018: llh: -3949.375966548649 + t_sim: 0.1 + t_fwd: 15 + t_adj: 20 note: benchmark collection reference also includes prior valuation, tested based on agreement of chi2 values Lucarelli_CellSystems2018: llh: -1681.6059879426584 - -Merkle_PCB2016: - llh: -1388.59682706751 - note: unchecked - -Raia_CancerResearch2011: - llh: 690.619495552297 - note: unchecked + t_sim: 0.05 + t_fwd: 2.5 + t_adj: 2.5 Schwen_PONE2014: llh: -943.9992988598723 + t_sim: 0.01 + t_fwd: 0.5 + t_adj: 1.5 note: benchmark collection reference value does not match, but model outputs do. maybe due to priors Sneyd_PNAS2002: llh: 319.79177818768756 + t_sim: 0.005 + t_fwd: 0.1 + t_adj: 0.5 note: benchmark collection reference ignores factor 1/2 -Sobotta_Frontiers2017: - llh: -1346.75391686389 - note: unchecked - -Swameye_PNAS2003: - llh: -142.118024712038 - note: unchecked +Smith_BMCSystBiol2013: + llh: -888198.436541014 + t_sim: 1.0 + t_fwd: 50.0 + t_adj: 5.0 + note: not part of original benchmark collection Weber_BMC2015: llh: -296.2017922646865 + t_sim: 0.005 + t_fwd: 0.1 + t_adj: 0.1 note: benchmark collection reference ignores factor -1/2 Zheng_PNAS2012: llh: 278.33353271001477 + t_sim: 0.005 + t_fwd: 0.05 + t_adj: 0.05 note: benchmark collection reference ignores factor 1/2 - diff --git a/deps/AMICI/tests/benchmark-models/evaluate_benchmark.py b/deps/AMICI/tests/benchmark-models/evaluate_benchmark.py new file mode 100644 index 000000000..0c6e2e412 --- /dev/null +++ b/deps/AMICI/tests/benchmark-models/evaluate_benchmark.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python3 + +""" +Aggregate computation times from different benchmarks and plot +""" +import os + +import matplotlib.pyplot as plt +import pandas as pd +import seaborn as sns + +# read benchmark results for different models + +outfile = "computation_times.csv" +df = pd.concat( + [ + pd.read_csv(f, header=[0], index_col=[0]) + .rename(columns={"0": "_".join(f.split("_")[:2])}) + .T + for f in os.listdir() + if f.endswith(".csv") + if f != outfile + ] +) +df.sort_values("np", inplace=True) + +df.to_csv(outfile) + + +ratios = ( + pd.concat( + [df[sensi] / df["t_sim"].values for sensi in ["t_fwd", "t_adj"]] + + [df.np], + axis=1, + ) + .reset_index() + .melt(id_vars=["index", "np"]) + .rename( + columns={"index": "model", "variable": "sensitivity", "value": "ratio"} + ) +) +ratios["sensitivity"] = ratios["sensitivity"].replace( + {"t_fwd": "forward", "t_adj": "adjoint"} +) + + +plt.figure(figsize=(10, 5)) +g = sns.barplot( + data=ratios, order=list(df.index), x="model", y="ratio", hue="sensitivity" +) +for ir, row in ratios.iterrows(): + if row.sensitivity == "adjoint": + continue + g.text( + ir, + row["np"], + int(row["np"]), + color="black", + ha="center", + weight="bold", + ) + +plt.xticks(rotation=30, horizontalalignment="right") +plt.tight_layout() +plt.savefig("computation_times.png") diff --git a/deps/AMICI/tests/benchmark-models/test_benchmark_collection.sh b/deps/AMICI/tests/benchmark-models/test_benchmark_collection.sh index d8d9e5f2f..6b9284f4e 100755 --- a/deps/AMICI/tests/benchmark-models/test_benchmark_collection.sh +++ b/deps/AMICI/tests/benchmark-models/test_benchmark_collection.sh @@ -20,40 +20,39 @@ Fujita_SciSignal2010 Isensee_JCB2018 Lucarelli_CellSystems2018 Schwen_PONE2014 +Smith_BMCSystBiol2013 Sneyd_PNAS2002 Weber_BMC2015 Zheng_PNAS2012" # -# -# PEtab needs fixing: Bachmann_MSB2011 -# -# Unsupported: -# -# Becker_Science2010: multiple models +# not merged: +# Becker_Science2010 (multiple models) https://github.com/Benchmarking-Initiative/Benchmark-Models-PEtab/tree/model_Becker_Science2010 +# Hass_PONE2017 (???) https://github.com/Benchmarking-Initiative/Benchmark-Models-PEtab/tree/model_Hass_PONE2017 +# Korkut_eLIFE2015 (???) https://github.com/Benchmarking-Initiative/Benchmark-Models-PEtab/tree/model_Korkut_eLIFE2015 +# Casaletto_PNAS2019 (yaml missing) https://github.com/Benchmarking-Initiative/Benchmark-Models-PEtab/tree/model_Casaletto_PNAS2019 +# Merkle_PCB2016 (model missing) https://github.com/Benchmarking-Initiative/Benchmark-Models-PEtab/tree/model_Merkle_PCB2016 +# Parmar_PCB2019 (SBML extensions) https://github.com/Benchmarking-Initiative/Benchmark-Models-PEtab/tree/model_Parmar_PCB2019 +# Swameye_PNAS2003 (splines) https://github.com/Benchmarking-Initiative/Benchmark-Models-PEtab/tree/model_Swameye_PNAS2003 +# Sobotta_Frontiers2017 (???) https://github.com/Benchmarking-Initiative/Benchmark-Models-PEtab/tree/model_Sobotta_Frontiers2017 +# Raia_CancerResearch2011 (state dependent sigmas) https://github.com/Benchmarking-Initiative/Benchmark-Models-PEtab/tree/model_Raia_CancerResearch2011 # # no reference value: -# Alkan_SciSignal2018 -# Blasi_CellSystems2016 -# Hass_PONE2017 -# Korkut_eLIFE2015 -# Perelson_Science1996 -# -# -# yaml missing: -# Casaletto_PNAS2019 +# Alkan_SciSignal2018 (d2d: Alkan_DDRP_SciSignal2018) +# Bertozzi_PNAS2020 (gh: vanako, doi: https://doi.org/10.1073/pnas.2006520117, code/data: https://github.com/gomohler/pnas2020 (R)) +# Blasi_CellSystems2016 (gh: Leonard Schmiester, doi: https://doi.org/10.1016/j.cels.2016.01.002, code/data: not available) +# Giordano_Nature2020 (gh: Paul Jonas Jost, doi: https://doi.org/10.1038/s41591-020-0883-7, code/data: http://users.dimi.uniud.it/~giulia.giordano/docs/papers/SIDARTHEcode.zip (MATLAB)) +# Laske_PLOSComputBiol2019 (gh: Clemens Peiter, doi: https://doi.org/10.1128/JVI.00080-12 (?), code/data: ???) +# Okuonghae_ChaosSolitonsFractals2020 (gh: Paul Jonas Jost, doi: https://doi.org/10.1016/j.chaos.2020.110032, code/data: ???) +# Oliveira_NatCommun2021 (gh: lorenapohl, doi: https://doi.org/10.1038/s41467-020-19798-3, code: https://github.com/cidacslab/Mathematical-and-Statistical-Modeling-of-COVID19-in-Brazil (python) data: https://infovis.sei.ba.gov.br/covid19/ ) +# Perelson_Science1996 (gh: Philipp Staedter, doi: https://doi.org/10.1126/science.271.5255.1582, code/data: ???) +# Rahman_MBS2016 (gh: Yannik Schaelte, doi: https://doi.org/10.1016/j.mbs.2016.07.009, code: not available, data: table in paper ...) +# Raimundez_PCB2020 (gh: Elba Raimundez, doi: https://doi.org/10.1371/journal.pcbi.1007147, code/data: https://zenodo.org/record/2908234#.Y5hUUS8w3yw (d2d)) +# SalazarCavazos_MBoC2020 (gh: Dilan Pathirana, doi: https://doi.org/10.1091/mbc.E19-09-0548, code/data: supplement (BNGL)) +# Zhao_QuantBiol2020 (gh: Iva Ewert, doi: https://doi.org/10.1007/s40484-020-0199-0, code: not available, data: table in supp) # -# Model missing: -# Merkle_PCB2016 -# -# SBML extensions: -# Parmar_PCB2019 -# -# Events: -# Swameye_PNAS2003 -# -# state-dependent sigmas: -# Raia_CancerResearch2011 +# covered by performance test: +# Froehlich_CellSystems2018 # # Unknown reasons: # Chen_MSB2009 @@ -90,8 +89,8 @@ 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 --verbose -y ${yaml} -o ${amici_model_dir} -n ${model} --flatten" - cmd_run="$script_path/test_petab_model.py --verbose -y ${yaml} -d ${amici_model_dir} -m ${model} -c" + cmd_import="amici_import_petab -y ${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} printf " %s " "${model}" @@ -110,3 +109,5 @@ for model in $models; do echo echo done + +cd "$script_path" && python evaluate_benchmark.py diff --git a/deps/AMICI/tests/benchmark-models/test_petab_benchmark.py b/deps/AMICI/tests/benchmark-models/test_petab_benchmark.py new file mode 100755 index 000000000..0b3c6d80e --- /dev/null +++ b/deps/AMICI/tests/benchmark-models/test_petab_benchmark.py @@ -0,0 +1,188 @@ +"""Tests for simulate_petab on PEtab benchmark problems.""" + +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 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 + +# 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" +) +# reuse compiled models from test_benchmark_collection.sh +benchmark_outdir = Path(__file__).parent.parent.parent / "test_bmc" +models = [ + str(petab_path.stem) + for petab_path in benchmark_path.glob("*") + if petab_path.is_dir() + if str(petab_path.stem) + not in ( + # excluded due to excessive runtime + "Bachmann_MSB2011", + "Chen_MSB2009", + "Froehlich_CellSystems2018", + "Raimundez_PCB2020", + "Lucarelli_CellSystems2018", + "Isensee_JCB2018", + "Beer_MolBioSystems2014", + "Alkan_SciSignal2018", + # excluded due to excessive numerical failures + "Crauste_CellSystems2017", + "Fujita_SciSignal2010", + ) +] + +debug = False +if debug: + debug_path = Path(__file__).parent / "debug" + debug_path.mkdir(exist_ok=True, parents=True) + + +@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): + if not scale and model in ( + "Smith_BMCSystBiol2013", + "Brannmark_JBC2010", + "Elowitz_Nature2000", + "Borghans_BiophysChem1997", + "Sneyd_PNAS2002", + "Bertozzi_PNAS2020", + "Okuonghae_ChaosSolitonsFractals2020", + ): + # not really worth the effort trying to fix these cases if they + # only fail on linear scale + pytest.skip() + + petab_problem = petab.Problem.from_yaml( + benchmark_path / model / (model + ".yaml") + ) + petab.flatten_timepoint_specific_output_overrides(petab_problem) + + # Only compute gradient for estimated parameters. + parameter_df_free = petab_problem.parameter_df.loc[ + petab_problem.x_free_ids + ] + parameter_ids = list(parameter_df_free.index) + + # Setup AMICI objects. + amici_model = amici.petab_import.import_petab_problem( + petab_problem, + model_output_dir=benchmark_outdir / model, + ) + amici_solver = amici_model.getSolver() + amici_solver.setAbsoluteTolerance(1e-12) + amici_solver.setRelativeTolerance(1e-12) + if model in ( + "Smith_BMCSystBiol2013", + "Oliveira_NatCommun2021", + ): + amici_solver.setAbsoluteTolerance(1e-10) + amici_solver.setRelativeTolerance(1e-10) + elif model in ("Okuonghae_ChaosSolitonsFractals2020",): + amici_solver.setAbsoluteTolerance(1e-14) + amici_solver.setRelativeTolerance(1e-14) + amici_solver.setMaxSteps(int(1e5)) + + if model in ("Brannmark_JBC2010",): + amici_model.setSteadyStateSensitivityMode( + amici.SteadyStateSensitivityMode.integrationOnly + ) + + amici_function, amici_derivative = simulate_petab_to_cached_functions( + petab_problem=petab_problem, + parameter_ids=parameter_ids, + amici_model=amici_model, + solver=amici_solver, + scaled_parameters=scale, + scaled_gradients=scale, + cache=not debug, + ) + + noise_level = 0.1 + + np.random.seed(0) + if scale: + point = np.asarray( + list( + petab_problem.scale_parameters( + dict(parameter_df_free.nominalValue) + ).values() + ) + ) + point_noise = np.random.randn(len(point)) * noise_level + else: + point = parameter_df_free.nominalValue.values + point_noise = np.random.randn(len(point)) * point * noise_level + point += point_noise # avoid small gradients at nominal value + + expected_derivative = amici_derivative(point) + + sizes = [ + 1e-1, + 1e-2, + 1e-3, + 1e-4, + 1e-5, + ] + if model in ("Okuonghae_ChaosSolitonsFractals2020",): + sizes.insert(0, 0.2) + + derivative = get_derivative( + function=amici_function, + point=point, + sizes=sizes, + direction_ids=parameter_ids, + method_ids=[MethodId.CENTRAL, MethodId.FORWARD, MethodId.BACKWARD], + success_checker=Consistency(atol=1e-4, rtol=0.2), + expected_result=expected_derivative, + relative_sizes=not scale, + ) + success = False + if derivative.df.success.all(): + check = NumpyIsCloseDerivativeCheck( + derivative=derivative, + expectation=expected_derivative, + point=point, + ) + success = check(rtol=RTOL, atol=ATOL) + + if debug: + df = pd.DataFrame( + [ + { + ( + "fd", + r.metadata["size_absolute"], + str(r.method_id), + ): r.value + for c in d.computers + for r in c.results + } + for d in derivative.directional_derivatives + ], + index=parameter_ids, + ) + df[("fd", "full", "")] = derivative.series.values + df[("amici", "", "")] = expected_derivative + + file_name = f"{model}_scale={scale}.tsv" + df.to_csv(debug_path / file_name, sep="\t") + + # The gradients for all parameters are correct. + assert success, derivative.df diff --git a/deps/AMICI/tests/benchmark-models/test_petab_model.py b/deps/AMICI/tests/benchmark-models/test_petab_model.py index 935065d10..cf8514753 100755 --- a/deps/AMICI/tests/benchmark-models/test_petab_model.py +++ b/deps/AMICI/tests/benchmark-models/test_petab_model.py @@ -3,19 +3,26 @@ """ Simulate a PEtab problem and compare results to reference values """ - import argparse +import contextlib import importlib import logging import os import sys +import amici +import numpy as np +import pandas as pd import petab import yaml from amici.logging import get_logger -from amici.petab_objective import (simulate_petab, rdatas_to_measurement_df, - LLH, RDATAS) -from petab.visualize import plot_petab_problem +from amici.petab_objective import ( + LLH, + RDATAS, + rdatas_to_measurement_df, + simulate_petab, +) +from petab.visualize import plot_problem logger = get_logger(f"amici.{__name__}", logging.WARNING) @@ -28,50 +35,81 @@ def parse_cli_args(): """ parser = argparse.ArgumentParser( - description='Simulate PEtab-format model using AMICI.') + description="Simulate PEtab-format model using AMICI." + ) # General options: - parser.add_argument('-v', '--verbose', dest='verbose', action='store_true', - help='More verbose output') - parser.add_argument('-c', '--check', dest='check', action='store_true', - help='Compare to reference value') - parser.add_argument('-p', '--plot', dest='plot', action='store_true', - help='Plot measurement and simulation results') + parser.add_argument( + "-v", + "--verbose", + dest="verbose", + action="store_true", + help="More verbose output", + ) + parser.add_argument( + "-c", + "--check", + dest="check", + action="store_true", + help="Compare to reference value", + ) + parser.add_argument( + "-p", + "--plot", + dest="plot", + action="store_true", + help="Plot measurement and simulation results", + ) # PEtab problem - parser.add_argument('-y', '--yaml', dest='yaml_file_name', - required=True, - help='PEtab YAML problem filename') + parser.add_argument( + "-y", + "--yaml", + dest="yaml_file_name", + required=True, + help="PEtab YAML problem filename", + ) # Corresponding AMICI model - parser.add_argument('-m', '--model-name', dest='model_name', - help='Name of the AMICI module of the model to ' - 'simulate.', required=True) - parser.add_argument('-d', '--model-dir', dest='model_directory', - help='Directory containing the AMICI module of the ' - 'model to simulate. Required if model is not ' - 'in python path.') - - parser.add_argument('-o', '--simulation-file', dest='simulation_file', - help='File to write simulation result to, in PEtab' - 'measurement table format.') - - args = parser.parse_args() - - return args + parser.add_argument( + "-m", + "--model-name", + dest="model_name", + help="Name of the AMICI module of the model to " "simulate.", + required=True, + ) + parser.add_argument( + "-d", + "--model-dir", + dest="model_directory", + help="Directory containing the AMICI module of the " + "model to simulate. Required if model is not " + "in python path.", + ) + + parser.add_argument( + "-o", + "--simulation-file", + dest="simulation_file", + help="File to write simulation result to, in PEtab" + "measurement table format.", + ) + + return parser.parse_args() def main(): """Simulate the model specified on the command line""" args = parse_cli_args() + loglevel = logging.DEBUG if args.verbose else logging.INFO + logger.setLevel(loglevel) - if args.verbose: - logger.setLevel(logging.DEBUG) - - logger.info(f"Simulating '{args.model_name}' " - f"({args.model_directory}) using PEtab data from " - f"{args.yaml_file_name}") + logger.info( + f"Simulating '{args.model_name}' " + f"({args.model_directory}) using PEtab data from " + f"{args.yaml_file_name}" + ) # load PEtab files problem = petab.Problem.from_yaml(args.yaml_file_name) @@ -84,63 +122,144 @@ def main(): amici_model = model_module.getModel() amici_solver = amici_model.getSolver() - if args.model_name == "Isensee_JCB2018": - amici_solver.setAbsoluteTolerance(1e-12) - amici_solver.setRelativeTolerance(1e-12) - - res = simulate_petab( - petab_problem=problem, amici_model=amici_model, - solver=amici_solver, log_level=logging.DEBUG) - rdatas = res[RDATAS] - llh = res[LLH] + amici_solver.setAbsoluteTolerance(1e-8) + amici_solver.setRelativeTolerance(1e-8) + amici_solver.setMaxSteps(int(1e4)) + if args.model_name in ("Brannmark_JBC2010", "Isensee_JCB2018"): + amici_model.setSteadyStateSensitivityMode( + amici.SteadyStateSensitivityMode.integrationOnly + ) + + times = dict() + + for label, sensi_mode in { + "t_sim": amici.SensitivityMethod.none, + "t_fwd": amici.SensitivityMethod.forward, + "t_adj": amici.SensitivityMethod.adjoint, + }.items(): + amici_solver.setSensitivityMethod(sensi_mode) + if sensi_mode == amici.SensitivityMethod.none: + amici_solver.setSensitivityOrder(amici.SensitivityOrder.none) + else: + amici_solver.setSensitivityOrder(amici.SensitivityOrder.first) + + res_repeats = [ + simulate_petab( + petab_problem=problem, + amici_model=amici_model, + solver=amici_solver, + log_level=loglevel, + ) + for _ in range(3) # repeat to get more stable timings + ] + res = res_repeats[0] + + times[label] = np.min( + [ + sum(r.cpu_time + r.cpu_timeB for r in res[RDATAS]) / 1000 + # only forwards/backwards simulation + for res in res_repeats + ] + ) + + if sensi_mode == amici.SensitivityMethod.none: + rdatas = res[RDATAS] + llh = res[LLH] + + times["np"] = sum(problem.parameter_df[petab.ESTIMATE]) + + pd.Series(times).to_csv( + f"./tests/benchmark-models/{args.model_name}_benchmark.csv" + ) + + for rdata in rdatas: + assert ( + rdata.status == amici.AMICI_SUCCESS + ), f"Simulation failed for {rdata.id}" # create simulation PEtab table - sim_df = rdatas_to_measurement_df(rdatas=rdatas, model=amici_model, - measurement_df=problem.measurement_df) + sim_df = rdatas_to_measurement_df( + rdatas=rdatas, model=amici_model, measurement_df=problem.measurement_df + ) sim_df.rename(columns={petab.MEASUREMENT: petab.SIMULATION}, inplace=True) if args.simulation_file: - sim_df.to_csv(index=False, sep="\t") + sim_df.to_csv(args.simulation_file, index=False, sep="\t") if args.plot: - try: + with contextlib.suppress(NotImplementedError): # visualize fit - axs = plot_petab_problem(petab_problem=problem, sim_data=sim_df) + axs = plot_problem(petab_problem=problem, simulations_df=sim_df) # save figure for plot_id, ax in axs.items(): - fig_path = os.path.join(args.model_directory, - args.model_name + "_" + plot_id - + "_vis.png") + fig_path = os.path.join( + args.model_directory, + f"{args.model_name}_{plot_id}_vis.png", + ) logger.info(f"Saving figure to {fig_path}") ax.get_figure().savefig(fig_path, dpi=150) - except NotImplementedError: - pass - if args.check: - references_yaml = os.path.join(os.path.dirname(__file__), - "benchmark_models.yaml") + references_yaml = os.path.join( + os.path.dirname(__file__), "benchmark_models.yaml" + ) with open(references_yaml) as f: refs = yaml.full_load(f) try: ref_llh = refs[args.model_name]["llh"] - logger.info(f"Reference llh: {ref_llh}") - if abs(ref_llh - llh) < 1e-3: - logger.info(f"Computed llh {llh} matches reference " - f"{ref_llh}. Absolute difference is " - f"{ref_llh - llh}.") + rdiff = np.abs((llh - ref_llh) / ref_llh) + rtol = 1e-3 + adiff = np.abs(llh - ref_llh) + atol = 1e-3 + tolstr = ( + f" Absolute difference is {adiff:.2e} " + f"(tol {atol:.2e}) and relative difference is " + f"{rdiff:.2e} (tol {rtol:.2e})." + ) + + if np.isclose(llh, ref_llh, rtol=rtol, atol=atol): + logger.info( + f"Computed llh {llh:.4e} matches reference {ref_llh:.4e}." + + tolstr + ) else: - logger.error(f"Computed llh {llh} does not match reference " - f"{ref_llh}. Absolute difference is " - f"{ref_llh - llh}." - f" Relative difference is {llh / ref_llh}") + logger.error( + f"Computed llh {llh:.4e} does not match reference " + f"{ref_llh:.4e}." + tolstr + ) sys.exit(1) except KeyError: - logger.error("No reference likelihood found for " - f"{args.model_name} in {references_yaml}") + logger.error( + "No reference likelihood found for " + f"{args.model_name} in {references_yaml}" + ) + + for label, key in { + "simulation": "t_sim", + "adjoint sensitivity": "t_adj", + "forward sensitivity": "t_fwd", + }.items(): + try: + ref = refs[args.model_name][key] + if times[key] > ref: + logger.error( + f"Computation time for {label} ({times[key]:.2e}) " + f"exceeds reference ({ref:.2e})." + ) + sys.exit(1) + else: + logger.info( + f"Computation time for {label} ({times[key]:.2e}) " + f"within reference ({ref:.2e})." + ) + except KeyError: + logger.error( + f"No reference time for {label} found for " + f"{args.model_name} in {references_yaml}" + ) if __name__ == "__main__": diff --git a/deps/AMICI/tests/conftest.py b/deps/AMICI/tests/conftest.py index 89eb1342d..9e9040051 100644 --- a/deps/AMICI/tests/conftest.py +++ b/deps/AMICI/tests/conftest.py @@ -2,16 +2,26 @@ import re import sys -from typing import List +from pathlib import Path +from typing import List, Set, Tuple import pytest - # stores passed SBML semantic test suite IDs passed_ids = [] +SBML_SEMANTIC_CASES_DIR = ( + Path(__file__).parent / "sbml-test-suite" / "cases" / "semantic" +) + + +@pytest.fixture +def sbml_semantic_cases_dir() -> Path: + """directory with sbml semantic test cases""" + return SBML_SEMANTIC_CASES_DIR -def parse_selection(selection_str: str) -> 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 @@ -19,20 +29,31 @@ def parse_selection(selection_str: str) -> List[int]: Valid input e.g.: "1", "1,3", "-3,4,6-7" """ indices = [] - for group in selection_str.split(','): - if not re.match(r'^(?:-?\d+)|(?:\d+(?:-\d+))$', group): + for group in selection_str.split(","): + if not re.match(r"^(?:-?\d+|\d+-\d*)$", group): print("Invalid selection", group) sys.exit() - spl = group.split('-') + spl = group.split("-") if len(spl) == 1: indices.append(int(spl[0])) elif len(spl) == 2: begin = int(spl[0]) if spl[0] else 0 - end = int(spl[1]) + end = int(spl[1]) if spl[1] else last indices.extend(range(begin, end + 1)) return indices +def get_all_semantic_case_ids(): + """Get iterator over test sorted IDs of all cases in the SBML semantic + suite""" + pattern = re.compile(r"\d{5}") + return sorted( + str(x.name) + for x in SBML_SEMANTIC_CASES_DIR.iterdir() + if pattern.match(x.name) + ) + + def pytest_addoption(parser): """Add pytest CLI options""" parser.addoption("--cases", help="Test cases to run") @@ -47,13 +68,11 @@ def pytest_generate_tests(metafunc): cases = metafunc.config.getoption("cases") if cases: # Run selected tests - test_numbers = set(parse_selection(cases)) + last_id = int(list(get_all_semantic_case_ids())[-1]) + test_numbers = sorted(set(parse_selection(cases, last_id))) else: # Run all tests - test_numbers = set(range(1, 1781)) - - # We skip this test due to NaNs in the Jacobian - test_numbers -= {1395} + test_numbers = get_all_semantic_case_ids() metafunc.parametrize("test_number", test_numbers) @@ -62,11 +81,13 @@ def pytest_sessionfinish(session, exitstatus): """Process test results""" global passed_ids terminalreporter = session.config.pluginmanager.get_plugin( - 'terminalreporter') + "terminalreporter" + ) terminalreporter.ensure_newline() # parse test names to get passed case IDs (don't know any better way to # access fixture values) from testSBMLSuite import format_test_id + passed_ids = [format_test_id(_) for _ in passed_ids] if passed_ids: write_passed_tags(passed_ids, terminalreporter) @@ -78,25 +99,58 @@ def write_passed_tags(passed_ids, out=sys.stdout): passed_component_tags = set() passed_test_tags = set() - from testSBMLSuite import get_tags_for_test for test_id in passed_ids: cur_component_tags, cur_test_tags = get_tags_for_test(test_id) passed_component_tags |= cur_component_tags passed_test_tags |= cur_test_tags - out.write("\nAt least one test with the following component tags has " - "passed:\n") - out.write(' ' + '\n '.join(sorted(passed_component_tags))) - out.write("\n\nAt least one test with the following test tags has " - "passed:\n") - out.write(' ' + '\n '.join(sorted(passed_test_tags))) + out.write( + "\nAt least one test with the following component tags has " + "passed:\n" + ) + out.write(" " + "\n ".join(sorted(passed_component_tags))) + out.write( + "\n\nAt least one test with the following test tags has " "passed:\n" + ) + out.write(" " + "\n ".join(sorted(passed_test_tags))) def pytest_runtest_logreport(report: "TestReport") -> None: """Collect test case IDs of passed SBML semantic test suite cases""" - if report.when == 'call'\ - and report.outcome == 'passed'\ - and '::test_sbml_testsuite_case[' in report.nodeid: - test_case_id = re.sub(r'^.*::test_sbml_testsuite_case\[(\d+)].*$', - r'\1', report.nodeid) + if ( + report.when == "call" + and report.outcome == "passed" + and "::test_sbml_testsuite_case[" in report.nodeid + ): + test_case_id = re.sub( + r"^.*::test_sbml_testsuite_case\[(\d+)].*$", r"\1", report.nodeid + ) passed_ids.append(test_case_id) + + +def get_tags_for_test(test_id: str) -> Tuple[Set[str], Set[str]]: + """Get sbml test suite tags for the given test ID + + Returns: + Tuple of set of strings for componentTags and testTags + """ + current_test_path = SBML_SEMANTIC_CASES_DIR / test_id + info_file = current_test_path / f"{test_id}-model.m" + with open(info_file) as f: + component_tags = set() + test_tags = set() + for line in f: + if line.startswith("testTags:"): + test_tags = set( + re.split(r"[ ,:]", line[len("testTags:") :].strip()) + ) + test_tags.discard("") + if line.startswith("componentTags:"): + component_tags = set( + re.split(r"[ ,:]", line[len("componentTags:") :].strip()) + ) + component_tags.discard("") + if test_tags and component_tags: + return component_tags, test_tags + print(f"No componentTags or testTags found for test case {test_id}.") + return component_tags, test_tags diff --git a/deps/AMICI/tests/cpp/CMakeLists.txt b/deps/AMICI/tests/cpp/CMakeLists.txt index 51bed78f0..cf8ddece7 100644 --- a/deps/AMICI/tests/cpp/CMakeLists.txt +++ b/deps/AMICI/tests/cpp/CMakeLists.txt @@ -5,18 +5,18 @@ # Download and unpack googletest at configure time configure_file(CMakeLists.txt.in googletest-download/CMakeLists.txt) execute_process( - COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . - RESULT_VARIABLE result - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googletest-download) + COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . + RESULT_VARIABLE result + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googletest-download) if(result) - message(FATAL_ERROR "CMake step for googletest failed: ${result}") + message(FATAL_ERROR "CMake step for googletest failed: ${result}") endif() execute_process( - COMMAND ${CMAKE_COMMAND} --build . - RESULT_VARIABLE result - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googletest-download) + COMMAND ${CMAKE_COMMAND} --build . + RESULT_VARIABLE result + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googletest-download) if(result) - message(FATAL_ERROR "Build step for googletest failed: ${result}") + message(FATAL_ERROR "Build step for googletest failed: ${result}") endif() # Prevent overriding the parent project's compiler/linker settings on Windows @@ -40,18 +40,16 @@ add_library(Upstream::amici ALIAS amici) # Amici testing library add_library(amici-testing testfunctions.cpp) -target_compile_definitions(amici-testing - PUBLIC NEW_OPTION_FILE="${CMAKE_CURRENT_SOURCE_DIR}/testOptions.h5" - PUBLIC HDFFILE="${CMAKE_CURRENT_SOURCE_DIR}/expectedResults.h5" - PUBLIC HDFFILEWRITE="${CMAKE_CURRENT_SOURCE_DIR}/writeResults.h5" - ) -target_include_directories(amici-testing - PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} - ) -target_link_libraries(amici-testing - PUBLIC Upstream::amici - PUBLIC gtest_main - ) +target_compile_definitions( + amici-testing + PUBLIC NEW_OPTION_FILE="${CMAKE_CURRENT_SOURCE_DIR}/testOptions.h5" + PUBLIC HDFFILE="${CMAKE_CURRENT_SOURCE_DIR}/expectedResults.h5" + PUBLIC HDFFILEWRITE="${CMAKE_CURRENT_SOURCE_DIR}/writeResults.h5") +target_include_directories(amici-testing PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) +target_link_libraries( + amici-testing + PUBLIC Upstream::amici + PUBLIC gtest_main) # Names of models for which tests are to be run set(TEST_MODELS @@ -59,18 +57,18 @@ set(TEST_MODELS steadystate jakstat_adjoint jakstat_adjoint_o2 - neuron neuron_o2 + neuron + neuron_o2 events nested_events robertson - calvetti - ) + calvetti) if(ENABLE_SWIG AND ENABLE_PYTHON) - add_custom_target(python-tests - COMMAND ${CMAKE_SOURCE_DIR}/scripts/run-python-tests.sh - DEPENDS - ) + add_custom_target( + python-tests + COMMAND ${CMAKE_SOURCE_DIR}/scripts/run-python-tests.sh + DEPENDS) endif() add_subdirectory(unittests) @@ -78,38 +76,38 @@ add_subdirectory(unittests) include(ExternalProject) foreach(MODEL IN ITEMS ${TEST_MODELS}) - # Build model - string(CONCAT MODEL_LIBRARY_DIR - "${CMAKE_CURRENT_BINARY_DIR}/external_model_${MODEL}-prefix/src/" - "external_model_${MODEL}-build/") - string(CONCAT MODEL_LIBRARY - "${MODEL_LIBRARY_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}model_${MODEL}" - "${CMAKE_STATIC_LIBRARY_SUFFIX}") - ExternalProject_Add(external_model_${MODEL} - PREFIX "" - SOURCE_DIR "${CMAKE_SOURCE_DIR}/models/model_${MODEL}/" - INSTALL_COMMAND "" - TEST_COMMAND "" - BUILD_ALWAYS 1 - DEPENDS amici - BUILD_BYPRODUCTS "${MODEL_LIBRARY}" - ) - # Rebuild if amici files are updated - ExternalProject_Add_StepDependencies(external_model_${MODEL} build amici) + # Build model + string( + CONCAT MODEL_LIBRARY_DIR + "${CMAKE_CURRENT_BINARY_DIR}/external_model_${MODEL}-prefix/src/" + "external_model_${MODEL}-build/") + string( + CONCAT MODEL_LIBRARY + "${MODEL_LIBRARY_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}model_${MODEL}" + "${CMAKE_STATIC_LIBRARY_SUFFIX}") + ExternalProject_Add( + external_model_${MODEL} + PREFIX "" + SOURCE_DIR "${CMAKE_SOURCE_DIR}/models/model_${MODEL}/" + INSTALL_COMMAND "" + TEST_COMMAND "" + BUILD_ALWAYS 1 + DEPENDS amici + BUILD_BYPRODUCTS "${MODEL_LIBRARY}") + # Rebuild if amici files are updated + ExternalProject_Add_StepDependencies(external_model_${MODEL} build amici) - add_library(model_${MODEL} STATIC IMPORTED) - add_dependencies(model_${MODEL} external_model_${MODEL}) + add_library(model_${MODEL} STATIC IMPORTED) + add_dependencies(model_${MODEL} external_model_${MODEL}) - set_target_properties(model_${MODEL} - PROPERTIES - INTERFACE_INCLUDE_DIRECTORIES - "${CMAKE_SOURCE_DIR}/models/model_${MODEL}/" - IMPORTED_LOCATION "${MODEL_LIBRARY}" - ) - # Build tests for this model - add_subdirectory(${MODEL}) - if(TARGET python-tests) - add_dependencies(python-tests external_model_${MODEL}) - endif() + set_target_properties( + model_${MODEL} + PROPERTIES INTERFACE_INCLUDE_DIRECTORIES + "${CMAKE_SOURCE_DIR}/models/model_${MODEL}/" IMPORTED_LOCATION + "${MODEL_LIBRARY}") + # Build tests for this model + add_subdirectory(${MODEL}) + if(TARGET python-tests) + add_dependencies(python-tests external_model_${MODEL}) + endif() endforeach() - diff --git a/deps/AMICI/tests/cpp/CMakeLists.txt.in b/deps/AMICI/tests/cpp/CMakeLists.txt.in index 0704ae5d1..a93f5955c 100644 --- a/deps/AMICI/tests/cpp/CMakeLists.txt.in +++ b/deps/AMICI/tests/cpp/CMakeLists.txt.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.15) project(googletest-download NONE) diff --git a/deps/AMICI/tests/cpp/expectedResults.h5 b/deps/AMICI/tests/cpp/expectedResults.h5 index 3525c1b4b..c87ab70dc 100644 Binary files a/deps/AMICI/tests/cpp/expectedResults.h5 and b/deps/AMICI/tests/cpp/expectedResults.h5 differ diff --git a/deps/AMICI/tests/cpp/steadystate/tests1.cpp b/deps/AMICI/tests/cpp/steadystate/tests1.cpp index 505f8f15e..e939a84ff 100644 --- a/deps/AMICI/tests/cpp/steadystate/tests1.cpp +++ b/deps/AMICI/tests/cpp/steadystate/tests1.cpp @@ -121,6 +121,10 @@ TEST(ExampleSteadystate, Maxtime) amici::hdf5::readSolverSettingsFromHDF5( NEW_OPTION_FILE, *solver, "/model_steadystate/nosensi/options"); + // Ensure the solver needs sufficiently many steps that time is checked + // at least once during integration + solver->setRelativeTolerance(1e-14); + auto rdata = runAmiciSimulation(*solver, nullptr, *model); ASSERT_EQ(amici::AMICI_SUCCESS, rdata->status); @@ -174,12 +178,6 @@ TEST(ExampleSteadystate, SensitivityForwardDense) amici::simulateVerifyWrite("/model_steadystate/sensiforwarddense/"); } -TEST(ExampleSteadystate, SensitivityForwardSPBCG) -{ - amici::simulateVerifyWrite( - "/model_steadystate/nosensiSPBCG/", 10 * TEST_ATOL, 10 * TEST_RTOL); -} - TEST(ExampleSteadystate, SensiFwdNewtonPreeq) { amici::simulateVerifyWrite("/model_steadystate/sensifwdnewtonpreeq/"); diff --git a/deps/AMICI/tests/cpp/testOptions.h5 b/deps/AMICI/tests/cpp/testOptions.h5 index e2bf3f658..1794ea6e6 100644 Binary files a/deps/AMICI/tests/cpp/testOptions.h5 and b/deps/AMICI/tests/cpp/testOptions.h5 differ diff --git a/deps/AMICI/tests/cpp/unittests/CMakeLists.txt b/deps/AMICI/tests/cpp/unittests/CMakeLists.txt index 5e1f7e68a..475c89f22 100644 --- a/deps/AMICI/tests/cpp/unittests/CMakeLists.txt +++ b/deps/AMICI/tests/cpp/unittests/CMakeLists.txt @@ -2,25 +2,17 @@ project(unittests) find_package(Boost COMPONENTS serialization) -set(SRC_LIST - testMisc.cpp - testExpData.cpp -) +set(SRC_LIST testMisc.cpp testExpData.cpp testSplines.cpp) add_executable(${PROJECT_NAME} ${SRC_LIST}) target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) -if(Boost_FOUND) - target_sources(${PROJECT_NAME} PRIVATE testSerialization.cpp) - target_include_directories(${PROJECT_NAME} PRIVATE "${Boost_INCLUDE_DIR}") +if(Boost_SERIALIZATION_FOUND) + target_sources(${PROJECT_NAME} PRIVATE testSerialization.cpp) endif() -target_link_libraries(${PROJECT_NAME} - amici-testing - Upstream::amici - ${Boost_LIBRARIES} - gtest_main - ) +target_link_libraries(${PROJECT_NAME} amici-testing Upstream::amici + Boost::serialization gtest_main) include(GoogleTest) diff --git a/deps/AMICI/tests/cpp/unittests/testExpData.cpp b/deps/AMICI/tests/cpp/unittests/testExpData.cpp index 4cf947341..416a41227 100644 --- a/deps/AMICI/tests/cpp/unittests/testExpData.cpp +++ b/deps/AMICI/tests/cpp/unittests/testExpData.cpp @@ -49,6 +49,7 @@ class ExpDataTest : public ::testing::Test { nz, // nz nz, // nztrue nmaxevent, // ne + 0, // nspl 0, // nJ 0, // nw 0, // ndwdx @@ -56,6 +57,9 @@ class ExpDataTest : public ::testing::Test { 0, // dwdw 0, // ndxdotdw {}, // ndJydy + 0, // ndxrdatadxsolver + 0, // ndxrdatadtcl + 0, // ndtotal_cldx_rdata 0, // nnz 0, // ubw 0 // lbw @@ -179,6 +183,17 @@ TEST_F(ExpDataTest, CopyConstructable) "ts"); } + +TEST_F(ExpDataTest, Equality) +{ + auto edata = ExpData(testModel); + auto edata2(edata); + ASSERT_TRUE(edata == edata2); + + edata2.id = "different"; + ASSERT_FALSE(edata == edata2); +} + TEST_F(ExpDataTest, DimensionChecks) { std::vector bad_std(ny, -0.1); diff --git a/deps/AMICI/tests/cpp/unittests/testMisc.cpp b/deps/AMICI/tests/cpp/unittests/testMisc.cpp index 686330f4f..80d2c3bc3 100644 --- a/deps/AMICI/tests/cpp/unittests/testMisc.cpp +++ b/deps/AMICI/tests/cpp/unittests/testMisc.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -64,13 +65,17 @@ class ModelTest : public ::testing::Test { nz, // nz nz, // nztrue nmaxevent, // ne + 0, // nspl 0, // nJ 0, // nw 0, // ndwdx 0, // ndwdp 0, // dwdw 0, // ndxdotdw - {}, // ndJydy + {}, // ndJydy + 0, // ndxrdatadxsolver + 0, // ndxrdatadtcl + 0, // ndtotal_cldx_rdata 0, // nnz 0, // ubw 0 // lbw @@ -298,16 +303,20 @@ class SolverTest : public ::testing::Test { nz, // nz nz, // nztrue ne, // ne + 0, // nspl 0, // nJ 0, // nw 0, // ndwdx 0, // ndwdp 0, // dwdw 0, // ndxdotdw - {}, // ndJydy + {}, // ndJydy + 0, // ndxrdatadxsolver + 0, // ndxrdatadtcl + 0, // ndtotal_cldx_rdata 1, // nnz 0, // ubw - 0 // lbw + 0 // lbw ), SimulationParameters( std::vector(3, 0.0), @@ -402,20 +411,13 @@ testSolverGetterSetters(CVodeSolver solver, ASSERT_EQ(static_cast(solver.getLinearMultistepMethod()), static_cast(lmm)); - solver.setPreequilibration(true); - ASSERT_EQ(solver.getPreequilibration(), true); - - solver.setStabilityLimitFlag(true); + solver.setStabilityLimitFlag(true); ASSERT_EQ(solver.getStabilityLimitFlag(), true); ASSERT_THROW(solver.setNewtonMaxSteps(badsteps), AmiException); solver.setNewtonMaxSteps(steps); ASSERT_EQ(solver.getNewtonMaxSteps(), steps); - ASSERT_THROW(solver.setNewtonMaxLinearSteps(badsteps), AmiException); - solver.setNewtonMaxLinearSteps(steps); - ASSERT_EQ(solver.getNewtonMaxLinearSteps(), steps); - ASSERT_THROW(solver.setMaxSteps(badsteps), AmiException); solver.setMaxSteps(steps); ASSERT_EQ(solver.getMaxSteps(), steps); @@ -449,6 +451,59 @@ testSolverGetterSetters(CVodeSolver solver, ASSERT_EQ(solver.getAbsoluteToleranceSteadyState(), tol); } +TEST_F(SolverTest, SteadyStateToleranceFactor) +{ + CVodeSolver s; + // test with unset steadystate tolerances + ASSERT_DOUBLE_EQ( + s.getRelativeToleranceSteadyState(), + s.getSteadyStateToleranceFactor() * s.getRelativeTolerance()); + ASSERT_DOUBLE_EQ( + s.getAbsoluteToleranceSteadyState(), + s.getSteadyStateToleranceFactor() * s.getAbsoluteTolerance()); + ASSERT_DOUBLE_EQ( + s.getRelativeToleranceSteadyStateSensi(), + s.getSteadyStateSensiToleranceFactor() * s.getRelativeTolerance()); + ASSERT_DOUBLE_EQ( + s.getAbsoluteToleranceSteadyState(), + s.getSteadyStateSensiToleranceFactor() * s.getAbsoluteTolerance()); + + // test with changed steadystate tolerance factor + s.setSteadyStateToleranceFactor(5); + ASSERT_DOUBLE_EQ( + s.getRelativeToleranceSteadyState(), + s.getSteadyStateToleranceFactor() * s.getRelativeTolerance()); + ASSERT_DOUBLE_EQ( + s.getAbsoluteToleranceSteadyState(), + s.getSteadyStateToleranceFactor() * s.getAbsoluteTolerance()); + s.setSteadyStateSensiToleranceFactor(5); + ASSERT_DOUBLE_EQ( + s.getRelativeToleranceSteadyStateSensi(), + s.getSteadyStateSensiToleranceFactor() * s.getRelativeTolerance()); + ASSERT_DOUBLE_EQ( + s.getAbsoluteToleranceSteadyState(), + s.getSteadyStateSensiToleranceFactor() * s.getAbsoluteTolerance()); + + + // test with steadystate tolerance override tolerance factor + s.setRelativeToleranceSteadyState(2); + ASSERT_NE(s.getRelativeToleranceSteadyState(), + s.getSteadyStateToleranceFactor() * s.getRelativeTolerance()); + ASSERT_EQ(s.getRelativeToleranceSteadyState(), 2); + s.setAbsoluteToleranceSteadyState(3); + ASSERT_NE(s.getAbsoluteToleranceSteadyState(), + s.getSteadyStateToleranceFactor() * s.getAbsoluteTolerance()); + ASSERT_EQ(s.getAbsoluteToleranceSteadyState(), 3); + s.setRelativeToleranceSteadyStateSensi(4); + ASSERT_NE(s.getRelativeToleranceSteadyStateSensi(), + s.getSteadyStateSensiToleranceFactor() * s.getRelativeTolerance()); + ASSERT_EQ(s.getRelativeToleranceSteadyStateSensi(), 4); + s.setAbsoluteToleranceSteadyStateSensi(5); + ASSERT_NE(s.getAbsoluteToleranceSteadyStateSensi(), + s.getSteadyStateSensiToleranceFactor() * s.getAbsoluteTolerance()); + ASSERT_EQ(s.getAbsoluteToleranceSteadyStateSensi(), 5); +} + class AmiVectorTest : public ::testing::Test { protected: std::vector vec1{ 1, 2, 4, 3 }; @@ -594,4 +649,90 @@ TEST_F(SunMatrixWrapperTest, BlockTranspose) SM_INDEXPTRS_S(B_sparse.get())[icol]); } +TEST(UnravelIndex, UnravelIndex) +{ + EXPECT_EQ(unravel_index(0, 2), std::make_pair((size_t) 0, (size_t) 0)); + EXPECT_EQ(unravel_index(1, 2), std::make_pair((size_t) 0, (size_t) 1)); + EXPECT_EQ(unravel_index(2, 2), std::make_pair((size_t) 1, (size_t) 0)); + EXPECT_EQ(unravel_index(3, 2), std::make_pair((size_t) 1, (size_t) 1)); + EXPECT_EQ(unravel_index(4, 2), std::make_pair((size_t) 2, (size_t) 0)); + EXPECT_EQ(unravel_index(5, 2), std::make_pair((size_t) 2, (size_t) 1)); + EXPECT_EQ(unravel_index(6, 2), std::make_pair((size_t) 3, (size_t) 0)); +} + +TEST(UnravelIndex, UnravelIndexSunMatDense) +{ + SUNMatrixWrapper A = SUNMatrixWrapper(3, 2); + + A.set_data(0, 0, 0); + A.set_data(1, 0, 1); + A.set_data(2, 0, 2); + A.set_data(0, 1, 3); + A.set_data(1, 1, 4); + A.set_data(2, 1, 5); + + for(int i = 0; i < 6; ++i) { + auto idx = unravel_index(i, A.get()); + EXPECT_EQ(A.get_data(idx.first, idx.second), i); + } +} + +TEST(UnravelIndex, UnravelIndexSunMatSparse) +{ + SUNMatrixWrapper D = SUNMatrixWrapper(4, 2); + + // [0, 3] + // [0, 0] + // [1, 0] + // [2, 0] + // data [1, 2, 3] + // colptrs [0, 2, 3] + // rowidxs [2, 3, 1] + D.set_data(0, 0, 0); + D.set_data(1, 0, 0); + D.set_data(2, 0, 1); + D.set_data(3, 0, 2); + D.set_data(0, 1, 3); + D.set_data(1, 1, 0); + D.set_data(2, 1, 0); + D.set_data(3, 1, 0); + + auto S = SUNSparseFromDenseMatrix(D.get(), 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)); + EXPECT_EQ(unravel_index(2, S), std::make_pair((sunindextype) 0, (sunindextype) 1)); + + SUNMatDestroy(S); +} + +TEST(ReturnCodeToStr, ReturnCodeToStr) +{ + EXPECT_EQ("AMICI_SUCCESS", simulation_status_to_str(AMICI_SUCCESS)); + EXPECT_EQ("AMICI_UNRECOVERABLE_ERROR", + simulation_status_to_str(AMICI_UNRECOVERABLE_ERROR)); +} + +TEST(SpanEqual, SpanEqual) +{ + std::vector a {1, 2, 3}; + std::vector b {1, 2, NAN}; + + EXPECT_TRUE(is_equal(a, a)); + EXPECT_TRUE(is_equal(b, b)); + EXPECT_FALSE(is_equal(a, b)); +} + +TEST(CpuTimer, CpuTimer) +{ + amici::CpuTimer timer; + auto elapsed = timer.elapsed_seconds(); + EXPECT_LE(0.0, elapsed); + EXPECT_GT(1.0, elapsed); + + elapsed = timer.elapsed_milliseconds(); + EXPECT_LT(0.0, elapsed); + EXPECT_GT(1000.0, elapsed); +} + } // namespace diff --git a/deps/AMICI/tests/cpp/unittests/testSerialization.cpp b/deps/AMICI/tests/cpp/unittests/testSerialization.cpp index f34942440..5b4fb1ed2 100644 --- a/deps/AMICI/tests/cpp/unittests/testSerialization.cpp +++ b/deps/AMICI/tests/cpp/unittests/testSerialization.cpp @@ -70,7 +70,6 @@ checkReturnDataEqual(amici::ReturnData const& r, amici::ReturnData const& s) ASSERT_TRUE(r.preeq_wrms == s.preeq_wrms || (std::isnan(r.preeq_wrms) && std::isnan(s.preeq_wrms))); ASSERT_EQ(r.preeq_numsteps, s.preeq_numsteps); - ASSERT_EQ(r.preeq_numlinsteps, s.preeq_numlinsteps); EXPECT_NEAR(r.preeq_cpu_time, s.preeq_cpu_time, 1e-16); ASSERT_EQ(r.posteq_status, s.posteq_status); @@ -79,7 +78,6 @@ checkReturnDataEqual(amici::ReturnData const& r, amici::ReturnData const& s) ASSERT_TRUE(r.posteq_wrms == s.posteq_wrms || (std::isnan(r.posteq_wrms) && std::isnan(s.posteq_wrms))); ASSERT_EQ(r.posteq_numsteps, s.posteq_numsteps); - ASSERT_EQ(r.posteq_numlinsteps, s.posteq_numlinsteps); EXPECT_NEAR(r.posteq_cpu_time, s.posteq_cpu_time, 1e-16); checkEqualArray(r.x0, s.x0, 1e-16, 1e-16, "x0"); @@ -108,8 +106,6 @@ class SolverSerializationTest : public ::testing::Test { solver.setMaxSteps(1e1); solver.setMaxStepsBackwardProblem(1e2); solver.setNewtonMaxSteps(1e3); - solver.setNewtonMaxLinearSteps(1e4); - solver.setPreequilibration(true); solver.setStateOrdering(static_cast(amici::SUNLinSolKLU::StateOrdering::COLAMD)); solver.setInterpolationType(amici::InterpolationType::polynomial); solver.setStabilityLimitFlag(false); @@ -146,6 +142,7 @@ TEST(ModelSerializationTest, ToFile) nz, // nz nz, // nztrue ne, // ne + 0, // nspl 0, // nJ 9, // nw 2, // ndwdx @@ -153,9 +150,12 @@ TEST(ModelSerializationTest, ToFile) 2, // dwdw 13, // ndxdotdw {}, // ndJydy - 15, // nnz - 16, // ubw - 17 // lbw + 9, // ndxrdatadxsolver + 0, // ndxrdatadtcl + 0, // ndtotal_cldx_rdata + 17, // nnz + 18, // ubw + 19 // lbw ), amici::SimulationParameters( std::vector(nk, 0.0), @@ -207,6 +207,7 @@ TEST(ReturnDataSerializationTest, ToString) nz, // nz nz, // nztrue ne, // ne + 0, // nspl 0, // nJ 9, // nw 10, // ndwdx @@ -214,9 +215,12 @@ TEST(ReturnDataSerializationTest, ToString) 12, // dwdw 13, // ndxdotdw {}, // ndJydy - 15, // nnz - 16, // ubw - 17 // lbw + 9, // ndxrdatadxsolver + 0, // ndxrdatadtcl + 0, // ndtotal_cldx_rdata + 17, // nnz + 18, // ubw + 19 // lbw ), amici::SimulationParameters( std::vector(nk, 0.0), diff --git a/deps/AMICI/tests/cpp/unittests/testSplines.cpp b/deps/AMICI/tests/cpp/unittests/testSplines.cpp new file mode 100644 index 000000000..df17cae33 --- /dev/null +++ b/deps/AMICI/tests/cpp/unittests/testSplines.cpp @@ -0,0 +1,923 @@ +#include +#include + +#include + +#include +#include +#include + +using std::exp; +using amici::HermiteSpline; +using amici::SplineBoundaryCondition; +using amici::SplineExtrapolation; +using amici::AmiException; + +#define ASSERT_APPROX(x, x0, rtol, atol) ASSERT_LE(std::abs((x) - (x0)), (atol) + (rtol) * std::abs(x0)) + +void test_spline_values( + HermiteSpline const& spline, + std::vector> const& expectations) +{ + for (auto const& [time, expected_value] : expectations) { + ASSERT_DOUBLE_EQ(spline.get_value(time), expected_value); + } +} + +void test_spline_values( + HermiteSpline const& spline, + std::vector> const& expectations, + const double rtol, const double atol) +{ + for (auto const& [time, expected_value] : expectations) { + ASSERT_APPROX(spline.get_value(time), expected_value, rtol, atol); + } +} + +void test_spline_sensitivities( + HermiteSpline const& spline, + std::vector>> const& expectations) +{ + for (auto const& [time, expected_values] : expectations) { + for (std::vector::size_type ip = 0; ip < expected_values.size(); ip++) + ASSERT_DOUBLE_EQ(spline.get_sensitivity(time, ip), expected_values[ip]); + } +} + +void test_spline_sensitivities( + HermiteSpline const& spline, + std::vector>> const& expectations, + const double rtol, const double atol) +{ + for (auto const& [time, expected_values] : expectations) { + for (std::vector::size_type ip = 0; ip < expected_values.size(); ip++) + ASSERT_APPROX(spline.get_sensitivity(time, ip), expected_values[ip], rtol, atol); + } +} + +TEST(Splines, SplineUniform) +{ + // Uniform grid + HermiteSpline spline({ 0.0, 1.0 }, + { 0.0, 2.0, 0.5, 1.0 }, + {}, + SplineBoundaryCondition::given, + SplineBoundaryCondition::given, + SplineExtrapolation::noExtrapolation, + SplineExtrapolation::noExtrapolation, + true, // node_derivative_by_FD + true, // equidistant_spacing + false); // logarithmic_parametrization + + spline.compute_coefficients(); + std::vector> expectations = { + // t, expected value + {0.00, 0.0}, + {0.25, 1.74609375}, + {1.0/3, 2.0}, + {0.50, 1.3437499999999996}, + {2.0/3, 0.5}, + {0.75, 0.484375}, + {1.00, 1.0}, + }; + test_spline_values(spline, expectations); + ASSERT_THROW(spline.get_value(-0.05), AmiException); + ASSERT_THROW(spline.get_value(1.05), AmiException); +} + +TEST(Splines, SplineNonUniform) +{ + // Non-uniform grid + HermiteSpline spline({ 0.0, 0.1, 0.5, 1.0 }, + { 0.0, 2.0, 0.5, 1.0 }, + {}, + SplineBoundaryCondition::given, + SplineBoundaryCondition::given, + SplineExtrapolation::noExtrapolation, + SplineExtrapolation::noExtrapolation, + true, // node_derivative_by_FD + false, // equidistant_spacing + false); // logarithmic_parametrization + + spline.compute_coefficients(); + std::vector> expectations = { + // t, expected value + {0.00, 0.0}, + {0.05, 1.1484375}, + {0.10, 2.0}, + {0.25, 2.0498046875}, + {0.50, 0.5}, + {0.75, 0.6015625}, + {1.00, 1.0}, + }; + test_spline_values(spline, expectations); + ASSERT_THROW(spline.get_value(-0.05), AmiException); + ASSERT_THROW(spline.get_value(1.05), AmiException); +} + +TEST(Splines, SplineExplicit) +{ + // Derivatives are given explicitly + HermiteSpline spline({ 0.0, 1.0 }, + { 0.0, 2.0, 0.5, 1.0, 0.75 }, + { 1.0, 0.0, 0.1, -0.1, 0.0 }, + SplineBoundaryCondition::given, + SplineBoundaryCondition::given, + SplineExtrapolation::noExtrapolation, + SplineExtrapolation::noExtrapolation, + false, // node_derivative_by_FD + true, // equidistant_spacing + false); // logarithmic_parametrization + + spline.compute_coefficients(); + std::vector> expectations = { + // t, expected value + {0.0, 0.0}, + {0.2, 1.8000000000000003}, + {0.25, 2.0}, + {0.4, 1.0243999999999998}, + {0.5, 0.5}, + {0.6, 0.6819999999999999}, + {0.75, 1.0}, + {0.8, 0.9707999999999999}, + {1.0, 0.75}, + }; + test_spline_values(spline, expectations); + ASSERT_THROW(spline.get_value(-0.05), AmiException); + ASSERT_THROW(spline.get_value(1.05), AmiException); +} + +TEST(Splines, SplineZeroBC) +{ + HermiteSpline spline({ 0.0, 1.0 }, + { 0.0, 2.0, 0.5, 1.0 }, + {}, + SplineBoundaryCondition::zeroDerivative, + SplineBoundaryCondition::zeroDerivative, + SplineExtrapolation::noExtrapolation, + SplineExtrapolation::noExtrapolation, + true, // node_derivative_by_FD + true, // equidistant_spacing + false); // logarithmic_parametrization + + spline.compute_coefficients(); + std::vector> expectations = { + // t, expected value + {0.0, 0.0}, + {0.25, 1.65234375}, + {0.5, 1.3437499999999996}, + {0.75, 0.5078125}, + {1.0, 1.0}, + }; + test_spline_values(spline, expectations); + ASSERT_THROW(spline.get_value(-0.05), AmiException); + ASSERT_THROW(spline.get_value(1.05), AmiException); +} + +TEST(Splines, SplineLogarithmic) +{ + // Logarithmic parametrization + HermiteSpline spline({ 0.0, 1.0 }, + { 0.2, 2.0, 0.5, 1.0, 0.75 }, + {}, + SplineBoundaryCondition::given, + SplineBoundaryCondition::given, + SplineExtrapolation::noExtrapolation, + SplineExtrapolation::noExtrapolation, + true, // node_derivative_by_FD + true, // equidistant_spacing + true); // logarithmic_parametrization + // log-space values [-1.60943791, 0.69314718, -0.69314718, 0, -0.28768207] + // log-space derivatives [36, 0.3, -4, 0.5, -1.33333333] + spline.compute_coefficients(); + std::vector> expectations = { + // t, expected value + {0.0, 0.2}, + {0.2, 2.07939779651678}, + {0.25, 2.0}, + {0.4, 0.947459046694449}, + {0.5, 0.5}, + {0.6, 0.545987404053269}, + {0.75, 1.0}, + {0.8, 0.996753014029391}, + {1.0, 0.75}, + }; + test_spline_values(spline, expectations, 1e-14, 0.0); + ASSERT_THROW(spline.get_value(-0.05), AmiException); + ASSERT_THROW(spline.get_value(1.05), AmiException); +} + +TEST(Splines, SplineUniformConstantExtrapolation) +{ + // Uniform grid + HermiteSpline spline({ 0.0, 1.0 }, + { 0.0, 2.0, 0.5, 1.0 }, + {}, + SplineBoundaryCondition::given, + SplineBoundaryCondition::given, + SplineExtrapolation::constant, + SplineExtrapolation::constant, + true, // node_derivative_by_FD + true, // equidistant_spacing + false); // logarithmic_parametrization + + spline.compute_coefficients(); + std::vector> expectations = { + // t, expected value + {-2.00, 0.0}, + {-1.00, 0.0}, + { 0.00, 0.0}, + { 0.25, 1.74609375}, + { 1.0/3, 2.0}, + { 0.50, 1.3437499999999996}, + { 2.0/3, 0.5}, + { 0.75, 0.484375}, + { 1.00, 1.0}, + { 2.00, 1.0}, + { 3.00, 1.0}, + }; + test_spline_values(spline, expectations); +} + +TEST(Splines, SplineUniformLinearExtrapolation) +{ + // Uniform grid + HermiteSpline spline({ 0.0, 1.0 }, + { 0.0, 2.0, 0.5, 1.0 }, + {}, + SplineBoundaryCondition::given, + SplineBoundaryCondition::given, + SplineExtrapolation::linear, + SplineExtrapolation::linear, + true, // node_derivative_by_FD + true, // equidistant_spacing + false); // logarithmic_parametrization + + spline.compute_coefficients(); + std::vector> expectations = { + // t, expected value + {-2.00, -12.0}, + {-1.00, -6.0}, + { 0.00, 0.0}, + { 0.25, 1.74609375}, + { 1.0/3, 2.0}, + { 0.50, 1.3437499999999996}, + { 2.0/3, 0.5}, + { 0.75, 0.484375}, + { 1.00, 1.0}, + { 2.00, 2.5}, + { 3.00, 4.0}, + }; + test_spline_values(spline, expectations); +} + +TEST(Splines, SplineUniformPolynomialExtrapolation) +{ + // Uniform grid + HermiteSpline spline({ 0.0, 1.0 }, + { 0.0, 2.0, 0.5, 1.0 }, + {}, + SplineBoundaryCondition::given, + SplineBoundaryCondition::given, + SplineExtrapolation::polynomial, + SplineExtrapolation::polynomial, + true, // node_derivative_by_FD + true, // equidistant_spacing + false); // logarithmic_parametrization + + spline.compute_coefficients(); + std::vector> expectations = { + // t, expected value + {-2.00, 429.0}, + {-1.00, 57.0}, + { 0.00, 0.0}, + { 0.25, 1.74609375}, + { 1.0/3, 2.0}, + { 0.50, 1.3437499999999996}, + { 2.0/3, 0.5}, + { 0.75, 0.484375}, + { 1.00, 1.0}, + { 2.00, -33.5}, + { 3.00, -248.0}, + }; + test_spline_values(spline, expectations); +} + +TEST(Splines, SplineUniformPeriodicExtrapolation) +{ + // Uniform grid + HermiteSpline spline({ 0.0, 1.0 }, + { 1.0, 2.0, 0.5, 1.0 }, + {}, + SplineBoundaryCondition::periodic, + SplineBoundaryCondition::periodic, + SplineExtrapolation::periodic, + SplineExtrapolation::periodic, + true, // node_derivative_by_FD + true, // equidistant_spacing + false); // logarithmic_parametrization + + spline.compute_coefficients(); + std::vector> expectations = { + // t, expected value + {-4.0/3, 0.5}, + {-0.50, 1.2812499999999996}, + { 0.00, 1.0}, + { 0.25, 1.9140625}, + { 1.0/3, 2.0}, + { 0.50, 1.2812499999999996}, + { 2.0/3, 0.5}, + { 0.75, 0.47265625}, + { 1.00, 1.0}, + { 1.25, 1.9140625}, + { 2.75, 0.47265625}, + }; + test_spline_values(spline, expectations); +} + +TEST(Splines, SplineNonUniformPeriodicExtrapolation) +{ + // Non-uniform grid + HermiteSpline spline({ 0.0, 0.1, 0.5, 1.0 }, + { 1.0, 2.0, 0.5, 1.0 }, + {}, + SplineBoundaryCondition::periodic, + SplineBoundaryCondition::periodic, + SplineExtrapolation::periodic, + SplineExtrapolation::periodic, + true, // node_derivative_by_FD + false, // equidistant_spacing + false); // logarithmic_parametrization + + spline.compute_coefficients(); + std::vector> expectations = { + // t, expected value + {-1.90, 2.0}, + {-0.25, 0.3203125}, + { 0.00, 1.0}, + { 0.05, 1.5296875}, + { 0.10, 2.0}, + { 0.25, 1.7568359375}, + { 0.50, 0.5}, + { 0.75, 0.3203125}, + { 1.00, 1.0}, + { 1.50, 0.5}, + { 2.05, 1.5296875}, + }; + test_spline_values(spline, expectations, 1e-14, 0.0); +} + +TEST(Splines, SplineUniformSensitivity) +{ + // Uniform grid + HermiteSpline spline({ 0.0, 1.0 }, + { 2.5, 3.25, 1.0, 4.5 }, + {}, + SplineBoundaryCondition::given, + SplineBoundaryCondition::given, + SplineExtrapolation::noExtrapolation, + SplineExtrapolation::noExtrapolation, + true, // node_derivative_by_FD + true, // equidistant_spacing + false); // logarithmic_parametrization + int n_params = 3; + std::vector dvaluesdp = { + 3.0, 1.0, 0.0, + 0.0, 0.0, 5.0, + 0.0, 0.0, 0.0, + -6.0, 1.0, 3.0 + }; + auto dslopesdp = std::vector(spline.n_nodes() * n_params); + spline.compute_coefficients(); + spline.compute_coefficients_sensi(n_params, 0, dvaluesdp, dslopesdp); + std::vector>> expectations = { + // t, expected values of sensitivities + {0.00, {3.0, 1.0, 0.0}}, + {0.25, {0.539062, 0.179688, 4.45312}}, + {1.0/3, {0.0, 0.0, 5.0}}, + {0.50, {0.1875, -0.125, 2.625}}, + {2.0/3, {0.0, 0.0, 0.0}}, + {0.75, {-1.07812, 0.179688, 0.1875}}, + {1.00, {-6.0, 1.0, 3.0}}, + }; + test_spline_sensitivities(spline, expectations, 1e-5, 1e-6); + ASSERT_THROW(spline.get_sensitivity(-0.05, 0), AmiException); + ASSERT_THROW(spline.get_sensitivity( 1.05, 1), AmiException); +} + +TEST(Splines, SplineNonUniformSensitivity) +{ + HermiteSpline spline({ 0.0, 0.1, 0.5, 1.0 }, + { 2.5, 3.25, 1.0, 4.5 }, + {}, + SplineBoundaryCondition::given, + SplineBoundaryCondition::given, + SplineExtrapolation::noExtrapolation, + SplineExtrapolation::noExtrapolation, + true, // node_derivative_by_FD + false, // equidistant_spacing + false); // logarithmic_parametrization + int n_params = 3; + std::vector dvaluesdp = { + 3.0, 1.0, 0.0, + 0.0, 0.0, 5.0, + 0.0, 0.0, 0.0, + -6.0, 1.0, 3.0 + }; + auto dslopesdp = std::vector(spline.n_nodes() * n_params); + spline.compute_coefficients(); + spline.compute_coefficients_sensi(n_params, 0, dvaluesdp, dslopesdp); + std::vector>> expectations = { + // t, expected values of sensitivities + {0.00, { 3.0, 1.0, 0.0}}, + {0.05, { 1.3125, 0.4375, 2.89062}}, + {0.10, { 0.0, 0.0, 5.0}}, + {0.30, {-0.45, -0.3, 3.6}}, + {0.50, { 0.0, 0.0, 0.0}}, + {0.75, {-2.625, 0.4375, 0.921875}}, + {1.00, {-6.0, 1.0, 3.0}}, + }; + test_spline_sensitivities(spline, expectations, 1e-5, 1e-6); + ASSERT_THROW(spline.get_sensitivity(-0.05, 0), AmiException); + ASSERT_THROW(spline.get_sensitivity( 1.05, 1), AmiException); +} + +TEST(Splines, SplineExplicitSensitivity) +{ + HermiteSpline spline({ 0.0, 1.0 }, + { 2.5, 3.25, 1.0, 4.5 }, + { 13.625, 7.5, 1.1585290151921035, 1.0 }, + SplineBoundaryCondition::given, + SplineBoundaryCondition::given, + SplineExtrapolation::noExtrapolation, + SplineExtrapolation::noExtrapolation, + false, // node_derivative_by_FD + true, // equidistant_spacing + false); // logarithmic_parametrization + int n_params = 3; + std::vector dvaluesdp = { + 3.0, 1.0, 0.0, + 0.0, 0.0, 5.0, + 0.0, 0.0, 0.0, + -6.0, 1.0, 3.0 + }; + std::vector dslopesdp = { + 0.0, 0.0, 18.75, + 0.0, 1.0, 3.0, + 4.0, -0.540302, 0.0, + 0.0, 0.0, 0.0, + }; + spline.compute_coefficients(); + spline.compute_coefficients_sensi(n_params, 0, dvaluesdp, dslopesdp); + std::vector>> expectations = { + // t, expected values of sensitivities + {0.00, { 3.0, 1.0, 0.0}}, + {0.25, { 0.46875, 0.109375, 4.37109}}, + {1.0/3, { 0.0, 0.0, 5.0}}, + {0.50, {-0.166667, 0.0641793, 2.625}}, + {2.0/3, { 0.0, 0.0, 0.0}}, + {0.75, {-0.75, 0.130923, 0.46875}}, + {1.00, {-6.0, 1.0, 3.0}}, + }; + test_spline_sensitivities(spline, expectations, 1e-5, 0.0); + ASSERT_THROW(spline.get_sensitivity(-0.05, 0), AmiException); + ASSERT_THROW(spline.get_sensitivity( 1.05, 1), AmiException); +} + +TEST(Splines, SplineZeroDerivativeSensitivity) +{ + HermiteSpline spline({ 0.0, 1.0 }, + { 2.5, 3.25, 1.0, 4.5 }, + {}, + SplineBoundaryCondition::zeroDerivative, + SplineBoundaryCondition::zeroDerivative, + SplineExtrapolation::noExtrapolation, + SplineExtrapolation::noExtrapolation, + true, // node_derivative_by_FD + true, // equidistant_spacing + false); // logarithmic_parametrization + int n_params = 3; + std::vector dvaluesdp = { + 3.0, 1.0, 0.0, + 0.0, 0.0, 5.0, + 0.0, 0.0, 0.0, + -6.0, 1.0, 3.0 + }; + auto dslopesdp = std::vector(spline.n_nodes() * n_params); + spline.compute_coefficients(); + spline.compute_coefficients_sensi(n_params, 0, dvaluesdp, dslopesdp); + std::vector>> expectations = { + // t, expected values of sensitivities + {0.00, { 3.0, 1.0, 0.0}}, + {0.25, { 0.679688, 0.226562, 4.21875}}, + {1.0/3, { 0.0, 0.0, 5.0}}, + {0.50, {0.1875, -0.125, 2.625}}, + {2.0/3, { 0.0, 0.0, 0.0}}, + {0.75, {-1.35938, 0.226562, 0.328125}}, + {1.00, {-6.0, 1.0, 3.0}}, + }; + test_spline_sensitivities(spline, expectations, 1e-5, 0.0); + ASSERT_THROW(spline.get_sensitivity(-0.05, 0), AmiException); + ASSERT_THROW(spline.get_sensitivity( 1.05, 1), AmiException); +} + +TEST(Splines, SplineLogarithmicSensitivity) +{ + HermiteSpline spline({ 0.0, 1.0 }, + { 2.5, 3.25, 1.0, 4.5 }, + {}, + SplineBoundaryCondition::given, + SplineBoundaryCondition::given, + SplineExtrapolation::noExtrapolation, + SplineExtrapolation::noExtrapolation, + true, // node_derivative_by_FD + true, // equidistant_spacing + true); // logarithmic_parametrization + int n_params = 3; + std::vector dvaluesdp = { + 3.0, 1.0, 0.0, + 0.0, 0.0, 5.0, + 0.0, 0.0, 0.0, + -6.0, 1.0, 3.0 + }; + auto dslopesdp = std::vector(spline.n_nodes() * n_params); + spline.compute_coefficients(); + spline.compute_coefficients_sensi(n_params, 0, dvaluesdp, dslopesdp); + std::vector>> expectations = { + // t, expected values of sensitivities + {0.00, { 3.0, 1.0, 0.0}}, + {0.25, { 0.585881, 0.195294, 4.38532}}, + {1.0/3, { 0.0, 0.0, 5.0}}, + {0.50, { 0.514003, -0.132395, 1.52044}}, + {2.0/3, { 0.0, 0.0, 0.0}}, + {0.75, {-0.820743, 0.13679, -0.0577988}}, + {1.00, {-6.0, 1.0, 3.0}}, + }; + test_spline_sensitivities(spline, expectations, 1e-6, 1e-6); + ASSERT_THROW(spline.get_sensitivity(-0.05, 0), AmiException); + ASSERT_THROW(spline.get_sensitivity( 1.05, 1), AmiException); +} + +TEST(Splines, SplineFinalValue_ConstantExtrapolation) +{ + HermiteSpline spline({ 0.0, 1.0 }, + { 2.5, 3.25, 1.0, 4.5 }, + {}, + SplineBoundaryCondition::given, + SplineBoundaryCondition::given, + SplineExtrapolation::noExtrapolation, + SplineExtrapolation::constant, + true, // node_derivative_by_FD + true, // equidistant_spacing + false); // logarithmic_parametrization + int n_params = 3; + std::vector dvaluesdp = { + 3.0, 1.0, 0.0, + 0.0, 0.0, 5.0, + 0.0, 0.0, 0.0, + -6.0, 1.0, 3.0 + }; + auto dslopesdp = std::vector(spline.n_nodes() * n_params); + spline.compute_coefficients(); + spline.compute_coefficients_sensi(n_params, 0, dvaluesdp, dslopesdp); + spline.compute_final_value(); + spline.compute_final_sensitivity(n_params, 0, dvaluesdp, dslopesdp); + ASSERT_DOUBLE_EQ(spline.get_final_value(), 4.5); + ASSERT_DOUBLE_EQ(spline.get_final_sensitivity(0), -6.0); + ASSERT_DOUBLE_EQ(spline.get_final_sensitivity(1), 1.0); + ASSERT_DOUBLE_EQ(spline.get_final_sensitivity(2), 3.0); +} + +TEST(Splines, SplineFinalValue_LinearExtrapolationPositiveDerivative) +{ + HermiteSpline spline({ 0.0, 1.0 }, + { 2.5, 3.25, 1.0, 4.5 }, + {}, + SplineBoundaryCondition::given, + SplineBoundaryCondition::given, + SplineExtrapolation::noExtrapolation, + SplineExtrapolation::linear, + true, // node_derivative_by_FD + true, // equidistant_spacing + false); // logarithmic_parametrization + int n_params = 3; + std::vector dvaluesdp = { + 3.0, 1.0, 0.0, + 0.0, 0.0, 5.0, + 0.0, 0.0, 0.0, + -6.0, 1.0, 3.0 + }; + auto dslopesdp = std::vector(spline.n_nodes() * n_params); + spline.compute_coefficients(); + spline.compute_coefficients_sensi(n_params, 0, dvaluesdp, dslopesdp); + spline.compute_final_value(); + spline.compute_final_sensitivity(n_params, 0, dvaluesdp, dslopesdp); + ASSERT_DOUBLE_EQ(spline.get_final_value(), INFINITY); + ASSERT_DOUBLE_EQ(spline.get_final_sensitivity(0), 0.0); + ASSERT_DOUBLE_EQ(spline.get_final_sensitivity(1), 0.0); + ASSERT_DOUBLE_EQ(spline.get_final_sensitivity(2), 0.0); +} + +TEST(Splines, SplineFinalValue_LinearExtrapolationNegativeDerivative) +{ + HermiteSpline spline({ 0.0, 1.0 }, + { 2.5, 3.25, 1.0, 0.0 }, + {}, + SplineBoundaryCondition::given, + SplineBoundaryCondition::given, + SplineExtrapolation::noExtrapolation, + SplineExtrapolation::linear, + true, // node_derivative_by_FD + true, // equidistant_spacing + false); // logarithmic_parametrization + int n_params = 3; + std::vector dvaluesdp = { + 3.0, 1.0, 0.0, + 0.0, 0.0, 5.0, + 0.0, 0.0, 0.0, + -6.0, 1.0, 3.0 + }; + auto dslopesdp = std::vector(spline.n_nodes() * n_params); + spline.compute_coefficients(); + spline.compute_coefficients_sensi(n_params, 0, dvaluesdp, dslopesdp); + spline.compute_final_value(); + spline.compute_final_sensitivity(n_params, 0, dvaluesdp, dslopesdp); + ASSERT_DOUBLE_EQ(spline.get_final_value(), -INFINITY); + ASSERT_DOUBLE_EQ(spline.get_final_sensitivity(0), 0.0); + ASSERT_DOUBLE_EQ(spline.get_final_sensitivity(1), 0.0); + ASSERT_DOUBLE_EQ(spline.get_final_sensitivity(2), 0.0); +} + +TEST(Splines, SplineFinalValue_LinearExtrapolationZeroDerivative) +{ + HermiteSpline spline({ 0.0, 1.0 }, + { 2.5, 3.25, 1.0, 1.0 }, + {}, + SplineBoundaryCondition::given, + SplineBoundaryCondition::given, + SplineExtrapolation::noExtrapolation, + SplineExtrapolation::linear, + true, // node_derivative_by_FD + true, // equidistant_spacing + false); // logarithmic_parametrization + int n_params = 3; + std::vector dvaluesdp = { + 3.0, 1.0, 0.0, + 0.0, 0.0, 5.0, + 0.0, 0.0, 0.0, + -6.0, 1.0, 3.0 + }; + auto dslopesdp = std::vector(spline.n_nodes() * n_params); + spline.compute_coefficients(); + spline.compute_coefficients_sensi(n_params, 0, dvaluesdp, dslopesdp); + spline.compute_final_value(); + spline.compute_final_sensitivity(n_params, 0, dvaluesdp, dslopesdp); + ASSERT_DOUBLE_EQ(spline.get_final_value(), 1.0); + ASSERT_TRUE(std::isnan(spline.get_final_sensitivity(0))); + ASSERT_TRUE(std::isnan(spline.get_final_sensitivity(1))); + ASSERT_TRUE(std::isnan(spline.get_final_sensitivity(2))); +} + +TEST(Splines, SplineFinalValue_LinearExtrapolationZeroDerivativeByBC) +{ + HermiteSpline spline({ 0.0, 1.0 }, + { 2.5, 3.25, 1.0, 2.0 }, + {}, + SplineBoundaryCondition::given, + SplineBoundaryCondition::zeroDerivative, + SplineExtrapolation::noExtrapolation, + SplineExtrapolation::linear, + true, // node_derivative_by_FD + true, // equidistant_spacing + false); // logarithmic_parametrization + int n_params = 3; + std::vector dvaluesdp = { + 3.0, 1.0, 0.0, + 0.0, 0.0, 5.0, + 0.0, 0.0, 0.0, + -6.0, 1.0, 3.0 + }; + auto dslopesdp = std::vector(spline.n_nodes() * n_params); + spline.compute_coefficients(); + spline.compute_coefficients_sensi(n_params, 0, dvaluesdp, dslopesdp); + spline.compute_final_value(); + spline.compute_final_sensitivity(n_params, 0, dvaluesdp, dslopesdp); + ASSERT_DOUBLE_EQ(spline.get_final_value(), 2.0); + ASSERT_DOUBLE_EQ(spline.get_final_sensitivity(0), -6.0); + ASSERT_DOUBLE_EQ(spline.get_final_sensitivity(1), 1.0); + ASSERT_DOUBLE_EQ(spline.get_final_sensitivity(2), 3.0); +} + +TEST(Splines, SplineFinalValue_PolynomialExtrapolationPositive) +{ + HermiteSpline spline({ 0.0, 1.0 }, + { -8.0, -6.0, -1.0, -2.0 }, + {}, + SplineBoundaryCondition::given, + SplineBoundaryCondition::given, + SplineExtrapolation::noExtrapolation, + SplineExtrapolation::polynomial, + true, // node_derivative_by_FD + true, // equidistant_spacing + false); // logarithmic_parametrization + int n_params = 3; + std::vector dvaluesdp = { + 3.0, 1.0, 0.0, + 0.0, 0.0, 5.0, + 0.0, 0.0, 0.0, + -6.0, 1.0, 3.0 + }; + auto dslopesdp = std::vector(spline.n_nodes() * n_params); + spline.compute_coefficients(); + spline.compute_coefficients_sensi(n_params, 0, dvaluesdp, dslopesdp); + spline.compute_final_value(); + spline.compute_final_sensitivity(n_params, 0, dvaluesdp, dslopesdp); + ASSERT_DOUBLE_EQ(spline.get_final_value(), INFINITY); + /* NB sensitivities for this case are not implemented, since they are unlikely to be used*/ + ASSERT_TRUE(std::isnan(spline.get_final_sensitivity(0))); + ASSERT_TRUE(std::isnan(spline.get_final_sensitivity(1))); + ASSERT_TRUE(std::isnan(spline.get_final_sensitivity(2))); +} + +TEST(Splines, SplineFinalValue_PolynomialExtrapolationNegative) +{ + HermiteSpline spline({ 0.0, 1.0 }, + { 2.5, 3.25, 1.0, 2.0 }, + {}, + SplineBoundaryCondition::given, + SplineBoundaryCondition::given, + SplineExtrapolation::noExtrapolation, + SplineExtrapolation::polynomial, + true, // node_derivative_by_FD + true, // equidistant_spacing + false); // logarithmic_parametrization + int n_params = 3; + std::vector dvaluesdp = { + 3.0, 1.0, 0.0, + 0.0, 0.0, 5.0, + 0.0, 0.0, 0.0, + -6.0, 1.0, 3.0 + }; + auto dslopesdp = std::vector(spline.n_nodes() * n_params); + spline.compute_coefficients(); + spline.compute_coefficients_sensi(n_params, 0, dvaluesdp, dslopesdp); + spline.compute_final_value(); + spline.compute_final_sensitivity(n_params, 0, dvaluesdp, dslopesdp); + ASSERT_DOUBLE_EQ(spline.get_final_value(), -INFINITY); + /* NB sensitivities for this case are not implemented, since they are unlikely to be used*/ + ASSERT_TRUE(std::isnan(spline.get_final_sensitivity(0))); + ASSERT_TRUE(std::isnan(spline.get_final_sensitivity(1))); + ASSERT_TRUE(std::isnan(spline.get_final_sensitivity(2))); +} + +TEST(Splines, SplineFinalValue_PeriodicExtrapolation) +{ + // Uniform grid + HermiteSpline spline({ 0.0, 1.0 }, + { 1.0, 2.0, 0.5, 1.0 }, + {}, + SplineBoundaryCondition::periodic, + SplineBoundaryCondition::periodic, + SplineExtrapolation::periodic, + SplineExtrapolation::periodic, + true, // node_derivative_by_FD + true, // equidistant_spacing + false); // logarithmic_parametrization + int n_params = 3; + std::vector dvaluesdp = { + 3.0, 1.0, 0.0, + 0.0, 0.0, 5.0, + 0.0, 0.0, 0.0, + -6.0, 1.0, 3.0 + }; + auto dslopesdp = std::vector(spline.n_nodes() * n_params); + spline.compute_coefficients(); + spline.compute_coefficients_sensi(n_params, 0, dvaluesdp, dslopesdp); + spline.compute_final_value(); + spline.compute_final_sensitivity(n_params, 0, dvaluesdp, dslopesdp); + ASSERT_TRUE(std::isnan(spline.get_final_value())); + ASSERT_TRUE(std::isnan(spline.get_final_sensitivity(0))); + ASSERT_TRUE(std::isnan(spline.get_final_sensitivity(1))); + ASSERT_TRUE(std::isnan(spline.get_final_sensitivity(2))); +} + +TEST(Splines, SplineFinalValue_PeriodicExtrapolationConstant) +{ + // Uniform grid + HermiteSpline spline({ 0.0, 1.0 }, + { 1.0, 1.0, 1.0, 1.0 }, + {}, + SplineBoundaryCondition::periodic, + SplineBoundaryCondition::periodic, + SplineExtrapolation::periodic, + SplineExtrapolation::periodic, + true, // node_derivative_by_FD + true, // equidistant_spacing + false); // logarithmic_parametrization + int n_params = 3; + std::vector dvaluesdp = { + 3.0, 1.0, 0.0, + 0.0, 0.0, 5.0, + 0.0, 0.0, 0.0, + -6.0, 1.0, 3.0 + }; + auto dslopesdp = std::vector(spline.n_nodes() * n_params); + spline.compute_coefficients(); + spline.compute_coefficients_sensi(n_params, 0, dvaluesdp, dslopesdp); + spline.compute_final_value(); + spline.compute_final_sensitivity(n_params, 0, dvaluesdp, dslopesdp); + ASSERT_DOUBLE_EQ(spline.get_final_value(), 1.0); + ASSERT_TRUE(std::isnan(spline.get_final_sensitivity(0))); + ASSERT_TRUE(std::isnan(spline.get_final_sensitivity(1))); + ASSERT_TRUE(std::isnan(spline.get_final_sensitivity(2))); +} + +TEST(Splines, SplineFinalValue_LogarithmicPositiveDerivative) +{ + HermiteSpline spline({ 0.0, 1.0 }, + { 2.5, 3.25, 1.0, 4.5 }, + {}, + SplineBoundaryCondition::given, + SplineBoundaryCondition::given, + SplineExtrapolation::noExtrapolation, + SplineExtrapolation::linear, + true, // node_derivative_by_FD + true, // equidistant_spacing + true); // logarithmic_parametrization + int n_params = 3; + std::vector dvaluesdp = { + 3.0, 1.0, 0.0, + 0.0, 0.0, 5.0, + 0.0, 0.0, 0.0, + -6.0, 1.0, 3.0 + }; + auto dslopesdp = std::vector(spline.n_nodes() * n_params); + spline.compute_coefficients(); + spline.compute_coefficients_sensi(n_params, 0, dvaluesdp, dslopesdp); + spline.compute_final_value(); + spline.compute_final_sensitivity(n_params, 0, dvaluesdp, dslopesdp); + ASSERT_DOUBLE_EQ(spline.get_final_value(), INFINITY); + ASSERT_DOUBLE_EQ(spline.get_final_sensitivity(0), 0.0); + ASSERT_DOUBLE_EQ(spline.get_final_sensitivity(1), 0.0); + ASSERT_DOUBLE_EQ(spline.get_final_sensitivity(2), 0.0); +} + +TEST(Splines, SplineFinalValue_LogarithmicNegativeDerivative) +{ + HermiteSpline spline({ 0.0, 1.0 }, + { 2.5, 3.25, 1.0, 0.5 }, + {}, + SplineBoundaryCondition::given, + SplineBoundaryCondition::given, + SplineExtrapolation::noExtrapolation, + SplineExtrapolation::linear, + true, // node_derivative_by_FD + true, // equidistant_spacing + true); // logarithmic_parametrization + int n_params = 3; + std::vector dvaluesdp = { + 3.0, 1.0, 0.0, + 0.0, 0.0, 5.0, + 0.0, 0.0, 0.0, + -6.0, 1.0, 3.0 + }; + auto dslopesdp = std::vector(spline.n_nodes() * n_params); + spline.compute_coefficients(); + spline.compute_coefficients_sensi(n_params, 0, dvaluesdp, dslopesdp); + spline.compute_final_value(); + spline.compute_final_sensitivity(n_params, 0, dvaluesdp, dslopesdp); + ASSERT_DOUBLE_EQ(spline.get_final_value(), 0.0); + ASSERT_DOUBLE_EQ(spline.get_final_sensitivity(0), 0.0); + ASSERT_DOUBLE_EQ(spline.get_final_sensitivity(1), 0.0); + ASSERT_DOUBLE_EQ(spline.get_final_sensitivity(2), 0.0); +} + +TEST(Splines, SplineFinalValue_LogarithmicZeroDerivative) +{ + HermiteSpline spline({ 0.0, 1.0 }, + { 2.5, 3.25, 1.0, 0.5 }, + {}, + SplineBoundaryCondition::given, + SplineBoundaryCondition::given, + SplineExtrapolation::noExtrapolation, + SplineExtrapolation::constant, + true, // node_derivative_by_FD + true, // equidistant_spacing + true); // logarithmic_parametrization + int n_params = 3; + std::vector dvaluesdp = { + 3.0, 1.0, 0.0, + 0.0, 0.0, 5.0, + 0.0, 0.0, 0.0, + -6.0, 1.0, 3.0 + }; + auto dslopesdp = std::vector(spline.n_nodes() * n_params); + spline.compute_coefficients(); + spline.compute_coefficients_sensi(n_params, 0, dvaluesdp, dslopesdp); + spline.compute_final_value(); + spline.compute_final_sensitivity(n_params, 0, dvaluesdp, dslopesdp); + ASSERT_DOUBLE_EQ(spline.get_final_value(), 0.5); + ASSERT_DOUBLE_EQ(spline.get_final_sensitivity(0), -6.0); + ASSERT_DOUBLE_EQ(spline.get_final_sensitivity(1), 1.0); + ASSERT_DOUBLE_EQ(spline.get_final_sensitivity(2), 3.0); +} diff --git a/deps/AMICI/tests/cpp/wrapTestModels.m b/deps/AMICI/tests/cpp/wrapTestModels.m index 28963e2e3..80d8d0593 100644 --- a/deps/AMICI/tests/cpp/wrapTestModels.m +++ b/deps/AMICI/tests/cpp/wrapTestModels.m @@ -4,14 +4,14 @@ function wrapTestModels() % % Return values: % void - + amiciPath = fileparts(mfilename('fullpath')); amiciPath = [amiciPath '/../../matlab']; - + %% EXAMPLE STEADYSTATE - + cd([amiciPath '/examples/example_steadystate/']); - + try [exdir,~,~]=fileparts(which('example_steadystate.m')); amiwrap('model_steadystate','model_steadystate_syms',exdir); @@ -19,10 +19,10 @@ function wrapTestModels() disp(err.message) cd(fileparts(mfilename('fullpath'))); end - + %% EXAMPLE DIRAC cd([amiciPath '/examples/example_dirac/']); - + try [exdir,~,~]=fileparts(which('example_dirac.m')); amiwrap('model_dirac','model_dirac_syms',exdir); @@ -30,10 +30,10 @@ function wrapTestModels() disp(err.message) cd(fileparts(mfilename('fullpath'))); end - + %% EXAMPLE JAKSTAT cd([amiciPath '/examples/example_jakstat_adjoint/']); - + try [exdir,~,~]=fileparts(which('example_jakstat_adjoint.m')); amiwrap('model_jakstat_adjoint', 'model_jakstat_adjoint_syms', exdir, 1); @@ -44,7 +44,7 @@ function wrapTestModels() %% EXAMPLE NEURON cd([amiciPath '/examples/example_neuron/']); - + try [exdir,~,~]=fileparts(which('example_neuron.m')); amiwrap('model_neuron', 'model_neuron_syms', exdir, 1); @@ -54,10 +54,10 @@ function wrapTestModels() end cd(fileparts(mfilename('fullpath'))); - + %% EXAMPLE EVENTS cd([amiciPath '/examples/example_events/']); - + try [exdir,~,~]=fileparts(which('example_events.m')); amiwrap('model_events', 'model_events_syms', exdir); @@ -65,10 +65,10 @@ function wrapTestModels() disp(err.message) cd(fileparts(mfilename('fullpath'))); end - + %% EXAMPLE NESTED EVENTS cd([amiciPath '/examples/example_nested_events/']); - + try [exdir,~,~]=fileparts(which('example_nested_events.m')); amiwrap('model_nested_events', 'model_nested_events_syms', exdir); @@ -78,10 +78,10 @@ function wrapTestModels() end cd(fileparts(mfilename('fullpath'))); - + %% EXAMPLE ROBERTSON cd([amiciPath '/examples/example_robertson/']); - + try [exdir,~,~]=fileparts(which('example_robertson.m')); amiwrap('model_robertson', 'model_robertson_syms', exdir); @@ -91,10 +91,10 @@ function wrapTestModels() end cd(fileparts(mfilename('fullpath'))); - + %% EXAMPLE CALVETTI cd([amiciPath '/examples/example_calvetti/']); - + try [exdir,~,~]=fileparts(which('example_calvetti.m')); amiwrap('model_calvetti', 'model_calvetti_syms', exdir); @@ -104,6 +104,5 @@ function wrapTestModels() end cd(fileparts(mfilename('fullpath'))); - -end +end diff --git a/deps/AMICI/tests/generateTestConfig/example.py b/deps/AMICI/tests/generateTestConfig/example.py index 99a9d91cb..963fdf64c 100644 --- a/deps/AMICI/tests/generateTestConfig/example.py +++ b/deps/AMICI/tests/generateTestConfig/example.py @@ -2,83 +2,86 @@ import numpy as np import pandas as pd + def dict2hdf5(object, dictionary): for key, value in dictionary.items(): if isArray(value): a = np.array(value) if not len(value): - dtype = 'f8' + dtype = "f8" elif isArray(value[0]): if isinstance(value[0][0], (np.float64, float)): - dtype = 'f8' + dtype = "f8" else: - dtype = '&1 | tee "$LOG" diff --git a/deps/AMICI/tests/performance/reference.yml b/deps/AMICI/tests/performance/reference.yml index 085363b6a..f72387ae5 100644 --- a/deps/AMICI/tests/performance/reference.yml +++ b/deps/AMICI/tests/performance/reference.yml @@ -1,5 +1,5 @@ # Reference wall times (seconds) with some buffer -create_sdist: 5 +create_sdist: 16 install_sdist: 150 petab_import: 2100 install_model: 120 @@ -8,7 +8,7 @@ install_model_O1: 90 install_model_O2: 120 forward_simulation: 2 forward_sensitivities: 2 -adjoint_sensitivities: 2 +adjoint_sensitivities: 2.5 forward_simulation_non_optimal_parameters: 2 adjoint_sensitivities_non_optimal_parameters: 5 forward_steadystate_sensitivities_non_optimal_parameters: 5 diff --git a/deps/AMICI/tests/performance/test.py b/deps/AMICI/tests/performance/test.py index cf58c116b..c497577ab 100755 --- a/deps/AMICI/tests/performance/test.py +++ b/deps/AMICI/tests/performance/test.py @@ -1,109 +1,136 @@ #!/usr/bin/env python3 - -import amici -import sys -import petab -import subprocess +import logging import os import re import shutil +import subprocess +import sys +from pathlib import Path +import amici +import petab from amici.petab_import import import_model def parse_args(): arg = sys.argv[1] - if '_' in arg and re.match(r'O[0-2]', arg.split("_")[-1]): + if "_" in arg and re.match(r"O[0-2]", arg.split("_")[-1]): optim = arg.split("_")[-1] - os.environ['AMICI_CXXFLAGS'] = f'-{optim}' - suffix = f'_{optim}' - arg = '_'.join(arg.split("_")[:-1]) + os.environ["AMICI_CXXFLAGS"] = f"-{optim}" + print(f"{os.environ['AMICI_CXXFLAGS']=}") + suffix = f"_{optim}" + arg = "_".join(arg.split("_")[:-1]) else: - suffix = '' + suffix = "" return arg, suffix def check_results(rdata): - diagnostics = ['numsteps', 'numstepsB', 'numrhsevals', 'numrhsevalsB', - 'numerrtestfails', 'numerrtestfailsB', - 'numnonlinsolvconvfails', 'numnonlinsolvconvfailsB', - 'preeq_cpu_time', 'preeq_cpu_timeB', - 'cpu_time', 'cpu_timeB', - 'posteq_cpu_time', 'posteq_cpu_timeB'] + diagnostics = [ + "numsteps", + "numstepsB", + "numrhsevals", + "numrhsevalsB", + "numerrtestfails", + "numerrtestfailsB", + "numnonlinsolvconvfails", + "numnonlinsolvconvfailsB", + "preeq_status", + "preeq_numsteps", + "preeq_numstepsB", + "preeq_cpu_time", + "preeq_cpu_timeB", + "cpu_time_total", + "cpu_time", + "cpu_timeB", + "posteq_status", + "posteq_cpu_time", + "posteq_cpu_timeB", + "posteq_numsteps", + "posteq_numstepsB", + ] for d in diagnostics: print(d, rdata[d]) - assert rdata['status'] == amici.AMICI_SUCCESS - - -def run_import(model_name): - git_dir = os.path.join(os.curdir, 'CS_Signalling_ERBB_RAS_AKT') - if not os.path.exists(git_dir): - subprocess.run([ - 'git', 'clone', '--depth', '1', - 'https://github.com/ICB-DCM/CS_Signalling_ERBB_RAS_AKT'] + assert rdata["status"] == amici.AMICI_SUCCESS + + +def run_import(model_name, model_dir: Path): + git_dir = Path("CS_Signalling_ERBB_RAS_AKT") + if not git_dir.exists(): + subprocess.run( + [ + "git", + "clone", + "--depth", + "1", + "https://github.com/ICB-DCM/CS_Signalling_ERBB_RAS_AKT", + ] ) - os.chdir(os.path.join(os.curdir, 'CS_Signalling_ERBB_RAS_AKT')) pp = petab.Problem.from_yaml( - 'FroehlichKes2018/PEtab/FroehlichKes2018.yaml' + git_dir / "FroehlichKes2018" / "PEtab" / "FroehlichKes2018.yaml" ) petab.lint_problem(pp) - import_model(model_name=model_name, - sbml_model=pp.sbml_model, - condition_table=pp.condition_df, - observable_table=pp.observable_df, - measurement_table=pp.measurement_df, - compile=False, - verbose=True) - - -def compile_model(model_name, model_dir): - if model_name != os.path.basename(model_dir): - shutil.copytree( - os.path.join(os.curdir, 'CS_Signalling_ERBB_RAS_AKT', - model_name), - model_dir - ) + amici.de_export.logger.setLevel(logging.DEBUG) + amici.sbml_import.logger.setLevel(logging.DEBUG) + 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, + compile=False, + verbose=True, + ) - subprocess.run(['python', 'setup.py', - 'build_ext', f'--build-lib=.', '--force'], - cwd=model_dir) + +def compile_model(model_dir_source: Path, model_dir_compiled: Path): + if model_dir_source != model_dir_compiled: + shutil.copytree(model_dir_source, model_dir_compiled) + + cmd = ["python", "setup.py", "build_ext", "--build-lib=.", "--force"] + print("Running:", " ".join(cmd)) + subprocess.run(cmd, cwd=model_dir_compiled, check=True) def prepare_simulation(arg, model, solver, edata): - if arg == 'forward_simulation': + if arg == "forward_simulation": solver.setSensitivityMethod(amici.SensitivityMethod.none) solver.setSensitivityOrder(amici.SensitivityOrder.none) - elif arg == 'forward_sensitivities': + elif arg == "forward_sensitivities": model.setParameterList(list(range(100))) solver.setSensitivityMethod(amici.SensitivityMethod.forward) solver.setSensitivityOrder(amici.SensitivityOrder.first) - elif arg == 'adjoint_sensitivities': + elif arg == "adjoint_sensitivities": solver.setSensitivityMethod(amici.SensitivityMethod.adjoint) solver.setSensitivityOrder(amici.SensitivityOrder.first) - elif arg == 'forward_simulation_non_optimal_parameters': + elif arg == "forward_simulation_non_optimal_parameters": tmp_par = model.getParameters() model.setParameters([0.1 for _ in tmp_par]) solver.setSensitivityMethod(amici.SensitivityMethod.none) solver.setSensitivityOrder(amici.SensitivityOrder.none) - elif arg == 'adjoint_sensitivities_non_optimal_parameters': + elif arg == "adjoint_sensitivities_non_optimal_parameters": tmp_par = model.getParameters() model.setParameters([0.1 for _ in tmp_par]) solver.setSensitivityMethod(amici.SensitivityMethod.adjoint) solver.setSensitivityOrder(amici.SensitivityOrder.first) - elif arg == 'forward_steadystate_sensitivities_non_optimal_parameters': + elif arg == "forward_steadystate_sensitivities_non_optimal_parameters": tmp_par = model.getParameters() model.setParameters([0.1 for _ in tmp_par]) solver.setSensitivityMethod(amici.SensitivityMethod.forward) solver.setSensitivityOrder(amici.SensitivityOrder.first) - edata.setTimepoints([float('inf')]) - elif arg == 'adjoint_steadystate_sensitivities_non_optimal_parameters': + model.setSteadyStateSensitivityMode( + amici.SteadyStateSensitivityMode.newtonOnly + ) + edata.setTimepoints([float("inf")]) + elif arg == "adjoint_steadystate_sensitivities_non_optimal_parameters": tmp_par = model.getParameters() model.setParameters([0.1 for _ in tmp_par]) solver.setSensitivityMethod(amici.SensitivityMethod.adjoint) solver.setSensitivityOrder(amici.SensitivityOrder.first) - edata.setTimepoints([float('inf')]) + edata.setTimepoints([float("inf")]) else: print("Unknown argument:", arg) sys.exit(1) @@ -112,18 +139,23 @@ def prepare_simulation(arg, model, solver, edata): def main(): arg, suffix = parse_args() - model_dir = os.path.join(os.curdir, 'CS_Signalling_ERBB_RAS_AKT', - 'CS_Signalling_ERBB_RAS_AKT_petab' + suffix) - model_name = 'CS_Signalling_ERBB_RAS_AKT_petab' + # Model is imported once to this directory + model_dir_source = Path("model_performance_test") + # and copied to and compiled in this directory with different compiler + # options + model_dir_compiled = Path(f"model_performance_test_{suffix}") + model_name = "model_performance_test" - if arg == 'import': - run_import(model_name) + if arg == "import": + run_import(model_name, model_dir_source) return - elif arg == 'compile': - compile_model(model_name, model_dir) + elif arg == "compile": + compile_model(model_dir_source, model_dir_compiled) return else: - model_module = amici.import_model_module(model_name, model_dir) + model_module = amici.import_model_module( + model_name, model_dir_compiled + ) model = model_module.getModel() solver = model.getSolver() # TODO @@ -139,5 +171,5 @@ def main(): check_results(rdata) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/deps/AMICI/tests/petab_test_suite/conftest.py b/deps/AMICI/tests/petab_test_suite/conftest.py index 2a4c9cdd6..df0b00ee8 100644 --- a/deps/AMICI/tests/petab_test_suite/conftest.py +++ b/deps/AMICI/tests/petab_test_suite/conftest.py @@ -1,9 +1,10 @@ """Pytest configuration for PEtab test suite""" -from typing import List import re import sys -import petabtests +from typing import List + +from petabtests.core import get_cases def parse_selection(selection_str: str) -> List[int]: @@ -14,11 +15,11 @@ def parse_selection(selection_str: str) -> List[int]: Valid input e.g.: "1", "1,3", "-3,4,6-7" """ indices = [] - for group in selection_str.split(','): - if not re.match(r'^(?:-?\d+)|(?:\d+(?:-\d+))$', group): + for group in selection_str.split(","): + if not re.match(r"^(?:-?\d+)|(?:\d+(?:-\d+))$", group): print("Invalid selection", group) sys.exit() - spl = group.split('-') + spl = group.split("-") if len(spl) == 1: indices.append(int(spl[0])) elif len(spl) == 2: @@ -31,18 +32,24 @@ def parse_selection(selection_str: str) -> List[int]: def pytest_addoption(parser): """Add pytest CLI options""" parser.addoption("--petab-cases", help="Test cases to run") - parser.addoption("--only-pysb", help="Run only PySB tests", - action="store_true") - parser.addoption("--only-sbml", help="Run only SBML tests", - action="store_true", ) + parser.addoption( + "--only-pysb", help="Run only PySB tests", action="store_true" + ) + parser.addoption( + "--only-sbml", + help="Run only SBML tests", + action="store_true", + ) def pytest_generate_tests(metafunc): """Parameterize tests""" # Run for all PEtab test suite cases - if "case" in metafunc.fixturenames \ - and "model_type" in metafunc.fixturenames: + if ( + "case" in metafunc.fixturenames + and "model_type" in metafunc.fixturenames + ): # Get CLI option cases = metafunc.config.getoption("--petab-cases") if cases: @@ -50,15 +57,33 @@ def pytest_generate_tests(metafunc): test_numbers = parse_selection(cases) else: # Run all tests - test_numbers = petabtests.CASES_LIST + test_numbers = None if metafunc.config.getoption("--only-sbml"): - model_types = ['sbml'] + argvalues = [ + (case, "sbml", version) + for version in ("v1.0.0", "v2.0.0") + for case in ( + test_numbers + if test_numbers + else get_cases("sbml", version=version) + ) + ] elif metafunc.config.getoption("--only-pysb"): - model_types = ['pysb'] + argvalues = [ + (case, "pysb", "v2.0.0") + for case in ( + test_numbers + if test_numbers + else get_cases("pysb", version="v2.0.0") + ) + ] else: - model_types = ['sbml', 'pysb'] - - argvalues = [(case, model_type) for model_type in model_types - for case in test_numbers] - metafunc.parametrize("case,model_type", argvalues) + argvalues = [] + for version in ("v1.0.0", "v2.0.0"): + for format in ("sbml", "pysb"): + argvalues.extend( + (case, format, version) + for case in test_numbers or get_cases(format, version) + ) + metafunc.parametrize("case,model_type,version", argvalues) 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 59c13c72b..35ee3adcf 100755 --- a/deps/AMICI/tests/petab_test_suite/test_petab_suite.py +++ b/deps/AMICI/tests/petab_test_suite/test_petab_suite.py @@ -1,22 +1,24 @@ #!/usr/bin/env python3 - """Run PEtab test suite (https://github.com/PEtab-dev/petab_test_suite)""" import logging -import os import sys import amici +import pandas as pd import 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, PysbPetabProblem +from amici.petab_import import import_petab_problem from amici.petab_objective import ( - simulate_petab, rdatas_to_measurement_df, create_parameterized_edatas) -from amici import SteadyStateSensitivityMode_simulationFSA + create_parameterized_edatas, + rdatas_to_measurement_df, + simulate_petab, +) logger = get_logger(__name__, logging.DEBUG) set_log_level(get_logger("amici.petab_import"), logging.DEBUG) @@ -24,72 +26,79 @@ logger.addHandler(stream_handler) -def test_case(case, model_type): +def test_case(case, model_type, version): """Wrapper for _test_case for handling test outcomes""" try: - _test_case(case, model_type) + _test_case(case, model_type, version) except Exception as e: - if isinstance(e, NotImplementedError) \ - or "Timepoint-specific parameter overrides" in str(e): - logger.info(f"Case {case} expectedly failed. " - "Required functionality is not yet " - f"implemented: {e}") + if isinstance( + e, NotImplementedError + ) or "Timepoint-specific parameter overrides" in str(e): + logger.info( + f"Case {case} expectedly failed. " + "Required functionality is not yet " + f"implemented: {e}" + ) pytest.skip(str(e)) else: raise e -def _test_case(case, model_type): +def _test_case(case, model_type, version): """Run a single PEtab test suite case""" case = petabtests.test_id_str(case) - logger.debug(f"Case {case} [{model_type}]") + logger.debug(f"Case {case} [{model_type}] [{version}]") # load - if model_type == "sbml": - case_dir = os.path.join(petabtests.SBML_DIR, case) - # import petab problem - yaml_file = os.path.join(case_dir, petabtests.problem_yaml_name(case)) - problem = petab.Problem.from_yaml(yaml_file) - elif model_type == "pysb": - import pysb - pysb.SelfExporter.cleanup() - pysb.SelfExporter.do_export = True - case_dir = os.path.join(petabtests.PYSB_DIR, case) - # import petab problem - yaml_file = os.path.join(case_dir, petabtests.problem_yaml_name(case)) - problem = PysbPetabProblem.from_yaml(yaml_file, - flatten=case.startswith('0006')) - else: - raise ValueError(f"Unsupported model_type: {model_type}") + case_dir = petabtests.get_case_dir(case, model_type, version) + yaml_file = case_dir / petabtests.problem_yaml_name(case) + problem = petab.Problem.from_yaml(yaml_file) # compile amici model - if case.startswith('0006') and model_type != "pysb": + if case.startswith("0006"): petab.flatten_timepoint_specific_output_overrides(problem) - model_output_dir = f'amici_models/model_{case}' + model_name = ( + f"petab_{model_type}_test_case_{case}" f"_{version.replace('.', '_')}" + ) + model_output_dir = f"amici_models/{model_name}" model = import_petab_problem( - problem, model_output_dir=model_output_dir, - force_compile=True) + petab_problem=problem, + model_output_dir=model_output_dir, + model_name=model_name, + force_compile=True, + ) + solver = model.getSolver() + solver.setSteadyStateToleranceFactor(1.0) # simulate - ret = simulate_petab(problem, model, log_level=logging.DEBUG) - - rdatas = ret['rdatas'] - chi2 = sum(rdata['chi2'] for rdata in rdatas) - llh = ret['llh'] - simulation_df = rdatas_to_measurement_df(rdatas, model, - problem.measurement_df) + ret = simulate_petab( + problem, + model, + solver=solver, + log_level=logging.DEBUG, + ) + + rdatas = ret["rdatas"] + chi2 = sum(rdata["chi2"] for rdata in rdatas) + llh = ret["llh"] + simulation_df = rdatas_to_measurement_df( + rdatas, model, problem.measurement_df + ) petab.check_measurement_df(simulation_df, problem.observable_df) simulation_df = simulation_df.rename( - columns={petab.MEASUREMENT: petab.SIMULATION}) + columns={petab.MEASUREMENT: petab.SIMULATION} + ) simulation_df[petab.TIME] = simulation_df[petab.TIME].astype(int) - solution = petabtests.load_solution(case, model_type) + solution = petabtests.load_solution(case, model_type, version=version) gt_chi2 = solution[petabtests.CHI2] gt_llh = solution[petabtests.LLH] gt_simulation_dfs = solution[petabtests.SIMULATION_DFS] - if case.startswith('0006'): + if case.startswith("0006"): # account for flattening - gt_simulation_dfs[0].loc[:, petab.OBSERVABLE_ID] = ('obs_a__10__c0', - 'obs_a__15__c0') + gt_simulation_dfs[0].loc[:, petab.OBSERVABLE_ID] = ( + "obs_a__10__c0", + "obs_a__15__c0", + ) tol_chi2 = solution[petabtests.TOL_CHI2] tol_llh = solution[petabtests.TOL_LLH] tol_simulations = solution[petabtests.TOL_SIMULATIONS] @@ -97,48 +106,80 @@ def _test_case(case, model_type): chi2s_match = petabtests.evaluate_chi2(chi2, gt_chi2, tol_chi2) llhs_match = petabtests.evaluate_llh(llh, gt_llh, tol_llh) simulations_match = petabtests.evaluate_simulations( - [simulation_df], gt_simulation_dfs, tol_simulations) - - logger.log(logging.DEBUG if chi2s_match else logging.ERROR, - f"CHI2: simulated: {chi2}, expected: {gt_chi2}," - f" match = {chi2s_match}") - logger.log(logging.DEBUG if simulations_match else logging.ERROR, - f"LLH: simulated: {llh}, expected: {gt_llh}, " - f"match = {llhs_match}") - logger.log(logging.DEBUG if simulations_match else logging.ERROR, - f"Simulations: match = {simulations_match}") - - check_derivatives(problem, model) + [simulation_df], gt_simulation_dfs, tol_simulations + ) + + logger.log( + logging.DEBUG if simulations_match else logging.ERROR, + f"Simulations: match = {simulations_match}", + ) + if not simulations_match: + with pd.option_context( + "display.max_rows", + None, + "display.max_columns", + None, + "display.width", + 200, + ): + logger.log( + logging.DEBUG, + f"x_ss: {model.getStateIds()} " + f"{[rdata.x_ss for rdata in rdatas]}", + ) + logger.log( + logging.ERROR, f"Expected simulations:\n{gt_simulation_dfs}" + ) + logger.log(logging.ERROR, f"Actual simulations:\n{simulation_df}") + logger.log( + logging.DEBUG if chi2s_match else logging.ERROR, + f"CHI2: simulated: {chi2}, expected: {gt_chi2}," + f" match = {chi2s_match}", + ) + logger.log( + logging.DEBUG if simulations_match else logging.ERROR, + f"LLH: simulated: {llh}, expected: {gt_llh}, " f"match = {llhs_match}", + ) + + check_derivatives(problem, model, solver) if not all([llhs_match, simulations_match]) or not chi2s_match: logger.error(f"Case {case} failed.") - raise AssertionError(f"Case {case}: Test results do not match " - "expectations") + raise AssertionError( + f"Case {case}: Test results do not match " "expectations" + ) logger.info(f"Case {case} passed.") -def check_derivatives(problem: petab.Problem, model: amici.Model) -> None: +def check_derivatives( + problem: petab.Problem, model: amici.Model, solver: amici.Solver +) -> None: """Check derivatives using finite differences for all experimental conditions Arguments: problem: PEtab problem model: AMICI model matching ``problem`` + solver: AMICI solver """ - problem_parameters = {t.Index: getattr(t, petab.NOMINAL_VALUE) for t in - problem.parameter_df.itertuples()} - solver = model.getSolver() - solver.setSensitivityMethod(amici.SensitivityMethod_forward) - solver.setSensitivityOrder(amici.SensitivityOrder_first) + problem_parameters = { + t.Index: getattr(t, petab.NOMINAL_VALUE) + for t in problem.parameter_df.itertuples() + } + solver.setSensitivityMethod(amici.SensitivityMethod.forward) + solver.setSensitivityOrder(amici.SensitivityOrder.first) # Required for case 9 to not fail in # amici::NewtonSolver::computeNewtonSensis model.setSteadyStateSensitivityMode( - SteadyStateSensitivityMode_simulationFSA) + SteadyStateSensitivityMode.integrateIfNewtonFails + ) for edata in create_parameterized_edatas( - amici_model=model, petab_problem=problem, - problem_parameters=problem_parameters): + amici_model=model, + petab_problem=problem, + problem_parameters=problem_parameters, + ): # check_derivatives does currently not support parameters in ExpData model.setParameters(edata.parameters) model.setParameterScale(edata.pscale) @@ -149,25 +190,27 @@ def check_derivatives(problem: petab.Problem, model: amici.Model) -> None: def run(): """Run the full PEtab test suite""" - n_success = 0 n_skipped = 0 - for case in petabtests.CASES_LIST: - try: - test_case(case) - n_success += 1 - except Skipped: - n_skipped += 1 - except Exception as e: - # run all despite failures - logger.error(f"Case {case} failed.") - logger.error(e) - - logger.info(f"{n_success} / {len(petabtests.CASES_LIST)} successful, " - f"{n_skipped} skipped") - if n_success != len(petabtests.CASES_LIST): + n_total = 0 + for version in ("v1.0.0", "v2.0.0"): + cases = petabtests.get_cases("sbml", version=version) + n_total += len(cases) + for case in cases: + try: + test_case(case, "sbml", version=version) + n_success += 1 + except Skipped: + n_skipped += 1 + except Exception as e: + # run all despite failures + logger.error(f"Case {case} failed.") + logger.error(e) + + logger.info(f"{n_success} / {n_total} successful, " f"{n_skipped} skipped") + if n_success != len(cases): sys.exit(1) -if __name__ == '__main__': +if __name__ == "__main__": run() diff --git a/deps/AMICI/tests/testSBMLSuite.py b/deps/AMICI/tests/testSBMLSuite.py index 9a4c9f648..cfad477ac 100755 --- a/deps/AMICI/tests/testSBMLSuite.py +++ b/deps/AMICI/tests/testSBMLSuite.py @@ -4,22 +4,17 @@ [https://github.com/sbmlteam/sbml-test-suite/releases] Usage: - python tests/testSBMLSuite.py SELECTION - SELECTION can be e.g.: `1`, `1,3`, or `-3,4,6-7` to select specific - test cases or 1-1780 to run all. - - pytest tests.testSBMLSuite -n CORES --cases SELECTION + pytest tests.testSBMLSuite -n CORES --cases=SELECTION CORES can be an integer or `auto` for all available cores. - SELECTION same as above. + SELECTION can be e.g.: `1`, `1,3`, `-3,4,6-7`, or `100-` to select + specific test cases. If `--cases` is omitted, all cases are run. """ import copy -import importlib import os -import re import shutil import sys -from typing import Tuple, Set +from pathlib import Path import amici import libsbml as sbml @@ -27,18 +22,13 @@ import pandas as pd import pytest from amici.constants import SymbolId - -# directory with sbml semantic test cases -TEST_PATH = os.path.join(os.path.dirname(__file__), 'sbml-test-suite', 'cases', - 'semantic') +from amici.gradient_check import check_derivatives +from numpy.testing import assert_allclose @pytest.fixture(scope="session") -def result_path(): - # ensure directory for test results is empty - upload_result_path = os.path.join(os.path.dirname(__file__), - 'amici-semantic-results') - return upload_result_path +def result_path() -> Path: + return Path(__file__).parent / "amici-semantic-results" @pytest.fixture(scope="function", autouse=True) @@ -54,44 +44,69 @@ def sbml_test_dir(): sys.path = old_path -def test_sbml_testsuite_case(test_number, result_path): - +def test_sbml_testsuite_case( + test_number, result_path, sbml_semantic_cases_dir +): test_id = format_test_id(test_number) model_dir = None + + if test_id == "01395": + pytest.skip("NaNs in the Jacobian") + + # test cases for which sensitivities are to be checked + # key: case ID; value: epsilon for finite differences + sensitivity_check_cases = { + # parameter-dependent conservation laws + "00783": 1.5e-2, + # initial events + "00995": 1e-3, + } + try: - current_test_path = os.path.join(TEST_PATH, test_id) + current_test_path = sbml_semantic_cases_dir / test_id # parse expected results - results_file = os.path.join(current_test_path, - test_id + '-results.csv') - results = pd.read_csv(results_file, delimiter=',') - results.rename(columns={c: c.replace(' ', '') - for c in results.columns}, - inplace=True) + results_file = current_test_path / f"{test_id}-results.csv" + results = pd.read_csv(results_file, delimiter=",") + results.rename( + columns={c: c.replace(" ", "") for c in results.columns}, + inplace=True, + ) # setup model - model_dir = os.path.join(os.path.dirname(__file__), 'SBMLTestModels', - test_id) - model, solver, wrapper = compile_model(current_test_path, test_id, - model_dir) + model_dir = Path(__file__).parent / "SBMLTestModels" / test_id + model, solver, wrapper = compile_model( + current_test_path, + test_id, + model_dir, + generate_sensitivity_code=test_id in sensitivity_check_cases, + ) settings = read_settings_file(current_test_path, test_id) - atol, rtol = apply_settings(settings, solver, model) + atol, rtol = apply_settings(settings, solver, model, test_id) # simulate model rdata = amici.runAmiciSimulation(model, solver) - if rdata['status'] != amici.AMICI_SUCCESS and test_id in [ - '00748', '00374', '00369' - ]: - raise amici.sbml_import.SBMLException('Simulation Failed') + if rdata["status"] != amici.AMICI_SUCCESS: + if test_id in ("00748", "00374", "00369"): + pytest.skip("Simulation Failed expectedly") + else: + raise RuntimeError("Simulation failed unexpectedly") # verify - simulated = verify_results(settings, rdata, results, wrapper, - model, atol, rtol) + simulated = verify_results( + settings, rdata, results, wrapper, model, atol, rtol + ) # record results write_result_file(simulated, test_id, result_path) + # check sensitivities for selected models + if epsilon := sensitivity_check_cases.get(test_id): + solver.setSensitivityOrder(amici.SensitivityOrder.first) + solver.setSensitivityMethod(amici.SensitivityMethod.forward) + check_derivatives(model, solver, epsilon=epsilon) + except amici.sbml_import.SBMLException as err: pytest.skip(str(err)) finally: @@ -99,63 +114,86 @@ def test_sbml_testsuite_case(test_number, result_path): shutil.rmtree(model_dir, ignore_errors=True) -def verify_results(settings, rdata, expected, wrapper, - model, atol, rtol): +def verify_results(settings, rdata, expected, wrapper, model, atol, rtol): """Verify test results""" amount_species, variables = get_amount_and_variables(settings) - # verify states + # collect states simulated = pd.DataFrame( - rdata['y'], - columns=[obs['name'] - for obs in wrapper.symbols[SymbolId.OBSERVABLE].values()] + rdata["y"], + columns=[ + obs["name"] + for obs in wrapper.symbols[SymbolId.OBSERVABLE].values() + ], ) - simulated['time'] = rdata['ts'] + simulated["time"] = rdata["ts"] + # collect parameters for par in model.getParameterIds(): - simulated[par] = rdata['ts'] * 0 + model.getParameterById(par) - - simulated.rename(columns={c: c.replace('amici_', '') - for c in simulated.columns}, inplace=True) + simulated[par] = rdata["ts"] * 0 + model.getParameterById(par) + # collect fluxes + for expr_idx, expr_id in enumerate(model.getExpressionIds()): + if expr_id.startswith("flux_"): + simulated[expr_id.removeprefix("flux_")] = rdata.w[:, expr_idx] + # handle renamed reserved symbols + simulated.rename( + columns={c: c.replace("amici_", "") for c in simulated.columns}, + inplace=True, + ) # SBML test suite case 01308 defines species with initialAmount and # hasOnlySubstanceUnits="true", but then request results as concentrations. requested_concentrations = [ - s for s in - settings['concentration'].replace(' ', '').replace('\n', '').split(',') + s + for s in settings["concentration"] + .replace(" ", "") + .replace("\n", "") + .split(",") if s ] # We only need to convert species that have only substance units concentration_species = [ - str(species_id) - for species_id, species in wrapper.symbols[SymbolId.SPECIES].items() - if str(species_id) in requested_concentrations and species['amount'] + str(state_id) + for state_id, state in { + **wrapper.symbols[SymbolId.SPECIES], + **wrapper.symbols[SymbolId.ALGEBRAIC_STATE], + }.items() + if str(state_id) in requested_concentrations + and state.get("amount", False) ] - amounts_to_concentrations(concentration_species, wrapper, - simulated, requested_concentrations) + amounts_to_concentrations( + concentration_species, wrapper, simulated, requested_concentrations + ) - concentrations_to_amounts(amount_species, wrapper, simulated, - requested_concentrations) + concentrations_to_amounts( + amount_species, wrapper, simulated, requested_concentrations + ) # simulated may contain `object` dtype columns and `expected` may - # contain `np.int64` columns so we cast everything to `np.float64`. + # contain `np.int64` columns, so we cast everything to `np.float64`. for variable in variables: - assert np.isclose( - simulated[variable].astype(np.float64).values, - expected[variable].astype(np.float64).values, - atol, rtol, equal_nan=True - ).all(), variable - - return simulated[variables + ['time']] + expectation = expected[variable].astype(np.float64).values + try: + actual = simulated[variable].astype(np.float64).values + except KeyError as e: + raise KeyError(f"Missing simulated value for `{variable}`") from e + assert_allclose( + actual, + expectation, + atol, + rtol, + equal_nan=True, + err_msg=f"Mismatch for {variable}", + ) + + return simulated[variables + ["time"]] def amounts_to_concentrations( - amount_species, - wrapper, - simulated, - requested_concentrations + amount_species, wrapper, simulated, requested_concentrations ): """ Convert AMICI simulated amounts to concentrations + Convert from concentration to amount: C=n/V n=CV (multiply by V) @@ -167,18 +205,16 @@ def amounts_to_concentrations( This allows for the reuse of the concentrations_to_amounts method... """ for species in amount_species: - if not species == '': + if species != "": simulated.loc[:, species] = 1 / simulated.loc[:, species] - concentrations_to_amounts([species], wrapper, simulated, - requested_concentrations) + concentrations_to_amounts( + [species], wrapper, simulated, requested_concentrations + ) simulated.loc[:, species] = 1 / simulated.loc[:, species] def concentrations_to_amounts( - amount_species, - wrapper, - simulated, - requested_concentrations + amount_species, wrapper, simulated, requested_concentrations ): """Convert AMICI simulated concentrations to amounts""" for species in amount_species: @@ -195,26 +231,28 @@ def concentrations_to_amounts( # Species with OnlySubstanceUnits don't have to be converted as long # as we don't request concentrations for them. Only applies when # called from amounts_to_concentrations. - if (is_amt and species not in requested_concentrations) \ - or comp is None: + if ( + is_amt and species not in requested_concentrations + ) or comp is None: continue simulated.loc[:, species] *= simulated.loc[ - :, comp if comp in simulated.columns else 'amici_' + comp + :, comp if comp in simulated.columns else f"amici_{comp}" ] -def write_result_file(simulated: pd.DataFrame, - test_id: str, result_path: str): +def write_result_file( + simulated: pd.DataFrame, test_id: str, result_path: Path +): """ Create test result file for upload to - http://sbml.org/Facilities/Database/Submission/Create + http://raterule.caltech.edu/Facilities/Database Requires csv file with test ID in name and content of [time, Species, ...] """ # TODO: only states are reported here, not compartments or parameters - filename = os.path.join(result_path, f'{test_id}.csv') + filename = result_path / f"{test_id}.csv" simulated.to_csv(filename, index=False) @@ -222,50 +260,59 @@ def get_amount_and_variables(settings): """Read amount and species from settings file""" # species for which results are expected as amounts - amount_species = settings['amount'] \ - .replace(' ', '') \ - .replace('\n', '') \ - .split(',') + amount_species = ( + settings["amount"].replace(" ", "").replace("\n", "").split(",") + ) # IDs of all variables for which results are expected/provided - variables = settings['variables'] \ - .replace(' ', '') \ - .replace('\n', '') \ - .split(',') + variables = ( + settings["variables"].replace(" ", "").replace("\n", "").split(",") + ) return amount_species, variables -def apply_settings(settings, solver, model): +def apply_settings(settings, solver, model, test_id: str): """Apply model and solver settings as specified in the test case""" - - ts = np.linspace(float(settings['start']), - float(settings['start']) - + float(settings['duration']), - int(settings['steps']) + 1) - atol = float(settings['absolute']) - rtol = float(settings['relative']) + # start/duration/steps may be empty + ts = np.linspace( + float(settings["start"] or 0), + float(settings["start"] or 0) + float(settings["duration"] or 0), + int(settings["steps"] or 0) + 1, + ) + atol = float(settings["absolute"]) + rtol = float(settings["relative"]) model.setTimepoints(ts) solver.setMaxSteps(int(1e6)) solver.setRelativeTolerance(rtol / 1e4) - solver.setAbsoluteTolerance(atol / 1e4) + + if test_id == "01148": + solver.setAbsoluteTolerance(atol / 1e6) + else: + solver.setAbsoluteTolerance(atol / 1e4) return atol, rtol -def compile_model(path, test_id, model_dir): +def compile_model( + sbml_dir: Path, + test_id: str, + model_dir: Path, + generate_sensitivity_code: bool = False, +): """Import the given test model to AMICI""" - sbml_file = find_model_file(path, test_id) - - wrapper = amici.SbmlImporter(sbml_file) + model_dir.mkdir(parents=True, exist_ok=True) - if not os.path.exists(model_dir): - os.makedirs(model_dir) + sbml_file = find_model_file(sbml_dir, test_id) + sbml_importer = amici.SbmlImporter(sbml_file) - model_name = 'SBMLTest' + test_id - wrapper.sbml2amici(model_name, output_dir=model_dir, - generate_sensitivity_code=False) + model_name = f"SBMLTest{test_id}" + sbml_importer.sbml2amici( + model_name, + output_dir=model_dir, + generate_sensitivity_code=generate_sensitivity_code, + ) # settings model_module = amici.import_model_module(model_name, model_dir) @@ -273,66 +320,37 @@ def compile_model(path, test_id, model_dir): model = model_module.getModel() solver = model.getSolver() - return model, solver, wrapper + return model, solver, sbml_importer -def find_model_file(current_test_path: str, test_id: str): +def find_model_file(current_test_path: Path, test_id: str) -> Path: """Find model file for the given test (guess filename extension)""" - sbml_file = os.path.join(current_test_path, test_id + '-sbml-l3v2.xml') + sbml_file = current_test_path / f"{test_id}-sbml-l3v2.xml" - # fallback l3v1 - if not os.path.isfile(sbml_file): - sbml_file = os.path.join(current_test_path, test_id + '-sbml-l3v1.xml') + if not sbml_file.is_file(): + # fallback l3v1 + sbml_file = current_test_path / f"{test_id}-sbml-l3v1.xml" - # fallback l2v5 - if not os.path.isfile(sbml_file): - sbml_file = os.path.join(current_test_path, test_id + '-sbml-l2v5.xml') + if not sbml_file.is_file(): + # fallback l2v5 + sbml_file = current_test_path / f"{test_id}-sbml-l2v5.xml" return sbml_file -def read_settings_file(current_test_path: str, test_id: str): +def read_settings_file(current_test_path: Path, test_id: str): """Read settings for the given test""" - settings_file = os.path.join(current_test_path, test_id + '-settings.txt') + settings_file = current_test_path / f"{test_id}-settings.txt" settings = {} with open(settings_file) as f: for line in f: - if not line == '\n': - (key, val) = line.split(':') - settings[key] = val + if line != "\n": + (key, val) = line.split(":") + settings[key] = val.strip() return settings def format_test_id(test_id) -> str: """Format numeric to 0-padded string""" - test_str = str(test_id) - test_str = '0'*(5-len(test_str)) + test_str - return test_str - - -def get_tags_for_test(test_id) -> Tuple[Set[str], Set[str]]: - """Get sbml test suite tags for the given test ID - - Returns: - Tuple of set of strings for componentTags and testTags - """ - - current_test_path = os.path.join(TEST_PATH, test_id) - info_file = os.path.join(current_test_path, f'{test_id}-model.m') - with open(info_file) as f: - component_tags = set() - test_tags = set() - for line in f: - if line.startswith('testTags:'): - test_tags = set( - re.split(r'[ ,:]', line[len('testTags:'):].strip())) - test_tags.discard('') - if line.startswith('componentTags:'): - component_tags = set( - re.split(r'[ ,:]', line[len('componentTags:'):].strip())) - component_tags.discard('') - if test_tags and component_tags: - return component_tags, test_tags - print(f"No componentTags or testTags found for test case {test_id}.") - return component_tags, test_tags + return f"{test_id:0>5}" diff --git a/deps/AMICI/version.txt b/deps/AMICI/version.txt index b76fc2675..5a03fb737 100644 --- a/deps/AMICI/version.txt +++ b/deps/AMICI/version.txt @@ -1 +1 @@ -0.11.23 +0.20.0 diff --git a/doc/minibatch_optimization.md b/doc/minibatch_optimization.md index e4fb0cc49..d8306f322 100644 --- a/doc/minibatch_optimization.md +++ b/doc/minibatch_optimization.md @@ -1,145 +1,150 @@ # Mini-batch optimization with parPE -Mini-batch optimization usually refers to gradient-based local optimization +Mini-batch optimization usually refers to gradient-based local optimization strategies such as stochastic gradient descent (SGD) with batch sizes between 1 (sometimes referred to as online learning) and the whole data set (full batch -optimization). In the context of parameter estimation for ODE models, such as -done in parPE, this means that in each step of optimization only a subset of -all experimental conditions is simulated. From those simulations, an estimate -of the objective function and its gradient is computed and used for -local optimization. ParPE has its own mini-batch optimization solver -implemented, which includes a series of different mini-batch optimization -algorithms, tuning possibilities and improvements tailored to parameter -estimation of ODE models. More background on the method can be found int the -[following preprint](https://www.biorxiv.org/content/10.1101/859884v1). - +optimization). In the context of parameter estimation for ODE models, such as +done in parPE, this means that in each step of optimization only a subset of +all experimental conditions is simulated. From those simulations, an estimate +of the objective function and its gradient is computed and used for +local optimization. ParPE has its own mini-batch optimization solver +implemented, which includes a series of different mini-batch optimization +algorithms, tuning possibilities and improvements tailored to parameter +estimation of ODE models. More background on the method can be found in +[this paper](https://doi.org/10.1038/s41467-021-27374-6). ## Using the mini-batch optimization solver of parPE -For the user of parPE, mini-batch optimization works similar to full-batch -optimization with Ipopt or Fides, only a group of optimizer settings needs to -be changed. For this purpose, we assume that the folder `parPE/misc` (which -includes the file `optimizationOptions.py`) is in the python path. +For the user of parPE, mini-batch optimization works similar to full-batch +optimization with Ipopt or Fides, only a group of optimizer settings needs to +be changed. For this purpose, we assume that the folder `parPE/misc` (which +includes the file `optimizationOptions.py`) is in `$PATH`. + 1. The optimizer itself has to be changed to the mini-batch solver via \ -`optimizationOptions.py -S optimizer 10` -2. At the moment, hierarchical optimization has to be disabled, as its +`optimizationOptions.py -s optimizer 10` + +2. At the moment, hierarchical optimization has to be disabled, as its incompatible with the current way how mini-batches are chosen: \ -`optimizationOptions.py -S hierarchicalOptimization 0` -3. As the computation time (but not necessarily the wall time) is typically -reduced when using mini-batch optimization, more local optimizations can be run -for most problems. Those can be set via -`optimizationOptions.py -S numStarts ` +`optimizationOptions.py -s hierarchicalOptimization 0` +3. As the computation time (but not necessarily the wall time) is typically +reduced when using mini-batch optimization, more local optimizations can be run +for most problems. Those can be set via +`optimizationOptions.py -s numStarts ` ## Setting mini-batch optimization specific hyperparameters Beyond the mentioned settings, mini-batch optimizers have a couple of specific hyperparameters which need to be set for the mini-batch optimizer itself. The most intuitive ones are the number of epochs, i.e., the number of passes -through the whole data set, which roughly corresponds to the maximum number of -iterations in full-batch optimization, and the mini-batch size. Those can be -set via \ -`optimizationOptions.py -S minibatch/batchSize -` \ -or \ -`optimizationOptions.py -S minibatch/maxEpochs -`, \ +through the whole data set, which roughly corresponds to the maximum number of +iterations in full-batch optimization, and the mini-batch size. Those can be +set via +`optimizationOptions.py -s minibatch/batchSize ` +or +`optimizationOptions.py -s minibatch/maxEpochs `, respectively. ### Optimization algorithms -The optimizations algorithm can be set via -`optimizationOptions.py -S -minibatch/parameterUpdater `, where `` + +The optimizations algorithm can be set via +`optimizationOptions.py -s +minibatch/parameterUpdater `, where `` can be chosen among the following ones: - * "Adam": Adapted version of the Adam algorithm by Kingma et al. - * "AdamClassic": Original version of the Adam algorithm by Kingma et al. - * "RmsProp": RMSProp algorithm as proposed by Hinton - * "Vanilla": Simple ("Vanilla") stochastic gradient descent - * "Momentum": Stochastic gradient descent with (vanilla) momentum + * `Adam`: Adapted version of the Adam algorithm by Kingma et al. + * `AdamClassic`: Original version of the Adam algorithm by Kingma et al. + * `RmsProp`: RMSProp algorithm as proposed by Hinton + * `Vanilla`: Simple ("Vanilla") stochastic gradient descent + * `Momentum`: Stochastic gradient descent with (vanilla) momentum ### Learning rate scheduling -The next important hyperparameter is the learning rate, which influences the + +The next important hyperparameter is the learning rate, which influences the step size of the optimizer. ParPE allows to choose an initial learning rate, -a final learning rate (for the last epoch), and a mode of interpolation +a final learning rate (for the last epoch), and a mode of interpolation between those two. These things can be set via: - * `optimizationOptions.py -S -minibatch/startLearningRate ` - * `optimizationOptions.py -S + * `optimizationOptions.py -s +minibatch/startLearningRate ` + * `optimizationOptions.py -s minibatch/endLearningRate ` - * `optimizationOptions.py -S + * `optimizationOptions.py -s minibatch/learningRateInterpMode `, where the interpolation -mode can be one out of `linear`, `inverseLinear`, and `logarithmic`, where +mode can be one out of `linear`, `inverseLinear`, and `logarithmic`, where logarithmic interpolation is the default. ### ODE-model specific implementations: Rescue interceptor and line-search + In addition to those settings, parPE implements some specific improvements for mini-batch optimization of ODE models: First, it has a "rescueInterceptor", -which tries to recover local optimization runs by shrinking the step size if +which tries to recover local optimization runs by shrinking the step size if the objective function cannot be evaluated. It can be activated via -`optimizationOptions.py -S +`optimizationOptions.py -s minibatch/rescueInterceptor `, where "integer flag" can be one of - * 0 (interceptor switched off) - * 1 (interceptor switched on) - * 2 (interceptor switched on *and* optimization is trying a cold restart if - the run cannot be recovered despite reaching the maximum number of step size + * `0` (interceptor switched off) + * `1` (interceptor switched on) + * `2` (interceptor switched on *and* optimization is trying a cold restart if + the run cannot be recovered despite reaching the maximum number of step size shrinkage steps) Second, parPE allows to perform mini-batch optimization using line-search. Line-search can be enabled by setting the option -`optimizationOptions.py -S -minibatch/lineSearchSteps `, where the maximum number -of line-search steps needs to be set. A maximum of 3 to 5 steps has shown to +`optimizationOptions.py -s +minibatch/lineSearchSteps `, where the maximum number +of line-search steps needs to be set. A maximum of 3 to 5 steps has shown to work best. However, enabling line-search will result in slightly higher computation times, so it is a priori not clear, whether line-search will be -beneficial for a certain application or not. +beneficial for a certain application or not. ## Choosing hyperparameters ### The learning rate + The learning rate is a crucial, but complicated hyperparameter: -Keep in mind that a good learning rate depends on the optimization algorithm: -For vanilla SGD, the learning rate is equal to the step size in parPE, whereas -for Adam and RmsProp, the learning rate is multiplied with a more sophisticated +Keep in mind that a good learning rate depends on the optimization algorithm: +For vanilla SGD, the learning rate is equal to the step size in parPE, whereas +for Adam and RmsProp, the learning rate is multiplied with a more sophisticated parameter update, which has in general a step length larger than 1. Hence, -a good learning rate for Vanilla SGD will have larger values than for RmsProp +a good learning rate for Vanilla SGD will have larger values than for RmsProp or Adam (at least for the implementation in parPE). -For many applications, typical initial optimizer step sizes are often in the +For many applications, typical initial optimizer step sizes are often in the range of `[0.1, 10]`. For high dimensional problems, step sizes are typically larger than for low dimensional ones. For Adam and RmsProp, the initial parameter update, which gets multiplied with -the learning rate, has roughly the size of the square root of the problem -dimension. Hence, if the problems has 10.000 free parameters, the initial step -size is a 100-fold larger than the learning rate. For RmsProp, the step size -behaves similar to the one of Adam, except that the step size is (initially) -biased towards 0 and hence smaller (up to at most 1 order of magnitude). -For SGD with momentum, step sizes should behave similar to those of vanilla +the learning rate, has roughly the size of the square root of the problem +dimension. Hence, if the problems has 10.000 free parameters, the initial step +size is a 100-fold larger than the learning rate. For RmsProp, the step size +behaves similar to the one of Adam, except that the step size is (initially) +biased towards 0 and hence smaller (up to at most 1 order of magnitude). +For SGD with momentum, step sizes should behave similar to those of vanilla SGD. These things should be considered when choosing a learning rate schedule. ### The mini-batch size + In first tests, small mini-batch sizes worked clearly better than large ones. -At the moment, there is is no way to a priori determine the best mini-batch +At the moment, there is no way to a priori determine the best mini-batch size, but this hyperparameter must be chosen in some trial-and-error manner. -Fur this purpose, it seems reasonable to start with very small mini-batch -sizes and then gradually increase them and check how this affects the +Fur this purpose, it seems reasonable to start with very small mini-batch +sizes and then gradually increase them and check how this affects the optimization results. -## Reporting of objective function values and gradients: Important differences -As already mentioned, parPE only evaluates the objective function and the -gradient on a subset of the data set in each optimization step. Hence, the +## Reporting of objective function values and gradients: Important differences + +As already mentioned, parPE only evaluates the objective function and the +gradient on a subset of the data set in each optimization step. Hence, the reported values in the optimization history are only estimates based on the current optimization step and typically highly stochastic. -If optimization is finished successfully, the objective function is recomputed -once on the whole data set (values stored in the last optimization step), in +If optimization is finished successfully, the objective function is recomputed +once on the whole data set (values stored in the last optimization step), in order to obtain a more reliable final result. -However, it can make sense to post-process the optimization runs when -analyzing the optimization result: If a run has finished, the values reported -as "finalParameter" should be used. Otherwise, it makes sense to simply -extract the last reported parameter vector of the optimization history for a +However, it can make sense to post-process the optimization runs when +analyzing the optimization result: If a run has finished, the values reported +as "finalParameter" should be used. Otherwise, it makes sense to simply +extract the last reported parameter vector of the optimization history for a specific local optimization run. -These extracted final parameter vectors can then be used to compute the -actual objective function value on the whole data set, possibly with -hierarchically optimized nuisance parameters, if possible. Such a -post-processing step and post-optimization evaluation allows at the moment the +These extracted final parameter vectors can then be used to compute the +actual objective function value on the whole data set, possibly with +hierarchically optimized nuisance parameters, if possible. Such a +post-processing step and post-optimization evaluation allows at the moment the best possible insight into the optimization result. diff --git a/doc/requirements_doc.txt b/doc/requirements_doc.txt index 2da00ed1e..ff4b5aee1 100644 --- a/doc/requirements_doc.txt +++ b/doc/requirements_doc.txt @@ -1,11 +1,12 @@ -Sphinx==4.0.3 -sphinx_rtd_theme>=0.5.1 -breathe>=4.26.1 +sphinx +sphinx_rtd_theme>=1.2.0 +breathe>=4.35.0 exhale>=0.2.3 -IPython>=7.19.0 +ipython>=8.13.2 m2r2>=0.2.7 sphinx-autodoc-typehints>=1.10.3 -nbsphinx>=0.8.1 +nbsphinx==0.9.1 +-e git+https://github.com/svenevs/exhale.git@a1a8551321e246e3ab81f5456e04a8159804595b#egg=exhale # to import parpe package: numpy diff --git a/doc/usage.rst b/doc/usage.rst index f0e06d9cd..edb87f9ed 100644 --- a/doc/usage.rst +++ b/doc/usage.rst @@ -12,3 +12,4 @@ Usage parpe_with_charliecloud.md snakemake_workflow.md hdf5.md + minibatch_optimization.md diff --git a/examples/parpeamici/steadystate/CMakeLists.txt b/examples/parpeamici/steadystate/CMakeLists.txt index b9842d24d..03d2f4f39 100644 --- a/examples/parpeamici/steadystate/CMakeLists.txt +++ b/examples/parpeamici/steadystate/CMakeLists.txt @@ -1,7 +1,8 @@ # Steady-state model example # This requires python to generate the model code from SBML -find_package(PythonInterp 3.7) +find_package(Python3 COMPONENTS Interpreter) +# TODO: check python version # Generate model code and data; build model include(ExternalProject) @@ -64,7 +65,6 @@ target_link_libraries(${PROJECT_NAME} Upstream::amici parpeoptimization parpeloadbalancer - ${CMAKE_THREAD_LIBS_INIT} ) # /example_steadystate executable diff --git a/examples/parpeamici/steadystate/create_steadystate_amici_model.py b/examples/parpeamici/steadystate/create_steadystate_amici_model.py index d93243f78..f649c15db 100755 --- a/examples/parpeamici/steadystate/create_steadystate_amici_model.py +++ b/examples/parpeamici/steadystate/create_steadystate_amici_model.py @@ -266,14 +266,14 @@ def append_measurements_for_condition( elif observable_id == 'obs_x1withsigma': noise_parameter = ['x1withsigma_sigma'] * model.nt() - measurement_df = measurement_df.append(pd.DataFrame( + measurement_df = pd.concat([measurement_df, pd.DataFrame( {ptc.OBSERVABLE_ID: [observable_id] * model.nt(), ptc.SIMULATION_CONDITION_ID: [condition_id] * model.nt(), ptc.TIME: np.array(model.getTimepoints()), ptc.MEASUREMENT: rdata['y'][:, iy], ptc.OBSERVABLE_PARAMETERS: scaling_parameter * model.nt(), ptc.NOISE_PARAMETERS: noise_parameter - }), ignore_index=True, sort=False) + })], ignore_index=True, sort=False) return measurement_df @@ -385,7 +385,8 @@ def create_test_data(measurement_file_name, parameter_file_name, yaml_config, df.loc[~df.observableParameters.isnull(), 'observableParameters'] = \ df.loc[~df.observableParameters.isnull(), 'observableParameters'] \ + "_test" - petab.write_parameter_df(df, test_measurement_file_name) + + petab.write_measurement_df(df, test_measurement_file_name) # parameters df = petab.get_parameter_df(parameter_file_name) diff --git a/examples/parpeamici/steadystate/steadyStateMultiConditionDataprovider.cpp b/examples/parpeamici/steadystate/steadyStateMultiConditionDataprovider.cpp index a1cd4129f..5d33657d3 100644 --- a/examples/parpeamici/steadystate/steadyStateMultiConditionDataprovider.cpp +++ b/examples/parpeamici/steadystate/steadyStateMultiConditionDataprovider.cpp @@ -29,7 +29,6 @@ void SteadyStateMultiConditionDataProvider::setupModelAndSolver() const { solver_->setSensitivityOrder(amici::SensitivityOrder::first); solver_->setSensitivityMethod(amici::SensitivityMethod::adjoint); solver_->setMaxSteps(10000); - solver_->setNewtonMaxLinearSteps(100); solver_->setNewtonMaxSteps(40); } diff --git a/examples/parpeloadbalancer/CMakeLists.txt b/examples/parpeloadbalancer/CMakeLists.txt index a75d3ca9c..d236c15e7 100644 --- a/examples/parpeloadbalancer/CMakeLists.txt +++ b/examples/parpeloadbalancer/CMakeLists.txt @@ -8,7 +8,6 @@ set(SRC_LIST_EXE add_executable(${PROJECT_NAME} ${SRC_LIST_EXE}) target_link_libraries(${PROJECT_NAME} - ${CMAKE_THREAD_LIBS_INIT} parpeloadbalancer ) diff --git a/examples/parpeloadbalancer/main.cpp b/examples/parpeloadbalancer/main.cpp index b3d220d8a..430f54540 100644 --- a/examples/parpeloadbalancer/main.cpp +++ b/examples/parpeloadbalancer/main.cpp @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include @@ -29,8 +31,8 @@ int master() { parpe::JobData jobdata[numJobs]; // mutex to wait for simulations to finish - pthread_cond_t cond = PTHREAD_COND_INITIALIZER; - pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + std::condition_variable cond; + std::mutex mutex; for (int i = 0; i < numJobs; ++i) { parpe::JobData *job = &jobdata[i]; @@ -43,12 +45,9 @@ int master() { } // wait for simulations to finish - pthread_mutex_lock(&mutex); - while (numJobsFinished < numJobs) - pthread_cond_wait(&cond, &mutex); - pthread_mutex_unlock(&mutex); - pthread_mutex_destroy(&mutex); - pthread_cond_destroy(&cond); + std::unique_lock lock(mutex); + cond.wait(lock, [&numJobsFinished, &numJobs]{ + return numJobsFinished == numJobs;}); // check results int errors = 0; diff --git a/include/parpeamici/amiciMisc.h b/include/parpeamici/amiciMisc.h index cedb4b903..f80eb05b1 100644 --- a/include/parpeamici/amiciMisc.h +++ b/include/parpeamici/amiciMisc.h @@ -1,9 +1,19 @@ +#include "amici/rdata.h" +#include "parpecommon/logging.h" #include #include +#include namespace parpe { using amici::getUnscaledParameter; using amici::getScaledParameter; + +std::unique_ptr +run_amici_simulation(amici::Solver& solver, + amici::ExpData const* edata, + amici::Model& model, + bool rethrow = false, + Logger* logger = nullptr); } diff --git a/include/parpeamici/amiciSimulationRunner.h b/include/parpeamici/amiciSimulationRunner.h index 771dab670..1193a5916 100644 --- a/include/parpeamici/amiciSimulationRunner.h +++ b/include/parpeamici/amiciSimulationRunner.h @@ -15,6 +15,8 @@ #include #include +#include +#include #include #include @@ -122,8 +124,8 @@ class AmiciSimulationRunner void queueSimulation(LoadBalancerMaster* loadBalancer, JobData* d, int* jobDone, - pthread_cond_t* jobDoneChangedCondition, - pthread_mutex_t* jobDoneChangedMutex, + std::condition_variable* jobDoneChangedCondition, + std::mutex* jobDoneChangedMutex, int jobIdx, const std::vector& optimizationParameters, amici::SensitivityOrder sensitivityOrder, diff --git a/include/parpeamici/hierarchicalOptimization.h b/include/parpeamici/hierarchicalOptimization.h index c9868cd1b..6620d93a1 100644 --- a/include/parpeamici/hierarchicalOptimization.h +++ b/include/parpeamici/hierarchicalOptimization.h @@ -7,9 +7,7 @@ #include -#include #include -#include namespace parpe { diff --git a/include/parpeamici/multiConditionProblem.h b/include/parpeamici/multiConditionProblem.h index 101437bf8..7ce634ba4 100644 --- a/include/parpeamici/multiConditionProblem.h +++ b/include/parpeamici/multiConditionProblem.h @@ -15,7 +15,6 @@ #include #include -#include /** @file multiConditionProblem.h * Interfaces between AMICI model and parPE optimization problem */ diff --git a/include/parpecommon/hdf5Misc.h b/include/parpecommon/hdf5Misc.h index 5247aa9b4..5a5d6fec9 100644 --- a/include/parpecommon/hdf5Misc.h +++ b/include/parpecommon/hdf5Misc.h @@ -9,7 +9,6 @@ #include #include #include -#include namespace parpe { diff --git a/include/parpecommon/misc.h b/include/parpecommon/misc.h index c092eccdc..b797c2da4 100644 --- a/include/parpecommon/misc.h +++ b/include/parpecommon/misc.h @@ -4,10 +4,7 @@ #include #include -#include -#include #include -#include #include #include diff --git a/include/parpecommon/model.h b/include/parpecommon/model.h index 121bbbe10..53c9ca98c 100644 --- a/include/parpecommon/model.h +++ b/include/parpecommon/model.h @@ -4,7 +4,6 @@ #include #include -#include namespace parpe { diff --git a/include/parpeloadbalancer/loadBalancerMaster.h b/include/parpeloadbalancer/loadBalancerMaster.h index 3e5ce9f36..7f40c2486 100644 --- a/include/parpeloadbalancer/loadBalancerMaster.h +++ b/include/parpeloadbalancer/loadBalancerMaster.h @@ -3,7 +3,10 @@ #include -#include +#include +#include +#include +#include #include #include #include @@ -19,8 +22,8 @@ struct JobData { JobData() = default; JobData(int *jobDone, - pthread_cond_t *jobDoneChangedCondition, - pthread_mutex_t *jobDoneChangedMutex) + std::condition_variable *jobDoneChangedCondition, + std::mutex *jobDoneChangedMutex) : jobDone(jobDone), jobDoneChangedCondition(jobDoneChangedCondition), jobDoneChangedMutex(jobDoneChangedMutex) { @@ -39,9 +42,9 @@ struct JobData { int *jobDone = nullptr; /** is signaled after jobDone has been incremented (if set) */ - pthread_cond_t *jobDoneChangedCondition = nullptr; + std::condition_variable *jobDoneChangedCondition = nullptr; /** is locked to signal jobDoneChangedCondition condition (if set) */ - pthread_mutex_t *jobDoneChangedMutex = nullptr; + std::mutex *jobDoneChangedMutex = nullptr; /** callback when job is finished (if set) */ std::function callbackJobFinished = nullptr; @@ -107,13 +110,6 @@ class LoadBalancerMaster { int getNumQueuedJobs() const; private: - /** - * @brief Thread entry point. This is run from run() - * @param `this` - * @return nullptr, always - */ - static void *threadEntryPoint(void *vpLoadBalancerMaster); - /** * @brief Main function of the load balancer thread. * @@ -180,7 +176,7 @@ class LoadBalancerMaster { MPI_Datatype mpiJobDataType = MPI_BYTE; /** Indicates whether we are ready to handle jobs */ - bool isRunning_ = false; + std::atomic_bool isRunning_ = false; /** Number of workers we can send jobs to */ int numWorkers = 0; @@ -208,7 +204,7 @@ class LoadBalancerMaster { std::vector sentJobsData; /** Mutex to protect access to `queue`. */ - pthread_mutex_t mutexQueue = PTHREAD_MUTEX_INITIALIZER; + mutable std::mutex mutexQueue; /** Semaphore to limit queue length and avoid potentially huge memory * allocation for all send and receive buffers. Note that using this might @@ -217,7 +213,11 @@ class LoadBalancerMaster { sem_t semQueue = {}; /** Thread that runs the message dispatcher. */ - pthread_t queueThread = 0; + std::thread queueThread; + + /** Signals whether the queue thread should keep running */ + std::atomic_bool queue_thread_continue_ = true; + /** Value to indicate that there is currently no known free worker. */ constexpr static int NO_FREE_WORKER = -1; diff --git a/include/parpeoptimization/localOptimizationCeres.h b/include/parpeoptimization/localOptimizationCeres.h index 6a30a0073..2e841dac7 100644 --- a/include/parpeoptimization/localOptimizationCeres.h +++ b/include/parpeoptimization/localOptimizationCeres.h @@ -3,7 +3,6 @@ #include -#include namespace parpe { diff --git a/include/parpeoptimization/localOptimizationIpoptTNLP.h b/include/parpeoptimization/localOptimizationIpoptTNLP.h index e0f319b83..3ef15f199 100644 --- a/include/parpeoptimization/localOptimizationIpoptTNLP.h +++ b/include/parpeoptimization/localOptimizationIpoptTNLP.h @@ -11,8 +11,6 @@ #include #endif -#include -#include #include #include diff --git a/include/parpeoptimization/multiStartOptimization.h b/include/parpeoptimization/multiStartOptimization.h index eba5d81d4..f5c68ef9c 100644 --- a/include/parpeoptimization/multiStartOptimization.h +++ b/include/parpeoptimization/multiStartOptimization.h @@ -3,7 +3,6 @@ #include -#include #include namespace parpe { diff --git a/misc/run_notebook.sh b/misc/run_notebook.sh index ed76f6f6b..1c6339965 100755 --- a/misc/run_notebook.sh +++ b/misc/run_notebook.sh @@ -27,7 +27,7 @@ fi source "${parpe_path}/build/venv/bin/activate" pip3 show ipython \ - || (pip3 install --upgrade jupyter jupyter_contrib_nbextensions \ + || (pip3 install --upgrade jupyter \ && python3 -m ipykernel install --user \ --name parpe --display-name "Python (parpe)") diff --git a/misc/setup_amici_model.sh b/misc/setup_amici_model.sh index 11301d68e..dbfa26f04 100755 --- a/misc/setup_amici_model.sh +++ b/misc/setup_amici_model.sh @@ -44,6 +44,7 @@ amici_root=${AMICI_ROOT:-${script_path}/../deps/AMICI/} cmake -S "${output_dir}" \ -B "${output_dir}/build" \ + -DCMAKE_BUILD_TYPE="Release" \ -DAmici_DIR="${amici_root}/build" \ -DParPE_DIR="${script_path}/../build" diff --git a/misc/venv.sh b/misc/venv.sh index 81177c23e..0d8c7df5e 100755 --- a/misc/venv.sh +++ b/misc/venv.sh @@ -30,11 +30,11 @@ if [[ ! -d "${venv_dir}" ]]; then source "${venv_dir}/bin/activate" fi - pip3 install wheel pytest + pip3 install wheel pytest numpy # install AMICI cd "${parpe_root}/deps/AMICI/python/sdist" - pip3 install -e . + pip3 install -e .[vis] # install parPE cd "${parpe_root}/python" diff --git a/python/parpe/hdf5_pe_input.py b/python/parpe/hdf5_pe_input.py index 312bdf2c2..115252679 100644 --- a/python/parpe/hdf5_pe_input.py +++ b/python/parpe/hdf5_pe_input.py @@ -3,7 +3,7 @@ import logging import sys from numbers import Number -from typing import Any, Collection, Optional, Dict, Tuple +from typing import Any, Collection, Optional, Dict, Tuple, Iterator import amici import coloredlogs @@ -13,7 +13,6 @@ import petab from amici.petab_import import PREEQ_INDICATOR_ID from amici.petab_import import petab_scale_to_amici_scale -from amici.petab_objective import subset_dict from colorama import Fore from colorama import init as init_colorama from pandas import DataFrame @@ -446,17 +445,17 @@ def _set_initial_concentration(condition_id, species_id, condition_map_preeq_fix = None if condition_map_preeq: condition_map_preeq_var, condition_map_preeq_fix = \ - subset_dict(condition_map_preeq, variable_par_ids, - fixed_par_ids) + _subset_dict(condition_map_preeq, variable_par_ids, + fixed_par_ids) condition_scale_map_preeq_var, _ = \ - subset_dict(condition_scale_map_preeq, variable_par_ids, - fixed_par_ids) + _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) + _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) + _subset_dict(condition_scale_map_sim, variable_par_ids, + fixed_par_ids) if condition_map_preeq: # merge after having removed potentially fixed parameters @@ -952,7 +951,7 @@ def _write_amici_options(self) -> None: # Required to handle parametric initial concentrations where newton # solver would fail with KLU error g.attrs['steadyStateSensitivityMode'] = \ - amici.SteadyStateSensitivityMode_simulationFSA + amici.SteadyStateSensitivityMode.integrationOnly num_model_parameters = \ self.f['/parameters/modelParameterNames'].shape[0] @@ -1058,6 +1057,23 @@ def write_parameter_map(f: h5py.File, mapping_matrix: np.array, data=mapping_matrix) +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 write_optimization_options(f: h5py.File) -> None: """ Create groups and write some default optimization settings @@ -1082,16 +1098,6 @@ def write_optimization_options(f: h5py.File) -> None: g.attrs["acceptable_obj_change_tol"] = 1e-12 g.attrs["watchdog_shortened_iter_trigger"] = 0 - # set fmincon options - g = f.require_group('optimizationOptions/fmincon') - g.attrs['MaxIter'] = 100 - g.attrs["TolX"] = 1e-8 - g.attrs["TolFun"] = 0 - g.attrs["MaxFunEvals"] = 1e7 - g.attrs["algorithm"] = np.string_("interior-point") - g.attrs["GradObj"] = np.string_("on") - g.attrs["display"] = np.string_("iter") - # set CERES options g = f.require_group('optimizationOptions/ceres') g.attrs['max_num_iterations'] = 100 diff --git a/python/pyproject.toml b/python/pyproject.toml new file mode 100644 index 000000000..9cebdd8d6 --- /dev/null +++ b/python/pyproject.toml @@ -0,0 +1,43 @@ +[build-system] +requires = ["setuptools", "setuptools-scm"] +build-backend = "setuptools.build_meta" + +[project] +name = "parpe" +version = "0.0.0" +authors = [ + {name = "Daniel Weindl", email = "sci@danielweindl.de"}, +] +description = "parpe python package" +requires-python = ">=3.9" +license = {text = "MIT"} +dependencies = [ + 'numpy>=1.18.1', + 'termcolor>=1.1.0', + 'colorama>=0.4.3', + 'petab>=0.1.18', + 'amici>=0.20.0', + 'h5py>=3.0.0', + 'python-libsbml>=5.17.0', + 'snakemake>=5.10.0', + 'coloredlogs>=15.0', + 'scipy', + 'matplotlib', +] + +[project.optional-dependencies] +test = ["pytest", "pre-commit"] + +[project.scripts] +parpe_petab_to_hdf5 = "parpe.hdf5_pe_input:main" + +[project.urls] +Documentation = "https://parpe.readthedocs.io/" +Repository = "https://github.com/ICB-DCM/parPE/" + +[tool.black] +line-length = 79 + +[tool.semantic_release] +version_toml = "pyproject.toml:project.version" + diff --git a/python/setup.py b/python/setup.py deleted file mode 100644 index 5ada124b1..000000000 --- a/python/setup.py +++ /dev/null @@ -1,28 +0,0 @@ -from setuptools import setup, find_packages - -ENTRY_POINTS = { - 'console_scripts': [ - 'parpe_petab_to_hdf5 = parpe.hdf5_pe_input:main', - ] -} - -setup( - name='parpe', - version='0.0.0', - url='https://github.com/ICB-DCM/parPE/', - author='Daniel Weindl', - author_email='daniel.weindl@helmholtz-muenchen.de', - description='parpe python package', - packages=find_packages(), - install_requires=['numpy>=1.18.1', - 'termcolor>=1.1.0', - 'colorama>=0.4.3', - 'petab>=0.1.18', - 'amici>=0.11.15', - 'h5py>=3.0.0', - 'python-libsbml>=5.17.0', - 'snakemake>=5.10.0', - 'coloredlogs>=15.0', - ], - entry_points=ENTRY_POINTS, -) diff --git a/sonar-project.properties b/sonar-project.properties index f73cc5b70..318fee945 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -4,7 +4,6 @@ # https://sonarcloud.io/documentation/analysis/languages/cfamily/ sonar.host.url=https://sonarcloud.io -sonar.login=45879b85332d963bf3ead20399bf5bd2c925c156 sonar.organization=icb-dcm sonar.projectKey=ICB-DCM_parPE @@ -19,4 +18,4 @@ sonar.cfamily.cache.enabled=true sonar.cfamily.cache.path=sonar_cache sonar.python.coverage.reportPaths=build/coverage_py.xml -sonar.python.version=3.7,3.8,3.9,3.10 +sonar.python.version=3.9,3.10,3.11 diff --git a/src/parpeamici/CMakeLists.txt b/src/parpeamici/CMakeLists.txt index e85c2566e..23e4710c0 100644 --- a/src/parpeamici/CMakeLists.txt +++ b/src/parpeamici/CMakeLists.txt @@ -2,7 +2,7 @@ find_package(Amici HINTS ${CMAKE_SOURCE_DIR}/deps/AMICI/build) -# dependecy: BOOST +# dependency: BOOST if(CMAKE_BUILD_TYPE MATCHES Release) set(Boost_USE_STATIC_LIBS TRUE) else() diff --git a/src/parpeamici/amiciMisc.cpp b/src/parpeamici/amiciMisc.cpp index d970a5ff3..0869c2963 100644 --- a/src/parpeamici/amiciMisc.cpp +++ b/src/parpeamici/amiciMisc.cpp @@ -1,6 +1,52 @@ #include +#include +#include +#include +#include +#include + +#include namespace parpe { +using amici::ReturnData; + +std::unique_ptr run_amici_simulation( + amici::Solver &solver, + const amici::ExpData *edata, + amici::Model &model, + bool rethrow, + Logger *logger) { + + auto rdata = amici::runAmiciSimulation(solver, edata, model, rethrow); + + if (logger != nullptr) { + // TODO: subclass amici::Logger to print messages without delay + + // for now, print collected messages after simulation + for(auto const& log_item: rdata->messages) { + auto lvl = loglevel::debug; + switch (log_item.severity) { + case amici::LogSeverity::debug: + lvl = loglevel::debug; + break; + case amici::LogSeverity::warning: + lvl = loglevel::warning; + break; + case amici::LogSeverity::error: + lvl = loglevel::error; + break; + } + if(!log_item.identifier.empty()) { + logger->logmessage(lvl, "[" + log_item.identifier + "] " + log_item.message); + } else { + logger->logmessage(loglevel::warning, log_item.message); + } + + } + } + return rdata; +} } // namespace parpe + diff --git a/src/parpeamici/amiciSimulationRunner.cpp b/src/parpeamici/amiciSimulationRunner.cpp index 1eb3fce55..c7449d5f1 100644 --- a/src/parpeamici/amiciSimulationRunner.cpp +++ b/src/parpeamici/amiciSimulationRunner.cpp @@ -37,8 +37,8 @@ AmiciSimulationRunner::runDistributedMemory(LoadBalancerMaster* loadBalancer, #endif // mutex and condition to wait for simulations to finish - pthread_cond_t simulationsCond = PTHREAD_COND_INITIALIZER; - pthread_mutex_t simulationsMutex = PTHREAD_MUTEX_INITIALIZER; + std::condition_variable simulationsCond; + std::mutex simulationsMutex; // multiple simulations may be grouped into one work package auto numJobsTotal = static_cast( @@ -75,14 +75,10 @@ AmiciSimulationRunner::runDistributedMemory(LoadBalancerMaster* loadBalancer, } // wait for simulations to finish - pthread_mutex_lock(&simulationsMutex); - while (numJobsFinished < numJobsTotal) // TODO don't wait for all to - // complete; stop early if errors - // occurred - pthread_cond_wait(&simulationsCond, &simulationsMutex); - pthread_mutex_unlock(&simulationsMutex); - pthread_mutex_destroy(&simulationsMutex); - pthread_cond_destroy(&simulationsCond); + // TODO don't wait for all to complete; stop early if errors occurred + std::unique_lock lock(simulationsMutex); + simulationsCond.wait(lock, [&numJobsFinished, &numJobsTotal]{ + return numJobsFinished == numJobsTotal;}); // unpack if (aggregate_) @@ -138,15 +134,15 @@ AmiciSimulationRunner::runSharedMemory(const messageHandlerFunc& messageHandler, #ifdef PARPE_ENABLE_MPI void AmiciSimulationRunner::queueSimulation( - LoadBalancerMaster* loadBalancer, - JobData* d, - int* jobDone, - pthread_cond_t* jobDoneChangedCondition, - pthread_mutex_t* jobDoneChangedMutex, - int jobIdx, - std::vector const& optimizationParameters, - amici::SensitivityOrder sensitivityOrder, - std::vector const& conditionIndices) const + LoadBalancerMaster* loadBalancer, + JobData* d, + int* jobDone, + std::condition_variable* jobDoneChangedCondition, + std::mutex* jobDoneChangedMutex, + int jobIdx, + std::vector const& optimizationParameters, + amici::SensitivityOrder sensitivityOrder, + std::vector const& conditionIndices) const { // TODO avoid copy optimizationParameters; reuse;; for const& in work // package need to split into(de)serialize diff --git a/src/parpeamici/multiConditionProblem.cpp b/src/parpeamici/multiConditionProblem.cpp index 40303c9fd..0ef2ec378 100644 --- a/src/parpeamici/multiConditionProblem.cpp +++ b/src/parpeamici/multiConditionProblem.cpp @@ -353,34 +353,10 @@ AmiciSimulationRunner::AmiciResultPackageSimple runAndLogSimulation( std::unique_ptr rdata; - // redirect AMICI output to parPE logging - amici::AmiciApplication amiciApp; - amiciApp.error = [logger]( - std::string const& identifier, - std::string const& message){ - if(!identifier.empty()) { - logger->logmessage(loglevel::error, "[" + identifier + "] " + message); - } else { - logger->logmessage(loglevel::error, message); - } - }; - amiciApp.warning = [logger]( - std::string const& identifier, - std::string const& message){ - if(!identifier.empty()) { - logger->logmessage(loglevel::warning, - "[" + identifier + "] " + message); - } else { - logger->logmessage(loglevel::warning, message); - } - }; - model.app = &amiciApp; // TODO: may dangle need to unset on exit - for(int trial = 1; trial <= maxNumTrials; ++trial) { /* It is currently not safe to reuse solver if an exception has * occurred,so clone every time */ auto solver = std::unique_ptr(solverTemplate.clone()); - solver->app = &amiciApp; if (!sendStates) { /* If we don't need the states, we can save memory here. * For current optimizers we only need the likelihood. For @@ -459,7 +435,7 @@ AmiciSimulationRunner::AmiciResultPackageSimple runAndLogSimulation( } try { - rdata = amiciApp.runAmiciSimulation(*solver, edata.get(), model); + rdata = run_amici_simulation(*solver, edata.get(), model, false, logger); } catch (std::exception const& e) { std::cerr< #include #include +#include #include #include #include @@ -394,28 +395,8 @@ StandaloneSimulator::runSimulation(int conditionIdx, // redirect AMICI output to parPE logging Logger logger("c" + std::to_string(conditionIdx)); - amici::AmiciApplication amiciApp; - amiciApp.error = [&logger](std::string const& identifier, - std::string const& message) { - if (!identifier.empty()) { - logger.logmessage(loglevel::error, "[" + identifier + "] " + message); - } else { - logger.logmessage(loglevel::error, message); - } - }; - amiciApp.warning = [&logger](std::string const& identifier, - std::string const& message) { - if (!identifier.empty()) { - logger.logmessage(loglevel::warning, - "[" + identifier + "] " + message); - } else { - logger.logmessage(loglevel::warning, message); - } - }; - model.app = &amiciApp; // TODO: may dangle need to unset on exit - solver.app = &amiciApp; // TODO: may dangle need to unset on exit - auto rdata = amiciApp.runAmiciSimulation(solver, edata.get(), model); + auto rdata = run_amici_simulation(solver, edata.get(), model, false, &logger); Expects(rdata != nullptr); diff --git a/src/parpecommon/misc.cpp b/src/parpecommon/misc.cpp index 7d4f9a69d..c09ea370c 100644 --- a/src/parpecommon/misc.cpp +++ b/src/parpecommon/misc.cpp @@ -1,13 +1,9 @@ #include #include -#include -#include #include #include -#include #include -#include #include #include #include diff --git a/src/parpeloadbalancer/CMakeLists.txt b/src/parpeloadbalancer/CMakeLists.txt index d9b6a9d8b..44ec38611 100644 --- a/src/parpeloadbalancer/CMakeLists.txt +++ b/src/parpeloadbalancer/CMakeLists.txt @@ -11,7 +11,6 @@ target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_link_libraries(${PROJECT_NAME} PUBLIC parpecommon - PUBLIC ${CMAKE_THREAD_LIBS_INIT} ) if(${PARPE_ENABLE_MPI}) diff --git a/src/parpeloadbalancer/loadBalancerMaster.cpp b/src/parpeloadbalancer/loadBalancerMaster.cpp index 5e66d8ca1..e72e50144 100644 --- a/src/parpeloadbalancer/loadBalancerMaster.cpp +++ b/src/parpeloadbalancer/loadBalancerMaster.cpp @@ -4,7 +4,7 @@ #include #include -#include +#include #include #include @@ -43,11 +43,7 @@ void LoadBalancerMaster::run() { #endif sem_init(&semQueue, 0, queueMaxLength); - pthread_attr_t threadAttr; - pthread_attr_init(&threadAttr); - pthread_attr_setdetachstate(&threadAttr, PTHREAD_CREATE_JOINABLE); - pthread_create(&queueThread, &threadAttr, threadEntryPoint, this); - pthread_attr_destroy(&threadAttr); + queueThread = std::thread(&LoadBalancerMaster::loadBalancerThreadRun, this); isRunning_ = true; } @@ -55,8 +51,6 @@ void LoadBalancerMaster::run() { LoadBalancerMaster::~LoadBalancerMaster() { terminate(); - - pthread_mutex_destroy(&mutexQueue); sem_destroy(&semQueue); } @@ -66,20 +60,15 @@ void LoadBalancerMaster::assertMpiActive() { } #endif -void *LoadBalancerMaster::threadEntryPoint(void *vpLoadBalancerMaster) { - auto master = static_cast(vpLoadBalancerMaster); - master->loadBalancerThreadRun(); - return nullptr; -} void LoadBalancerMaster::loadBalancerThreadRun() { // dispatch queued work packages - while (true) { + while (queue_thread_continue_) { int freeWorkerIndex = NO_FREE_WORKER; // empty send queue while there are free workers - while((freeWorkerIndex = getNextFreeWorkerIndex()) >= 0 + while(queue_thread_continue_ && (freeWorkerIndex = getNextFreeWorkerIndex()) >= 0 && sendQueuedJob(freeWorkerIndex)) {} // check if any job finished @@ -114,10 +103,6 @@ int LoadBalancerMaster::handleFinishedJobs() { // handle all finished jobs, if any while (true) { - // add cancellation point to avoid invalid reads in - // loadBalancer.recvRequests - pthread_testcancel(); - // check for waiting incoming message MPI_Status status; int messageWaiting = 0; @@ -150,7 +135,7 @@ int LoadBalancerMaster::getNextFreeWorkerIndex() { JobData *LoadBalancerMaster::getNextJob() { - pthread_mutex_lock(&mutexQueue); + std::unique_lock lock(mutexQueue); JobData *nextJob = nullptr; if (!queue.empty()) { @@ -158,8 +143,6 @@ JobData *LoadBalancerMaster::getNextJob() { queue.pop(); } - pthread_mutex_unlock(&mutexQueue); - return nextJob; } @@ -188,7 +171,7 @@ void LoadBalancerMaster::queueJob(JobData *data) { sem_wait(&semQueue); - pthread_mutex_lock(&mutexQueue); + std::unique_lock lock(mutexQueue); if (lastJobId == INT_MAX) // Unlikely, but prevent overflow lastJobId = 0; @@ -202,22 +185,16 @@ void LoadBalancerMaster::queueJob(JobData *data) { printf("\x1b[33mQueued job with size %dB. New queue length is %d.\x1b[0m\n", size, queue.size()); #endif - pthread_mutex_unlock(&mutexQueue); } void LoadBalancerMaster::terminate() { - // avoid double termination - pthread_mutex_lock(&mutexQueue); if (!isRunning_) { - pthread_mutex_unlock(&mutexQueue); + // avoid double termination return; } isRunning_ = false; - pthread_mutex_unlock(&mutexQueue); - - pthread_cancel(queueThread); - // wait until canceled - pthread_join(queueThread, nullptr); + queue_thread_continue_ = false; + queueThread.join(); } int LoadBalancerMaster::handleReply(MPI_Status *mpiStatus) { @@ -254,12 +231,15 @@ int LoadBalancerMaster::handleReply(MPI_Status *mpiStatus) { // signal job done - pthread_mutex_lock(data->jobDoneChangedMutex); + std::unique_lock lock; + if(data->jobDoneChangedMutex) { + lock = std::unique_lock(*data->jobDoneChangedMutex); + } if(data->jobDone) ++(*data->jobDone); - pthread_cond_signal(data->jobDoneChangedCondition); - pthread_mutex_unlock(data->jobDoneChangedMutex); - + if (data->jobDoneChangedCondition) { + data->jobDoneChangedCondition->notify_all(); + } return workerIdx; } @@ -301,6 +281,7 @@ bool LoadBalancerMaster::isRunning() const int LoadBalancerMaster::getNumQueuedJobs() const { + std::unique_lock lock(mutexQueue); return queue.size(); } diff --git a/src/parpeoptimization/CMakeLists.txt b/src/parpeoptimization/CMakeLists.txt index f584d07bc..4573f9ffa 100644 --- a/src/parpeoptimization/CMakeLists.txt +++ b/src/parpeoptimization/CMakeLists.txt @@ -104,7 +104,6 @@ target_link_libraries(${PROJECT_NAME} PUBLIC parpecommon PUBLIC ${HDF5_HL_LIBRARIES} PUBLIC ${HDF5_C_LIBRARIES} - PUBLIC ${CMAKE_THREAD_LIBS_INIT} ) install(TARGETS ${PROJECT_NAME} EXPORT ParPETargets ARCHIVE DESTINATION lib) diff --git a/src/parpeoptimization/localOptimizationIpoptTNLP.cpp b/src/parpeoptimization/localOptimizationIpoptTNLP.cpp index ba8ea9414..a16d0d836 100644 --- a/src/parpeoptimization/localOptimizationIpoptTNLP.cpp +++ b/src/parpeoptimization/localOptimizationIpoptTNLP.cpp @@ -4,8 +4,6 @@ #include #include -#include -#include //#include namespace parpe { @@ -202,7 +200,7 @@ LocalOptimizationIpoptTNLP::finalize_solution( // obj_value 0.0 along with the respective flag. This does not make too // much sense. Set to NAN. if(status == INVALID_NUMBER_DETECTED && obj_value == 0.0) { - obj_value = NAN; + obj_value = std::numeric_limits::quiet_NaN(); } reporter.finished(obj_value, gsl::span(x, n), status); diff --git a/src/parpeoptimization/optimizationProblem.cpp b/src/parpeoptimization/optimizationProblem.cpp index d138dcb78..dae0d9e56 100644 --- a/src/parpeoptimization/optimizationProblem.cpp +++ b/src/parpeoptimization/optimizationProblem.cpp @@ -404,7 +404,7 @@ void OptimizationReporter::finished(double optimalCost, // the optimal parameters from the optimizer. since we don't know them, rather set to nan if (logger_) logger_->logmessage(loglevel::info, "cachedCost != optimalCost && parameters.empty()"); - cached_parameters_.assign(cached_parameters_.size(), NAN); + cached_parameters_.assign(cached_parameters_.size(), std::numeric_limits::quiet_NaN()); cached_cost_ = optimalCost; } // else: our cached parameters were better. use those diff --git a/python/swig/CMakeLists.txt b/swig/CMakeLists.txt similarity index 100% rename from python/swig/CMakeLists.txt rename to swig/CMakeLists.txt diff --git a/python/swig/parpe.i b/swig/parpe.i similarity index 100% rename from python/swig/parpe.i rename to swig/parpe.i diff --git a/python/swig/std_unique_ptr.i b/swig/std_unique_ptr.i similarity index 100% rename from python/swig/std_unique_ptr.i rename to swig/std_unique_ptr.i diff --git a/templates/CMakeLists.template.txt b/templates/CMakeLists.template.txt index 677ae3119..b38426e78 100644 --- a/templates/CMakeLists.template.txt +++ b/templates/CMakeLists.template.txt @@ -26,9 +26,12 @@ project(${MODEL_NAME}) # for IDE find_package(ParPE REQUIRED) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-unused-function -fopenmp -D_GNU_SOURCE") # -D_GNU_SOURCE for pthread recursive mutex issues +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-unused-function -fopenmp") -execute_process(COMMAND sh -c "cd ${CMAKE_CURRENT_SOURCE_DIR} && git describe --abbrev=4 --dirty=-dirty --always --tags | tr -d '\n'" OUTPUT_VARIABLE GIT_VERSION) +execute_process( + COMMAND sh -c "cd ${CMAKE_CURRENT_SOURCE_DIR} && git describe --abbrev=4 --dirty=-dirty --always --tags | tr -d '\n'" + OUTPUT_VARIABLE GIT_VERSION +) message(STATUS "Building version ${GIT_VERSION}") add_definitions(-DGIT_VERSION="${GIT_VERSION}") diff --git a/tests/parpe.supp b/tests/parpe.supp index 823ad2e02..578bc96da 100644 --- a/tests/parpe.supp +++ b/tests/parpe.supp @@ -7,7 +7,7 @@ } { - + ompi_mpi_init Memcheck:Cond ... fun:ompi_mpi_init @@ -58,7 +58,7 @@ } { - + opal_hwloc_base_get_topology Memcheck:Leak match-leak-kinds: indirect fun:*alloc @@ -69,7 +69,7 @@ } { - + opal_hwloc_base_get_topology Memcheck:Leak match-leak-kinds: definite fun:*alloc @@ -80,6 +80,18 @@ ... } +{ + libhwloc + Memcheck:Leak + match-leak-kinds: all + fun:calloc + ... + obj:/usr/lib/x86_64-linux-gnu/libhwloc.so.15.*.* + ... + fun:hwloc_topology_load + ... +} + { <_dlerror_run OpenMPI?> Memcheck:Leak @@ -151,7 +163,7 @@ } { - + libgomp Memcheck:Leak ... obj:*libgomp.so.* @@ -175,7 +187,7 @@ } { - + hwloc_topology_init Memcheck:Leak match-leak-kinds: reachable fun:malloc @@ -184,7 +196,7 @@ } { - + hwloc_topology_destroy Memcheck:Leak match-leak-kinds: reachable fun:malloc @@ -193,7 +205,7 @@ } { - + opal_hwloc_base_get_topology Memcheck:Leak match-leak-kinds: reachable fun:malloc @@ -202,7 +214,7 @@ } { - + opal_hwloc_base_get_topology Memcheck:Leak match-leak-kinds: possible fun:malloc @@ -212,18 +224,17 @@ } { - + dl Memcheck:Leak match-leak-kinds: reachable - fun:calloc - fun:_dl_check_map_versions + fun:*alloc + ... fun:dl_open_worker fun:_dl_catch_exception fun:_dl_open fun:dlopen_doit fun:_dl_catch_exception fun:_dl_catch_error - fun:_dlerror_run ... } @@ -248,7 +259,7 @@ } { - + dl Memcheck:Leak match-leak-kinds: definite fun:malloc @@ -268,7 +279,7 @@ { - + boost Memcheck:Leak match-leak-kinds: reachable ... @@ -277,7 +288,7 @@ } { - + boost Memcheck:Leak match-leak-kinds: reachable ... @@ -286,7 +297,7 @@ } { - + dl Memcheck:Leak match-leak-kinds: reachable ... @@ -295,7 +306,7 @@ } { - + opal_shmem_base_select Memcheck:Leak match-leak-kinds: reachable fun:malloc @@ -304,7 +315,7 @@ } { - + orte_submit_init Memcheck:Leak match-leak-kinds: definite fun:malloc @@ -313,7 +324,7 @@ } { - + orte_finalize Memcheck:Cond ... fun:orte_finalize @@ -321,7 +332,7 @@ } { - + libgflags Memcheck:Leak match-leak-kinds: reachable ... @@ -330,7 +341,7 @@ } { - + __dlerror_main_freeres Memcheck:Cond fun:check_free fun:free_key_mem @@ -343,7 +354,7 @@ } { - + __dlerror_main_freeres Memcheck:Addr1 fun:check_free fun:free_key_mem @@ -356,7 +367,7 @@ } { - + libevent Memcheck:Addr8 ... obj:*/libevent-2.1.so* @@ -364,28 +375,19 @@ ... } -{ - - Memcheck:Leak - match-leak-kinds: indirect - fun:malloc - fun:strdup - ... - fun:pmix_server_init -} { - + pmix_server_init Memcheck:Leak - match-leak-kinds: definite - fun:realloc + match-leak-kinds: all + fun:*alloc ... fun:pmix_server_init ... } { - + ??? Memcheck:Leak match-leak-kinds: indirect fun:malloc @@ -403,7 +405,7 @@ } { - + ??? Memcheck:Leak match-leak-kinds: definite fun:realloc @@ -419,3 +421,100 @@ obj:* obj:* } + + +{ + orte_submit_init + Memcheck:Leak + match-leak-kinds: indirect + fun:calloc + ... + fun:orte_submit_init +} + +{ + HDF5 + Memcheck:Leak + match-leak-kinds: reachable + fun:malloc + fun:H5E__get_stack + fun:H5E_clear_stack + fun:H5Iget_type + fun:_ZN2H511IdComponent10p_valid_idEl + fun:_ZNK2H511IdComponent11incRefCountEl + fun:_ZN2H58DataTypeC1El + fun:_ZN2H58AtomTypeC1El + fun:_ZN2H58PredTypeC1El + fun:_ZN2H58PredType13makePredTypesEv + fun:_ZN2H58PredType12getPredTypesEv + obj:/usr/lib/x86_64-linux-gnu/libhdf5_serial_cpp.so.103.3.0 +} + + +{ + calloc + Memcheck:Leak + match-leak-kinds: reachable + fun:calloc + ... + fun:_dl_catch_error +} + +{ + orte_init + Memcheck:Param + socketcall.getsockopt(optlen) + fun:getsockopt_syscall + fun:getsockopt + ... + fun:orte_init + ... +} + +{ + orte_init + Memcheck:Param + socketcall.getsockopt(optlen_out) + fun:getsockopt_syscall + fun:getsockopt + ... + fun:orte_init +} + +{ + libunwind + Memcheck:Param + write(buf) + fun:syscall + obj:/usr/lib/x86_64-linux-gnu/libunwind.so.8.0.1 + ... +} + +{ + gtest + Memcheck:Leak + match-leak-kinds: reachable + fun:_Znwm + ... + fun:_ZN7testing*UninterestingCall* + ... +} + + +{ + _Ux86_64_setcontext + Memcheck:Addr8 + fun:_Ux86_64_setcontext + ... +} + +{ + MPI / ORTE + Memcheck:Param + setsockopt(optlen) + fun:setsockopt_syscall + fun:setsockopt + ... + fun:orte_init + ... +} diff --git a/tests/parpeamici/simulationResultWriterTest.cpp b/tests/parpeamici/simulationResultWriterTest.cpp index 7ba3f8e4c..a60564254 100644 --- a/tests/parpeamici/simulationResultWriterTest.cpp +++ b/tests/parpeamici/simulationResultWriterTest.cpp @@ -39,8 +39,8 @@ TEST(SimulationResultWriter, ResultWriter) { amici::ReturnData rdata( timepoints, amici::ModelDimensions(nx, nx, nx, nx, 0, 0, 0, nytrue, nytrue, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - std::vector(), 0, 0, 0), + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + std::vector(), 0, 0, 0, 0, 0, 0), 0, 0, timepoints.size(), 0, std::vector(), amici::SecondOrderMode::none, amici::SensitivityOrder::none, diff --git a/tests/parpecommon/CMakeLists.txt b/tests/parpecommon/CMakeLists.txt index cef64daa8..68f2b03ac 100644 --- a/tests/parpecommon/CMakeLists.txt +++ b/tests/parpecommon/CMakeLists.txt @@ -9,7 +9,6 @@ set(SRC_LIST add_executable(${PROJECT_NAME} ${SRC_LIST}) target_link_libraries(${PROJECT_NAME} - ${CMAKE_THREAD_LIBS_INIT} parpecommon gtest_main ${GCOV_LIBRARY} diff --git a/tests/parpeloadbalancer/CMakeLists.txt b/tests/parpeloadbalancer/CMakeLists.txt index 461f3ec9e..fc724f27d 100644 --- a/tests/parpeloadbalancer/CMakeLists.txt +++ b/tests/parpeloadbalancer/CMakeLists.txt @@ -9,7 +9,6 @@ set(SRC_LIST add_executable(${PROJECT_NAME} ${SRC_LIST}) target_link_libraries(${PROJECT_NAME} - ${CMAKE_THREAD_LIBS_INIT} parpeloadbalancer gmock_main ${GCOV_LIBRARY} diff --git a/tests/parpeloadbalancer/loadBalancerMasterTest.cpp b/tests/parpeloadbalancer/loadBalancerMasterTest.cpp index efbebc3c2..73a75a3d0 100644 --- a/tests/parpeloadbalancer/loadBalancerMasterTest.cpp +++ b/tests/parpeloadbalancer/loadBalancerMasterTest.cpp @@ -22,19 +22,23 @@ int MPI_Comm_size(MPI_Comm comm, int *size) { return MPI_SUCCESS; } -int MPI_Testany(int count, MPI_Request array_of_requests[], int *index, - int *flag, MPI_Status *status) { - sleep(1000); // do nothing and wait to be killed - +int MPI_Testany(int /*count*/, MPI_Request /*array_of_requests*/[], int */*index*/, + int */*flag*/, MPI_Status */*status*/) { + sleep(1); return 0; } -int MPI_Iprobe(int source, int tag, MPI_Comm comm, int *flag, - MPI_Status *status) { - sleep(1000); +int MPI_Iprobe(int /*source*/, int /*tag*/, MPI_Comm /*comm*/, int */*flag*/, + MPI_Status */*status*/) { return 0; } +int MPI_Isend(const void */*buf*/, int /*count*/, MPI_Datatype /*datatype*/, + int /*dest*/, int /*tag*/, MPI_Comm /*comm*/, + MPI_Request */*request*/) { + sleep(1); + return 0; +} class MockMPI { public: @@ -46,6 +50,11 @@ class MockMPI { MOCK_CONST_METHOD5(MPI_Iprobe, int(int source, int tag, MPI_Comm comm, int *flag, MPI_Status *status)); + MOCK_CONST_METHOD7(MPI_Isend, int(const void *buf, int count, + MPI_Datatype datatype, int dest, + int tag, MPI_Comm comm, + MPI_Request *request)); + MockMPI() { _MPI_Comm_size = [this](MPI_Comm comm, int *size){ return MPI_Comm_size(comm, size); }; } diff --git a/tests/petab-test-suite/conftest.py b/tests/petab-test-suite/conftest.py index e3f0a6681..15da29cf1 100644 --- a/tests/petab-test-suite/conftest.py +++ b/tests/petab-test-suite/conftest.py @@ -15,7 +15,7 @@ def parse_selection(selection_str: str) -> List[int]: """ indices = [] for group in selection_str.split(','): - if not re.match(r'^(?:-?\d+)|(?:\d+(?:-\d+))$', group): + if not re.match(r'^(?:-?\d+|\d+-\d+)$', group): print("Invalid selection", group) sys.exit() spl = group.split('-') @@ -45,6 +45,6 @@ def pytest_generate_tests(metafunc): test_numbers = parse_selection(cases) else: # Run all tests - test_numbers = petabtests.CASES_LIST + test_numbers = petabtests.get_cases('sbml', version="v1.0.0") metafunc.parametrize("case", test_numbers) diff --git a/tests/petab-test-suite/test_petab_test_suite.py b/tests/petab-test-suite/test_petab_test_suite.py index ef16c6352..f6f67ffe2 100755 --- a/tests/petab-test-suite/test_petab_test_suite.py +++ b/tests/petab-test-suite/test_petab_test_suite.py @@ -7,13 +7,14 @@ import shutil import subprocess import sys +from pathlib import Path from typing import List, Union import petabtests import pytest from _pytest.outcomes import Skipped -from amici.logging import get_logger, set_log_level +from amici.logging import get_logger, set_log_level logger = get_logger(__name__, logging.DEBUG) set_log_level(get_logger("amici.petab_import"), logging.DEBUG) @@ -43,7 +44,7 @@ def check_run(cmd: List[str]) -> subprocess.CompletedProcess: ret = subprocess.run(cmd, check=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, encoding='utf-8') if ret.returncode != 0: - raise AssertionError(f"{' '.join(cmd)} exited with status " + raise AssertionError(f"{' '.join(map(str, cmd))} exited with status " f"{ret.returncode}:\n" f"{ret.stdout}") @@ -52,6 +53,8 @@ def check_run(cmd: List[str]) -> subprocess.CompletedProcess: def _test_case(case: Union[int, str]) -> None: """Run a single PEtab test suite case""" + model_type = 'sbml' + version = "v1.0.0" case = petabtests.test_id_str(case) logger.debug(f"Case {case}") @@ -59,23 +62,27 @@ def _test_case(case: Union[int, str]) -> None: # Ignore some that are not supported by AMICI # 0012 compartments # 0009 newton failure preeq - if case in ['0012']: + # 0018 requires fixing import code for RateRules + if case in ['0012', '0018']: raise NotImplementedError(case) - case_dir = os.path.join(petabtests.CASES_DIR, case) - yaml_file = os.path.join(case_dir, petabtests.problem_yaml_name(case)) - test_dir = os.path.dirname(os.path.abspath(__file__)) - parpe_dir = os.path.dirname(os.path.dirname(test_dir)) + case_dir = petabtests.get_case_dir(case, model_type, version) + yaml_file = case_dir / petabtests.problem_yaml_name(case) + yaml_file = case_dir / petabtests.problem_yaml_name(case) + test_dir = Path(__file__).parent.absolute() + parpe_dir = test_dir.parents[1] model_name = f'model_{case}' - amici_model_dir = f'petab_test_models/model_{case}' - parpe_model_dir = f'petab_test_models/model_{case}_parpe' - hdf5_input = os.path.join(parpe_model_dir, 'input.h5') - hdf5_output = os.path.join(parpe_model_dir, 'out.h5') + amici_model_dir = Path('petab_test_models') / f'model_{case}' + parpe_model_dir = Path('petab_test_models') / f'model_{case}_parpe' + hdf5_input = parpe_model_dir / 'input.h5' + hdf5_output = parpe_model_dir / 'out.h5' # create amici model from PEtab cmd = ['amici_import_petab', '-y', yaml_file, '-o', amici_model_dir, '-n', model_name] - print(" ".join(cmd)) + if case == '0006': + cmd.append('--flatten') + print(" ".join(map(str, cmd))) check_run(cmd) # must not exist when calling setup_amici_model.sh @@ -83,30 +90,31 @@ def _test_case(case: Union[int, str]) -> None: shutil.rmtree(parpe_model_dir) # set up for parPE - cmd = [os.path.join(parpe_dir, 'misc', 'setup_amici_model.sh'), + cmd = [parpe_dir / 'misc' / 'setup_amici_model.sh', amici_model_dir, parpe_model_dir] - print(" ".join(cmd)) + print(" ".join(map(str, cmd))) check_run(cmd) # create input hdf5 file cmd = ['parpe_petab_to_hdf5', '-y', yaml_file, '-d', amici_model_dir, '-n', model_name, '-o', hdf5_input] - print(" ".join(cmd)) + if case == '0006': + cmd.append('--flatten') + print(" ".join(map(str, cmd))) check_run(cmd) # simulate model using nominal parameters - cmd = [os.path.join(parpe_model_dir, 'build', - f'simulateNominal_{model_name}'), + cmd = [parpe_model_dir / 'build' / f'simulateNominal_{model_name}', hdf5_input, hdf5_output] - print(" ".join(cmd)) + print(" ".join(map(str, cmd))) ret = check_run(cmd) # check output - g = re.search(r'Likelihood: (\d+\.\d+)', ret.stdout).group(0) + g = re.search(r'Likelihood: (\d+\.\d+)', ret.stdout)[0] llh_actual = - float(g.split(' ')[1]) print("Actual llh:", llh_actual) - solution = petabtests.load_solution(case, 'sbml') + solution = petabtests.load_solution(case, model_type, version=version) gt_llh = solution[petabtests.LLH] assert llh_actual == pytest.approx(gt_llh, @@ -122,7 +130,8 @@ def run() -> None: n_success = 0 n_skipped = 0 - for case in petabtests.CASES_LIST: + all_cases = list(petabtests.get_cases('sbml', version="v1.0.0")) + for case in all_cases: try: test_case(case) n_success += 1 @@ -133,9 +142,9 @@ def run() -> None: logger.error(f"Case {case} failed.") logger.error(e) - logger.info(f"{n_success} / {len(petabtests.CASES_LIST)} successful, " + logger.info(f"{n_success} / {len(all_cases)} successful, " f"{n_skipped} skipped") - if n_success != len(petabtests.CASES_LIST): + if n_success != len(all_cases): sys.exit(1)