Skip to content
Open

v0.39 #1082

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,26 @@
# DynamicPPL Changelog

## 0.39.0

### Breaking changes

#### Parent and leaf contexts

The `DynamicPPL.NodeTrait` function has been removed.
Instead of implementing this, parent contexts should subtype `DynamicPPL.AbstractParentContext`.
This is an abstract type which requires you to overload two functions, `DynamicPPL.childcontext` and `DynamicPPL.setchildcontext`.

There should generally be few reasons to define your own parent contexts (the only one we are aware of, outside of DynamicPPL itself, is `Turing.Inference.GibbsContext`), so this change should not really affect users.

Leaf contexts require no changes, apart from a removal of the `NodeTrait` function.

`ConditionContext` and `PrefixContext` are no longer exported.
You should not need to use these directly, please use `AbstractPPL.condition` and `DynamicPPL.prefix` instead.

#### Miscellaneous

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

## 0.38.9

Remove warning when using Enzyme as the AD backend.
Expand Down
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name = "DynamicPPL"
uuid = "366bfd00-2699-11ea-058f-f148b4cae6d8"
version = "0.38.9"
version = "0.39.0"

[deps]
ADTypes = "47edcb42-4c32-4615-8424-f2b9edc5f35b"
Expand Down
2 changes: 1 addition & 1 deletion benchmarks/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ DynamicPPL = {path = "../"}
ADTypes = "1.14.0"
BenchmarkTools = "1.6.0"
Distributions = "0.25.117"
DynamicPPL = "0.38"
DynamicPPL = "0.39"
Enzyme = "0.13"
ForwardDiff = "0.10.38, 1"
LogDensityProblems = "2.1.2"
Expand Down
2 changes: 1 addition & 1 deletion docs/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Accessors = "0.1"
Distributions = "0.25"
Documenter = "1"
DocumenterMermaid = "0.1, 0.2"
DynamicPPL = "0.38"
DynamicPPL = "0.39"
FillArrays = "0.13, 1"
ForwardDiff = "0.10, 1"
JET = "0.9, 0.10, 0.11"
Expand Down
46 changes: 36 additions & 10 deletions docs/src/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -352,13 +352,6 @@ Base.empty!
SimpleVarInfo
```

### Tilde-pipeline

```@docs
tilde_assume!!
tilde_observe!!
```

### Accumulators

The subtypes of [`AbstractVarInfo`](@ref) store the cumulative log prior and log likelihood, and sometimes other variables that change during executing, in what are called accumulators.
Expand Down Expand Up @@ -463,15 +456,48 @@ By default, it does not perform any actual sampling: it only evaluates the model
If you wish to sample new values, see the section on [VarInfo initialisation](#VarInfo-initialisation) just below this.

The behaviour of a model execution can be changed with evaluation contexts, which are a field of the model.
Contexts are subtypes of `AbstractPPL.AbstractContext`.

All contexts are subtypes of `AbstractPPL.AbstractContext`.

Contexts are split into two kinds:

**Leaf contexts**: These are the most important contexts as they ultimately decide how model evaluation proceeds.
For example, `DefaultContext` evaluates the model using values stored inside a VarInfo's metadata, whereas `InitContext` obtains new values either by sampling or from a known set of parameters.
DynamicPPL has more leaf contexts which are used for internal purposes, but these are the two that are exported.

```@docs
DefaultContext
PrefixContext
ConditionContext
InitContext
```

To implement a leaf context, you need to subtype `AbstractPPL.AbstractContext` and implement the `tilde_assume!!` and `tilde_observe!!` methods for your context.

```@docs
tilde_assume!!
tilde_observe!!
```

**Parent contexts**: These essentially act as 'modifiers' for leaf contexts.
For example, `PrefixContext` adds a prefix to all variable names during evaluation, while `ConditionContext` marks certain variables as observed.

To implement a parent context, you have to subtype `DynamicPPL.AbstractParentContext`, and implement the `childcontext` and `setchildcontext` methods.
If needed, you can also implement `tilde_assume!!` and `tilde_observe!!` for your context.
This is optional; the default implementation is to simply delegate to the child context.

```@docs
AbstractParentContext
childcontext
setchildcontext
```

Since contexts form a tree structure, these functions are automatically defined for manipulating context stacks.
They are mainly useful for modifying the fundamental behaviour (i.e. the leaf context), without affecting any of the modifiers (i.e. parent contexts).

```@docs
leafcontext
setleafcontext
```

### VarInfo initialisation

The function `init!!` is used to initialise, or overwrite, values in a VarInfo.
Expand Down
13 changes: 9 additions & 4 deletions src/DynamicPPL.jl
Original file line number Diff line number Diff line change
Expand Up @@ -94,16 +94,21 @@ export AbstractVarInfo,
values_as_in_model,
# LogDensityFunction
LogDensityFunction,
# Contexts
# Leaf contexts
AbstractContext,
contextualize,
DefaultContext,
PrefixContext,
ConditionContext,
InitContext,
# Parent contexts
AbstractParentContext,
childcontext,
setchildcontext,
leafcontext,
setleafcontext,
# Tilde pipeline
tilde_assume!!,
tilde_observe!!,
# Initialisation
InitContext,
AbstractInitStrategy,
InitFromPrior,
InitFromUniform,
Expand Down
82 changes: 33 additions & 49 deletions src/contexts.jl
Original file line number Diff line number Diff line change
@@ -1,48 +1,32 @@
"""
NodeTrait(context)
NodeTrait(f, context)
AbstractParentContext

Specifies the role of `context` in the context-tree.
An abstract context that has a child context.

The officially supported traits are:
- `IsLeaf`: `context` does not have any decendants.
- `IsParent`: `context` has a child context to which we often defer.
Expects the following methods to be implemented:
- [`childcontext`](@ref)
- [`setchildcontext`](@ref)
"""
abstract type NodeTrait end
NodeTrait(_, context) = NodeTrait(context)

"""
IsLeaf

Specifies that the context is a leaf in the context-tree.
"""
struct IsLeaf <: NodeTrait end
"""
IsParent
Subtypes of `AbstractParentContext` must implement the following interface:

Specifies that the context is a parent in the context-tree.
- `DynamicPPL.childcontext(context::AbstractParentContext)`: Return the child context.
- `DynamicPPL.setchildcontext(parent::AbstractParentContext, child::AbstractContext)`: Reconstruct
`parent` but now using `child` as its child context.
"""
struct IsParent <: NodeTrait end
abstract type AbstractParentContext <: AbstractContext end

"""
childcontext(context)
childcontext(context::AbstractParentContext)

Return the descendant context of `context`.
"""
childcontext

"""
setchildcontext(parent::AbstractContext, child::AbstractContext)
setchildcontext(parent::AbstractParentContext, child::AbstractContext)

Reconstruct `parent` but now using `child` is its [`childcontext`](@ref),
effectively updating the child context.

# Examples
```jldoctest
julia> using DynamicPPL: DynamicTransformationContext
julia> using DynamicPPL: DynamicTransformationContext, ConditionContext

