AbstractPPL.AbstractContext
— TypeAbstractContext
Common base type for evaluation contexts.
AbstractPPL.AbstractModelTrace
— TypeAbstractModelTrace
Common base class for various trace or "variable info" types.
AbstractPPL.AbstractProbabilisticProgram
— TypeAbstractProbabilisticProgram
Common base type for models expressed as probabilistic programs.
AbstractPPL.ConcretizedSlice
— TypeConcretizedSlice(::Base.Slice)
An indexing object wrapping the range of a Base.Slice
object representing the concrete indices a :
indicates. Behaves the same, but prints differently, namely, still as :
.
AbstractPPL.VarName
— TypeVarName(vn::VarName, optic)
VarName(vn::VarName, indexing::Tuple)
Return a copy of vn
with a new index optic
/indexing
.
julia> VarName(@varname(x[1][2:3]), Accessors.IndexLens((2,)))
x[2]
julia> VarName(@varname(x[1][2:3]), ((2,),))
x[2]
julia> VarName(@varname(x[1][2:3]))
x
AbstractPPL.VarName
— TypeVarName{sym}(optic=identity)
A variable identifier for a symbol sym
and optic optic
.
The Julia variable in the model corresponding to sym
can refer to a single value or to a hierarchical array structure of univariate, multivariate or matrix variables. The field lens
stores the indices requires to access the random variable from the Julia variable indicated by sym
as a tuple of tuples. Each element of the tuple thereby contains the indices of one optic operation.
VarName
s can be manually constructed using the VarName{sym}(optic)
constructor, or from an optic expression through the @varname
convenience macro.
Examples
julia> vn = VarName{:x}(Accessors.IndexLens((Colon(), 1)) ⨟ Accessors.IndexLens((2, )))
x[:, 1][2]
julia> getoptic(vn)
(@o _[Colon(), 1][2])
julia> @varname x[:, 1][1+1]
x[:, 1][2]
Core.Symbol
— MethodSymbol(vn::VarName)
Return a Symbol
representation of the variable identifier VarName
.
Examples
julia> Symbol(@varname(x[1][2:3]))
Symbol("x[1][2:3]")
julia> Symbol(@varname(x[1][:]))
Symbol("x[1][:]")
AbstractPPL.combine_indices
— Methodcombine_indices(optic)
Return sequential indexing into a single Tuple
of indices, e.g. x[:][1][2]
becomes ((Colon(), ), (1, ), (2, ))
.
The result is compatible with subsumes_indices
for Tuple
input.
AbstractPPL.concretize
— Methodconcretize(l, x)
Return l
instantiated on x
, i.e. any information related to the runtime shape of x
is evaluated. This concerns begin
, end
, and :
slices.
Basically, every index is converted to a concrete value using Base.to_index
on x
. However, :
slices are only converted to ConcretizedSlice
(as opposed to Base.Slice{Base.OneTo}
), to keep the result close to the original indexing.
AbstractPPL.concretize
— Methodconcretize(vn::VarName, x)
Return vn
concretized on x
, i.e. any information related to the runtime shape of x
is evaluated. This concerns begin
, end
, and :
slices.
Examples
julia> x = (a = [1.0 2.0; 3.0 4.0; 5.0 6.0], );
julia> getoptic(@varname(x.a[1:end, end][:], true)) # concrete=true required for @varname
(@o _.a[1:3, 2][:])
julia> y = zeros(10, 10);
julia> @varname(y[:], true)
y[:]
julia> # The underlying value is conretized, though:
AbstractPPL.getoptic(AbstractPPL.concretize(@varname(y[:]), y)).indices[1]
ConcretizedSlice(Base.OneTo(100))
AbstractPPL.condition
— Functioncondition(model, observations)
Condition the generative model model
on some observed data, creating a new model of the (possibly unnormalized) posterior distribution over them.
observations
can be of any supported internal trace type, or a fixed probability expression.
The invariant
m = decondition(condition(m, obs))
should hold for generative models m
and arbitrary obs
.
AbstractPPL.decondition
— Functiondecondition(conditioned_model)
Remove the conditioning (i.e., observation data) from conditioned_model
, turning it into a generative model over prior and observed variables.
The invariant
m == condition(decondition(m), obs)
should hold for models m
with conditioned variables obs
.
AbstractPPL.evaluate!!
— Functionevaluate!!
General API for model operations, e.g. prior evaluation, log density, log joint etc.
AbstractPPL.getoptic
— Methodgetoptic(vn::VarName)
Return the optic of the Julia variable used to generate vn
.
Examples
julia> getoptic(@varname(x[1][2:3]))
(@o _[1][2:3])
julia> getoptic(@varname(y))
identity (generic function with 1 method)
AbstractPPL.getsym
— Methodgetsym(vn::VarName)
Return the symbol of the Julia variable used to generate vn
.
Examples
julia> getsym(@varname(x[1][2:3]))
:x
julia> getsym(@varname(y))
:y
AbstractPPL.inspace
— Methodinspace(vn::Union{VarName, Symbol}, space::Tuple)
Check whether vn
's variable symbol is in space
. The empty tuple counts as the "universal space" containing all variables. Subsumption (see subsume
) is respected.
Examples
julia> inspace(@varname(x[1][2:3]), ())
true
julia> inspace(@varname(x[1][2:3]), (:x,))
true
julia> inspace(@varname(x[1][2:3]), (@varname(x),))
true
julia> inspace(@varname(x[1][2:3]), (@varname(x[1:10]), :y))
true
julia> inspace(@varname(x[1][2:3]), (@varname(x[:][2:4]), :y))
true
julia> inspace(@varname(x[1][2:3]), (@varname(x[1:10]),))
true
AbstractPPL.is_static_optic
— Methodis_static_optic(l)
Return true
if l
is one or a composition of identity
, PropertyLens
, and IndexLens
; false
if l
is one or a composition of DynamicIndexLens
; and undefined otherwise.
AbstractPPL.reconcretize_index
— Methodreconcretize_index(original_index, lowered_index)
Create the index to be emitted in concretize
. original_index
is the original, unconcretized index, and lowered_index
the respective position of the result of to_indices
.
The only purpose of this are special cases like :
, which we want to avoid becoming a Base.Slice(OneTo(...))
– it would confuse people when printed. Instead, we concretize to a ConcretizedSlice
based on the lowered_index
, just what you'd get with an explicit begin:end
AbstractPPL.subsumes
— Methodsubsumes(u::VarName, v::VarName)
Check whether the variable name v
describes a sub-range of the variable u
. Supported indexing:
- Scalar:
```jldoctest julia> subsumes(@varname(x), @varname(x[1, 2])) true
julia> subsumes(@varname(x[1, 2]), @varname(x[1, 2][3])) true ```
- Array of scalar: basically everything that fulfills
issubset
.
```jldoctest julia> subsumes(@varname(x[[1, 2], 3]), @varname(x[1, 3])) true
julia> subsumes(@varname(x[1:3]), @varname(x[2][1])) true ```
- Slices:
jldoctest julia> subsumes(@varname(x[2, :]), @varname(x[2, 10][1])) true
Currently not supported are:
- Boolean indexing, literal
CartesianIndex
(these could be added, though) - Linear indexing of multidimensional arrays:
x[4]
does not subsumex[2, 2]
for a matrixx
- Trailing ones:
x[2, 1]
does not subsumex[2]
for a vectorx
AbstractPPL.subsumes_indices
— Methodsubsumes_indices(left_indices::Tuple, right_indices::Tuple)
Return true
if right_indices
is subsumed by left_indices
. left_indices
is assumed to be concretized and consist of either Int
s or AbstractArray
s of scalar indices that are supported by array A.
Currently not supported are:
- Boolean indexing, literal
CartesianIndex
(these could be added, though) - Linear indexing of multidimensional arrays:
x[4]
does not subsumex[2, 2]
for a matrixx
- Trailing ones:
x[2, 1]
does not subsumex[2]
for a vectorx
AbstractPPL.subsumes_indices
— Methodsubsumes_indices(t, u)
Return true
if the indexing represented by t
subsumes u
.
This is mostly useful for comparing compositions involving IndexLens
e.g. _[1][2].a[2]
and _[1][2].a
. In such a scenario we do the following:
- Combine
[1][2]
into aTuple
of indices usingcombine_indices
. - Do the same for
[1][2]
. - Compare the two tuples from (1) and (2) using
subsumes_indices
. - Since we're still undecided, we call
subsume(@o(_.a[2]), @o(_.a))
which then returnsfalse
.
Example
julia> t = @o(_[1].a); u = @o(_[1]);
julia> subsumes_indices(t, u)
false
julia> subsumes_indices(u, t)
true
julia> # `identity` subsumes all.
subsumes_indices(identity, t)
true
julia> # None subsumes `identity`.
subsumes_indices(t, identity)
false
julia> AbstractPPL.subsumes(@o(_[1][2].a[2]), @o(_[1][2].a))
false
julia> AbstractPPL.subsumes(@o(_[1][2].a), @o(_[1][2].a[2]))
true
AbstractPPL.vsym
— FunctionAccessors.set
— Methodset(obj, vn::VarName{sym}, value)
Alias for set(obj, PropertyLens{sym}() ⨟ getoptic(vn), value)
.
Example
julia> Accessors.set(nt, @varname(a), 10)
(a = 10, b = (c = [1, 2, 3],))
julia> Accessors.set(nt, @varname(b.c[1]), 10)
(a = 1, b = (c = [10, 2, 3],))
Base.get
— Methodget(obj, vn::VarName{sym})
Alias for (PropertyLens{sym}() ⨟ getoptic(vn))(obj)
. ```
Base.rand
— Methodrand([rng=Random.default_rng()], [T=NamedTuple], model::AbstractProbabilisticProgram) -> T
Draw a sample from the joint distribution of the model specified by the probabilistic program.
The sample will be returned as format specified by T
.
DensityInterface.logdensityof
— Methodlogdensityof(model, trace)
Evaluate the (possibly unnormalized) density of the model specified by the probabilistic program in model
, at specific values for the random variables given through trace
.
trace
can be of any supported internal trace type, or a fixed probability expression.
logdensityof
should interact with conditioning and deconditioning in the way required by probability theory.
AbstractPPL.@varname
— Macro@varname(expr, concretize=false)
A macro that returns an instance of VarName
given a symbol or indexing expression expr
.
If concretize
is true
, the resulting expression will be wrapped in a concretize
call.
Note that expressions involving dynamic indexing, i.e. begin
and/or end
, will always need to be concretized as VarName
only supports non-dynamic indexing as determined by is_static_index
. See examples below.
Examples
Dynamic indexing
julia> x = (a = [1.0 2.0; 3.0 4.0; 5.0 6.0], );
julia> @varname(x.a[1:end, end][:], true)
x.a[1:3, 2][:]
julia> @varname(x.a[end], false) # disable concretization
ERROR: LoadError: Variable name `x.a[end]` is dynamic and requires concretization!
[...]
julia> @varname(x.a[end]) # concretization occurs by default if deemed necessary
x.a[6]
julia> # Note that "dynamic" here refers to usage of `begin` and/or `end`,
# _not_ "information only available at runtime", i.e. the following works.
[@varname(x.a[i]) for i = 1:length(x.a)][end]
x.a[6]
julia> # Potentially surprising behaviour, but this is equivalent to what Base does:
@varname(x[2:2:5]), 2:2:5
(x[2:2:4], 2:2:4)
General indexing
Under the hood optic
s are used for the indexing:
julia> getoptic(@varname(x))
identity (generic function with 1 method)
julia> getoptic(@varname(x[1]))
(@o _[1])
julia> getoptic(@varname(x[:, 1]))
(@o _[Colon(), 1])
julia> getoptic(@varname(x[:, 1][2]))
(@o _[Colon(), 1][2])
julia> getoptic(@varname(x[1,2][1+5][45][3]))
(@o _[1, 2][6][45][3])
This also means that we support property access:
julia> getoptic(@varname(x.a))
(@o _.a)
julia> getoptic(@varname(x.a[1]))
(@o _.a[1])
julia> x = (a = [(b = rand(2), )], ); getoptic(@varname(x.a[1].b[end], true))
(@o _.a[1].b[2])
Interpolation can be used for variable names, or array name, but not the lhs of a .
expression. Variables within indices are always evaluated in the calling scope.
julia> name, i = :a, 10;
julia> @varname(x.$name[i, i+1])
x.a[10, 11]
julia> @varname($name)
a
julia> @varname($name[1])
a[1]
julia> @varname($name.x[1])
a.x[1]
julia> @varname(b.$name.x[1])
b.a.x[1]
AbstractPPL.@vsym
— Macro@vsym(expr)
A macro that returns the variable symbol given the input variable expression expr
. For example, @vsym x[1]
returns :x
.
Examples
julia> @vsym x
:x
julia> @vsym x[1,1][2,3]
:x
julia> @vsym x[end]
:x