Skip to content

Commit

Permalink
Fix Swig ExpData constructor interface (#465)
Browse files Browse the repository at this point in the history
* fix(swig) add ExpData default constructor so that swig generates it

* fix(python) fix ExpData convenience wrapper

* version bump

* fix(CI) allow run-codecov.sh to fail the build, these are tests after all

* fix(tests) remove earm model from pysb tests as it fails to compile in travis (why?)

* fix(style) fix codacy unused variable issue

* fix(CI) fix python coverage report

* fix(CI) fix cpp coverage from python

* fix(CI) test both install from archive and setup.py

* fix(CI) fix archive installation

* fix(python) objective function derivatives

* fix(python) add docs, seperate install scripts, fixes #457

* fix(python) fix generation of sx0_fixedParmaters, make checkDerivative fail for given tolerances
  • Loading branch information
FFroehlich authored Nov 18, 2018
1 parent 11c8ec3 commit a9aaf0e
Show file tree
Hide file tree
Showing 14 changed files with 201 additions and 79 deletions.
6 changes: 5 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@ matrix:
- pyenv shell 2.7 3.6
compiler: gcc
env: ENABLE_GCOV_COVERAGE=TRUE
after_success:
after_script:
# this can fail the build, after_success cannot
- ./scripts/run-codecov.sh
- bash <(curl -s https://codecov.io/bash) -f coverage.info -X fix -F cpp
- bash <(curl -s https://codecov.io/bash) -f coverage_py.xml -F python


- os: osx
osx_image: xcode9.3
Expand Down Expand Up @@ -58,6 +60,8 @@ install:
- ./scripts/buildBNGL.sh
- export BNGPATH=$(pwd)/ThirdParty/BioNetGen-2.3.2
- ./scripts/buildAmici.sh
- ./scripts/installAmiciArchive.sh
- ./scripts/installAmiciSource.sh

script:
# - cd $BASE_DIR/python/sdist
Expand Down
3 changes: 3 additions & 0 deletions include/amici/edata.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ class ExpData {
public:
/** default constructor */
ExpData();

/** default copy constructor, needs to be declared to be generated in swig*/
ExpData(const ExpData &) = default;

/**
* constructor that only initializes dimensions
Expand Down
21 changes: 14 additions & 7 deletions python/amici/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
from .amici import *
hdf5_enabled = True
has_clibs = True
except (ImportError, ModuleNotFoundError, AttributeError):
except (ImportError, ModuleNotFoundError, AttributeError): # pragma: no cover
try:
from . import amici_without_hdf5 as amici
from .amici_without_hdf5 import *
Expand Down Expand Up @@ -92,21 +92,28 @@ def runAmiciSimulation(model, solver, edata=None):
return rdataToNumPyArrays(rdata)


def ExpData(rdata, sigma_y, sigma_z):
""" Convenience wrapper for ExpData constructor
def ExpData(*args):
""" Convenience wrapper for ExpData constructors
Arguments:
rdata: rdataToNumPyArrays output
sigma_y: standard deviation for ObservableData
sigma_z: standard deviation for EventData
args: arguments
Returns:
ExpData Instance
Raises:
"""
return amici.ExpData(rdata['ptr'].get(), sigma_y, sigma_z)
if isinstance(args[0], dict):
# rdata dict created by rdataToNumPyArrays
return amici.ExpData(args[0]['ptr'].get(), *args[1:])
elif isinstance(args[0], 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(args[0].get(), *args[:1])
else:
return amici.ExpData(*args)


def runAmiciSimulations(model, solver, edata_list, num_threads=1):
Expand Down
43 changes: 34 additions & 9 deletions python/amici/ode_export.py
Original file line number Diff line number Diff line change
Expand Up @@ -1073,9 +1073,31 @@ def _computeEquation(self, name):
[comp._dt for comp in self._states]
)

elif name in ['sx0', 'sx0_fixedParameters']:
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_fixedParametersdx*sx+dx0_fixedParametersdp
# if x0_fixedParameters>0 else 0
# sx0_fixedParameters = sx+deltasx =
# dx0_fixedParametersdx*sx+dx0_fixedParametersdp
self._eqs[name] = \
self.eq('x0_fixedParameters').jacobian(self.sym('p'))

for ip in range(self._eqs[name].shape[1]):
self._eqs[name][:,ip] += \
self.eq('x0_fixedParameters').jacobian(self.sym('x')) \
* self.sym('sx0') \

for index, formula in enumerate(
self.eq('x0_fixedParameters')
):
if formula == 0 or formula == 0.0:
self._eqs[name][index, :] = \
sp.zeros(1, self._eqs[name].shape[1])


elif name == 'JB':
self._eqs[name] = self.eq('J').transpose()

Expand Down Expand Up @@ -1155,7 +1177,11 @@ def _derivative(self, eq, var, name=None):

# partial derivative
if self.eq(eq).size and self.sym(var).size:
self._eqs[name] = self.eq(eq).jacobian(self.sym(var))
if eq == 'Jy':
eq = self.eq(eq).transpose()
else:
eq = self.eq(eq)
self._eqs[name] = eq.jacobian(self.sym(var))
else:
self._eqs[name] = sp.DenseMatrix([])

Expand Down Expand Up @@ -1643,15 +1669,17 @@ def _getFunctionBody(self, function, symbol):
return lines

if function == 'sx0_fixedParameters':
# here we specifically want to overwrite some of the values with 0
# here we only want to overwrite values where x0_fixedParameters
# was applied
lines.append(' ' * 4 + 'switch(ip) {')
for ipar in range(self.model.np()):
lines.append(' ' * 8 + f'case {ipar}:')
for index, formula in enumerate(
self.model.eq('x0_fixedParameters')
):
if formula != 0 and formula != 0.0:
lines.append(' ' * 12 + f'{function}[{index}] = 0.0;')
lines.append(' ' * 12 + f'{function}[{index}] = '
f'{symbol[index, ipar]};')
lines.append(' ' * 12 + 'break;')
lines.append('}')
elif function in sensi_functions:
Expand Down Expand Up @@ -1724,11 +1752,8 @@ def _writeModelHeader(self):
Raises:
"""
if any([math != 0 and math != 0.0 for math in
self.model.eq('sx0_fixedParameters')]):
self.allow_reinit_fixpar_initcond = False
else:
self.allow_reinit_fixpar_initcond = True

self.allow_reinit_fixpar_initcond = True

templateData = {
'MODELNAME': str(self.modelName),
Expand Down
8 changes: 0 additions & 8 deletions scripts/buildAmici.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,5 @@ CPPUTEST_BUILD_DIR=${AMICI_PATH}/ThirdParty/cpputest-master/build/
CppUTest_DIR=${CPPUTEST_BUILD_DIR} cmake -DCMAKE_BUILD_TYPE=Debug ..
make

# Disabled until cmake package is made compatible with updated setup.py
#make python-wheel
#pip3 install --user --prefix= `ls -t ${AMICI_PATH}/build/python/amici-*.whl | head -1`

make python-sdist
set -x
python3 -m venv ${AMICI_PATH}/build/venv --clear
source ${AMICI_PATH}/build/venv/bin/activate
pip3 install --upgrade pip setuptools pkgconfig wheel numpy scipy matplotlib pysb
pip3 install $(ls -t ${AMICI_PATH}/build/python/amici-*.tar.gz | head -1)
24 changes: 24 additions & 0 deletions scripts/installAmiciArchive.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/bin/bash
#
# Build libamici
#
set -e

SCRIPT_PATH=$(dirname $BASH_SOURCE)
AMICI_PATH=$(cd $SCRIPT_PATH/.. && pwd)

# Disabled until cmake package is made compatible with updated setup.py
#make python-wheel
#pip3 install --user --prefix= `ls -t ${AMICI_PATH}/build/python/amici-*.whl | head -1`

rm -f ${AMICI_PATH}/python/sdist/amici/*.cxx
rm -f ${AMICI_PATH}/python/sdist/amici/*.so
rm -f ${AMICI_PATH}/python/sdist/amici/amici.py
rm -f ${AMICI_PATH}/python/sdist/amici/amici_without_hdf5.py

# test install from archive
python3 -m venv ${AMICI_PATH}/build/venvArchive --clear
source ${AMICI_PATH}/build/venvArchive/bin/activate
pip3 install --upgrade pip setuptools pkgconfig wheel numpy
pip3 install $(ls -t ${AMICI_PATH}/build/python/amici-*.tar.gz | head -1)
deactivate
16 changes: 10 additions & 6 deletions scripts/buildAmiciDev.sh → scripts/installAmiciSource.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,18 @@ set -e
SCRIPT_PATH=$(dirname $BASH_SOURCE)
AMICI_PATH=$(cd $SCRIPT_PATH/.. && pwd)

cd ${AMICI_PATH}/build
python3 -m venv ${AMICI_PATH}/build/venvDev --clear
rm -f ${AMICI_PATH}/python/sdist/amici/*.so
# Disabled until cmake package is made compatible with updated setup.py
#make python-wheel
#pip3 install --user --prefix= `ls -t ${AMICI_PATH}/build/python/amici-*.whl | head -1`

rm -f ${AMICI_PATH}/python/sdist/amici/*.cxx
rm -f ${AMICI_PATH}/python/sdist/amici/*.so
rm -f ${AMICI_PATH}/python/sdist/amici/amici.py
rm -f ${AMICI_PATH}/python/sdist/amici/amici_without_hdf5.py
source ${AMICI_PATH}/build/venvDev/bin/activate
pip3 install --upgrade pip setuptools pkgconfig wheel numpy scipy matplotlib jupyter pysb
export ENABLE_AMICI_DEBUGGING=TRUE

# test install from setup.py
python3 -m venv ${AMICI_PATH}/build/venv --clear
source ${AMICI_PATH}/build/venv/bin/activate
pip3 install --upgrade pip setuptools pkgconfig wheel numpy scipy matplotlib pysb
pip3 install --verbose -e ${AMICI_PATH}/python/sdist
deactivate
13 changes: 13 additions & 0 deletions src/model_header.ODE_template.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ extern void w_TPL_MODELNAME(realtype *w, const realtype t, const realtype *x, co
extern void x0_TPL_MODELNAME(realtype *x0, const realtype t, const realtype *p, const realtype *k);
extern void x0_fixedParameters_TPL_MODELNAME(realtype *x0, const realtype t, const realtype *p, const realtype *k);
extern void sx0_TPL_MODELNAME(realtype *sx0, const realtype t,const realtype *x0, const realtype *p, const realtype *k, const int ip);
extern void sx0_fixedParameters_TPL_MODELNAME(realtype *sx0, const realtype t,const realtype *x0, const realtype *p, const realtype *k, const int ip);
extern void xBdot_TPL_MODELNAME(realtype *xBdot, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *xB, const realtype *w, const realtype *dwdx);
extern void xdot_TPL_MODELNAME(realtype *xdot, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w);
extern void y_TPL_MODELNAME(double *y, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w);
Expand Down Expand Up @@ -594,6 +595,18 @@ class Model_TPL_MODELNAME : public amici::Model_ODE {
sx0_TPL_MODELNAME(sx0, t, x0, p, k, ip);
}

/** model specific implementation of fsx0_fixedParameters
* @param sx0 initial state sensitivities
* @param t initial time
* @param x0 initial state
* @param p parameter vector
* @param k constant vector
* @param ip sensitivity index
**/
virtual void fsx0_fixedParameters(realtype *sx0, const realtype t,const realtype *x0, const realtype *p, const realtype *k, const int ip) override {
sx0_fixedParameters_TPL_MODELNAME(sx0, t, x0, p, k, ip);
}

/** model specific implementation of fsxdot
* @param sxdot sensitivity rhs
* @param t timepoint
Expand Down
23 changes: 15 additions & 8 deletions tests/testCoverage.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,22 @@
Generate coverage reports for the testModels and testSBML scripts
exported format is cobertura xml
"""

import coverage
# only consider amici module and ignore the swig generated amici.py
cov = coverage.Coverage(
source=['amici'],
omit=['*/amici.py','*/amici_without_hdf5.py']
)

# ignore code blocks containing import statements
cov.exclude('import')
cov.set_option('report:exclude_lines', ['pragma: no cover', ' raise ',
'except:'])

# coverage needs to be started before all other imports such that everything
# that happens during module import is also added to the report
cov.start()

import unittest
import sys

Expand All @@ -14,13 +28,6 @@
import testPandas
import testPYSB

# only consider amici module and ignore the swig generated amici.py
cov = coverage.Coverage(source=['amici'],omit=['*/amici.py','*/amici_without_hdf5.py'])

# ignore code blocks containing import statements
cov.exclude('import')
cov.start()

# build the testSuite from testModels and testSBML
suite = unittest.TestSuite()
suite.addTest(testSBML.TestAmiciSBMLModel())
Expand Down
Loading

0 comments on commit a9aaf0e

Please sign in to comment.