Probabilistic programs as traced computations.
sample, observe and factor publish savepoints (sites
:inference/choose and :inference/factor), so a probabilistic program is
run and replayed by spindel.trace like any other computation. This
namespace adds what is specific to inference and nothing else: a policy that
scores, pure functions over scored traces, and Metropolis-Hastings as replay
plus an accept step.
Every entry's :note carries
:dist the site's distribution (nil for a factor) :log-prob log density of the entry's value under :dist, for EVERY site, sampled ones included (a factor's weight) :log-proposal log density under whatever drew the value; absent when the value was not drawn (observed, constrained, kept) :observed? :constrained? :kept? :symmetric? :factor?
A deterministic site is recorded with {:deterministic? true} only; it is
not among entries, so it neither scores nor moves.
and the world accumulates the importance weight at [:inference :log-weight]: log p of what was observed, constrained or factored, and
log p - log q of what a proposal drew. A policy writes it to the world it
is about to resume, so the weight of a fork starts from the weight at its
site.
Probabilistic programs as traced computations.
`sample`, `observe` and `factor` publish savepoints (sites
`:inference/choose` and `:inference/factor`), so a probabilistic program is
run and replayed by `spindel.trace` like any other computation. This
namespace adds what is specific to inference and nothing else: a policy that
scores, pure functions over scored traces, and Metropolis-Hastings as replay
plus an accept step.
Every entry's `:note` carries
:dist the site's distribution (nil for a factor)
:log-prob log density of the entry's value under :dist, for EVERY
site, sampled ones included (a factor's weight)
:log-proposal log density under whatever drew the value; absent when the
value was not drawn (observed, constrained, kept)
:observed? :constrained? :kept? :symmetric? :factor?
A `deterministic` site is recorded with `{:deterministic? true}` only; it is
not among `entries`, so it neither scores nor moves.
and the world accumulates the importance weight at `[:inference
:log-weight]`: `log p` of what was observed, constrained or factored, and
`log p - log q` of what a proposal drew. A policy writes it to the world it
is about to resume, so the weight of a fork starts from the weight at its
site.(anchor? sp)Whether a site is worth an anchor: only a site a move can start from. An observe or a factor is never replayed from, and an anchor is a forked world. Once a keyed region has been entered, sites take no anchors: children can still be in flight, and old downstream checkpoints would be invalidated by later key updates. Independent keys use reexecute; a broader change needs a valid upstream anchor. This also covers nested child spins. Pass as :anchor? to trace/run and trace/replay.
Whether a site is worth an anchor: only a site a move can start from. An observe or a factor is never replayed from, and an anchor is a forked world. Once a keyed region has been entered, sites take no anchors: children can still be in flight, and old downstream checkpoints would be invalidated by later key updates. Independent keys use reexecute; a broader change needs a valid upstream anchor. This also covers nested child spins. Pass as :anchor? to trace/run and trace/replay.
(as-draw d)A policy :draw, (fn [sp old-entry]), from a foerster.proposal (or
d itself when it is already such a function). A proposal's :log-q
becomes the draw's :log-proposal; when it moves a site that had a value
and is not the prior, its density of proposing that old value back,
:reverse-log-proposal, is what an MH move scores the reverse with
(mh-log-ratio).
A policy `:draw`, `(fn [sp old-entry])`, from a `foerster.proposal` (or `d` itself when it is already such a function). A proposal's `:log-q` becomes the draw's `:log-proposal`; when it moves a site that had a value and is not the prior, its density of proposing that old value back, `:reverse-log-proposal`, is what an MH move scores the reverse with (`mh-log-ratio`).
(barrier-site? sp)Whether a savepoint is where SMC parks a particle: an observation, a
stream site, or a factor marked :barrier.
Whether a savepoint is where SMC parks a particle: an observation, a stream site, or a factor marked `:barrier`.
(canonical-anchor? sp)anchor? for a run in canonical worlds: also the start site, so a move
can re-execute the model from its start when no later anchor precedes
its sites (a keyed model, whose sites take no anchors).
`anchor?` for a run in canonical worlds: also the start site, so a move can re-execute the model from its start when no later anchor precedes its sites (a keyed model, whose sites take no anchors).
(canonical? trace)Whether trace runs in canonical worlds (:world-policy :fork): forks
of a caller's world whose state the model may read and write.
Whether `trace` runs in canonical worlds (`:world-policy :fork`): forks of a caller's world whose state the model may read and write.
(changed-addresses old new)Addresses rewritten, added or removed. Released Spindel 0.1.140 does not report :trace/removed, so deletion accounting conservatively scans keys.
Addresses rewritten, added or removed. Released Spindel 0.1.140 does not report :trace/removed, so deletion accounting conservatively scans keys.
(choices trace){address value} of the sample sites of trace.
{address value} of the sample sites of `trace`.
(copy-complete t opts)Fork a settled end-world without re-running its effects. The caller owns shared anchors and must keep every live population reachable until release.
Fork a settled end-world without re-running its effects. The caller owns shared anchors and must keep every live population reachable until release.
(entries trace)The inference entries of trace in program order, each with its :address.
The inference entries of `trace` in program order, each with its :address.
(irreversible-redraw? old-by-address new-entries)Whether new redrew a kept value (:redrawn?) that the reverse move
would keep instead: the new value lies inside the OLD site's support, so
the old state cannot be reached back. old-by-address maps addresses to
old's entries.
Whether `new` redrew a kept value (`:redrawn?`) that the reverse move would keep instead: the new value lies inside the OLD site's support, so the old state cannot be reached back. `old-by-address` maps addresses to `old`'s entries.
(latent-addresses trace)Addresses of the sample sites of trace that inference may move.
Addresses of the sample sites of `trace` that inference may move.
(latent-count trace)How many latent sites trace has.
How many latent sites `trace` has.
(latent-index trace){:addresses [address …] :position {address i}} of the latent sites of
trace: carried by a trace an incremental move made (patched with what
the move changed), built from the entries otherwise.
{:addresses [address …] :position {address i}} of the latent sites of
`trace`: carried by a trace an incremental move made (patched with what
the move changed), built from the entries otherwise.(latent? entry)Whether a trace entry is a sample site inference may move: not observed, not constrained.
Whether a trace entry is a sample site inference may move: not observed, not constrained.
(legacy-trace trace)trace in the legacy shape of a particle's trace ([:inference :trace]):
{address {:value :distribution :log-prob :observed?}}, and
{address {:value :deterministic? true}} for deterministic sites.
`trace` in the legacy shape of a particle's trace (`[:inference :trace]`):
{address {:value :distribution :log-prob :observed?}}, and
{address {:value :deterministic? true}} for deterministic sites.(log-joint trace)log p(choices, observations) of trace: the sum of every entry's
:log-prob.
log p(choices, observations) of `trace`: the sum of every entry's :log-prob.
(log-weight trace)The importance weight the world of trace accumulated.
The importance weight the world of `trace` accumulated.
(mark-canonical! world)Mark world, a fork of the caller's world in which a model is about to
run (:world-policy :fork), as canonical and record its cells as the
base wrote-state? compares a run's cells with.
Mark `world`, a fork of the caller's world in which a model is about to run (`:world-policy :fork`), as canonical and record its cells as the base `wrote-state?` compares a run's cells with.
(mh-chain trace n)(mh-chain trace
n
{:keys [on-step first-iteration iteration-stride]
move :step
:or {move mh-step iteration-stride 1}
:as opts})n Metropolis-Hastings moves from trace; opts as for mh-step.
Returns a CPS operation resolving {:trace final :moves m :accepted k}: of
the m moves made, k were accepted. :on-step (fn [step-result]) sees every
step. :step (fn [trace opts]) -> CPS resolving a step result replaces
mh-step (e.g. foerster.moves.hmc/within-gibbs); a step that makes several
moves reports :moves and :accepted-moves, otherwise it is one move,
accepted when :accepted?. A move's randomness is keyed by its number in
the trace's world, so the moves made from one world need distinct numbers:
:iteration-stride (default 1) reserves that many RNG indices per outer
iteration, for composed transitions. They are numbered from
:first-iteration, or — when it is not given —
from a counter the world keeps, so calling mh-chain again on the same
trace (after a rejection, the world is the same) draws afresh.
`n` Metropolis-Hastings moves from `trace`; `opts` as for `mh-step`.
Returns a CPS operation resolving {:trace final :moves m :accepted k}: of
the m moves made, k were accepted. `:on-step` (fn [step-result]) sees every
step. `:step` (fn [trace opts]) -> CPS resolving a step result replaces
`mh-step` (e.g. `foerster.moves.hmc/within-gibbs`); a step that makes several
moves reports `:moves` and `:accepted-moves`, otherwise it is one move,
accepted when `:accepted?`. A move's randomness is keyed by its number in
the trace's world, so the moves made from one world need distinct numbers:
:iteration-stride (default 1) reserves that many RNG indices per outer
iteration, for composed transitions. They are numbered from
`:first-iteration`, or — when it is not given —
from a counter the world keeps, so calling `mh-chain` again on the same
trace (after a rejection, the world is the same) draws afresh.(mh-log-ratio old new log-selection)(mh-log-ratio old new log-selection from)(mh-log-ratio old new log-selection from score)(mh-log-ratio old new log-selection from score changed)Log acceptance ratio of moving from old to new, where new is old
replayed from its earliest target with kept values elsewhere.
log a = [log p(new) - log p(old)] + [log q(old | new) - log q(new | old)] + [log s(targets | new) - log s(targets | old)]
q(new | old) is the density of everything new drew afresh; q(old | new) is
the density of everything of old that new did not keep, which the
reverse move would have to draw (from the prior, hence its :log-prob, or a
block's :sample density). A
symmetric move cancels on both sides. s is the probability of selecting the
targets, log-selection; it differs between the traces when the move
changed how many sites there are to select from.
A kept value that fell out of its site's support is drawn again
(:redrawn?). That is reversible only if the reverse move would redraw there
too, i.e. if the new value is outside the OLD support; otherwise the reverse
keeps it, the old state cannot be reached back, and the move is impossible.
Entries upstream of the replayed address are the SAME entries in both
traces and cancel everywhere. That relies on a fork sharing the notes of its
source by reference, which holds for the in-process worlds a session forks
and would not survive a serialized trace. With from, the replayed
address, only the entries from it on are compared: the ratio is the same,
and it costs the replayed suffix instead of the whole trace. score
(fn [entry]) is an entry's log target (default its :log-prob), e.g.
tempered-score. With changed, the addresses an update rewrote, added
or removed (changed-addresses), only those are compared: every other
entry is the same in both and cancels.
Log acceptance ratio of moving from `old` to `new`, where `new` is `old`
replayed from its earliest target with kept values elsewhere.
log a = [log p(new) - log p(old)]
+ [log q(old | new) - log q(new | old)]
+ [log s(targets | new) - log s(targets | old)]
q(new | old) is the density of everything `new` drew afresh; q(old | new) is
the density of everything of `old` that `new` did not keep, which the
reverse move would have to draw (from the prior, hence its :log-prob, or a
block's :sample density). A
symmetric move cancels on both sides. s is the probability of selecting the
targets, `log-selection`; it differs between the traces when the move
changed how many sites there are to select from.
A kept value that fell out of its site's support is drawn again
(`:redrawn?`). That is reversible only if the reverse move would redraw there
too, i.e. if the new value is outside the OLD support; otherwise the reverse
keeps it, the old state cannot be reached back, and the move is impossible.
Entries upstream of the replayed address are the SAME entries in both
traces and cancel everywhere. That relies on a fork sharing the notes of its
source by reference, which holds for the in-process worlds a session forks
and would not survive a serialized trace. With `from`, the replayed
address, only the entries from it on are compared: the ratio is the same,
and it costs the replayed suffix instead of the whole trace. `score`
(fn [entry]) is an entry's log target (default its :log-prob), e.g.
`tempered-score`. With `changed`, the addresses an update rewrote, added
or removed (`changed-addresses`), only those are compared: every other
entry is the same in both and cancels.(mh-step t)(mh-step t
{:keys [select propose iteration constraints policy-options temperature
keep-old? shared-anchors? until]
:or {select uniform-site propose proposal/prior iteration 0}
:as opts})One MH move using keyed re-execution for complete independent regions and anchored replay otherwise. :select / :propose / :constraints / :temperature retain their usual meaning. :keep-old? and :shared-anchors? are ownership policies; :until denotes an anchored replay frontier.
One MH move using keyed re-execution for complete independent regions and anchored replay otherwise. :select / :propose / :constraints / :temperature retain their usual meaning. :keep-old? and :shared-anchors? are ownership policies; :until denotes an anchored replay frontier.
(policy)(policy {fallback :else :as opts})The scoring policy.
Options:
:constraints {address value} fix those sample sites; their density enters
the weight
:keep? reuse the value a sample site had in the replayed trace,
rescored under the site's distribution NOW. A site that was
constrained stays constrained. A latent value that fell
outside its site's new support is drawn again (:redrawn?,
see mh-log-ratio), unless :out-of-support is :keep
:out-of-support :keep — with :keep?, keep such a value anyway, scored
−∞ (Gen's update: the new trace has density zero)
:draw (fn [sp old-entry]) -> nil (not my site) or
{:value v :log-proposal lq}, or {:value v :symmetric? true}
for a symmetric move around the old value
:interventions {selector transform}: a selected sample site takes
{:do v} (that value, no score), {:policy (fn [choices])}
(a value from the choices made so far), {:dist d} (a new
mechanism) or {:shift δ} (the old one moved by δ). A key
that is not a selector (spindel.select) is an address; the
world's [:inference :interventions] (intervene!) apply too
Mechanism replacements preserve factual noise by default;
:coupling :fresh explicitly introduces independent noise.
:noise {address u}: the site takes its mechanism's value for u
(foerster.model.mechanism), observed sites included — a
counterfactual world. Sites without noise are marked
:unaligned?
:strict? with :noise, refuse a site without compatible factual noise;
otherwise draw fresh, including observed sites
:simulate-observed? true: observed sites draw a fresh value from their
distribution instead of scoring the data (predictive runs:
with :constraints holding a posterior draw's latents)
:temperature β in [0, 1]: observations and factors count β·log p — the
target p(x)·L(x)^β of tempered SMC (foerster.inference.tempering).
Their notes keep :log-lik, the untempered log p
:init? start a sample site at its :init option. Only for the
first state of a Markov chain, which may be anything; an
:init value is not a draw, so it has no place in a move or
in a weighted sample.
:else policy for every other site (default: its payload)
With no options every sample site is drawn from its prior: forward simulation, likelihood weighting.
Every choose entry's note records :barrier, its barrier index (see
barrier-site?); policy-options recovers the options.
The scoring policy.
Options:
:constraints {address value} fix those sample sites; their density enters
the weight
:keep? reuse the value a sample site had in the replayed trace,
rescored under the site's distribution NOW. A site that was
constrained stays constrained. A latent value that fell
outside its site's new support is drawn again (`:redrawn?`,
see `mh-log-ratio`), unless :out-of-support is :keep
:out-of-support :keep — with :keep?, keep such a value anyway, scored
−∞ (Gen's `update`: the new trace has density zero)
:draw (fn [sp old-entry]) -> nil (not my site) or
{:value v :log-proposal lq}, or {:value v :symmetric? true}
for a symmetric move around the old value
:interventions {selector transform}: a selected sample site takes
`{:do v}` (that value, no score), `{:policy (fn [choices])}`
(a value from the choices made so far), `{:dist d}` (a new
mechanism) or `{:shift δ}` (the old one moved by δ). A key
that is not a selector (`spindel.select`) is an address; the
world's `[:inference :interventions]` (`intervene!`) apply too
Mechanism replacements preserve factual noise by default;
:coupling :fresh explicitly introduces independent noise.
:noise {address u}: the site takes its mechanism's value for u
(`foerster.model.mechanism`), observed sites included — a
counterfactual world. Sites without noise are marked
`:unaligned?`
:strict? with :noise, refuse a site without compatible factual noise;
otherwise draw fresh, including observed sites
:simulate-observed? true: observed sites draw a fresh value from their
distribution instead of scoring the data (predictive runs:
with `:constraints` holding a posterior draw's latents)
:temperature β in [0, 1]: observations and factors count β·log p — the
target p(x)·L(x)^β of tempered SMC (`foerster.inference.tempering`).
Their notes keep `:log-lik`, the untempered log p
:init? start a sample site at its `:init` option. Only for the
first state of a Markov chain, which may be anything; an
`:init` value is not a draw, so it has no place in a move or
in a weighted sample.
:else policy for every other site (default: its payload)
With no options every sample site is drawn from its prior: forward
simulation, likelihood weighting.
Every choose entry's note records `:barrier`, its barrier index (see
`barrier-site?`); `policy-options` recovers the options.(policy-options policy)The options a policy was made from, or nil for another function.
The options a `policy` was made from, or nil for another function.
(prior-draw sp _old-entry)A policy :draw of a fresh value from a site's own distribution (the
savepoint-level form of foerster.proposal/prior, for internal policies).
A policy `:draw` of a fresh value from a site's own distribution (the savepoint-level form of `foerster.proposal/prior`, for internal policies).
(redraw-log-density note value)log of the density with which a replay draws value afresh at a site
whose note is note (decide-choose): the site's own :proposal if it has
one, else its law's draw density (a block's :sample), else its law. This is
what a reverse move pays for a site it would have to draw.
log of the density with which a replay draws `value` afresh at a site whose note is `note` (`decide-choose`): the site's own `:proposal` if it has one, else its law's draw density (a block's :sample), else its law. This is what a reverse move pays for a site it would have to draw.
(reexecute t targets move opts)Rewrite selected sites through one execution interface. Complete independent keyed regions re-run their owning keys; other moves replay from the latest valid preceding anchor. Incremental parent-side inference effects and keyed streaming frontiers are refused unless safe prefix replay is available. Result metadata ::changed / ::restart describes the score delta to compare.
Incremental reuse (a keyed update; a replay adopting the replayed world's
finished computations) is used only when the run wrote no world state
(wrote-state?), under either world policy. A move on a run that wrote
state replays from the latest anchor before its sites (the world as it was
at that site) without adoption; in canonical worlds a keyed model
re-executes from its start site (canonical-anchor?). A keyed update or
adopting replay whose own run writes state is given back and replayed
without reuse.
Rewrite selected sites through one execution interface. Complete independent keyed regions re-run their owning keys; other moves replay from the latest valid preceding anchor. Incremental parent-side inference effects and keyed streaming frontiers are refused unless safe prefix replay is available. Result metadata ::changed / ::restart describes the score delta to compare. Incremental reuse (a keyed update; a replay adopting the replayed world's finished computations) is used only when the run wrote no world state (`wrote-state?`), under either world policy. A move on a run that wrote state replays from the latest anchor before its sites (the world as it was at that site) without adoption; in canonical worlds a keyed model re-executes from its start site (`canonical-anchor?`). A keyed update or adopting replay whose own run writes state is given back and replayed without reuse.
(replay-sound trace address policy opts)(replay-sound trace address policy opts reuse?)trace/replay of trace from address, adopting finished computations
from trace's world only when that is sound: neither trace's run nor the
replay wrote world state (wrote-state?). A replay that adopted and then
wrote state is given back and run again without adoption. reuse? false
never adopts.
`trace/replay` of `trace` from `address`, adopting finished computations from `trace`'s world only when that is sound: neither `trace`'s run nor the replay wrote world state (`wrote-state?`). A replay that adopted and then wrote state is given back and run again without adoption. `reuse?` false never adopts.
(site-law policy sp)How policy (made by policy) decides the latent sample site sp, for a
driver that sums over a site's values instead of drawing one
(foerster.inference.enumerate): {:fixed? true} when the policy sets the value
without drawing it — a {:do v} or {:policy f} intervention (the
policy's own or the world's, intervene!) selects the site, or a
constraint names its address — else {:dist d}, the law the value is
drawn from: the site's own, or the one a {:dist d} or {:shift δ}
intervention puts in its place. Deciding the site under the policy with
{address v} added to its constraints then scores v under that law. The
precedence is the policy's (decide-choose). A mechanism replacement with
:coupling :fresh draws whatever the constraints say, and a
counterfactual policy (:noise) predicts rather than scores: neither has
a law to sum over, so both are refused.
How `policy` (made by `policy`) decides the latent sample site `sp`, for a
driver that sums over a site's values instead of drawing one
(`foerster.inference.enumerate`): `{:fixed? true}` when the policy sets the value
without drawing it — a `{:do v}` or `{:policy f}` intervention (the
policy's own or the world's, `intervene!`) selects the site, or a
constraint names its address — else `{:dist d}`, the law the value is
drawn from: the site's own, or the one a `{:dist d}` or `{:shift δ}`
intervention puts in its place. Deciding the site under the policy with
`{address v}` added to its constraints then scores v under that law. The
precedence is the policy's (`decide-choose`). A mechanism replacement with
`:coupling :fresh` draws whatever the constraints say, and a
counterfactual policy (`:noise`) predicts rather than scores: neither has
a law to sum over, so both are refused.(target-temperature opts)Effective target beta: explicit context, then policy options, then one.
Effective target beta: explicit context, then policy options, then one.
(tempered-score beta)The log target of a trace entry at temperature beta, for entries recorded
under any temperature: observations and factors by their :log-lik, a
site drawn from its own draw density (a block's :sample) by that density
plus β times the rest of its target (see decide-choose).
The log target of a trace entry at temperature `beta`, for entries recorded under any temperature: observations and factors by their `:log-lik`, a site drawn from its own draw density (a block's :sample) by that density plus β times the rest of its target (see `decide-choose`).
(uniform-site trace _iteration)Select one latent site uniformly. A selection is {:targets #{address} :log-selection (fn [trace])}.
Select one latent site uniformly. A selection is
{:targets #{address} :log-selection (fn [trace])}.(update-gibbs t address)(update-gibbs t
address
{:keys [iteration constraints policy-options keep-old?]
:as opts
:or {iteration 0}})Exact finite-support Gibbs on complete traces with fixed site structure. Every other latent must stay fixed with no auxiliary redraws. Candidates violating this contract are refused; they are not an approximate Gibbs move. :temperature, target policy and ownership follow mh-step.
Exact finite-support Gibbs on complete traces with fixed site structure. Every other latent must stay fixed with no auxiliary redraws. Candidates violating this contract are refused; they are not an approximate Gibbs move. :temperature, target policy and ownership follow mh-step.
(update-step t)(update-step t opts)Incremental-aware MH, also used by the ordinary shared transition path.
Incremental-aware MH, also used by the ordinary shared transition path.
(with-proposal {p :proposal pol :policy :as opts})opts with its :proposal (a foerster.proposal) made its :policy's
:draw: the particle methods' :proposal option. The prior needs no
policy. A policy that is not made by policy, or that has its own
:draw, cannot take one, and is refused.
`opts` with its `:proposal` (a `foerster.proposal`) made its `:policy`'s `:draw`: the particle methods' `:proposal` option. The prior needs no policy. A policy that is not made by `policy`, or that has its own `:draw`, cannot take one, and is refused.
(world-legacy-trace world)legacy-trace of the savepoint trace world has recorded so far, kept up
to date incrementally in the world itself: a run that asks at every site
converts each entry once instead of the whole trace each time. The cache
forks with the world, as the trace does; it is rebuilt if the trace no
longer extends the order it was built from. It assumes entries are only
appended, as in a forward run (the site kernels of kernel-infer); a
replay that rewrites entries in place must use legacy-trace.
`legacy-trace` of the savepoint trace `world` has recorded so far, kept up to date incrementally in the world itself: a run that asks at every site converts each entry once instead of the whole trace each time. The cache forks with the world, as the trace does; it is rebuilt if the trace no longer extends the order it was built from. It assumes entries are only appended, as in a forward run (the site kernels of `kernel-infer`); a replay that rewrites entries in place must use `legacy-trace`.
(wrote-state? trace)Whether the run that produced trace wrote world state (see above): a
Spindel atom whose value is not the identical value it had at the run's
base, or one the run created. A trace without a world counts as having
written.
Whether the run that produced `trace` wrote world state (see above): a Spindel atom whose value is not the identical value it had at the run's base, or one the run created. A trace without a world counts as having written.
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 |