Skip to content

Commit

Permalink
Add fix and unfix (#488)
Browse files Browse the repository at this point in the history
* aded FixedContext and everything that goes with it

* initial work on making fix compat with compiler

* added support for dot tilde

* exported and added testing for fix and unfix

* added equivalent support to condition

* fixed some docstrings

* added lots of documentation plus some doctests for fixing

* added docs on fix

* bump patch version

* renamd getvalue and hasvalue for contexts to more descriptive
get_conditioned_value and has_conditioned_value

* formatting

* Update src/model.jl

* fixeed typo in docstring

* fixed docs

* Apply suggestions from code review

Co-authored-by: David Widmann <devmotion@users.noreply.github.com>

* Update src/model.jl

* Apply suggestions from code review

* Update Project.toml

* Update docs/src/api.md

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

---------

Co-authored-by: Hong Ge <3279477+yebai@users.noreply.github.com>
Co-authored-by: David Widmann <devmotion@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
  • Loading branch information
4 people committed Jul 13, 2023
1 parent e8172f0 commit 7d312cd
Show file tree
Hide file tree
Showing 7 changed files with 705 additions and 48 deletions.
2 changes: 1 addition & 1 deletion Project.toml
@@ -1,6 +1,6 @@
name = "DynamicPPL"
uuid = "366bfd00-2699-11ea-058f-f148b4cae6d8"
version = "0.23.1"
version = "0.23.2"

[deps]
AbstractMCMC = "80f14c24-f653-4e6a-9b94-39d6b0f70001"
Expand Down
29 changes: 28 additions & 1 deletion docs/src/api.md
Expand Up @@ -82,6 +82,34 @@ Similarly, one can specify with [`AbstractPPL.decondition`](@ref) that certain,
decondition
```

## Fixing and unfixing

We can also _fix_ a collection of variables in a [`Model`](@ref) to certain using [`fix`](@ref).

This might seem quite similar to the aforementioned [`condition`](@ref) and its siblings,
but they are indeed different operations:

- `condition`ed variables are considered to be _observations_, and are thus
included in the computation [`logjoint`](@ref) and [`loglikelihood`](@ref),
but not in [`logprior`](@ref).
- `fix`ed variables are considered to be _constant_, and are thus not included
in any log-probability computations.

The differences are more clearly spelled out in the docstring of [`fix`](@ref) below.

```@docs
fix
DynamicPPL.fixed
```

The difference between [`fix`](@ref) and [`condition`](@ref) is described in the docstring of [`fix`](@ref) above.

Similarly, we can [`unfix`](@ref) variables, i.e. return them to their original meaning:

```@docs
unfix
```

## Utilities

It is possible to manually increase (or decrease) the accumulated log density from within a model function.
Expand Down Expand Up @@ -327,4 +355,3 @@ dot_tilde_assume
tilde_observe
dot_tilde_observe
```

2 changes: 2 additions & 0 deletions src/DynamicPPL.jl
Expand Up @@ -119,6 +119,8 @@ export AbstractVarInfo,
pointwise_loglikelihoods,
condition,
decondition,
fix,
unfix,
# Convenience macros
@addlogprob!,
@submodel
Expand Down
52 changes: 45 additions & 7 deletions src/compiler.jl
Expand Up @@ -66,8 +66,8 @@ function contextual_isassumption(context::AbstractContext, vn)
return contextual_isassumption(NodeTrait(context), context, vn)
end
function contextual_isassumption(context::ConditionContext, vn)
if hasvalue(context, vn)
val = getvalue(context, vn)
if hasconditioned(context, vn)
val = getconditioned(context, vn)
# TODO: Do we even need the `>: Missing`, i.e. does it even help the compiler?
if eltype(val) >: Missing && val === missing
return true
Expand All @@ -76,14 +76,48 @@ function contextual_isassumption(context::ConditionContext, vn)
end
end

# We might have nested contexts, e.g. `ContextionContext{.., <:PrefixContext{..., <:ConditionContext}}`
# We might have nested contexts, e.g. `ConditionContext{.., <:PrefixContext{..., <:ConditionContext}}`
# so we defer to `childcontext` if we haven't concluded that anything yet.
return contextual_isassumption(childcontext(context), vn)
end
function contextual_isassumption(context::PrefixContext, vn)
return contextual_isassumption(childcontext(context), prefix(context, vn))
end

isfixed(expr, vn) = false
isfixed(::Union{Symbol,Expr}, vn) = :($(DynamicPPL.contextual_isfixed)(__context__, $vn))

"""
contextual_isfixed(context, vn)
Return `true` if `vn` is considered fixed by `context`.
"""
contextual_isfixed(::IsLeaf, context, vn) = false
function contextual_isfixed(::IsParent, context, vn)
return contextual_isfixed(childcontext(context), vn)
end
function contextual_isfixed(context::AbstractContext, vn)
return contextual_isfixed(NodeTrait(context), context, vn)
end
function contextual_isfixed(context::PrefixContext, vn)
return contextual_isfixed(childcontext(context), prefix(context, vn))
end
function contextual_isfixed(context::FixedContext, vn)
if hasfixed(context, vn)
val = getfixed(context, vn)
# TODO: Do we even need the `>: Missing`, i.e. does it even help the compiler?
if eltype(val) >: Missing && val === missing
return false
else
return true
end
end

# We might have nested contexts, e.g. `FixedContext{.., <:PrefixContext{..., <:FixedContext}}`
# so we defer to `childcontext` if we haven't concluded that anything yet.
return contextual_isfixed(childcontext(context), vn)
end

# If we're working with, say, a `Symbol`, then we're not going to `view`.
maybe_view(x) = x
maybe_view(x::Expr) = :(@views($x))
Expand Down Expand Up @@ -341,12 +375,14 @@ function generate_tilde(left, right)
$(AbstractPPL.drop_escape(varname(left))), $dist
)
$isassumption = $(DynamicPPL.isassumption(left, vn))
if $isassumption
if $(DynamicPPL.isfixed(left, vn))
$left = $(DynamicPPL.getfixed_nested)(__context__, $vn)
elseif $isassumption
$(generate_tilde_assume(left, dist, vn))
else
# If `vn` is not in `argnames`, we need to make sure that the variable is defined.
if !$(DynamicPPL.inargnames)($vn, __model__)
$left = $(DynamicPPL.getvalue_nested)(__context__, $vn)
$left = $(DynamicPPL.getconditioned_nested)(__context__, $vn)
end

