You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -57,7 +55,7 @@ The purpose of this notebook is to demonstrate how to perform Bayesian inference
57
55
* Specification
58
56
* Least Squares Solution
59
57
* Gradient-free Bayesian Inference
60
-
*Wrap`odeint` in an Aesara operator for use in PyMC
58
+
*Wrapping`odeint` in a Pytensor operator for use in PyMC
61
59
* Bayesian inference using gradient-free methods
62
60
* Slice Sampler
63
61
* DEMetropolisZ Sampler
@@ -66,21 +64,23 @@ The purpose of this notebook is to demonstrate how to perform Bayesian inference
66
64
* Sequential Monte Carlo (SMC) Sampler
67
65
* Bayesian Inference with Gradients
68
66
*`pymc.ode.DifferentialEquation` specification with the NUTs Sampler
69
-
* Looping in PyMC with `aesara.scan` and the NUTs Sampler
67
+
* Looping in PyMC with `Pytensor.scan` and the NUTs Sampler
70
68
### Key Conclusions
71
-
* Based on the experiments in this notebook, the most simple and efficient method for performing Bayesian inference on the Lotka-Volterra equations was to specify the ODE system in Scipy, wrap the function as an Aesara op, and use a Differential Evolution Metropolis (DEMetropolis) sampler in PyMC.
69
+
* Based on the experiments in this notebook, the most simple and efficient method for performing Bayesian inference on the Lotka-Volterra equations was to specify the ODE system in Scipy, wrap the function as an Pytensor op, and use a Differential Evolution Metropolis (DEMetropolis) sampler in PyMC.
72
70
73
71
+++ {"tags": []}
74
72
75
73
## Background
76
74
### Motivation
77
-
Ordinary differential equation models (ODEs) are used in a variety of science and engineering domains to model the time evolution of physical variables. A natural choice to estimate the values and uncertainty of model parameters given experimental data is Bayesian inference. However, ODEs can be challenging to specify and solve in the Bayesian setting, therefore, this notebook steps through multiple methods for solving an ODE inference problem using PyMC. The Lotka-Volterra model used in this example has often been used for benchmarking Bayesian inference methods (e.g., in [Stan](https://mc-stan.org/users/documentation/case-studies/lotka-volterra-predator-prey.html)).
75
+
Ordinary differential equation models (ODEs) are used in a variety of science and engineering domains to model the time evolution of physical variables. A natural choice to estimate the values and uncertainty of model parameters given experimental data is Bayesian inference. However, ODEs can be challenging to specify and solve in the Bayesian setting, therefore, this notebook steps through multiple methods for solving an ODE inference problem using PyMC. The Lotka-Volterra model used in this example has often been used for benchmarking Bayesian inference methods (e.g., in [Stan](https://mc-stan.org/users/documentation/case-studies/lotka-volterra-predator-prey.html)). See also Richard McElraith's discussion of this model in [Statistical Rethinking](http://xcelab.net/rm/statistical-rethinking/), Chapter 16 of the Second Edition.
76
+
77
+
+++ {"tags": []}
78
78
79
79
### Lotka-Volterra Predator-Prey Model
80
80
The Lotka-Volterra model describes the interaction between a predator and prey species. This ODE given by:
81
81
$$
82
82
\begin{aligned}
83
-
\frac{d x}{dt} &=\alpha x -\beta xy \\
83
+
\frac{d x}{dt} &=\alpha x -\beta xy \\
84
84
\frac{d y}{dt} &=-\gamma y + \delta xy
85
85
\end{aligned}
86
86
$$
@@ -127,7 +127,7 @@ def plot_data(ax, lw=2, title="Hudson's Bay Company Data"):
127
127
```
128
128
129
129
```{code-cell} ipython3
130
-
fig, ax = plt.subplots(figsize=(9, 4))
130
+
fig, ax = plt.subplots(figsize=(7, 4))
131
131
plot_data(ax);
132
132
```
133
133
@@ -166,6 +166,7 @@ def plot_model(
166
166
lw=3,
167
167
title="Hudson's Bay Company Data and\nExample Model Run",
@@ -238,17 +239,17 @@ Looks right. If we didn't care about uncertainty, then we would be done. But w
238
239
239
240
+++
240
241
241
-
Like other Numpy or Scipy-based functions, the `scipy.integrate.odeint` function cannot be used directly in a PyMC model because PyMC needs to know the variable input and output types to compile. Therefore, we use an Aesara wrapper to give the variable types to PyMC. Then the function can be used in PyMC in conjunction with gradient-free samplers.
242
+
Like other Numpy or Scipy-based functions, the `scipy.integrate.odeint` function cannot be used directly in a PyMC model because PyMC needs to know the variable input and output types to compile. Therefore, we use a Pytensor wrapper to give the variable types to PyMC. Then the function can be used in PyMC in conjunction with gradient-free samplers.
242
243
243
244
+++
244
245
245
-
### Convert Python Function to an Aesara Operator using @as_op decorator
246
-
We tell PyMC the input variable types and the output variable types using the `@as_op` decorator. `odeint` returns Numpy arrays, but we tell PyMC that they are Aesara double float tensors for this purpose.
246
+
### Convert Python Function to a Pytensor Operator using @as_op decorator
247
+
We tell PyMC the input variable types and the output variable types using the `@as_op` decorator. `odeint` returns Numpy arrays, but we tell PyMC that they are Pytensor double float tensors for this purpose.
247
248
248
249
```{code-cell} ipython3
249
-
# decorator with input and output types as aesara double float tensors
250
-
@as_op(itypes=[at.dvector], otypes=[at.dmatrix])
251
-
def aesara_forward_model_matrix(theta):
250
+
# decorator with input and output types a Pytensor double float tensors
@@ -326,7 +329,7 @@ Having good gradient free samplers can open up the models that can be fit within
326
329
327
330
Let's give them a shot.
328
331
329
-
A few notes on running these inferences. For each sampler, the number of tuning steps and draws have been reduced to run the inference in a reasonable amount of time (on the order of minutes). This is not a sufficient number of draws to get a good inferences, in some cases, but it works for demonstration purposes. In addition, multicore processing was not working for the aesara op function on all machines, so inference is performed on one core.
332
+
A few notes on running these inferences. For each sampler, the number of tuning steps and draws have been reduced to run the inference in a reasonable amount of time (on the order of minutes). This is not a sufficient number of draws to get a good inferences, in some cases, but it works for demonstration purposes. In addition, multicore processing was not working for the Pytensor op function on all machines, so inference is performed on one core.
plot_inference(ax, trace, title=f"Data and Inference Model Runs\n{sampler} Sampler");
492
495
```
493
496
@@ -500,7 +503,7 @@ At this number of samples and tuning scheme, the SMC algorithm results in wider
500
503
501
504
+++
502
505
503
-
As outlined in the SMC tutorial on PyMC.io, the SMC sampler is often combined with a `pm.Simulator` function rather than an aesara op. Here is a rewrite of the PyMC - odeint model for the SMC sampler.
506
+
As outlined in the SMC tutorial on PyMC.io, the SMC sampler is often combined with a `pm.Simulator` function rather than a Pytensor op. Here is a rewrite of the PyMC - odeint model for the SMC sampler.
504
507
505
508
The simulator function needs to have the correct signature (e.g., accept an rng argument first).
plot_inference(ax, trace, title=f"Data and Inference Model Runs\n{sampler} Sampler");
601
604
```
602
605
@@ -621,7 +624,7 @@ The major observation here is that the posterior shape is pretty difficult for a
621
624
622
625
+++
623
626
624
-
The PyMC default NUTs sampler can only be used if gradients are supplied to the sampler. In this section, we will solve the system of ODEs within PyMC in two different ways that supply the sampler with gradients. The first is the built-in `pymc.ode.DifferentialEquation` solver, and the second is to forward simulate using `aesara.scan`, which allows looping. Note that there may be other better and faster ways to perform Bayesian inference with ODEs using gradients, such as the [sunode](https://sunode.readthedocs.io/en/latest/index.html) project.
627
+
The PyMC default NUTs sampler can only be used if gradients are supplied to the sampler. In this section, we will solve the system of ODEs within PyMC in two different ways that supply the sampler with gradients. The first is the built-in `pymc.ode.DifferentialEquation` solver, and the second is to forward simulate using `pytensor.scan`, which allows looping. Note that there may be other better and faster ways to perform Bayesian inference with ODEs using gradients, such as the [sunode](https://sunode.readthedocs.io/en/latest/index.html) project, and [diffrax](https://www.pymc-labs.io/blog-posts/jax-functions-in-pymc-3-quick-examples/), which relies on JAX.
@@ -708,11 +711,11 @@ Despite a paucity of samples, the NUTs sampler is starting to converge to the co
708
711
709
712
+++
710
713
711
-
### Simulate with Aesara Scan
714
+
### Simulate with Pytensor Scan
712
715
713
716
+++
714
717
715
-
Finally, we can write the system of ODEs as a forward simulation solver within PyMC. The way to write for-loops in PyMC is with `aesara.scan.` Gradients are then supplied to the sampler via autodifferentiation.
718
+
Finally, we can write the system of ODEs as a forward simulation solver within PyMC. The way to write for-loops in PyMC is with `pytensor.scan.` Gradients are then supplied to the sampler via autodifferentiation.
716
719
717
720
First, we should test that the time steps are sufficiently small to get a reasonable estimate.
718
721
@@ -722,7 +725,7 @@ First, we should test that the time steps are sufficiently small to get a reason
722
725
723
726
+++
724
727
725
-
Create a function that accepts different numbers of time steps for testing. The function also demonstrates how `aesara.scan` is used.
728
+
Create a function that accepts different numbers of time steps for testing. The function also demonstrates how `pytensor.scan` is used.
726
729
727
730
```{code-cell} ipython3
728
731
# Lotka-Volterra forward simulation model using scan
@@ -940,25 +943,21 @@ If we ran the samplers for long enough to get good inferences, we would expect t
940
943
941
944
### Key Conclusions
942
945
We performed Bayesian inference on a system of ODEs in 4 main ways:
943
-
* Scipy `odeint` wrapped in an aesara`op` and sampled with non-gradient-based samplers (comparing 5 different samplers).
946
+
* Scipy `odeint` wrapped in a Pytensor`op` and sampled with non-gradient-based samplers (comparing 5 different samplers).
944
947
* Scipy `odeint` wrapped in a `pm.Simulator` function and sampled with a non-likelihood-based sequential Monte Carlo (SMC) sampler.
945
948
* PyMC `ode.DifferentialEquation` sampled with NUTs.
946
-
* Forward simulation using `aesara.scan` and sampled with NUTs.
949
+
* Forward simulation using `pytensor.scan` and sampled with NUTs.
947
950
948
-
The "winner" for this problem was the Scipy `odeint` solver with a differential evolution (DE) Metropolis sampler. The improved efficiency of the NUTs sampler did not make up for the inefficiency in using the slow ODE solvers with gradients. Sticking with Scipy and DEMetropolis is also the simplest workflow for a scientist with a working numeric model and the desire to perform Bayesian inference. Just wrapping the numeric model in an aesara op and plugging it into a PyMC model can get you a long way!
951
+
The "winner" for this problem was the Scipy `odeint` solver with a differential evolution (DE) Metropolis sampler. The improved efficiency of the NUTs sampler did not make up for the inefficiency in using the slow ODE solvers with gradients. Sticking with Scipy and DEMetropolis is also the simplest workflow for a scientist with a working numeric model and the desire to perform Bayesian inference. Just wrapping the numeric model in a Pytensor op and plugging it into a PyMC model can get you a long way!
949
952
950
953
+++
951
954
952
955
## Authors
953
-
Adapted by Greg Brunkhorst from multiple PyMC.io example notebooks by Sanmitra Ghosh, Demetri Pananos, and the PyMC Team.
956
+
Organized and rewritten by Greg Brunkhorst from multiple PyMC.io example notebooks by Sanmitra Ghosh, Demetri Pananos, and the PyMC Team.
0 commit comments