API
Part of the API of DynamicPPL is defined in the more lightweight interface package AbstractPPL.jl and reexported here.
Model
Macros
A core component of DynamicPPL is the @model
macro. It can be used to define probabilistic models in an intuitive way by specifying random variables and their distributions with ~
statements. These statements are rewritten by @model
as calls of internal functions for sampling the variables and computing their log densities.
DynamicPPL.@model
— Macro@model(expr[, warn = false])
Macro to specify a probabilistic model.
If warn
is true
, a warning is displayed if internal variable names are used in the model definition.
Examples
Model definition:
@model function model(x, y = 42)
+API · DynamicPPL API
Part of the API of DynamicPPL is defined in the more lightweight interface package AbstractPPL.jl and reexported here.
Model
Macros
A core component of DynamicPPL is the @model
macro. It can be used to define probabilistic models in an intuitive way by specifying random variables and their distributions with ~
statements. These statements are rewritten by @model
as calls of internal functions for sampling the variables and computing their log densities.
DynamicPPL.@model
— Macro@model(expr[, warn = false])
Macro to specify a probabilistic model.
If warn
is true
, a warning is displayed if internal variable names are used in the model definition.
Examples
Model definition:
@model function model(x, y = 42)
...
-end
To generate a Model
, call model(xvalue)
or model(xvalue, yvalue)
.
sourceOne can nest models and call another model inside the model function with @submodel
.
DynamicPPL.@submodel
— Macro@submodel model
+end
To generate a Model
, call model(xvalue)
or model(xvalue, yvalue)
.
sourceOne can nest models and call another model inside the model function with @submodel
.
DynamicPPL.@submodel
— Macro@submodel model
@submodel ... = model
Run a Turing model
nested inside of a Turing model.
Examples
julia> @model function demo1(x)
x ~ Normal()
return 1 + abs(x)
@@ -475,7 +17,7 @@
false
We can check that the log joint probability of the model accumulated in vi
is correct:
julia> x = vi[@varname(x)];
julia> getlogp(vi) ≈ logpdf(Normal(), x) + logpdf(Uniform(0, 1 + abs(x)), 0.4)
-true
source@submodel prefix=... model
+true
source@submodel prefix=... model
@submodel prefix=... ... = model
Run a Turing model
nested inside of a Turing model and add "prefix
." as a prefix to all random variables inside of the model
.
Valid expressions for prefix=...
are:
prefix=false
: no prefix is used.prefix=true
: attempt to automatically determine the prefix from the left-hand side ... = model
by first converting into a VarName
, and then calling Symbol
on this.prefix=expression
: results in the prefix Symbol(expression)
.
The prefix makes it possible to run the same Turing model multiple times while keeping track of all random variables correctly.
Examples
Example models
julia> @model function demo1(x)
x ~ Normal()
return 1 + abs(x)
@@ -552,7 +94,7 @@
julia> # (×) Automatic prefixing without a left-hand side expression does not work!
@model submodel_prefix_error() = @submodel prefix=true inner()
ERROR: LoadError: cannot automatically prefix with no left-hand side
-[...]
Notes
- The choice
prefix=expression
means that the prefixing will incur a runtime cost. This is also the case for prefix=true
, depending on whether the expression on the the right-hand side of ... = model
requires runtime-information or not, e.g. x = model
will result in the static prefix x
, while x[i] = model
will be resolved at runtime.
sourceType
A Model
can be created by calling the model function, as defined by @model
.
DynamicPPL.Model
— Typestruct Model{F,argnames,defaultnames,missings,Targs,Tdefaults,Ctx<:AbstactContext}
+[...]
Notes
- The choice
prefix=expression
means that the prefixing will incur a runtime cost. This is also the case for prefix=true
, depending on whether the expression on the the right-hand side of ... = model
requires runtime-information or not, e.g. x = model
will result in the static prefix x
, while x[i] = model
will be resolved at runtime.
sourceType
A Model
can be created by calling the model function, as defined by @model
.
DynamicPPL.Model
— Typestruct Model{F,argnames,defaultnames,missings,Targs,Tdefaults,Ctx<:AbstactContext}
f::F
args::NamedTuple{argnames,Targs}
defaults::NamedTuple{defaultnames,Tdefaults}
@@ -564,7 +106,7 @@
Model{typeof(f),(:x, :y),(:x,),(),Tuple{Float64,Float64},Tuple{Int64}}(f, (x = 1.0, y = 2.0), (x = 42,))
julia> Model{(:y,)}(f, (x = 1.0, y = 2.0), (x = 42,)) # with special definition of missings
-Model{typeof(f),(:x, :y),(:x,),(:y,),Tuple{Float64,Float64},Tuple{Int64}}(f, (x = 1.0, y = 2.0), (x = 42,))
sourceModel
s are callable structs.
DynamicPPL.Model
— Method(model::Model)([rng, varinfo, sampler, context])
Sample from the model
using the sampler
with random number generator rng
and the context
, and store the sample and log joint probability in varinfo
.
The method resets the log joint probability of varinfo
and increases the evaluation number of sampler
.
sourceBasic properties of a model can be accessed with getargnames
, getmissings
, and nameof
.
Base.nameof
— Methodnameof(model::Model)
Get the name of the model
as Symbol
.
sourceDynamicPPL.getargnames
— Functiongetargnames(model::Model)
Get a tuple of the argument names of the model
.
sourceDynamicPPL.getmissings
— Functiongetmissings(model::Model)
Get a tuple of the names of the missing arguments of the model
.
sourceEvaluation
With rand
one can draw samples from the prior distribution of a Model
.
Base.rand
— Functionrand([rng=Random.default_rng()], [T=NamedTuple], model::Model)
Generate a sample of type T
from the prior distribution of the model
.
sourceOne can also evaluate the log prior, log likelihood, and log joint probability.
DynamicPPL.logprior
— Functionlogprior(model::Model, varinfo::AbstractVarInfo)
Return the log prior probability of variables varinfo
for the probabilistic model
.
See also logjoint
and loglikelihood
.
sourcelogprior(model::Model, chain::AbstractMCMC.AbstractChains)
Return an array of log prior probabilities evaluated at each sample in an MCMC chain
.
Examples
julia> using MCMCChains, Distributions
+Model{typeof(f),(:x, :y),(:x,),(:y,),Tuple{Float64,Float64},Tuple{Int64}}(f, (x = 1.0, y = 2.0), (x = 42,))
sourceModel
s are callable structs.
DynamicPPL.Model
— Method(model::Model)([rng, varinfo, sampler, context])
Sample from the model
using the sampler
with random number generator rng
and the context
, and store the sample and log joint probability in varinfo
.
The method resets the log joint probability of varinfo
and increases the evaluation number of sampler
.
sourceBasic properties of a model can be accessed with getargnames
, getmissings
, and nameof
.
Base.nameof
— Methodnameof(model::Model)
Get the name of the model
as Symbol
.
sourceDynamicPPL.getargnames
— Functiongetargnames(model::Model)
Get a tuple of the argument names of the model
.
sourceDynamicPPL.getmissings
— Functiongetmissings(model::Model)
Get a tuple of the names of the missing arguments of the model
.
sourceEvaluation
With rand
one can draw samples from the prior distribution of a Model
.
Base.rand
— Functionrand([rng=Random.default_rng()], [T=NamedTuple], model::Model)
Generate a sample of type T
from the prior distribution of the model
.
sourceOne can also evaluate the log prior, log likelihood, and log joint probability.
DynamicPPL.logprior
— Functionlogprior(model::Model, varinfo::AbstractVarInfo)
Return the log prior probability of variables varinfo
for the probabilistic model
.
See also logjoint
and loglikelihood
.
sourcelogprior(model::Model, chain::AbstractMCMC.AbstractChains)
Return an array of log prior probabilities evaluated at each sample in an MCMC chain
.
Examples
julia> using MCMCChains, Distributions
julia> @model function demo_model(x)
s ~ InverseGamma(2, 3)
@@ -577,7 +119,7 @@
julia> # construct a chain of samples using MCMCChains
chain = Chains(rand(10, 2, 3), [:s, :m]);
-julia> logprior(demo_model([1., 2.]), chain);
sourcelogprior(model::Model, θ)
Return the log prior probability of variables θ
for the probabilistic model
.
See also logjoint
and loglikelihood
.
Examples
julia> @model function demo(x)
+julia> logprior(demo_model([1., 2.]), chain);
sourcelogprior(model::Model, θ)
Return the log prior probability of variables θ
for the probabilistic model
.
See also logjoint
and loglikelihood
.
Examples
julia> @model function demo(x)
m ~ Normal()
for i in eachindex(x)
x[i] ~ Normal(m, 1.0)
@@ -595,7 +137,7 @@
julia> # Truth.
logpdf(Normal(), 100.0)
--5000.918938533205
sourceStatsAPI.loglikelihood
— Functionloglikelihood(model::Model, varinfo::AbstractVarInfo)
Return the log likelihood of variables varinfo
for the probabilistic model
.
sourceloglikelihood(model::Model, chain::AbstractMCMC.AbstractChains)
Return an array of log likelihoods evaluated at each sample in an MCMC chain
.
Examples
julia> using MCMCChains, Distributions
+-5000.918938533205
sourceStatsAPI.loglikelihood
— Functionloglikelihood(model::Model, varinfo::AbstractVarInfo)
Return the log likelihood of variables varinfo
for the probabilistic model
.
sourceloglikelihood(model::Model, chain::AbstractMCMC.AbstractChains)
Return an array of log likelihoods evaluated at each sample in an MCMC chain
.
Examples
julia> using MCMCChains, Distributions
julia> @model function demo_model(x)
s ~ InverseGamma(2, 3)
@@ -608,7 +150,7 @@
julia> # construct a chain of samples using MCMCChains
chain = Chains(rand(10, 2, 3), [:s, :m]);
-julia> loglikelihood(demo_model([1., 2.]), chain);
sourceloglikelihood(model::Model, θ)
Return the log likelihood of variables θ
for the probabilistic model
.
See also logjoint
and logprior
.
Examples
julia> @model function demo(x)
+julia> loglikelihood(demo_model([1., 2.]), chain);
sourceloglikelihood(model::Model, θ)
Return the log likelihood of variables θ
for the probabilistic model
.
See also logjoint
and logprior
.
Examples
julia> @model function demo(x)
m ~ Normal()
for i in eachindex(x)
x[i] ~ Normal(m, 1.0)
@@ -626,7 +168,7 @@
julia> # Truth.
logpdf(Normal(100.0, 1.0), 1.0)
--4901.418938533205
sourceDynamicPPL.logjoint
— Functionlogjoint(model::Model, varinfo::AbstractVarInfo)
Return the log joint probability of variables varinfo
for the probabilistic model
.
See logprior
and loglikelihood
.
sourcelogjoint(model::Model, chain::AbstractMCMC.AbstractChains)
Return an array of log joint probabilities evaluated at each sample in an MCMC chain
.
Examples
julia> using MCMCChains, Distributions
+-4901.418938533205
sourceDynamicPPL.logjoint
— Functionlogjoint(model::Model, varinfo::AbstractVarInfo)
Return the log joint probability of variables varinfo
for the probabilistic model
.
See logprior
and loglikelihood
.
sourcelogjoint(model::Model, chain::AbstractMCMC.AbstractChains)
Return an array of log joint probabilities evaluated at each sample in an MCMC chain
.
Examples
julia> using MCMCChains, Distributions
julia> @model function demo_model(x)
s ~ InverseGamma(2, 3)
@@ -639,7 +181,7 @@
julia> # construct a chain of samples using MCMCChains
chain = Chains(rand(10, 2, 3), [:s, :m]);
-julia> logjoint(demo_model([1., 2.]), chain);
sourcelogjoint(model::Model, θ)
Return the log joint probability of variables θ
for the probabilistic model
.
See logprior
and loglikelihood
.
Examples
julia> @model function demo(x)
+julia> logjoint(demo_model([1., 2.]), chain);
sourcelogjoint(model::Model, θ)
Return the log joint probability of variables θ
for the probabilistic model
.
See logprior
and loglikelihood
.
Examples
julia> @model function demo(x)
m ~ Normal()
for i in eachindex(x)
x[i] ~ Normal(m, 1.0)
@@ -657,7 +199,7 @@
julia> # Truth.
logpdf(Normal(100.0, 1.0), 1.0) + logpdf(Normal(), 100.0)
--9902.33787706641
sourceLogDensityProblems.jl interface
The LogDensityProblems.jl interface is also supported by simply wrapping a Model
in a DynamicPPL.LogDensityFunction
:
DynamicPPL.LogDensityFunction
— TypeLogDensityFunction
A callable representing a log density function of a model
.
Fields
varinfo
: varinfo used for evaluation
model
: model used for evaluation
context
: context used for evaluation; if nothing
, leafcontext(model.context)
will be used when applicable
Examples
julia> using Distributions
+-9902.33787706641
sourceLogDensityProblems.jl interface
The LogDensityProblems.jl interface is also supported by simply wrapping a Model
in a DynamicPPL.LogDensityFunction
:
DynamicPPL.LogDensityFunction
— TypeLogDensityFunction
A callable representing a log density function of a model
.
Fields
varinfo
: varinfo used for evaluation
model
: model used for evaluation
context
: context used for evaluation; if nothing
, leafcontext(model.context)
will be used when applicable
Examples
julia> using Distributions
julia> using DynamicPPL: LogDensityFunction, contextualize
@@ -690,7 +232,7 @@
f_prior = LogDensityFunction(contextualize(model, DynamicPPL.PriorContext()), VarInfo(model));
julia> LogDensityProblems.logdensity(f_prior, [0.0]) == logpdf(Normal(), 0.0)
-true
sourceCondition and decondition
A Model
can be conditioned on a set of observations with AbstractPPL.condition
or its alias |
.
Base.:|
— Methodmodel | (x = 1.0, ...)
Return a Model
which now treats variables on the right-hand side as observations.
See condition
for more information and examples.
sourceAbstractPPL.condition
— Functioncondition(model::Model; values...)
+true
sourceCondition and decondition
A Model
can be conditioned on a set of observations with AbstractPPL.condition
or its alias |
.
Base.:|
— Methodmodel | (x = 1.0, ...)
Return a Model
which now treats variables on the right-hand side as observations.
See condition
for more information and examples.
sourceAbstractPPL.condition
— Functioncondition(model::Model; values...)
condition(model::Model, values::NamedTuple)
Return a Model
which now treats the variables in values
as observations.
See also: decondition
, conditioned
Limitations
This does currently not work with variables that are provided to the model as arguments, e.g. @model function demo(x) ... end
means that condition
will not affect the variable x
.
Therefore if one wants to make use of condition
and decondition
one should not be specifying any random variables as arguments.
This is done for the sake of backwards compatibility.
Examples
Simple univariate model
julia> using Distributions
julia> @model function demo()
@@ -799,8 +341,8 @@
julia> keys(VarInfo(demo_outer_prefix()))
1-element Vector{VarName{Symbol("inner.m"), typeof(identity)}}:
- inner.m
From this we can tell what the correct way to condition m
within demo_inner
is in the two different models.
sourcecondition([context::AbstractContext,] values::NamedTuple)
-condition([context::AbstractContext]; values...)
Return ConditionContext
with values
and context
if values
is non-empty, otherwise return context
which is DefaultContext
by default.
See also: decondition
sourceDynamicPPL.conditioned
— Functionconditioned(model::Model)
Return the conditioned values in model
.
Examples
julia> using Distributions
+ inner.m
From this we can tell what the correct way to condition m
within demo_inner
is in the two different models.
sourcecondition([context::AbstractContext,] values::NamedTuple)
+condition([context::AbstractContext]; values...)
Return ConditionContext
with values
and context
if values
is non-empty, otherwise return context
which is DefaultContext
by default.
See also: decondition
sourceDynamicPPL.conditioned
— Functionconditioned(model::Model)
Return the conditioned values in model
.
Examples
julia> using Distributions
julia> using DynamicPPL: conditioned, contextualize
@@ -838,7 +380,7 @@
1.0
julia> keys(VarInfo(cm)) # <= no variables are sampled
-VarName[]
sourceconditioned(context::AbstractContext)
Return NamedTuple
of values that are conditioned on under context`.
Note that this will recursively traverse the context stack and return a merged version of the condition values.
sourceSimilarly, one can specify with AbstractPPL.decondition
that certain, or all, random variables are not observed.
AbstractPPL.decondition
— Functiondecondition(model::Model)
+VarName[]
sourceconditioned(context::AbstractContext)
Return NamedTuple
of values that are conditioned on under context`.
Note that this will recursively traverse the context stack and return a merged version of the condition values.
sourceSimilarly, one can specify with AbstractPPL.decondition
that certain, or all, random variables are not observed.
AbstractPPL.decondition
— Functiondecondition(model::Model)
decondition(model::Model, variables...)
Return a Model
for which variables...
are not considered observations. If no variables
are provided, then all variables currently considered observations will no longer be.
This is essentially the inverse of condition
. This also means that it suffers from the same limitiations.
Note that currently we only support variables
to take on explicit values provided to condition
.
Examples
julia> using Distributions
julia> @model function demo()
@@ -916,7 +458,7 @@
deconditioned_model_2 = deconditioned_model | (@varname(m[1]) => missing);
julia> m = deconditioned_model_2(); (m[1] ≠ 1.0 && m[2] == 2.0)
-true
sourcedecondition(context::AbstractContext, syms...)
Return context
but with syms
no longer conditioned on.
Note that this recursively traverses contexts, deconditioning all along the way.
See also: condition
sourceFixing and unfixing
We can also fix a collection of variables in a Model
to certain using fix
.
This might seem quite similar to the aforementioned condition
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
and loglikelihood
, but not in logprior
.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
below.
DynamicPPL.fix
— Functionfix(model::Model; values...)
+true
sourcedecondition(context::AbstractContext, syms...)
Return context
but with syms
no longer conditioned on.
Note that this recursively traverses contexts, deconditioning all along the way.
See also: condition
sourceFixing and unfixing
We can also fix a collection of variables in a Model
to certain using fix
.
This might seem quite similar to the aforementioned condition
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
and loglikelihood
, but not in logprior
.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
below.
DynamicPPL.fix
— Functionfix(model::Model; values...)
fix(model::Model, values::NamedTuple)
Return a Model
which now treats the variables in values
as fixed.
Examples
Simple univariate model
julia> using Distributions
julia> @model function demo()
@@ -1038,8 +580,8 @@
julia> # And the difference is the missing log-probability of `m`:
logjoint(model_fixed, (x=1.0,)) + logpdf(Normal(), 1.0) == logjoint(model_conditioned, (x=1.0,))
-true
sourcefix([context::AbstractContext,] values::NamedTuple)
-fix([context::AbstractContext]; values...)
Return FixedContext
with values
and context
if values
is non-empty, otherwise return context
which is DefaultContext
by default.
See also: unfix
sourceDynamicPPL.fixed
— Functionfixed(model::Model)
Return the fixed values in model
.
Examples
julia> using Distributions
+true
sourcefix([context::AbstractContext,] values::NamedTuple)
+fix([context::AbstractContext]; values...)
Return FixedContext
with values
and context
if values
is non-empty, otherwise return context
which is DefaultContext
by default.
See also: unfix
sourceDynamicPPL.fixed
— Functionfixed(model::Model)
Return the fixed values in model
.
Examples
julia> using Distributions
julia> using DynamicPPL: fixed, contextualize
@@ -1077,7 +619,7 @@
1.0
julia> keys(VarInfo(cm)) # <= no variables are sampled
-VarName[]
sourcefixed(context::AbstractContext)
Return the values that are fixed under context
.
Note that this will recursively traverse the context stack and return a merged version of the fix values.
sourceThe difference between fix
and condition
is described in the docstring of fix
above.
Similarly, we can unfix
variables, i.e. return them to their original meaning:
DynamicPPL.unfix
— Functionunfix(model::Model)
+VarName[]
sourcefixed(context::AbstractContext)
Return the values that are fixed under context
.
Note that this will recursively traverse the context stack and return a merged version of the fix values.
sourceThe difference between fix
and condition
is described in the docstring of fix
above.
Similarly, we can unfix
variables, i.e. return them to their original meaning:
DynamicPPL.unfix
— Functionunfix(model::Model)
unfix(model::Model, variables...)
Return a Model
for which variables...
are not considered fixed. If no variables
are provided, then all variables currently considered fixed will no longer be.
This is essentially the inverse of fix
. This also means that it suffers from the same limitiations.
Note that currently we only support variables
to take on explicit values provided to fix
.
Examples
julia> using Distributions
julia> @model function demo()
@@ -1155,7 +697,7 @@
unfixed_model_2 = fix(unfixed_model, @varname(m[1]) => missing);
julia> m = unfixed_model_2(); (m[1] ≠ 1.0 && m[2] == 2.0)
-true
sourceunfix(context::AbstractContext, syms...)
Return context
but with syms
no longer fixed.
Note that this recursively traverses contexts, unfixing all along the way.
See also: fix
sourceUtilities
It is possible to manually increase (or decrease) the accumulated log density from within a model function.
DynamicPPL.@addlogprob!
— Macro@addlogprob!(ex)
Add the result of the evaluation of ex
to the joint log probability.
Examples
This macro allows you to include arbitrary terms in the likelihood
julia> myloglikelihood(x, μ) = loglikelihood(Normal(μ, 1), x);
+true
sourceunfix(context::AbstractContext, syms...)
Return context
but with syms
no longer fixed.
Note that this recursively traverses contexts, unfixing all along the way.
See also: fix
sourceUtilities
It is possible to manually increase (or decrease) the accumulated log density from within a model function.
DynamicPPL.@addlogprob!
— Macro@addlogprob!(ex)
Add the result of the evaluation of ex
to the joint log probability.
Examples
This macro allows you to include arbitrary terms in the likelihood
julia> myloglikelihood(x, μ) = loglikelihood(Normal(μ, 1), x);
julia> @model function demo(x)
μ ~ Normal()
@@ -1192,7 +734,7 @@
true
julia> loglikelihood(demo(x), (μ=0.2,)) ≈ myloglikelihood(x, 0.2)
-true
sourceReturn values of the model function for a collection of samples can be obtained with generated_quantities
.
DynamicPPL.generated_quantities
— Functiongenerated_quantities(model::Model, parameters::NamedTuple)
+true