Skip to content
Permalink
Browse files

Cython compiler directives for faster simulations (#403)

I created a PR for Cython (cython/cython#2595)
which added compiler directive support for cython.inline(), which
is now present in Cython>=0.92

This PR introduces user-configurable Cython compiler directives
within the ScipyOdeSimulator. By default, these directives disable
bounds, wrap around, "None", and initialized checks. Some initial
benchmarking on asv shows a speed improvement of about 5% on my
laptop (every little helps?)
  • Loading branch information...
alubbock committed Feb 5, 2019
1 parent f3db9b5 commit 2afc7092978a7198e7835670e737b937abc74861
Showing with 30 additions and 13 deletions.
  1. +15 −4 benchmarks/simulator.py
  2. +15 −9 pysb/simulator/scipyode.py
@@ -14,23 +14,34 @@ def setup(self):
[[p.value for p in self.model.parameters]], self.nsims, axis=0)
integrator_options_common = {
'model': self.model,
'tspan': np.linspace(0, 1000, 101),
'atol': 1e-6,
'rtol': 1e-6,
'mxsteps': 20000,
'tspan': np.linspace(0, 20000, 101),
'param_values': self.parameter_set
}

self.sim_lsoda = ScipyOdeSimulator(
integrator='lsoda',
compiler='cython',
integrator_options={'atol': 1e-6, 'rtol': 1e-6, 'mxstep': 20000},
**integrator_options_common
)
self.sim_lsoda_no_compiler_directives = ScipyOdeSimulator(
integrator='lsoda',
compiler='cython',
cython_directives={},
integrator_options={'atol': 1e-6, 'rtol': 1e-6, 'mxstep': 20000},
**integrator_options_common
)

self.sim_cupsoda = CupSodaSimulator(
integrator_options={'atol': 1e-6, 'rtol': 1e-6, 'max_steps': 20000},
**integrator_options_common
)

def time_scipy_lsoda(self):
self.sim_lsoda.run()

def time_scipy_lsoda_no_compiler_directives(self):
self.sim_lsoda_no_compiler_directives.run()

def time_cupsoda(self):
self.sim_cupsoda.run()
@@ -31,10 +31,6 @@
import importlib


CYTHON_DECL = '#cython: boundscheck=False, wraparound=False, ' \
'nonecheck=False, initializedcheck=False\n'


class ScipyOdeSimulator(Simulator):
"""
Simulate a model using SciPy ODE integration
@@ -136,6 +132,13 @@ class ScipyOdeSimulator(Simulator):
}
}

default_cython_directives = {
'boundscheck': False,
'wraparound': False,
'nonecheck': False,
'initializedcheck': False
}

def __init__(self, model, tspan=None, initials=None, param_values=None,
verbose=False, **kwargs):

@@ -152,6 +155,8 @@ def __init__(self, model, tspan=None, initials=None, param_values=None,
integrator = kwargs.pop('integrator', 'vode')
compiler_mode = kwargs.pop('compiler', None)
integrator_options = kwargs.pop('integrator_options', {})
cython_directives = kwargs.pop('cython_directives',
self.default_cython_directives)
if kwargs:
raise ValueError('Unknown keyword argument(s): {}'.format(
', '.join(kwargs.keys())
@@ -204,11 +209,12 @@ def __init__(self, model, tspan=None, initials=None, param_values=None,
if self._compiler == 'cython':
if not Cython:
raise ImportError('Cython library is not installed')
code_eqs = CYTHON_DECL + code_eqs

def rhs(t, y, p):
# note that the evaluated code sets ydot as a side effect
Cython.inline(code_eqs, quiet=True)
Cython.inline(
code_eqs, quiet=True,
cython_compiler_directives=cython_directives)

return ydot

@@ -320,10 +326,10 @@ def jacobian(t, y, p):
with self._patch_distutils_logging:
jacobian(0.0, self.initials[0], self.param_values[0])
else:
jac_eqs = CYTHON_DECL + jac_eqs

def jacobian(t, y, p):
Cython.inline(jac_eqs, quiet=True)
Cython.inline(
jac_eqs, quiet=True,
cython_compiler_directives=cython_directives)
return jac

with _set_cflags_no_warnings(self._logger):

0 comments on commit 2afc709

Please sign in to comment.
You can’t perform that action at this time.