diff --git a/.github/actions/install-apt-dependencies/action.yml b/.github/actions/install-apt-dependencies/action.yml new file mode 100644 index 0000000000..0474e5befa --- /dev/null +++ b/.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/.github/actions/setup-doxygen/action.yml b/.github/actions/setup-doxygen/action.yml new file mode 100644 index 0000000000..f6a62181c1 --- /dev/null +++ b/.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/.github/actions/setup-sonar-tools/action.yml b/.github/actions/setup-sonar-tools/action.yml new file mode 100644 index 0000000000..d791c120bf --- /dev/null +++ b/.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/.github/actions/setup-swig/action.yml b/.github/actions/setup-swig/action.yml new file mode 100644 index 0000000000..0eb8a04473 --- /dev/null +++ b/.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/.github/workflows/deploy_branch.yml b/.github/workflows/deploy_branch.yml index 77883907d3..73294286a1 100644 --- a/.github/workflows/deploy_branch.yml +++ b/.github/workflows/deploy_branch.yml @@ -21,12 +21,10 @@ jobs: 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: | diff --git a/.github/workflows/deploy_release.yml b/.github/workflows/deploy_release.yml index ce493533e7..001d0328bf 100644 --- a/.github/workflows/deploy_release.yml +++ b/.github/workflows/deploy_release.yml @@ -24,12 +24,10 @@ jobs: 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: | diff --git a/.github/workflows/test_benchmark_collection_models.yml b/.github/workflows/test_benchmark_collection_models.yml index 9cfa50ea2a..e5d13946e7 100644 --- a/.github/workflows/test_benchmark_collection_models.yml +++ b/.github/workflows/test_benchmark_collection_models.yml @@ -37,11 +37,8 @@ jobs: with: fetch-depth: 20 - # install dependencies - - name: apt - run: | - sudo apt-get update \ - && sudo apt-get install -y swig libatlas-base-dev + - name: Install apt dependencies + uses: ./.github/actions/install-apt-dependencies - run: echo "${HOME}/.local/bin/" >> $GITHUB_PATH diff --git a/.github/workflows/test_doc.yml b/.github/workflows/test_doc.yml index 98c023ba79..717ec9cc53 100644 --- a/.github/workflows/test_doc.yml +++ b/.github/workflows/test_doc.yml @@ -16,7 +16,6 @@ on: jobs: doxygen: name: Test Doxygen - runs-on: ubuntu-22.04 strategy: @@ -32,26 +31,14 @@ jobs: - uses: actions/checkout@v3 - run: git fetch --prune --unshallow - - name: apt - run: | - sudo apt-get update \ - && sudo apt-get install -y \ - bison \ - ragel \ - graphviz \ - texlive-latex-extra - - - 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 strategy: @@ -68,11 +55,9 @@ jobs: - run: git fetch --prune --unshallow - run: echo "AMICI_DIR=$(pwd)" >> $GITHUB_ENV - - run: echo "SWIG=${AMICI_DIR}/ThirdParty/swig-4.1.1/install/bin/swig" >> $GITHUB_ENV - - name: Build doxygen - run: | - sudo scripts/downloadAndBuildDoxygen.sh + - name: Set up doxygen + uses: ./.github/actions/setup-doxygen # install amici dependencies - name: apt @@ -85,10 +70,7 @@ jobs: pandoc \ python3-venv \ - - name: Build swig - run: | - sudo scripts/downloadAndBuildSwig.sh + - uses: ./.github/actions/setup-swig - name: sphinx - run: | - scripts/run-sphinx.sh + run: scripts/run-sphinx.sh diff --git a/.github/workflows/test_install.yml b/.github/workflows/test_install.yml index 6721008599..0ad1ec0a14 100644 --- a/.github/workflows/test_install.yml +++ b/.github/workflows/test_install.yml @@ -22,17 +22,14 @@ jobs: - 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: | @@ -70,16 +67,8 @@ jobs: - run: echo "AMICI_DIR=$(pwd)" >> $GITHUB_ENV - # install amici dependencies - - name: apt - run: | - sudo apt-get update \ - && sudo apt-get install -y \ - g++ \ - libatlas-base-dev \ - libboost-serialization-dev \ - libhdf5-serial-dev \ - swig + - name: Install apt dependencies + uses: ./.github/actions/install-apt-dependencies - name: Create AMICI sdist run: | diff --git a/.github/workflows/test_performance.yml b/.github/workflows/test_performance.yml index 871421ffd0..de9dd686d9 100644 --- a/.github/workflows/test_performance.yml +++ b/.github/workflows/test_performance.yml @@ -35,11 +35,9 @@ jobs: with: fetch-depth: 20 - # install dependencies - - name: apt - run: | - sudo apt-get update \ - && sudo apt-get install -y swig libatlas-base-dev + - name: Install apt dependencies + uses: ./.github/actions/install-apt-dependencies + - run: pip3 install petab shyaml build - run: echo "${HOME}/.local/bin/" >> $GITHUB_PATH diff --git a/.github/workflows/test_petab_test_suite.yml b/.github/workflows/test_petab_test_suite.yml index 70acd254df..26a4ac7a26 100644 --- a/.github/workflows/test_petab_test_suite.yml +++ b/.github/workflows/test_petab_test_suite.yml @@ -34,14 +34,14 @@ jobs: 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 + && sudo apt-get install -y python3-venv - name: Build BNGL run: | diff --git a/.github/workflows/test_python_cplusplus.yml b/.github/workflows/test_python_cplusplus.yml index 44eeec8acb..546b297712 100644 --- a/.github/workflows/test_python_cplusplus.yml +++ b/.github/workflows/test_python_cplusplus.yml @@ -35,23 +35,11 @@ jobs: - 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=5.0.1.3006" >> $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 - - # TODO: add to ci image - - 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/ \ + - name: Set up Sonar tools + uses: ./.github/actions/setup-sonar-tools + + - name: Install apt dependencies + uses: ./.github/actions/install-apt-dependencies # install amici dependencies - name: apt @@ -60,12 +48,7 @@ jobs: && sudo apt-get install -y \ cmake \ g++ \ - libatlas-base-dev \ - libboost-serialization-dev \ - libboost-chrono-dev \ - libhdf5-serial-dev \ python3-venv \ - swig \ lcov \ libboost-math-dev diff --git a/.github/workflows/test_python_ver_matrix.yml b/.github/workflows/test_python_ver_matrix.yml index a0f3072ec9..d19ee4809c 100644 --- a/.github/workflows/test_python_ver_matrix.yml +++ b/.github/workflows/test_python_ver_matrix.yml @@ -41,15 +41,8 @@ jobs: 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 diff --git a/.github/workflows/test_sbml_semantic_test_suite.yml b/.github/workflows/test_sbml_semantic_test_suite.yml index 68bfaed8e3..0fde56b8f9 100644 --- a/.github/workflows/test_sbml_semantic_test_suite.yml +++ b/.github/workflows/test_sbml_semantic_test_suite.yml @@ -40,10 +40,10 @@ jobs: - 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 }} diff --git a/.github/workflows/test_valgrind.yml b/.github/workflows/test_valgrind.yml index 1138e663c0..7b513f28dd 100644 --- a/.github/workflows/test_valgrind.yml +++ b/.github/workflows/test_valgrind.yml @@ -34,20 +34,17 @@ jobs: - 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 \ python3-venv \ - swig \ - valgrind \ - libboost-math-dev + valgrind - name: Build AMICI run: | @@ -78,20 +75,17 @@ jobs: - 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 \ python3-venv \ - swig \ - valgrind \ - libboost-math-dev + valgrind - name: Build AMICI run: | diff --git a/python/sdist/amici/numpy.py b/python/sdist/amici/numpy.py index d9b34b6447..b84e52cc2b 100644 --- a/python/sdist/amici/numpy.py +++ b/python/sdist/amici/numpy.py @@ -137,7 +137,8 @@ def __deepcopy__(self, memo): :returns: SwigPtrView deep copy """ - other = SwigPtrView(self._swigptr) + # 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) @@ -151,6 +152,18 @@ def __repr__(self): """ 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): """ @@ -297,6 +310,10 @@ def __getitem__( 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: @@ -340,9 +357,13 @@ 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", @@ -363,7 +384,9 @@ def __init__(self, edata: Union[ExpDataPtr, ExpData]): f"Unsupported pointer {type(edata)}, must be" f"amici.ExpDataPtr!" ) - self._field_dimensions = { # observables + self._field_dimensions = { + "ts": [edata.nt()], + # observables "observedData": [edata.nt(), edata.nytrue()], "observedDataStdDev": [edata.nt(), edata.nytrue()], # event observables @@ -378,6 +401,7 @@ def __init__(self, edata: Union[ExpDataPtr, ExpData]): len(edata.fixedParametersPreequilibration) ], } + edata.ts = edata.ts_ edata.observedData = edata.getObservedData() edata.observedDataStdDev = edata.getObservedDataStdDev() edata.observedEvents = edata.getObservedEvents() diff --git a/python/sdist/amici/petab_objective.py b/python/sdist/amici/petab_objective.py index 87409ee446..e3111d3b68 100644 --- a/python/sdist/amici/petab_objective.py +++ b/python/sdist/amici/petab_objective.py @@ -516,6 +516,7 @@ def create_parameter_mapping( 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 ), diff --git a/python/tests/test_swig_interface.py b/python/tests/test_swig_interface.py index b1afa0b76b..a746552b55 100644 --- a/python/tests/test_swig_interface.py +++ b/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): @@ -442,3 +443,30 @@ def test_edata_repr(): 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/scripts/downloadAndBuildDoxygen.sh b/scripts/downloadAndBuildDoxygen.sh index c51c05c599..19d86be5a1 100755 --- a/scripts/downloadAndBuildDoxygen.sh +++ b/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_7 --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/scripts/downloadAndBuildSwig.sh b/scripts/downloadAndBuildSwig.sh index b7d7f9d865..5fa0896f6c 100755 --- a/scripts/downloadAndBuildSwig.sh +++ b/scripts/downloadAndBuildSwig.sh @@ -1,11 +1,13 @@ #!/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.1.1 +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}" diff --git a/swig/edata.i b/swig/edata.i index 59dcb4fa8a..f2f7d0da8a 100644 --- a/swig/edata.i +++ b/swig/edata.i @@ -74,6 +74,14 @@ def _edata_repr(self: "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 {