Hi all,
I am trying to implement a simple exponential ODE model in the Bayesian framework using both odeint
and solve_ivp
methods from the scipy.integrate
library.
import pymc as pm
import pytensor
import pytensor.tensor as pt
import arviz as az
import matplotlib.pyplot as plt
from pytensor.compile.ops import as_op
from scipy.integrate import odeint
from scipy.integrate import solve_ivp
The equation is very simple:
def exponential_decay(t, y):
dydt = -0.5 * y
return dydt'
ODE-solution with solve_ivp
without using pymc
:
sol = solve_ivp(
exponential_decay,
t_span= [0, 10],
y0 = [10],
max_step=0.1)
fig, ax = plt.subplots()
ax.scatter(
sol.t,
sol.y[0],
s=50,
marker="^",
label="ode-solution")
ax.grid()
ax.set_xlabel("Time [s]")
ax.set_ylabel("y [m]")
ax.legend()
plt.show()
Then, I proceed with generating synthetic data with Gaussian noise for the ODE model:
ode_array_size = len(sol.y[0])
mu, sigma = sol.y[0], 1
simulated_data_y = np.random.normal(mu, sigma, ode_array_size)
I learned from this example that we would need to convert the data-type to pytensor in order to use PyMC (Link: PyMC ODE Example):
@as_op(itypes=[pt.dvector], otypes=[pt.dvector])
def pytensor_ode_model(param):
t_start, t_stop, y_init = param
sol = solve_ivp(
exponential_decay,
t_span = [t_start, t_stop],
y0 = [y_init],
max_step=0.1)
return sol.y[0]
Afterwards, one could get sample and get the distribution of the parameters:
t_start = 0.1
t_stop = 10
y_init = 10
with pm.Model() as model:
y_init = pm.Normal("y_init", mu=y_init, sigma=1)
t_start = pm.TruncatedNormal("t_start", mu=t_start, sigma=0.1, lower=0, initval=t_start)
t_stop = pm.TruncatedNormal("t_stop", mu=t_stop, sigma=1, lower=0, initval=t_stop)
sigma = pm.HalfNormal("sigma", 1)
ode_solution = pytensor_ode_model(pm.math.stack([y_init, t_start, t_stop]))
pm.Normal(
"Y_obs",
mu=ode_solution,
sigma=sigma,
observed=simulated_data_y)
vars_list = list(model.values_to_rvs.keys())[:-1]
trace = pm.sample(
step=[pm.Slice(vars_list)],
tune=2000,
draws=2000,
cores=1)
I get some errors about Input dimension mismatch:
ValueError: Input dimension mismatch: (input[0].shape[0] = 200, input[1].shape[0] = 101)
Apply node that caused the error: Composite{switch(i4, (((-0.5 * sqr(((i0 - i1) / i2))) - 0.9189385332046727) - i3), -inf)}(Y_obs{[ 1.022617 ... 63610e-02]}, FromFunctionOp{pytensor_ode_model}.0, ExpandDims{axis=0}.0, Log.0, Gt.0)
If I would solve the same equation using odeint
, I would be able to proceed without errors.
So, I am not sure what are the changes that I would need to make to resolve the input dimension problem using solve_ivp
?
Thank you for any hints towards resolving this issue!