Liking cljdoc? Tell your friends :D

Extending

Trace algorithms build on sites, traces and policies; numerical methods require explicit block capabilities. Custom population programs compose weighting, selection, Markov moves and transport under scoped ownership contracts; see Inference Programs. A common result interface does not remove each method's model requirements.

You can extend the system at each level. From least to most involved:

A distribution

Implement dist/Distribution — see distributions.

A block

A group of latents with a log density and its gradient, for HMC — see the blocks notebook and, for compiled densities, foerster-raster.

A policy

A policy decides every site of a run. foerster.runtime.trace/policy builds one from options, and the particle methods take it as :policy (all but BBVI, whose guide decides the latent sites):

OptionEffect
:constraints {address value}fix those sites; their density enters the weight (conditioning)
:interventions {selector-or-address transform}replace a site's mechanism: {:do v} and {:policy (fn [choices])} fix the value and score nothing; {:dist d} and {:shift δ} replace the law, and the site is then decided and scored under it like any other
:draw (fn [savepoint old-entry])a custom proposal for latent sites: return nil (not my site), {:value v :log-proposal lq}, or {:value v :symmetric? true}; the weight gets log p(v) − lq
:keep?reuse the values of the replayed trace, rescored
:noise {address u}counterfactual noise for mechanisms
:strict? true with :noiserefuse a counterfactual site without compatible factual noise
:init?start sites at their :init option (a chain's first state)
:out-of-support :keepwith :keep?, keep a value outside its site's new support, scored −∞ (Gen's update)
:simulate-observed?observed sites draw a fresh value instead of scoring the data (predictive runs)
:temperature βobservations and factors count β·log p (the tempered target p(x)·L(x)^β)
:elsethe policy for every other site (default: its payload)

A policy that only fixes sites (:constraints) or intervenes on them keeps SMC's evidence :unbiased (foerster.contract): PMMH relies on this for its θ. A policy with any other option is the caller's responsibility, and the evidence is :unverified.

