1
1
"""
2
- is_rhs_model(x)
2
+ Submodel{M,AutoPrefix}
3
3
4
- Return `true` if `x` is a model or model wrapper, and `false` otherwise.
4
+ A wrapper around a model, plus a flag indicating whether it should be automatically
5
+ prefixed with the left-hand variable in a `~` statement.
5
6
"""
6
- is_rhs_model (x) = false
7
-
8
- """
9
- Distributional
10
-
11
- Abstract type for type indicating that something is "distributional".
12
- """
13
- abstract type Distributional end
14
-
15
- """
16
- should_auto_prefix(distributional)
17
-
18
- Return `true` if the `distributional` should use automatic prefixing, and `false` otherwise.
19
- """
20
- function should_auto_prefix end
21
-
22
- """
23
- is_rhs_model(x)
24
-
25
- Return `true` if the `distributional` is a model, and `false` otherwise.
26
- """
27
- function is_rhs_model end
28
-
29
- """
30
- Sampleable{M} <: Distributional
31
-
32
- A wrapper around a model indicating it is sampleable.
33
- """
34
- struct Sampleable{M,AutoPrefix} <: Distributional
35
- model:: M
36
- end
37
-
38
- should_auto_prefix (:: Sampleable{<:Any,AutoPrefix} ) where {AutoPrefix} = AutoPrefix
39
- is_rhs_model (x:: Sampleable ) = is_rhs_model (x. model)
40
-
41
- # TODO : Export this if it end up having a purpose beyond `to_submodel`.
42
- """
43
- to_sampleable(model[, auto_prefix])
44
-
45
- Return a wrapper around `model` indicating it is sampleable.
46
-
47
- # Arguments
48
- - `model::Model`: the model to wrap.
49
- - `auto_prefix::Bool`: whether to prefix the variables in the model. Default: `true`.
50
- """
51
- to_sampleable (model, auto_prefix:: Bool = true ) = Sampleable {typeof(model),auto_prefix} (model)
52
-
53
- """
54
- rand_like!!(model_wrap, context, varinfo)
55
-
56
- Returns a tuple with the first element being the realization and the second the updated varinfo.
57
-
58
- # Arguments
59
- - `model_wrap::ReturnedModelWrapper`: the wrapper of the model to use.
60
- - `context::AbstractContext`: the context to use for evaluation.
61
- - `varinfo::AbstractVarInfo`: the varinfo to use for evaluation.
62
- """
63
- function rand_like!! (
64
- model_wrap:: Sampleable , context:: AbstractContext , varinfo:: AbstractVarInfo
65
- )
66
- return rand_like!! (model_wrap. model, context, varinfo)
67
- end
68
-
69
- """
70
- ReturnedModelWrapper
71
-
72
- A wrapper around a model indicating it is a model over its return values.
73
-
74
- This should rarely be constructed explicitly; see [`returned(model)`](@ref) instead.
75
- """
76
- struct ReturnedModelWrapper{M<: Model }
7
+ struct Submodel{M,AutoPrefix}
77
8
model:: M
78
9
end
79
10
80
- is_rhs_model (:: ReturnedModelWrapper ) = true
81
-
82
- function rand_like!! (
83
- model_wrap:: ReturnedModelWrapper , context:: AbstractContext , varinfo:: AbstractVarInfo
84
- )
85
- # Return's the value and the (possibly mutated) varinfo.
86
- return _evaluate!! (model_wrap. model, varinfo, context)
87
- end
88
-
89
- """
90
- returned(model)
91
-
92
- Return a `model` wrapper indicating that it is a model over its return-values.
93
- """
94
- returned (model:: Model ) = ReturnedModelWrapper (model)
95
-
96
11
"""
97
12
to_submodel(model::Model[, auto_prefix::Bool])
98
13
@@ -106,8 +21,8 @@ the model can be sampled from but not necessarily evaluated for its log density.
106
21
`left ~ right` such as [`condition`](@ref), will also not work with `to_submodel`.
107
22
108
23
!!! warning
109
- To avoid variable names clashing between models, it is recommend leave argument `auto_prefix` equal to `true`.
110
- If one does not use automatic prefixing, then it's recommended to use [`prefix(::Model, input)`](@ref) explicitly.
24
+ To avoid variable names clashing between models, it is recommended to leave the argument `auto_prefix` equal to `true`.
25
+ If one does not use automatic prefixing, then it's recommended to use [`prefix(::Model, input)`](@ref) explicitly, i.e. `to_submodel(prefix(model, @varname(my_prefix)))`
111
26
112
27
# Arguments
113
28
- `model::Model`: the model to wrap.
@@ -229,11 +144,51 @@ julia> @model illegal_likelihood() = a ~ to_submodel(inner())
229
144
illegal_likelihood (generic function with 2 methods)
230
145
231
146
julia> model = illegal_likelihood() | (a = 1.0,);
232
-
233
147
julia> model()
234
148
ERROR: ArgumentError: `~` with a model on the right-hand side of an observe statement is not supported
235
149
[...]
236
150
```
237
151
"""
238
- to_submodel (model:: Model , auto_prefix:: Bool = true ) =
239
- to_sampleable (returned (model), auto_prefix)
152
+ to_submodel (m:: Model , auto_prefix:: Bool = true ) = Submodel {typeof(m),auto_prefix} (m)
153
+
154
+ # When automatic prefixing is used, the submodel itself doesn't carry the
155
+ # prefix, as the prefix is obtained from the LHS of `~` (whereas the submodel
156
+ # is on the RHS). The prefix can only be obtained in `tilde_assume!!`, and then
157
+ # passed into this function.
158
+ #
159
+ # `parent_context` here refers to the context of the model that contains the
160
+ # submodel.
161
+ function _evaluate!! (
162
+ submodel:: Submodel{M,AutoPrefix} ,
163
+ vi:: AbstractVarInfo ,
164
+ parent_context:: AbstractContext ,
165
+ left_vn:: VarName ,
166
+ ) where {M<: Model ,AutoPrefix}
167
+ # First, we construct the context to be used when evaluating the submodel. There
168
+ # are several considerations here:
169
+ # (1) We need to apply an appropriate PrefixContext when evaluating the submodel, but
170
+ # _only_ if automatic prefixing is supposed to be applied.
171
+ submodel_context_prefixed = if AutoPrefix
172
+ PrefixContext (left_vn, submodel. model. context)
173
+ else
174
+ submodel. model. context
175
+ end
176
+
177
+ # (2) We need to respect the leaf-context of the parent model. This, unfortunately,
178
+ # means disregarding the leaf-context of the submodel.
179
+ submodel_context = setleafcontext (
180
+ submodel_context_prefixed, leafcontext (parent_context)
181
+ )
182
+
183
+ # (3) We need to use the parent model's context to wrap the whole thing, so that
184
+ # e.g. if the user conditions the parent model, the conditioned variables will be
185
+ # correctly picked up when evaluating the submodel.
186
+ eval_context = setleafcontext (parent_context, submodel_context)
187
+
188
+ # (4) Finally, we need to store that context inside the submodel.
189
+ model = contextualize (submodel. model, eval_context)
190
+
191
+ # Once that's all set up nicely, we can just _evaluate!! the wrapped model. This
192
+ # returns a tuple of submodel.model's return value and the new varinfo.
193
+ return _evaluate!! (model, vi)
194
+ end
0 commit comments