imranal imranal - 2 months ago 33
Python Question

Interchanging between different scipy ode solvers

I have a made a solver which can interchange between

scipy.integrate.ode
and
scipy.integrate.odeint
. Here is the code.

def f(y,s,C,u,v):
y0 = y[0] # u
y1 = y[1] # u'
y2 = y[2] # v
y3 = y[3] # v'
dy = np.zeros_like(y)
dy[0] = y1
dy[2] = y3

C = C.subs({u:y0,v:y2})
dy[1] = -C[0,0][0]*dy[0]**2\
-2*C[0,0][1]*dy[0]*dy[2]\
-C[0,1][1]*dy[2]**2
dy[3] = -C[1,0][0]*dy[0]**2\
-2*C[1,0][1]*dy[0]*dy[2]\
-C[1,1][1]*dy[2]**2
return dy

def solve(C,u0,s0,s1,ds,solver=None):
from sympy.abc import u,v
if solver == None: # use lsoda from scipy.integrate.odeint
s = np.arange(s0,s1+ds,ds)
print 'Running solver ...'
return sc.odeint(f,u0,s,args=(C,u,v))
else: # use any other solver from scipy.integrate.ode
r = sc.ode(f).set_integrator(solver) # vode,zvode,lsoda,dopri5,dop853
r.set_f_params(C,u,v)
r.set_initial_value(u0)
#t = []
y = []
print 'Running solver ...'
while r.successful() and r.t <= s1:
r.integrate(r.t + ds)
y.append(r.y)#; t.append(r.t)
return np.array(y)


The problem I experience is as following. If I decide to use the solver from
scipy.integrate.odeint
then the parameters of
f
have to be specified in the order as they are in the code. However, if I decide to use the solvers from
scipy.integrate.ode
I have to change the order of the parameters of the function
f(y,s,C,u,v)
to
f(s,y,C,u,v)
, otherwise I get the error

TypeError: 'float' object has no attribute '__getitem__'


If I do this, then
scipy.integrate.odeint
generates the same error for
f
defined as
f(s,y,C,u,v)
. How can I operate with a unified
f
regardless of the order of the parameters ?

Edit :

To sum the problem up :

scipy.integrate.ode
solvers work if the function f is defined as
f(s,y,C,u,v)
, and
scipy.integrate.odeint
solver works if the function f is defined as
f(y,s,C,u,v)
. Why is this occurring, and how I can I fix this?

Edit :

Scipy -- version 0.16.0

Answer

Why is this occurring, and how I can I fix this?

It is occurring because of an unfortunate API design decision made years ago. odeint and the ode class require different signatures for the system to be solved.

You can fix it by adding a wrapper that changes the order of the first two arguments when you use, say, the ode class. For example, you could change this:

    r = sc.ode(f).set_integrator(solver)

to

    r = sc.ode(lambda t, x, *args: f(x, t, *args)).set_integrator(solver)

Better documentation of this discrepancy is in progress: https://github.com/scipy/scipy/pull/5151

Comments