A translation of concepts, and for each system what foerster does not have
yet. Throughout, infer is org.replikativ.foerster.core, m
foerster.measure, diagnostics foerster.diagnostics, and a model is a
function returning a spindel spin (language).
Three differences hold for all of them:
:id and
reached by ordinary control flow. There is no vectorized ~ over arrays:
observations are sites in a loop/recur (:id [:y i]), and effects
inside closures passed to map or for are not seen by the spin macro
(the rules).sp/with-context; inside a spin await it.| Stan | foerster |
|---|---|
parameters { real mu; } and mu ~ normal(0, 1); | (sample (dist/normal 0.0 1.0) :id :mu) |
real<lower=0> sigma; with sigma ~ normal(0, 1); | (sample (dist/half-normal 1.0) :id :sigma): the support comes from the distribution |
y ~ normal(mu, sigma); (data) | (observe (dist/normal mu sigma) y :id :y) |
target += lp; | (factor lp) |
transformed parameters, generated quantities | ordinary let bindings; (deterministic v :id :a) records a value in the trace |
log_lik in generated quantities | (diagnostics/pointwise-log-likelihood measure) |
y_rep in generated quantities | (infer/predictive model measure n) |
sample(chains=4) (NUTS) | (infer/infer model {:method :nuts :iterations … :chains 4 :burn …}) on a block site, or :rmh on ordinary sites |
| R-hat, ESS, MCSE in the summary | (diagnostics/summary measure :mu) — the same rank-normalized split R-hat and bulk/tail ESS |
| bridge sampling (evidence) | (m/log-marginal measure) of an SMC or importance-sampling run |
| ADVI | {:method :bbvi}: mean-field, score-function gradients |
optimize(), laplace() | optimize/map-estimate, optimize/laplace on a block site |
Not in foerster yet:
{:method :nuts}, with Stan's
step-size and diagonal-metric adaptation) and HMC run on numerical blocks
whose gradient is hand-written or compiled by raster; constrained
latents (:positive, [:interval a b]) are transformed automatically
when the block is written in natural coordinates, but there is no
simplex or correlation-matrix transform yet.| PyMC | foerster |
|---|---|
with pm.Model(): | (defn model [data] (spin …)) |
pm.Normal("mu", 0, 1) | (sample (dist/normal 0.0 1.0) :id :mu) |
pm.Normal("y", mu, sigma, observed=y) | (observe (dist/normal mu sigma) y :id :y), one per datum |
pm.Potential("p", lp) | (factor lp) |
pm.Deterministic("d", x) | (deterministic x :id :d) |
pm.sample(chains=4) | (infer/infer model {:method :rmh :chains 4 …}) |
pm.sample_smc() | {:method :tempered} — also tempered SMC; or {:method :smc} in program order |
pm.sample_prior_predictive() | (gfi/run-policy model (trace/policy {:simulate-observed? true})) (workflow) |
pm.sample_posterior_predictive(idata) | (infer/predictive model measure n) |
az.summary, az.rhat, az.ess | diagnostics/summary, rhat, ess-bulk, ess-tail, mcse |
az.loo, az.waic, az.compare | diagnostics/loo, waic, compare |
pm.find_MAP() | optimize/map-estimate on a block site; optimize/laplace for the Gaussian around it |
pm.do(model, {"x": 1}) | {:policy (trace/policy {:interventions {:x {:do 1}}})} on a particle method |
Not in foerster yet: automatic gradients (NUTS runs on blocks with a supplied or raster-compiled gradient), simplex and other matrix transforms, ADVI with reparameterization gradients, Gaussian-process and other random-process building blocks, and vectorized distributions over arrays.
| Turing | foerster |
|---|---|
@model function f(y) … end | (defn f [y] (spin …)) |
mu ~ Normal(0, 1) | (sample (dist/normal 0.0 1.0) :id :mu) |
y ~ Normal(mu, 1) with y an argument | (observe (dist/normal mu 1.0) y :id :y) |
@addlogprob! lp | (factor lp) |
model \| (; mu = 0.3) | the particle methods' :policy (trace/policy {:constraints {:mu 0.3}}), or gfi/generate |
sample(m, MH(), n) | {:method :mh :iterations n} |
sample(m, SMC(), n), PG(n), IS() | {:method :smc}, {:method :pgibbs}, {:method :importance} |
Gibbs(:a => HMC(…), :b => PG(…)) | k/block-gibbs-kernel, or kernels composed by k/cycle / k/mixture (algorithms) |
MCMCThreads(), 1000, 4 | :chains 4 |
predict(m_missing, chain) | (infer/predictive model measure n) |
generated_quantities | deterministic sites, or infer/query with a function of the value |
pointwise_loglikelihoods | diagnostics/pointwise-log-likelihood |
| MCMCChains summary | diagnostics/summary |
maximum_a_posteriori(m) | optimize/map-estimate on a block site |
Not in foerster yet: automatic differentiation of the model (NUTS runs on
blocks), Bijectors beyond log and interval transforms, particle Gibbs as a component of a Gibbs sampler
(k/cycle and k/mixture compose Markov-chain kernels only: MH, block
Gibbs and HMC). Conditioning from
outside (|) works for the particle methods and the generative function
interface; the Markov-chain methods take no :policy, so write the
observation into the model.
foerster implements Gen's generative function interface over its traces
(foerster.gfi). Each operation is a CPS operation: await it in a spin.
| Gen | foerster |
|---|---|
@gen function f() … end | (defn f [] (spin …)) |
{:x} ~ normal(0, 1) | (sample (dist/normal 0.0 1.0) :id :x) |
{:x => i => :y} ~ … | :id [:x i :y], or with-scope (addresses) |
{:sub} ~ g() | (await (g)) inside with-scope [:sub] |
choicemap((:x, 1.0)) | a map {:x 1.0} |
simulate(f, args) | (gfi/simulate (f)) |
generate(f, args, constraints) | (gfi/generate (f) constraints) → {:trace :weight} |
assess(f, args, choices) | (gfi/assess (f) choices) |
update(tr, args, argdiffs, constraints) | (gfi/update tr constraints) → {:trace :weight :discard} |
regenerate(tr, args, argdiffs, sel) | (gfi/regenerate tr selection) |
select(:x) | #{:x}, or a spindel selector (select/id, select/path, select/prefix) |
mh(tr, sel) | (gfi/mh tr selection) → {:trace :accepted?} |
mh(tr, proposal, args, involution) | (involutive/step tr {:propose :log-q :involution}) |
get_choices, get_retval, get_score | trace/choices, (:trace/result t), trace/log-joint |
importance_sampling, particle_filter_* | infer/importance-sampling, infer/smc-infer, smc/stream |
| SMCP3 | smc-infer with :anchors and :smcp3 |
Traces hold worlds: give them back with gfi/close!, and after update or
regenerate release the one you do not keep (spindel.trace/release!).
Not in foerster yet:
update and regenerate cannot change them, and there
are no argdiffs for incremental re-execution.Map, Unfold, Recurse, Switch) and the static
modeling language. Replay restarts from the earliest changed site; the
design for incremental re-execution through spindel's spin reuse is in
design.mh(tr, proposal, args) with a proposal program is
gfi/mh-proposal; involutive moves that add or remove sites —
reversible jump — are involutive/step with :removed.)@param, train!) and amortized training
of guides. A model's few parameters fit by maximum marginal likelihood
(learn/maximize-evidence); foerster.learn exports trajectories as
training data for guides, which train elsewhere (finetune-rstr).propose, project and trace translators (enumeration is
{:method :enumerate}).foerster keeps Anglican's design — sample and observe as checkpoints of
a continuation-passing program, inference as a handler — and most of its
algorithms.
| Anglican | foerster |
|---|---|
(defquery q [data] …) | (defn q [data] (spin …)) |
(defm f [x] …) | (defn f [x] (spin …)), called with (await (f x)) |
(sample (normal 0 1)) | (sample (dist/normal 0.0 1.0) :id :mu) — name sites you will read |
(observe (normal mu 1) y) | (observe (dist/normal mu 1.0) y) |
(predict :mu mu) | the program's return value, or (deterministic mu :id :mu) |
(store …), (retrieve …) | state in the particle's world (spindel atoms and signals fork with it) |
(doquery :smc q [data] :number-of-particles 1000) | (infer/infer (q data) {:method :smc :particles 1000}) |
:importance, :smc, :pimh, :pgibbs, :pgas, :ipmcmc, :bbvi | the same keywords for infer/infer |
:lmh | {:method :mh} |
:pcascade | (cascade/cascade model n opts) |
(gamma shape rate) | (dist/gamma shape scale): scale, not rate |
with-primitive-procedures | not needed: a model calls any Clojure function |
doquery returns a lazy, unbounded sequence of samples; infer/infer
resolves one finite measure.
Not in foerster yet:
process/mem
and process/crp-draw exist; Dirichlet and Gaussian processes are built
on them by hand). Nested inference is infer/conditional.map and reduce inside a query; in foerster an observe inside a
closure passed to map is not seen by the macro — use loop/recur.:almh (adaptive site choice), and an anytime lazy stream of samples.| WebPPL | foerster |
|---|---|
var model = function() { … } | (defn model [] (spin …)) |
sample(Gaussian({mu: 0, sigma: 1})) | (sample (dist/normal 0.0 1.0) :id :mu) |
observe(Gaussian({mu, sigma: 1}), y) | (observe (dist/normal mu 1.0) y) |
factor(s) | (factor s) |
condition(b) | (factor (if b 0.0 ##-Inf)) |
Infer({method: 'SMC', particles: 1000, model}) | (infer/infer (model) {:method :smc :particles 1000}) |
Infer({method: 'MCMC', kernel: 'MH'}) | {:method :mh} |
Infer({method: 'forward'}) | gfi/simulate, or {:method :importance} on a model without data |
expectation(d, f) | (:mean (infer/query measure f)) |
d.normalizationConstant | (m/log-marginal measure) |
WebPPL's sources include an asynchronous particle filter and particle MCMC, prior art for foerster's cascade and particle-MCMC methods (literature).
'enumerate' is {:method :enumerate}, mem is process/mem, and a
nested Infer (a posterior used as a distribution inside another model) is
infer/conditional. Not in foerster yet: rejection sampling, incremental
MH, and variational inference with guide programs and trainable parameters
('optimize'); foerster's BBVI learns a mean-field guide only.
:world-policy :fork; worlds).Can you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |