This is a really short update. Here are a couple things I have been working on since the last blog post.
1) Continuing to write the API so that it is intuitive
2) Writing unit tests
When I last made a blog post about the API, users were to pass the ode parameters and initial conditions as a single vector. That was just to get things off the ground. Now that API is working, I can go back and make that a little more user friendly. The
ODEModel (the meat and potatoes of the API) inherits from the
theano.Op class, so in the
make_node method I changed
def make_node(self, x): x = theano.tensor.as_tensor_variable(x) return theano.Apply(self, [x], [x.type()])
def make_node(self, odeparams, initial_condition): if np.ndim(odeparams)>1: odeparams = np.ravel(odeparams) if np.ndim(initial_condition) > 1: odeparams = np.ravel(initial_condition) odeparams = tt.as_tensor_variable(odeparams) initial_condition = tt.as_tensor_variable(initial_condition) x = tt.concatenate([odeparams,initial_condition]) return theano.Apply(self, [x], [x.type()])
The little bit of logic at the beginning is to get around some shape problems with
theano. So now to use the API, instead of doing
all_params = pm.math.stack([<parameters here>, <initial conditions here>]) forward = ode_model(all_params)
you can do
forward = ode_model(odeparams = [<parameters here>], initial_condition = [<initial conditions here>])
which I think is much better. Explicit is better than implicit. This change actually makes some models run much faster which is interesting.