Getting started with pyrenew
pyrenew is a flexible tool for simulating and making statistical
inferences from epidemiologic models, with an emphasis on renewal
models. Built on numpyro, pyrenew provides core components for model
building and pre-defined models for processing various observational
processes.
Prerequisites
The following tutorials assume that you are familiar with both infectious disease dynamics and Python programming.
- You must have a Python3 installation (use tools like pyenv or compile and install from the release page).
- You should be able to install packages, load modules in python, and manage virtual environments (we recommend uv).
- You should understand the concept of a class and metaclass in python.
- You should be familiar with Bayesian inference and have a working understanding of MCMC methods used to fit Bayesian models to data (some resources are available here, and here).
Installing pyrenew
You can install pyrenew using either uv or pip. To install pyrenew
using uv, run the following command from within the directory
containing the pyrenew project:
uv sync
To install pyrenew using pip, run the following command:
pip install git+https://github.com/CDCgov/PyRenew@main
The fundamentals
The pyrenew package leverages numpyro’s flexibility to build models
via composition. It’s core components are the metaclasses
RandomVariable and Model (in Python, a metaclass is a class whose
instances are also classes, where a class is a template for making
objects). Within the pyrenew package, a RandomVariable is a quantity
that models can estimate and sample from, including deterministic
quantities. The benefit of this design is that the definition of the
sample() function can be arbitrary, allowing the user to either sample
from a distribution using numpyro.sample(), compute fixed quantities
(like a mechanistic equation), or return a fixed value (like a
pre-computed PMF.) For instance, when estimating a PMF, the
RandomVariable sampling function may roughly be defined as:
# define a new class called MyRandVar that inherits from the RandomVariable class
class MyRandVar(RandomVariable):
#define a method called sample that returns an object of type ArrayLike
def sample(...) -> ArrayLike:
# calls sample function from NumPyro package
return numpyro.sample(...)
We can instead use a fixed quantity for that variable (like a
pre-computed PMF), where the RandomVariable’s sample function could be
defined as:
# instead define MyRandVar to still inherit from the RandVariable class
class MyRandVar(RandomVariable):
#define sample method that still returns an ArrayLike object
def sample(...) -> ArrayLike:
#sampling method is a pre-computed PMF, a JAX NumPy array with explicit elements
return jax.numpy.array([0.2, 0.7, 0.1])
Thus, when a Model samples from MyRandVar, it could be either adding
random variables to be estimated (first example) or just retrieving some
quantity needed for other calculations (second example).
The Model metaclass provides basic functionality for estimating and
simulation. Like RandomVariable, the Model metaclass has a
sample() method that defines the model structure. Ultimately, models
can be nested (or inherited), providing a straightforward way to add
layers of complexity. Currently the Model metaclass contains of two
model classes
RtInfectionsRenewalModel- a basic renewal model consisting of infections and reproduction numbers.HospitalAdmissionsModel- a model which extends a basic renewal model to account for latent and observed hospital admissions.