;; a proposal that knows where the data is
(infer/importance-sampling (model) 1000
  {:proposal (proposal/only #{:mu} (proposal/distribution (dist/normal 7.0 1.0)))})

A :proposal (foerster.proposal, algorithms) is chosen from outside the program, per inference call; it becomes the policy's :draw, which also takes a raw (fn [savepoint old-entry]) for internal policies. A proposal the program itself computes — from its data, or an amortized guide — is a sample site's :proposal option instead (language); it is weighed the same way, and a :draw that answers for a site takes precedence over it.

An inference program

Inference programs expose scoped initialization, target advancement, resampling, invariant moves and final projection over complete traces or persistent numerical values. Their operations compose inside ordinary spin control flow. They keep population mass separate from selection weights and release owned sessions before returning.

An MH move

(trace/mh-step trace opts) is one Metropolis–Hastings move over a trace; (trace/mh-chain trace n opts) runs n of them. Both are CPS operations.

Option
:select (fn [trace iteration]){:targets #{address} :log-selection (fn [trace])} — which sites to move and the log probability of selecting them (default: one latent uniformly)
:proposea foerster.proposal for the target (default proposal/prior; proposal/random-walk for a random walk); the reverse move is scored with its log-q, so asymmetric proposals are exact
:stepfor mh-chain: a step function replacing mh-step (e.g. hmc/within-gibbs)
:on-stepfor mh-chain: (fn [{:keys [trace accepted?]}]) called after every step
:first-iterationfor mh-chain: the number of its first move; a move's randomness is keyed by its number. Without it the numbers continue from a counter the trace's world keeps, so calling mh-chain again on the same trace draws afresh

A proposal program — (gfi/mh-proposal trace proposal & args) is Gen's metropolis_hastings(trace, proposal, args): proposal is (fn [choices & args]) returning a spin whose sample sites are named by the model addresses it proposes, any number of them, jointly and correlated as the program likes. The move runs the proposal forward, updates the model with what it drew, and scores the reverse move by assessing the proposal on the replaced values under the new choices; accepted on w(update) + log q(old | new) − log q(new | old). The proposal must be able to propose the reverse move.

(defn along-the-ridge [{:keys [x y]}]          ; x and y move together
  (spin (let [x' (sample (dist/normal x 0.6) :id :x)]
          (sample (dist/normal (- y (- x' x)) 0.15) :id :y))))

((gfi/mh-proposal trace along-the-ridge) on-step on-error)  ; {:trace :accepted?}

For moves that are not "redraw these sites" — scaling, swapping, splitting — use involutive MCMC (foerster.moves.involutive/step) and the generative function interface (foerster.runtime.gfi), shown in the programmable inference notebook. An involution may change the model's dimension (reversible jump): it sets the new sites through :choices and names under :removed the sites whose values it moved into the auxiliary variables, as in a split of one component into two and the matching merge (test/…/reversible_jump_test.clj).

A proposal

A proposal (foerster.proposal) draws a latent site's value. from-fn makes one from a function of the site:

(def near-data
  (proposal/from-fn
   (fn [{:keys [address dist old trace]}]   ; old: the value being moved, if any
     (when (= :mu address)                  ; nil: the site's own law decides
       (let [q (dist/normal 7.0 1.0) v (dist/draw q)]
         {:value v :log-q (dist/logpdf q v)})))
   ;; optional: the density of proposing v, which an MH move scores its
   ;; reverse with
   (fn [_ v] (dist/logpdf (dist/normal 7.0 1.0) v))))

(infer/smc-infer (model) 100 {:proposal near-data})

(:trace site) is a delay of the particle's choices so far. Implementing PProposal (propose, log-q, verified?) directly works as well; verified? says whether log-q is the density propose draws from by construction, which decides whether SMC's evidence is :unbiased.

A proposal is not a Markov kernel. Markov-chain descriptions implement PMarkovKernel (transition-id) and are compiled by foerster.moves.transition. They are combined with k/cycle and k/mixture (algorithms).

A kernel

k/kernel makes a Markov kernel of your own from a step over traces. It composes with the built-in kernels, runs as chains (kernel-infer), and rejuvenates SMC (:rejuvenate {:kernel k}):

(def mine
  (k/kernel 1000
            (fn [t context]                     ; context: target, replay frontier,
              (trace/mh-step t (assoc context   ; ownership, :iteration
                                      :select my-select
                                      :propose (proposal/random-walk 0.3))))
            {:retains? true :samples :all :burn 100}))

(infer/kernel-infer (model) (k/cycle 500 [mine (k/single-site-mh-kernel 1)]) 4)

Passing context on to mh-step keeps the target, the replay frontier (SMC's moves stop at the particle's barrier) and the ownership options; declare :retains? true when it does, and :tempering? true when the step targets a temperature below one. Leaving the target invariant is your obligation: a result whose moves include your kernel says so (:unverified evidence).

Learned twists and proposals

A steered program (foerster.inference.steer/model) records each step's state and its reward in the trace, and foerster.amortized.learn reads them back as training data: (learn/trajectories measure) gives every particle's {:states :reward :weight :log-weight}, and (learn/draws measure n) resamples n of them by weight into unweighted draws from the target. A value estimate trained on them is the next run's :value; a proposal trained on them returns its steps as (steer/weighted state log-w), with log-w = log p − log q of the step, so the target does not change (algorithms). Training itself is outside foerster.

An algorithm

Every algorithm here is a handler of spindel savepoints: open a session on a world, install handlers for :inference/choose and :inference/factor, and resume, fork, copy or abandon the savepoints they receive. foerster.inference.smc is the reference; the design guide explains the structure and spindel's savepoints the primitive.

Can you improve this documentation?Edit on GitHub

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
←Move to previous article
→Move to next article
Ctrl+/Jump to the search field
× close