passing dictionaries of parameters and initial values to scipy.integrate.odeint

Question:

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.

Asked By: Michiel Karrenbelt

||

Answers:

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)
Answered By: MB-F