v0.36.0
DynamicPPL v0.36.0
Breaking changes
Submodels: conditioning
Variables in a submodel can now be conditioned and fixed in a correct way.
See #857 for a full illustration, but essentially it means you can now do this:
@model function inner()
x ~ Normal()
return y ~ Normal()
end
@model function outer()
return a ~ to_submodel(inner() | (x=1.0,))
end
and the a.x
variable will be correctly conditioned.
(Previously, you would have to condition inner()
with the variable a.x
, meaning that you would need to know what prefix to use before you had actually prefixed it.)
Submodel prefixing
The way in which VarNames in submodels are prefixed has been changed.
This is best explained through an example.
Consider this model and submodel:
using DynamicPPL, Distributions
@model inner() = x ~ Normal()
@model outer() = a ~ to_submodel(inner())
In previous versions, the inner variable x
would be saved as a.x
.
However, this was represented as a single symbol Symbol("a.x")
:
julia> dump(keys(VarInfo(outer()))[1])
VarName{Symbol("a.x"), typeof(identity)}
optic: identity (function of type typeof(identity))
Now, the inner variable is stored as a field x
on the VarName a
:
julia> dump(keys(VarInfo(outer()))[1])
VarName{:a, Accessors.PropertyLens{:x}}
optic: Accessors.PropertyLens{:x} (@o _.x)
In practice, this means that if you are trying to condition a variable in the submodel, you now need to use
outer() | (@varname(a.x) => 1.0,)
instead of either of these (which would have worked previously)
outer() | (@varname(var"a.x") => 1.0,)
outer() | (a.x=1.0,)
In a similar way, if the variable on the left-hand side of your tilde statement is not just a single identifier, any fields or indices it accesses are now properly respected.
Consider the following setup:
using DynamicPPL, Distributions
@model inner() = x ~ Normal()
@model function outer()
a = Vector{Float64}(undef, 1)
a[1] ~ to_submodel(inner())
return a
end
In this case, the variable sampled is actually the x
field of the first element of a
:
julia> only(keys(VarInfo(outer()))) == @varname(a[1].x)
true
Before this version, it used to be a single variable called var"a[1].x"
.
Note that if you are sampling from a model with submodels, this doesn't affect the way you interact with the MCMCChains.Chains
object, because VarNames are converted into Symbols when stored in the chain.
(This behaviour will likely be changed in the future, in that Chains should be indexable by VarNames and not just Symbols, but that has not been implemented yet.)
AD testing utilities
DynamicPPL.TestUtils.AD.run_ad
now links the VarInfo by default.
To disable this, pass the linked=false
keyword argument.
If the calculated value or gradient is incorrect, it also throws a DynamicPPL.TestUtils.AD.ADIncorrectException
rather than a test failure.
This exception contains the actual and expected gradient so you can inspect it if needed; see the documentation for more information.
From a practical perspective, this means that if you need to add this to a test suite, you need to use @test run_ad(...) isa Any
rather than just run_ad(...)
.
SimpleVarInfo linking / invlinking
Linking a linked SimpleVarInfo, or invlinking an unlinked SimpleVarInfo, now displays a warning instead of an error.
VarInfo constructors
VarInfo(vi::VarInfo, values)
has been removed. You can replace this directly with unflatten(vi, values)
instead.
The metadata
argument to VarInfo([rng, ]model[, sampler, context, metadata])
has been removed.
If you were not using this argument (most likely), then there is no change needed.
If you were using the metadata
argument to specify a blank VarNamedVector
, then you should replace calls to VarInfo
with DynamicPPL.typed_vector_varinfo
instead (see 'Other changes' below).
The UntypedVarInfo
constructor and type is no longer exported.
If you needed to construct one, you should now use DynamicPPL.untyped_varinfo
instead.
The TypedVarInfo
constructor and type is no longer exported.
The type has been replaced with DynamicPPL.NTVarInfo
.
The constructor has been replaced with DynamicPPL.typed_varinfo
.
Note that the exact kind of VarInfo returned by VarInfo(rng, model, ...)
is an implementation detail.
Previously, it was guaranteed that this would always be a VarInfo whose metadata was a NamedTuple
containing Metadata
structs.
Going forward, this is no longer the case, and you should only assume that the returned object obeys the AbstractVarInfo
interface.
Other changes
While these are technically breaking, they are only internal changes and do not affect the public API.
The following four functions have been added and/or reworked to make it easier to construct VarInfos with different types of metadata:
DynamicPPL.untyped_varinfo([rng, ]model[, sampler, context])
DynamicPPL.typed_varinfo([rng, ]model[, sampler, context])
DynamicPPL.untyped_vector_varinfo([rng, ]model[, sampler, context])
DynamicPPL.typed_vector_varinfo([rng, ]model[, sampler, context])
The reason for this change is that there were several flavours of VarInfo.
Some, like typed_varinfo
, were easy to construct because we had convenience methods for them; however, the others were more difficult.
This change makes it easier to access different VarInfo types, and also makes it more explicit which one you are constructing.
Merged pull requests:
- Fix
condition
andfix
in submodels (#892) (@penelopeysm) - Make PrefixContext contain a varname rather than symbol (#896) (@penelopeysm)