I'm trying to integrate a system of differential equations using spicy.itegrate.odeint.
First, parameters and initial conditions are sampled and returned in two dictionaries (x0
and p
). Then the model is created and written as a function to a file, looking roughly as follows (with dummy equations):
def model(x, t, p):
xdot = [
x['rate1'], p["a"]
x['rate2'], p["b"] * x["state1"] - p["c"] * x["state2"]
x['rate3'], p["c"] * x["state2"]
x["state4"], x["rate1"] + x["rate2"]
x["state5"], - x["rate2"] + x["rate3"]
]
return xdot
This is so that I can easily generate different models from simple inputs. Thus, what might normally be hardcoded variables, are now keys in a dictionary with the corresponding value. I do this because assigning variables dynamically is considered bad practice.
When I try to integrate the system using odeint
, as follows
sol = odeint(model, x0, t, args=(p,),
atol=1.0e-8, rtol=1.0e-6)
where, thus, x0
is a dictionary of initial conditions, and p
of parameters (and t
a list of floats). I get the following error:
TypeError: float() argument must be a string or a number, not 'dict'
Obviously, scipy is not happy with my attempt to pass a dictionary to parameterize and initialize my model. The question is if there is a way for me to resolve this, or whether I am forced to assign all values in my dictionary to variables with the name of their corresponding key. The latter does not allow me to pass the same set of initial conditions and parameters to all models, since they differ both in states and parameters. Thus, wish to pass the same set of parameters to all models, regardless of wether the parameters are in the model or not.
For performance reasons scipy functions like odeint
work with arrays where each parameter is associated with a fixed position.
A solution to access parameters by name is to convert them to a namedtuple
which gives them both, a name and a position. However, the conversion needs to be done inside the function because odeint
passes the parameters as a numpy.array
to the model function.
This example should convey the idea:
from scipy.integrate import odeint
from collections import namedtuple
params = namedtuple('params', ['a', 'b', 'c', 'd'])
def model(x, t0):
x = params(*x)
xdot = [1,
x.a + x.b,
x.c / x.a,
1/x.d**2] # whatever
return xdot
x0 = params(a=1, b=0, c=2, d=0.5)
t = [0, 0.5, 1]
sol = odeint(model, x0, t)