TuringLang / DynamicPPL.jl

Implementation of domain-specific language (DSL) for dynamic probabilistic programming
https://turinglang.org/DynamicPPL.jl/
MIT License
167 stars 29 forks source link

Convenience macros to use within `@model` #714

Open torfjelde opened 1 week ago

torfjelde commented 1 week ago

This is an issue which have been discussed multiple times before, but generally as part of other issues / PRs rather than as its own issue.

The question is mainly: should we provide some convenience macros for users that can be used within @model to achieve different behaviors?

More specifically, I have the following macros in mind (with names ofc being up for discussion) to start with:

  1. @isobservation(x): returns true if the variable x is considered an observation according to the model. Ref: #412
  2. @observed_value(x): returns the observed value for x (assuming it's indeed observed). Ref: #412

Others that we might also want to consider, but these I think require more thought:

  1. @is_post_inference: indicates whether we're performing "post-inference" analysis rather than performing inference (or something else). Ref: #510 .

I bring this up now because the argument against this has always been "we don't want to have too many macros", but in #696 , we're introducing @prefix (in addition to @returned_quantities, but this is meant to replace @submodel) and seem to be embracing macros quite a bit more? Hence it seems like a useful moment to re-raise the issue.

Related issues: #412 , #510.

yebai commented 1 week ago

@is_post_inference

Can we do a slightly different but possibly more robust workflow for advanced users:

  1. One makes use of cm = contextualise(model, CustomisedPostInferContext) to wrap extra context information into model
  2. One uses Turing.@getcontext() isa CustomisedPostInerContext inside their model to determine whether to execute a particular code block.

( we need to have a convention that Turing.@getcontext() retrieves the user specified contexts for models)

This way, we avoid the difficult step of automatically determining the evaluation mode, as raised in https://github.com/TuringLang/DynamicPPL.jl/issues/510#issuecomment-2236065007.

torfjelde commented 1 week ago

Any thoughts on the @isobservation and @observed_value @yebai ? My motivation for re-opening this can of worms is that I saw some user doing the following in their model

if !@isdefined variable_that_might_be_conditioned
    variable_that_might_be_conditioned = Matrix{Float64}(undef, n, m)
end 

which is, in some ways, the right idea, but it ofc won't work 😕 Adding something like @isobservation would give users a clear way of achieving these things.

One uses Turing.@getcontext() isa CustomisedPostInerContext inside their model to determine whether to execute a particular code block.

So the workflow you outline here is already in place right? The only difference is __context__ instead of @getcontext, which IMHO isn't much of a difference 🤷

This way, we avoid the difficult step of automatically determining the evaluation mode, as raised in

Aye, this is definitively a complication! However, I think there are a few methods that might be okay to say "yes, I'm in post inference mode", e.g.

predict
generated_quantities
pointwise_loglikelihoods

where it would be possible to have these contexts just define a method that returns true, e.g.

is_post_inference(::AbstractContext) = false
is_post_inference(::GeneratedQuantitiesCtx) = true
is_post_inference(::PredictCtx) = true
is_post_inference(::PointwiseLikelihoodContext) = true

So this would then be a opt-in sort of thing, which we whould we quite selective with.