Skip to content

Commit

Permalink
Adopt DensityInterface.jl (#41)
Browse files Browse the repository at this point in the history
  • Loading branch information
phipsgabler committed Nov 28, 2021
1 parent d730194 commit 5e8f1a3
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 23 deletions.
4 changes: 3 additions & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ uuid = "7a57a42e-76ec-4ea3-a279-07e840d6d9cf"
keywords = ["probablistic programming"]
license = "MIT"
desc = "Common interfaces for probabilistic programming"
version = "0.3.1"
version = "0.4"

[deps]
AbstractMCMC = "80f14c24-f653-4e6a-9b94-39d6b0f70001"
DensityInterface = "b429d917-457f-4dbc-8f4c-0cc954292b1d"
Setfield = "efcf1570-3423-57d1-acb7-fd33fddbac46"

[compat]
AbstractMCMC = "2, 3"
DensityInterface = "0.4"
Setfield = "0.7.1, 0.8"
julia = "1"
44 changes: 30 additions & 14 deletions interface.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,14 @@ There are three interrelating aspects that this interface intends to standardize
- Sampling
- “Conversions” between different conditionings of models

Therefore, the interface consists of:
Therefore, the interface consists of an `AbstractProbabilisticProgram` supertype, together with
functions

- `condition(::Model, ::Trace) -> ConditionedModel`
- `decondition(::ConditionedModel) -> GenerativeModel`
- `sample(::Model, ::Sampler = Exact(), [Int])` (from `AbstractMCMC.sample`)
- `logdensity(::Model, ::Trace)`
- `logdensityof(::Model, ::Trace)` and `densityof(::Model, ::Trace)` (from
[DensityInterface.jl](https://github.com/JuliaMath/DensityInterface.jl))


### Traces & probability expressions
Expand Down Expand Up @@ -198,32 +200,46 @@ Not all variants need to be supported – for example, a posterior model might n
model.


### Density Calculation
### Density Evaluation

Since the different “versions” of how a model is to be understood as generative or conditioned are
to be expressed in the type or dispatch they support, there should be no need for separate functions
`logjoint`, `loglikelihood`, etc., which force these semantic distinctions on the implementor; one
`logdensity` should suffice for all, with the distinction being made by the capabilities of the
concrete model instance.
`logjoint`, `loglikelihood`, etc., which force these semantic distinctions on the implementor; we
therefore adapt the interface of
[DensityInterface.jl](https://github.com/JuliaMath/DensityInterface.jl). Its main function
`logdensityof` should suffice for variants, with the distinction being made by the capabilities of
the concrete model instance.

Note that this generalizes `logpdf`, too, since the posterior density will of course in general be
unnormalized and hence not a probability density.
DensityInterface.jl also requires the trait function `DensityKind`, which is set to `HasDensity()`
for the `AbstractProbabilisticProgram` type. Additional functions

The evaluation will usually work with the internal, concrete trace type, like `VarInfo` in Turing.jl:
```
DensityInterface.densityof(d, x) = exp(logdensityof(d, x))
DensityInterface.logdensityof(d) = Base.Fix1(logdensityof, d)
DensityInterface.densityof(d) = Base.Fix1(densityof, d)
```

are provided automatically (repeated here for clarity).

Note that `logdensityof` strictly generalizes `logpdf`, since the posterior density will of course
in general be unnormalized and hence not a probability density.

The evaluation will usually work with the internal, concrete trace type, like `VarInfo` in
Turing.jl:

```julia
logdensity(m, vi)
logdensityof(m, vi)
```

But the user will more likely work on the interface using probability expressions:

```julia
logdensity(m, @T(X = ...))
logdensityof(m, @T(X = ))
```

(Note that this would replace the current `prob` string macro in Turing.jl.)

Densities need not be normalized.
Densities need (and usually, will) not be normalized.


#### Implementation notes
Expand All @@ -232,15 +248,15 @@ It should be able to make this fall back on the internal method with the right d
implementation of `maketrace`:

```julia
logdensity(m, t::ProbabilityExpression) = logdensity(m, maketrace(m, t))
logdensityof(m, t::ProbabilityExpression) = logdensityof(m, maketrace(m, t))
```

There is one open question – should normalized and unnormalized densities be able to be
distinguished? This could be done by dispatch as well, e.g., if the caller wants to make sure
normalization:

```
logdensity(g, @T(X = ..., Y = ..., Z = ...); normalized=Val{true})
logdensityof(g, @T(X = , Y = , Z = ); normalized=Val{true})
```

Although there is proably a better way through traits; maybe like for arrays, with
Expand Down
7 changes: 4 additions & 3 deletions src/AbstractPPL.jl
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
module AbstractPPL

# VarName
export VarName, getsym, getindexing, getlens, inspace, subsumes, varname, vsym, @varname, @vsym
export VarName, getsym, getlens, inspace, subsumes, varname, vsym, @varname, @vsym


# Abstract model functions
export AbstractProbabilisticProgram, condition, decondition, logdensity
export AbstractProbabilisticProgram, condition, decondition, logdensityof, densityof


# Abstract traces
export AbstractModelTrace


include("varname.jl")
include("abstractprobprog.jl")
include("abstractmodeltrace.jl")
include("abstractprobprog.jl")
include("deprecations.jl")

end # module
11 changes: 7 additions & 4 deletions src/abstractprobprog.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using AbstractMCMC
using DensityInterface


"""
Expand All @@ -8,19 +9,21 @@ Common base type for models expressed as probabilistic programs.
"""
abstract type AbstractProbabilisticProgram <: AbstractMCMC.AbstractModel end

DensityInterface.DensityKind(::AbstractProbabilisticProgram) = HasDensity()


"""
logdensity(model, trace)
logdensityof(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.
`logdensity` should interact with conditioning and deconditioning in the way required by probability
theory.
`logdensityof` should interact with conditioning and deconditioning in the way required by
probability theory.
"""
function logdensity end
DensityInterface.logdensityof(::AbstractProbabilisticProgram, ::AbstractModelTrace)


"""
Expand Down
1 change: 0 additions & 1 deletion src/varname.jl
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,6 @@ julia> getlens(@varname(y))
"""
getlens(vn::VarName) = vn.lens

@deprecate getindexing(vn::VarName) getlens(vn)

"""
get(obj, vn::VarName{sym})
Expand Down

0 comments on commit 5e8f1a3

Please sign in to comment.