$value, __varinfo__ = $(DynamicPPL.tilde_observe!!)(
Expand Down Expand Up @@ -400,12 +436,14 @@ function generate_dot_tilde(left, right)
$(AbstractPPL.drop_escape(varname(left))), $right
)
$isassumption = $(DynamicPPL.isassumption(left, vn))
if $isassumption
if $(DynamicPPL.isfixed(left, vn))
$left .= $(DynamicPPL.getfixed_nested)(__context__, $vn)
elseif $isassumption
$(generate_dot_tilde_assume(left, right, vn))
else
# If `vn` is not in `argnames`, we need to make sure that the variable is defined.
if !$(DynamicPPL.inargnames)($vn, __model__)
$left .= $(DynamicPPL.getvalue_nested)(__context__, $vn)
$left .= $(DynamicPPL.getconditioned_nested)(__context__, $vn)
end

$value, __varinfo__ = $(DynamicPPL.dot_tilde_observe!!)(
Expand Down

2 comments on commit 7d312cd

@yebai
Copy link
Member

@yebai yebai commented on 7d312cd Jul 13, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request created: JuliaRegistries/General/87445

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v0.23.2 -m "<description of version>" 7d312cd593f8b0b6daab3eeed75e39f6ee8adc00
git push origin v0.23.2

Please sign in to comment.