julia> ctx = ConditionContext((; a = 1));

Expand All @@ -60,12 +44,11 @@ setchildcontext
"""
leafcontext(context::AbstractContext)

Return the leaf of `context`, i.e. the first descendant context that `IsLeaf`.
Return the leaf of `context`, i.e. the first descendant context that is not an
`AbstractParentContext`.
"""
leafcontext(context::AbstractContext) =
leafcontext(NodeTrait(leafcontext, context), context)
leafcontext(::IsLeaf, context::AbstractContext) = context
leafcontext(::IsParent, context::AbstractContext) = leafcontext(childcontext(context))
leafcontext(context::AbstractContext) = context
leafcontext(context::AbstractParentContext) = leafcontext(childcontext(context))

"""
setleafcontext(left::AbstractContext, right::AbstractContext)
Expand All @@ -80,12 +63,10 @@ original leaf context of `left`.
```jldoctest
julia> using DynamicPPL: leafcontext, setleafcontext, childcontext, setchildcontext, AbstractContext, DynamicTransformationContext

julia> struct ParentContext{C} <: AbstractContext
julia> struct ParentContext{C} <: AbstractParentContext
context::C
end

julia> DynamicPPL.NodeTrait(::ParentContext) = DynamicPPL.IsParent()

julia> DynamicPPL.childcontext(context::ParentContext) = context.context

julia> DynamicPPL.setchildcontext(::ParentContext, child) = ParentContext(child)
Expand All @@ -104,21 +85,10 @@ julia> # Append another parent context.
ParentContext(ParentContext(ParentContext(DefaultContext())))
```
"""
function setleafcontext(left::AbstractContext, right::AbstractContext)
return setleafcontext(
NodeTrait(setleafcontext, left), NodeTrait(setleafcontext, right), left, right
)
end
function setleafcontext(
::IsParent, ::IsParent, left::AbstractContext, right::AbstractContext
)
function setleafcontext(left::AbstractParentContext, right::AbstractContext)
return setchildcontext(left, setleafcontext(childcontext(left), right))
end
function setleafcontext(::IsParent, ::IsLeaf, left::AbstractContext, right::AbstractContext)
return setchildcontext(left, setleafcontext(childcontext(left), right))
end
setleafcontext(::IsLeaf, ::IsParent, left::AbstractContext, right::AbstractContext) = right
setleafcontext(::IsLeaf, ::IsLeaf, left::AbstractContext, right::AbstractContext) = right
setleafcontext(::AbstractContext, right::AbstractContext) = right

"""
DynamicPPL.tilde_assume!!(
Expand All @@ -138,10 +108,15 @@ This function should return a tuple `(x, vi)`, where `x` is the sampled value (w
must be in unlinked space!) and `vi` is the updated VarInfo.
"""
function tilde_assume!!(
context::AbstractContext, right::Distribution, vn::VarName, vi::AbstractVarInfo
context::AbstractParentContext, right::Distribution, vn::VarName, vi::AbstractVarInfo
)
return tilde_assume!!(childcontext(context), right, vn, vi)
end
function tilde_assume!!(
context::AbstractContext, ::Distribution, ::VarName, ::AbstractVarInfo
)
return error("tilde_assume!! not implemented for context of type $(typeof(context))")
end

"""
DynamicPPL.tilde_observe!!(
Expand Down Expand Up @@ -171,11 +146,20 @@ This function should return a tuple `(left, vi)`, where `left` is the same as th
`vi` is the updated VarInfo.
"""
function tilde_observe!!(
context::AbstractContext,
context::AbstractParentContext,
right::Distribution,
left,
vn::Union{VarName,Nothing},
vi::AbstractVarInfo,
)
return tilde_observe!!(childcontext(context), right, left, vn, vi)
end
function tilde_observe!!(
context::AbstractContext,
::Distribution,
::Any,
::Union{VarName,Nothing},
::AbstractVarInfo,
)
return error("tilde_observe!! not implemented for context of type $(typeof(context))")
end
Loading