Skip to content

Commit bd7951c

Browse files
committed
Remove PrefixContext
1 parent 79150ba commit bd7951c

File tree

17 files changed

+414
-607
lines changed

17 files changed

+414
-607
lines changed

HISTORY.md

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

55
Removed the method `returned(::Model, values, keys)`; please use `returned(::Model, ::AbstractDict{<:VarName})` instead.
66

7+
### Breaking changes
8+
9+
`PrefixContext` is removed, etc., etc.... (I'll write this if I actually have to)
10+
711
## 0.38.4
812

913
Improve performance of VarNamedVector. It should now be very nearly on par with Metadata for all models we've benchmarked on.

docs/src/api.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -467,7 +467,6 @@ Contexts are subtypes of `AbstractPPL.AbstractContext`.
467467

468468
```@docs
469469
DefaultContext
470-
PrefixContext
471470
ConditionContext
472471
InitContext
473472
```

src/DynamicPPL.jl

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,6 @@ export AbstractVarInfo,
9797
# Contexts
9898
contextualize,
9999
DefaultContext,
100-
PrefixContext,
101100
ConditionContext,
102101
# Tilde pipeline
103102
tilde_assume!!,
@@ -174,9 +173,9 @@ include("contexts.jl")
174173
include("contexts/default.jl")
175174
include("contexts/init.jl")
176175
include("contexts/transformation.jl")
177-
include("contexts/prefix.jl")
178-
include("contexts/conditionfix.jl") # Must come after contexts/prefix.jl
176+
include("contexts/conditionfix.jl")
179177
include("model.jl")
178+
include("prefix.jl")
180179
include("varname.jl")
181180
include("distribution_wrappers.jl")
182181
include("submodel.jl")

src/compiler.jl

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ function make_varname_expression(expr)
4242
end
4343

4444
"""
45-
isassumption(expr[, vn])
45+
isassumption(expr[, left_vn])
4646
4747
Return an expression that can be evaluated to check if `expr` is an assumption in the
4848
model.
@@ -55,16 +55,19 @@ Let `expr` be `:(x[1])`. It is an assumption in the following cases:
5555
5656
When `expr` is not an expression or symbol (i.e., a literal), this expands to `false`.
5757
58-
If `vn` is specified, it will be assumed to refer to a expression which
58+
If `left_vn` is specified, it will be assumed to refer to a expression which
5959
evaluates to a `VarName`, and this will be used in the subsequent checks.
60-
If `vn` is not specified, `AbstractPPL.varname(expr, need_concretize(expr))` will be
60+
If `left_vn` is not specified, `AbstractPPL.varname(expr, need_concretize(expr))` will be
6161
used in its place.
6262
"""
63-
function isassumption(expr::Union{Expr,Symbol}, vn=make_varname_expression(expr))
63+
function isassumption(expr::Union{Expr,Symbol}, left_vn=make_varname_expression(expr))
64+
@gensym vn
6465
return quote
65-
if $(DynamicPPL.contextual_isassumption)(
66-
__model__.context, $(DynamicPPL.prefix)(__model__.context, $vn)
67-
)
66+
# TODO(penelopeysm): This re-prefixing seems a bit wasteful. I'd really like
67+
# the whole `isassumption` thing to be simplified, though, so I'll
68+
# leave it till later.
69+
$vn = $(DynamicPPL.maybe_prefix)($left_vn, __model__.prefix)
70+
if $(DynamicPPL.contextual_isassumption)(__model__.context, $vn)
6871
# Considered an assumption by `__model__.context` which means either:
6972
# 1. We hit the default implementation, e.g. using `DefaultContext`,
7073
# which in turn means that we haven't considered if it's one of
@@ -78,8 +81,8 @@ function isassumption(expr::Union{Expr,Symbol}, vn=make_varname_expression(expr)
7881
# TODO: Support by adding context to model, and use `model.args`
7982
# as the default conditioning. Then we no longer need to check `inargnames`
8083
# since it will all be handled by `contextual_isassumption`.
81-
if !($(DynamicPPL.inargnames)($vn, __model__)) ||
82-
$(DynamicPPL.inmissings)($vn, __model__)
84+
if !($(DynamicPPL.inargnames)($left_vn, __model__)) ||
85+
$(DynamicPPL.inmissings)($left_vn, __model__)
8386
true
8487
else
8588
$(maybe_view(expr)) === missing
@@ -99,7 +102,7 @@ isassumption(expr) = :(false)
99102
100103
Return `true` if `vn` is considered an assumption by `context`.
101104
"""
102-
function contextual_isassumption(context::AbstractContext, vn)
105+
function contextual_isassumption(context::AbstractContext, vn::VarName)
103106
if hasconditioned_nested(context, vn)
104107
val = getconditioned_nested(context, vn)
105108
# TODO: Do we even need the `>: Missing`, i.e. does it even help the compiler?
@@ -115,9 +118,7 @@ end
115118

116119
isfixed(expr, vn) = false
117120
function isfixed(::Union{Symbol,Expr}, vn)
118-
return :($(DynamicPPL.contextual_isfixed)(
119-
__model__.context, $(DynamicPPL.prefix)(__model__.context, $vn)
120-
))
121+
return :($(DynamicPPL.contextual_isfixed)(__model__.context, $vn))
121122
end
122123

123124
"""
@@ -413,7 +414,9 @@ function generate_assign(left, right)
413414
return quote
414415
$right_val = $right
415416
if $(DynamicPPL.is_extracting_values)(__varinfo__)
416-
$vn = $(DynamicPPL.prefix)(__model__.context, $(make_varname_expression(left)))
417+
$vn = $(DynamicPPL.maybe_prefix)(
418+
$(make_varname_expression(left)), __model__.prefix
419+
)
417420
__varinfo__ = $(map_accumulator!!)(
418421
$acc -> push!($acc, $vn, $right_val), __varinfo__, Val(:ValuesAsInModel)
419422
)
@@ -448,24 +451,23 @@ function generate_tilde(left, right)
448451

449452
# Otherwise it is determined by the model or its value,
450453
# if the LHS represents an observation
451-
@gensym vn isassumption value dist
454+
@gensym left_vn vn isassumption value dist
452455

453456
return quote
454457
$dist = $right
455-
$vn = $(DynamicPPL.resolve_varnames)($(make_varname_expression(left)), $dist)
456-
$isassumption = $(DynamicPPL.isassumption(left, vn))
458+
$left_vn = $(DynamicPPL.resolve_varnames)($(make_varname_expression(left)), $dist)
459+
$vn = $(DynamicPPL.maybe_prefix)($left_vn, __model__.prefix)
460+
$isassumption = $(DynamicPPL.isassumption(left, left_vn))
457461
if $(DynamicPPL.isfixed(left, vn))
458-
$left = $(DynamicPPL.getfixed_nested)(
459-
__model__.context, $(DynamicPPL.prefix)(__model__.context, $vn)
460-
)
462+
$left = $(DynamicPPL.getfixed_nested)(__model__.context, $vn)
461463
elseif $isassumption
462464
$(generate_tilde_assume(left, dist, vn))
463465
else
464-
# If `vn` is not in `argnames`, we need to make sure that the variable is defined.
465-
if !$(DynamicPPL.inargnames)($vn, __model__)
466-
$left = $(DynamicPPL.getconditioned_nested)(
467-
__model__.context, $(DynamicPPL.prefix)(__model__.context, $vn)
468-
)
466+
# If `left_vn` is not in `argnames`, we need to make sure that the variable is defined.
467+
# (Note: we use the unprefixed `left_vn` here rather than `vn` which will have had
468+
# prefixes applied!)
469+
if !$(DynamicPPL.inargnames)($left_vn, __model__)
470+
$left = $(DynamicPPL.getconditioned_nested)(__model__.context, $vn)
469471
end
470472

471473
$value, __varinfo__ = $(DynamicPPL.tilde_observe!!)(
@@ -495,6 +497,7 @@ function generate_tilde_assume(left, right, vn)
495497
return quote
496498
$value, __varinfo__ = $(DynamicPPL.tilde_assume!!)(
497499
__model__.context,
500+
__model__.prefix,
498501
$(DynamicPPL.unwrap_right_vn)($(DynamicPPL.check_tilde_rhs)($right), $vn)...,
499502
__varinfo__,
500503
)

src/contexts.jl

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ setleafcontext(::IsLeaf, ::IsLeaf, left::AbstractContext, right::AbstractContext
123123
"""
124124
DynamicPPL.tilde_assume!!(
125125
context::AbstractContext,
126+
prefix::Union{VarName,Nothing},
126127
right::Distribution,
127128
vn::VarName,
128129
vi::AbstractVarInfo
@@ -134,13 +135,21 @@ sampled value and updated `vi`.
134135
135136
`vn` is the VarName on the left-hand side of the tilde statement.
136137
138+
`prefix` is the currently active prefix; this is `nothing` if there is no active prefix.
139+
For example, in `a ~ to_submodel(inner_model)`, when executing `inner_model`, the active
140+
prefix will be `@varname(a)`.
141+
137142
This function should return a tuple `(x, vi)`, where `x` is the sampled value (which
138143
must be in unlinked space!) and `vi` is the updated VarInfo.
139144
"""
140145
function tilde_assume!!(
141-
context::AbstractContext, right::Distribution, vn::VarName, vi::AbstractVarInfo
146+
context::AbstractContext,
147+
prefix::Union{VarName,Nothing},
148+
right::Distribution,
149+
vn::VarName,
150+
vi::AbstractVarInfo,
142151
)
143-
return tilde_assume!!(childcontext(context), right, vn, vi)
152+
return tilde_assume!!(childcontext(context), prefix, right, vn, vi)
144153
end
145154

146155
"""

src/contexts/conditionfix.jl

Lines changed: 0 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,6 @@ hasconditioned_nested(::IsLeaf, context, vn) = hasconditioned(context, vn)
8383
function hasconditioned_nested(::IsParent, context, vn)
8484
return hasconditioned(context, vn) || hasconditioned_nested(childcontext(context), vn)
8585
end
86-
function hasconditioned_nested(context::PrefixContext, vn)
87-
return hasconditioned_nested(collapse_prefix_stack(context), vn)
88-
end
8986

9087
"""
9188
getconditioned_nested(context, vn)
@@ -101,9 +98,6 @@ end
10198
function getconditioned_nested(::IsLeaf, context, vn)
10299
return error("context $(context) does not contain value for $vn")
103100
end
104-
function getconditioned_nested(context::PrefixContext, vn)
105-
return getconditioned_nested(collapse_prefix_stack(context), vn)
106-
end
107101
function getconditioned_nested(::IsParent, context, vn)
108102
return if hasconditioned(context, vn)
109103
getconditioned(context, vn)
@@ -172,9 +166,6 @@ function conditioned(context::ConditionContext)
172166
# precedence over decendants of `context`.
173167
return _merge(context.values, conditioned(childcontext(context)))
174168
end
175-
function conditioned(context::PrefixContext)
176-
return conditioned(collapse_prefix_stack(context))
177-
end
178169

179170
struct FixedContext{Values,Ctx<:AbstractContext} <: AbstractContext
180171
values::Values
@@ -237,9 +228,6 @@ hasfixed_nested(::IsLeaf, context, vn) = hasfixed(context, vn)
237228
function hasfixed_nested(::IsParent, context, vn)
238229
return hasfixed(context, vn) || hasfixed_nested(childcontext(context), vn)
239230
end
240-
function hasfixed_nested(context::PrefixContext, vn)
241-
return hasfixed_nested(collapse_prefix_stack(context), vn)
242-
end
243231

244232
"""
245233
getfixed_nested(context, vn)
@@ -255,9 +243,6 @@ end
255243
function getfixed_nested(::IsLeaf, context, vn)
256244
return error("context $(context) does not contain value for $vn")
257245
end
258-
function getfixed_nested(context::PrefixContext, vn)
259-
return getfixed_nested(collapse_prefix_stack(context), vn)
260-
end
261246
function getfixed_nested(::IsParent, context, vn)
262247
return if hasfixed(context, vn)
263248
getfixed(context, vn)
@@ -351,117 +336,3 @@ function fixed(context::FixedContext)
351336
# precedence over decendants of `context`.
352337
return _merge(context.values, fixed(childcontext(context)))
353338
end
354-
function fixed(context::PrefixContext)
355-
return fixed(collapse_prefix_stack(context))
356-
end
357-
358-
###########################################################################
359-
### Interaction of PrefixContext with ConditionContext and FixedContext ###
360-
###########################################################################
361-
362-
"""
363-
collapse_prefix_stack(context::AbstractContext)
364-
365-
Apply `PrefixContext`s to any conditioned or fixed values inside them, and remove
366-
the `PrefixContext`s from the context stack.
367-
368-
!!! note
369-
If you are reading this docstring, you might probably be interested in a more
370-
thorough explanation of how PrefixContext and ConditionContext / FixedContext
371-
interact with one another, especially in the context of submodels.
372-
The DynamicPPL documentation contains [a separate page on this
373-
topic](https://turinglang.org/DynamicPPL.jl/previews/PR892/internals/submodel_condition/)
374-
which explains this in much more detail.
375-
376-
```jldoctest
377-
julia> using DynamicPPL: collapse_prefix_stack
378-
379-
julia> c1 = PrefixContext(@varname(a), ConditionContext((x=1, )));
380-
381-
julia> collapse_prefix_stack(c1)
382-
ConditionContext(Dict(a.x => 1), DefaultContext())
383-
384-
julia> # Here, `x` gets prefixed only with `a`, whereas `y` is prefixed with both.
385-
c2 = PrefixContext(@varname(a), ConditionContext((x=1, ), PrefixContext(@varname(b), ConditionContext((y=2,)))));
386-
387-
julia> collapsed = collapse_prefix_stack(c2);
388-
389-
julia> # `collapsed` really looks something like this:
390-
# ConditionContext(Dict{VarName{:a}, Int64}(a.b.y => 2, a.x => 1), DefaultContext())
391-
# To avoid fragility arising from the order of the keys in the doctest, we test
392-
# this indirectly:
393-
collapsed.values[@varname(a.x)], collapsed.values[@varname(a.b.y)]
394-
(1, 2)
395-
```
396-
"""
397-
function collapse_prefix_stack(context::PrefixContext)
398-
# Collapse the child context (thus applying any inner prefixes first)
399-
collapsed = collapse_prefix_stack(childcontext(context))
400-
# Prefix any conditioned variables with the current prefix
401-
# Note: prefix_conditioned_variables is O(N) in the depth of the context stack.
402-
# So is this function. In the worst case scenario, this is O(N^2) in the
403-
# depth of the context stack.
404-
return prefix_cond_and_fixed_variables(collapsed, context.vn_prefix)
405-
end
406-
function collapse_prefix_stack(context::AbstractContext)
407-
return collapse_prefix_stack(NodeTrait(collapse_prefix_stack, context), context)
408-
end
409-
collapse_prefix_stack(::IsLeaf, context) = context
410-
function collapse_prefix_stack(::IsParent, context)
411-
new_child_context = collapse_prefix_stack(childcontext(context))
412-
return setchildcontext(context, new_child_context)
413-
end
414-
415-
"""
416-
prefix_cond_and_fixed_variables(context::AbstractContext, prefix::VarName)
417-
418-
Prefix all the conditioned and fixed variables in a given context with a single
419-
`prefix`.
420-
421-
```jldoctest
422-
julia> using DynamicPPL: prefix_cond_and_fixed_variables, ConditionContext
423-
424-
julia> c1 = ConditionContext((a=1, ))
425-
ConditionContext((a = 1,), DefaultContext())
426-
427-
julia> prefix_cond_and_fixed_variables(c1, @varname(y))
428-
ConditionContext(Dict(y.a => 1), DefaultContext())
429-
```
430-
"""
431-
function prefix_cond_and_fixed_variables(ctx::ConditionContext, prefix::VarName)
432-
# Replace the prefix of the conditioned variables
433-
vn_dict = to_varname_dict(ctx.values)
434-
prefixed_vn_dict = Dict(
435-
AbstractPPL.prefix(vn, prefix) => value for (vn, value) in vn_dict
436-
)
437-
# Prefix the child context as well
438-
prefixed_child_ctx = prefix_cond_and_fixed_variables(childcontext(ctx), prefix)
439-
return ConditionContext(prefixed_vn_dict, prefixed_child_ctx)
440-
end
441-
function prefix_cond_and_fixed_variables(ctx::FixedContext, prefix::VarName)
442-
# Replace the prefix of the conditioned variables
443-
vn_dict = to_varname_dict(ctx.values)
444-
prefixed_vn_dict = Dict(
445-
AbstractPPL.prefix(vn, prefix) => value for (vn, value) in vn_dict
446-
)
447-
# Prefix the child context as well
448-
prefixed_child_ctx = prefix_cond_and_fixed_variables(childcontext(ctx), prefix)
449-
return FixedContext(prefixed_vn_dict, prefixed_child_ctx)
450-
end
451-
function prefix_cond_and_fixed_variables(c::AbstractContext, prefix::VarName)
452-
return prefix_cond_and_fixed_variables(
453-
NodeTrait(prefix_cond_and_fixed_variables, c), c, prefix
454-
)
455-
end
456-
function prefix_cond_and_fixed_variables(
457-
::IsLeaf, context::AbstractContext, prefix::VarName
458-
)
459-
return context
460-
end
461-
function prefix_cond_and_fixed_variables(
462-
::IsParent, context::AbstractContext, prefix::VarName
463-
)
464-
return setchildcontext(
465-
context, prefix_cond_and_fixed_variables(childcontext(context), prefix)
466-
)
467-
end

src/contexts/default.jl

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,22 @@ NodeTrait(::DefaultContext) = IsLeaf()
2121

2222
"""
2323
DynamicPPL.tilde_assume!!(
24-
::DefaultContext, right::Distribution, vn::VarName, vi::AbstractVarInfo
24+
::DefaultContext,
25+
prefix::Union{VarName,Nothing},
26+
right::Distribution,
27+
vn::VarName,
28+
vi::AbstractVarInfo
2529
)
2630
2731
Handle assumed variables. For `DefaultContext`, this function extracts the value associated
2832
with `vn` from `vi`, If `vi` does not contain an appropriate value then this will error.
2933
"""
3034
function tilde_assume!!(
31-
::DefaultContext, right::Distribution, vn::VarName, vi::AbstractVarInfo
35+
::DefaultContext,
36+
::Union{VarName,Nothing},
37+
right::Distribution,
38+
vn::VarName,
39+
vi::AbstractVarInfo,
3240
)
3341
y = getindex_internal(vi, vn)
3442
f = from_maybe_linked_internal_transform(vi, vn, right)

src/contexts/init.jl

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,11 @@ end
153153
NodeTrait(::InitContext) = IsLeaf()
154154

155155
function tilde_assume!!(
156-
ctx::InitContext, dist::Distribution, vn::VarName, vi::AbstractVarInfo
156+
ctx::InitContext,
157+
::Union{VarName,Nothing},
158+
dist::Distribution,
159+
vn::VarName,
160+
vi::AbstractVarInfo,
157161
)
158162
in_varinfo = haskey(vi, vn)
159163
# `init()` always returns values in original space, i.e. possibly

0 commit comments

Comments
 (0)