Skip to content

Commit f2e676b

Browse files
authored
Merge branch 'breaking' into mhauru/accumulators-stage2
2 parents 037555b + f20e86c commit f2e676b

File tree

12 files changed

+254
-717
lines changed

12 files changed

+254
-717
lines changed

HISTORY.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44

55
**Breaking changes**
66

7+
### Submodel macro
8+
9+
The `@submodel` macro is fully removed; please use `to_submodel` instead.
10+
711
### Accumulators
812

913
This release overhauls how VarInfo objects track variables such as the log joint probability. The new approach is to use what we call accumulators: Objects that the VarInfo carries on it that may change their state at each `tilde_assume!!` and `tilde_observe!!` call based on the value of the variable in question. They replace both variables that were previously hard-coded in the `VarInfo` object (`logp` and `num_produce`) and some contexts. This brings with it a number of breaking changes:
@@ -27,11 +31,11 @@ This version therefore excises the context argument, and instead uses `model.con
2731
The upshot of this is that many functions that previously took a context argument now no longer do.
2832
There were very few such functions where the context argument was actually used (most of them simply took `DefaultContext()` as the default value).
2933

30-
`evaluate!!(model, varinfo, ext_context)` is deprecated, and broadly speaking you should replace calls to that with `new_model = contextualize(model, ext_context); evaluate!!(new_model, varinfo)`.
34+
`evaluate!!(model, varinfo, ext_context)` is removed, and broadly speaking you should replace calls to that with `new_model = contextualize(model, ext_context); evaluate!!(new_model, varinfo)`.
3135
If the 'external context' `ext_context` is a parent context, then you should wrap `model.context` appropriately to ensure that its information content is not lost.
3236
If, on the other hand, `ext_context` is a `DefaultContext`, then you can just drop the argument entirely.
3337

34-
To aid with this process, `contextualize` is now exported from DynamicPPL.
38+
**To aid with this process, `contextualize` is now exported from DynamicPPL.**
3539

3640
The main situation where one _did_ want to specify an additional evaluation context was when that context was a `SamplingContext`.
3741
Doing this would allow you to run the model and sample fresh values, instead of just using the values that existed in the VarInfo object.
@@ -50,9 +54,10 @@ However, here are the more user-facing ones:
5054

5155
And a couple of more internal changes:
5256

53-
- `evaluate!!`, `evaluate_threadsafe!!`, and `evaluate_threadunsafe!!` no longer accept context arguments
57+
- Just like `evaluate!!`, the other functions `_evaluate!!`, `evaluate_threadsafe!!`, and `evaluate_threadunsafe!!` now no longer accept context arguments
5458
- `evaluate!!` no longer takes rng and sampler (if you used this, you should use `evaluate_and_sample!!` instead, or construct your own `SamplingContext`)
5559
- The model evaluation function, `model.f` for some `model::Model`, no longer takes a context as an argument
60+
- The internal representation and API dealing with submodels (i.e., `ReturnedModelWrapper`, `Sampleable`, `should_auto_prefix`, `is_rhs_model`) has been simplified. If you need to check whether something is a submodel, just use `x isa DynamicPPL.Submodel`. Note that the public API i.e. `to_submodel` remains completely untouched.
5661

5762
## 0.36.12
5863

docs/src/api.md

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -146,24 +146,12 @@ to_submodel
146146

147147
Note that a `[to_submodel](@ref)` is only sampleable; one cannot compute `logpdf` for its realizations.
148148

149-
In the past, one would instead embed sub-models using [`@submodel`](@ref), which has been deprecated since the introduction of [`to_submodel(model)`](@ref)
150-
151-
```@docs
152-
@submodel
153-
```
154-
155149
In the context of including models within models, it's also useful to prefix the variables in sub-models to avoid variable names clashing:
156150

157151
```@docs
158152
DynamicPPL.prefix
159153
```
160154

161-
Under the hood, [`to_submodel`](@ref) makes use of the following method to indicate that the model it's wrapping is a model over its return-values rather than something else
162-
163-
```@docs
164-
returned(::Model)
165-
```
166-
167155
## Utilities
168156

169157
It is possible to manually increase (or decrease) the accumulated log likelihood or prior from within a model function.

src/DynamicPPL.jl

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,6 @@ export AbstractVarInfo,
128128
to_submodel,
129129
# Convenience macros
130130
@addlogprob!,
131-
@submodel,
132131
value_iterator_from_chain,
133132
check_model,
134133
check_model_and_trace,
@@ -176,6 +175,7 @@ include("sampler.jl")
176175
include("varname.jl")
177176
include("distribution_wrappers.jl")
178177
include("contexts.jl")
178+
include("submodel.jl")
179179
include("varnamedvector.jl")
180180
include("accumulators.jl")
181181
include("default_accumulators.jl")
@@ -186,7 +186,6 @@ include("simple_varinfo.jl")
186186
include("context_implementations.jl")
187187
include("compiler.jl")
188188
include("pointwise_logdensities.jl")
189-
include("submodel_macro.jl")
190189
include("transforming.jl")
191190
include("logdensityfunction.jl")
192191
include("model_utils.jl")
@@ -227,6 +226,21 @@ if isdefined(Base.Experimental, :register_error_hint)
227226
)
228227
end
229228
end
229+
230+
Base.Experimental.register_error_hint(MethodError) do io, exc, argtypes, _
231+
is_evaluate_three_arg =
232+
exc.f === AbstractPPL.evaluate!! &&
233+
length(argtypes) == 3 &&
234+
argtypes[1] <: Model &&
235+
argtypes[2] <: AbstractVarInfo &&
236+
argtypes[3] <: AbstractContext
237+
if is_evaluate_three_arg
238+
print(
239+
io,
240+
"\n\nThe method `evaluate!!(model, varinfo, new_ctx)` has been removed. Instead, you should store the `new_ctx` in the `model.context` field using `new_model = contextualize(model, new_ctx)`, and then call `evaluate!!(new_model, varinfo)` on the new model. (Note that, if the model already contained a non-default context, you will need to wrap the existing context.)",
241+
)
242+
end
243+
end
230244
end
231245
end
232246

src/compiler.jl

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -176,11 +176,7 @@ function check_tilde_rhs(@nospecialize(x))
176176
end
177177
check_tilde_rhs(x::Distribution) = x
178178
check_tilde_rhs(x::AbstractArray{<:Distribution}) = x
179-
check_tilde_rhs(x::ReturnedModelWrapper) = x
180-
function check_tilde_rhs(x::Sampleable{<:Any,AutoPrefix}) where {AutoPrefix}
181-
model = check_tilde_rhs(x.model)
182-
return Sampleable{typeof(model),AutoPrefix}(model)
183-
end
179+
check_tilde_rhs(x::Submodel{M,AutoPrefix}) where {M,AutoPrefix} = x
184180

185181
"""
186182
check_dot_tilde_rhs(x)

src/context_implementations.jl

Lines changed: 7 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -63,31 +63,10 @@ By default, calls `tilde_assume(context, right, vn, vi)` and accumulates the log
6363
probability of `vi` with the returned value.
6464
"""
6565
function tilde_assume!!(context, right, vn, vi)
66-
return if is_rhs_model(right)
67-
# Here, we apply the PrefixContext _not_ to the parent `context`, but
68-
# to the context of the submodel being evaluated. This means that later=
69-
# on in `make_evaluate_args_and_kwargs`, the context stack will be
70-
# correctly arranged such that it goes like this:
71-
# parent_context[1] -> parent_context[2] -> ... -> PrefixContext ->
72-
# submodel_context[1] -> submodel_context[2] -> ... -> leafcontext
73-
# See the docstring of `make_evaluate_args_and_kwargs`, and the internal
74-
# DynamicPPL documentation on submodel conditioning, for more details.
75-
#
76-
# NOTE: This relies on the existence of `right.model.model`. Right now,
77-
# the only thing that can return true for `is_rhs_model` is something
78-
# (a `Sampleable`) that has a `model` field that itself (a
79-
# `ReturnedModelWrapper`) has a `model` field. This may or may not
80-
# change in the future.
81-
if should_auto_prefix(right)
82-
dppl_model = right.model.model # This isa DynamicPPL.Model
83-
prefixed_submodel_context = PrefixContext(vn, dppl_model.context)
84-
new_dppl_model = contextualize(dppl_model, prefixed_submodel_context)
85-
right = to_submodel(new_dppl_model, true)
86-
end
87-
rand_like!!(right, context, vi)
66+
return if right isa DynamicPPL.Submodel
67+
_evaluate!!(right, vi, context, vn)
8868
else
89-
value, vi = tilde_assume(context, right, vn, vi)
90-
return value, vi
69+
tilde_assume(context, right, vn, vi)
9170
end
9271
end
9372

@@ -129,17 +108,14 @@ accumulate the log probability, and return the observed value and updated `vi`.
129108
Falls back to `tilde_observe!!(context, right, left, vi)` ignoring the information about variable name
130109
and indices; if needed, these can be accessed through this function, though.
131110
"""
132-
function tilde_observe!!(context::DefaultContext, right, left, vn, vi)
133-
is_rhs_model(right) && throw(
134-
ArgumentError(
135-
"`~` with a model on the right-hand side of an observe statement is not supported",
136-
),
137-
)
111+
function tilde_observe!!(::DefaultContext, right, left, vn, vi)
112+
right isa DynamicPPL.Submodel &&
113+
throw(ArgumentError("`x ~ to_submodel(...)` is not supported when `x` is observed"))
138114
vi = accumulate_observe!!(vi, right, left, vn)
139115
return left, vi
140116
end
141117

142-
function assume(rng::Random.AbstractRNG, spl::Sampler, dist)
118+
function assume(::Random.AbstractRNG, spl::Sampler, dist)
143119
return error("DynamicPPL.assume: unmanaged inference algorithm: $(typeof(spl))")
144120
end
145121

0 commit comments

Comments
 (0)