-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Re-implementation of the pyrenews basicrenewlmodel using the proposed modularization #26
Re-implementation of the pyrenews basicrenewlmodel using the proposed modularization #26
Conversation
OK, @dylanhmorris. This is ready for review! Please see the notes at the description of the PR. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some comments and suggestions inline.
Additional general comment: Right now, we use the name <Quantity>Observation
for classes that represent a key quantity and optionally allow us to observe that quantity with noise: e.g.InfectionsObservation
for infections; HospitalizationsObservation
for hospital admissions. I think this could be misleading, since we also use those classes to sample those quantities when they are latent. Some options:
Option 1: Base class Observable
, with optional user-defined observation models
Have these quantities be of base class Observable
or ObservableQuantity
and name them things like
class Infections(Observable)
...
class HospitalAdmissions(Observable)
...
Observable
classes are made actually observed giving them a non-null observation model at instantiation. This is close the to current pattern, as these lines of getting-started.ipynb
:
infections_obs = InfectionsObservation(
gen_int=jnp.array([0.25, 0.25, 0.25, 0.25]),
inf_observation_model=PoissonObservation(
rate_varname='infections_mean',
counts_varname='infections_obs',
)
)
Option 2: Inheritance
You could also do this via inheritance, though it's not my preference
class Infections(Observable):
...
class ObservedInfections(Infections)
def observe(self, data):
...
Option 3: Fully separate the latent observables from their observation processes
With this option, you'd use an object of class Infections
to sample predicted infections and then (optionally) an object of class Observation
(e.g. NegativeBinomialObservation
, PoissonObservation
) to observe it, but you'd do that at the level of the Model
class model()
function (or sample()
function if we rename it per my suggestion above).
e.g. something like the following:
class MyModel(Model):
def __init__(self):
self.infections = Infections()
self.obs_dist = NegativeBinomialObservation()
def model(self, data):
pred_infections = self.infections.sample()
inf_obs = self.obs_dist.sample(
"observed_infections",
pred_infections)
Note that (especially if we go with option three) we go this route I'm not convinced that the base class for things like Infections
, HospitalAdmissions
etc should be named Observable
. We can separately discuss the name.
OK @dylanhmorris, the latest version features the following changes:
We can discuss details tomorrow during the team meeting. |
- Change the name of a couple of classes to make them clearer. - The HospitalizationsModel doesn't take observed infections.
Also:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should think about module naming on the following fronts:
- Plural versus singular
- Noun-like versus adjective-like
Currently we have pyrenew.processes
and pyrenew.observations
(plural nouns) and pyrenew.latent
(adjective)
Thank you, @dylanhmorris! That was 💨! |
This PR corresponds to the materialization of the programming pattern proposal. The main changes are:
Structure
processes
,observations
,latent
, andmodels
.RandomProcess
andModel
, which are used by the new submodules. TheModel
class includes the following pre-defined methods:_init_model()
: Creates the NUTS kernel and MCMC samplerrun()
: runs the MCMC.print_summary()
: A wrapper ofMCMC.print_summary
.spread_draws()
: A wrapper ofmcmcutils.spread_draws()
(see below).RandomProcess
andModel
have asample()
function with three parameters:self
,random_variables
(dict), andconstants
(dict).RandomProcess
andModel
return acollections.namedtuple
object.Re-factoring and organization
basic.py
model into two different models:BasicRenewalModel
andHospitalizationsModel
, the latter composed byBasicRenewalModel
.spread_draws
(from thepyrenew_demo.ipynp
) tomcmcutils.py
within the library.New functionalities + code
RandomProcess
' (latent
):Infections
andHospitalizations
(abstractions from thebasic.py
) model.RtRandomWalkProcess
(also frombasic.py
).getting-started
andpyrenew_demo
(from the previous version of the project)Rough edges