I would like to lower the time Scipy's odeint takes for solving a differential equation.
To practice, I used the example covered in Python in scientific computations as template. Because odeint takes a function f
as argument, I wrote this function as a statically typed Cython version and hoped
the running time of odeint would decrease significantly.
The function f
is contained in file called ode.pyx
as follows:
import numpy as np
cimport numpy as np
from libc.math cimport sin, cos
def f(y, t, params):
cdef double theta = y[0], omega = y[1]
cdef double Q = params[0], d = params[1], Omega = params[2]
cdef double derivs[2]
derivs[0] = omega
derivs[1] = -omega/Q + np.sin(theta) + d*np.cos(Omega*t)
return derivs
def fCMath(y, double t, params):
cdef double theta = y[0], omega = y[1]
cdef double Q = params[0], d = params[1], Omega = params[2]
cdef double derivs[2]
derivs[0] = omega
derivs[1] = -omega/Q + sin(theta) + d*cos(Omega*t)
return derivs
I then create a file setup.py
to complie the function:
from distutils.core import setup
from Cython.Build import cythonize
setup(ext_modules=cythonize('ode.pyx'))
The script solving the differential equation (also containing the Python
version of f
) is called solveODE.py
and looks as:
import ode
import numpy as np
from scipy.integrate import odeint
import time
def f(y, t, params):
theta, omega = y
Q, d, Omega = params
derivs = [omega,
-omega/Q + np.sin(theta) + d*np.cos(Omega*t)]
return derivs
params = np.array([2.0, 1.5, 0.65])
y0 = np.array([0.0, 0.0])
t = np.arange(0., 200., 0.05)
start_time = time.time()
odeint(f, y0, t, args=(params,))
print("The Python Code took: %.6s seconds" % (time.time() - start_time))
start_time = time.time()
odeint(ode.f, y0, t, args=(params,))
print("The Cython Code took: %.6s seconds ---" % (time.time() - start_time))
start_time = time.time()
odeint(ode.fCMath, y0, t, args=(params,))
print("The Cython Code incorpoarting two of DavidW_s suggestions took: %.6s seconds ---" % (time.time() - start_time))
I then run:
python setup.py build_ext --inplace
python solveODE.py
in the terminal.
The time for the python version is approximately 0.055 seconds, whilst the Cython version takes roughly 0.04 seconds.
Does somebody have a recommendation to improve on my attempt of solving the differential equation, preferably without tinkering with the odeint routine itself, with Cython?
Edit
I incorporated DavidW's suggestion in the two files ode.pyx
and solveODE.py
It took only roughly 0.015 seconds to run the code with these suggestions.
numba
instead ofcython
, but any difference is likely to be small. most of the computation time is likely the context switching taking place whenodeint
calls your function. you may honestly see the best gains from writing your own numerical integration function (again with cython or numba) to avoid the context switching – Indefatigablef
andode.f
are python objects that require a context switch at least once per call (4000 calls for 0-200 in steps of 0.05) otherwiseodeint
wouldn't be able to take any old custom user function. I've gotten a 4x speedup with numba, but I'm working rn to get more... – Indefatigableimport numba
and@numba.jit
on the line before your function definition. Use your Python version unchanged. Be aware that the first time you run it will be slow (while it compiles) so it's worth timing it twice. – FeminacyCython
. – Wrapping