Passing a parameterized function descriptor to Python

I have a generic function that defines an ODE form that I plan to integrate using scipy.integrate.odeint , for example:

 def my_ode(K, tau, y, u): return K*u/tau - y/tau # dydt 

I have several objects in my code that have the dynamics of the form defined in my_ode , but with unique parameters K and tau . I would really like to just pass a unique my_ode descriptor with the parameters that were already set when initializing my objects, so when I update my objects, all I need to do is something like soln = odeint(my_ode, t, y, u) for some simulation time t .

For example, if I define a class:

 class MyThing: def __init__(self, ode, y0): # I would rather not maintain K and tau in the objects, I just want the ODE with unique parameters here. self.ode = ode self.y = y0 self.time = 0.0 def update(self, t, u): # I want this to look something like: self.y = scipy.integrate.odeint(self.ode, t, self.y, u) 

Can I do something with Lambdas when I initialize MyThing instances to assign K and tau parameters at initialization first and never pass them again? I'm a little stuck.

+6
source share
2 answers

Solution with lambdas

It looks like I can do this job using lambdas to generate unique function descriptors when initializing my objects. For odeint compatibility, I need to define my functions so that the first two arguments are time and initial state:

 def my_ode(t, y, u, K, tau): return K*u/tau - y/tau # dydt 

Next, I can initialize MyThing objects using lambdas to set K and tau as:

 thing1 = MyThing(lambda t, y, u: my_ode(t, y, u, 10.0, 0.5), 0.0) 

The function descriptor assigned to thing1.ode is now the function descriptor returned by the lambda (this may not be the right way to say this) with values ​​for K and tau . Now in thing1.update I need to make some changes to make it work with odeint :

 def update(self, t_step, t_end, u): t_array = np.arange(self.time, t_end, t_step) # time values at which to evaluate ODE response = scipy.integrate.odeint(self.ode, self.y, t_array, (u,)) self.y = response[-1] # current state is the last evaluated state 

One thing that confused me a bit is that any additional arguments in ODE should be passed as a tuple to odeint . It looks like what I want.

There is also a more object-oriented approach using scipy.integrate.ode , which allows for the phased integration of the function and is great for my modeling goals. To do this, I install the ODE object and update it with something like:

 class MyThing(): def __init__(self, ode, y0): self.ode = integrate.ode(ode) # define the ODE self.ode.set_integrator("dopri5") # choose an integrator self.ode.set_initial_value(y0) def update(self, u, t_step): """Update the ODE step-wise.""" self.ode.set_f_params(u) # need to pass extra parameters with this method self.ode.integrate(self.ode.t + t_step) # step-wise update return self.ode.successful() def get_output(self): """Get output from ODE function.""" return self.ode.y 
0
source

If you have:

 def my_ode(K, tau, y, u): return K*u/tau - y/tau 

you can define something like:

 def make_ode_helper(k, tau): return lambda (y, u): my_ode(K, tau, y, u) 

and should be able to initialize MyThing with:

 mt = new MyThing(make_ode_helper(k, tau), y0) 

then you can call this helper only with parameters y and u:

 someresult = ode_helper(y, u) 
+2
source

All Articles