Numerical blocks as choice sites (the spindel side of the spindel ↔ raster block contract, spindel-raster doc/contract.md).
A block is a fixed-shape group of latent variables with the density factors they touch, given as a description (data) and capabilities (functions over primitive arrays):
(block {:block/id :gauss :block/latents [{:name :mu :shape [2] :support :real}] :block/target :complete-conditional} {:log-density (fn [^doubles theta inputs] lp) :value+grad (fn [^doubles theta inputs] [lp ^doubles grad])})
(block-dist b inputs) is the block at its inputs as a distribution whose
density is the block's log target, so a block site is an ordinary choice
site: (sample (block-dist b inputs) :id :mu :init [0.0 0.0]). Its value is
θ, the latents flattened in declared order, as a vector of doubles.
The target includes every factor the latents touch: their priors and the
observations that depend on them, which must then not be observed again as
sites of their own. A block without :sample cannot be drawn from; start
it at an :init. :sample need not draw from the target (it cannot know
its normalizer); a block drawn from under importance sampling or SMC, or
proposed from by single-site MH, also needs :sample-log-density, the log
density of what :sample draws, which weighs the draw:
log target(θ) − log sample-density(θ).
Constrained latents (:support :positive or [:interval a b]) need the
block to declare :block/coordinates :constrained and give its
capabilities in those natural coordinates (σ itself, p itself): block
then works in unconstrained θ — log σ, logit of p — adding each
transform's log-Jacobian to the density and the chain rule to the
gradient, so HMC and NUTS move freely. θ is what the trace holds;
constrain maps it back. Without the declaration, a block's capabilities
are in unconstrained coordinates already and every latent must be :real.
Numerical blocks as choice sites (the spindel side of the spindel ↔ raster
block contract, spindel-raster doc/contract.md).
A block is a fixed-shape group of latent variables with the density factors
they touch, given as a description (data) and capabilities (functions over
primitive arrays):
(block {:block/id :gauss
:block/latents [{:name :mu :shape [2] :support :real}]
:block/target :complete-conditional}
{:log-density (fn [^doubles theta inputs] lp)
:value+grad (fn [^doubles theta inputs] [lp ^doubles grad])})
`(block-dist b inputs)` is the block at its inputs as a distribution whose
density is the block's log target, so a block site is an ordinary choice
site: `(sample (block-dist b inputs) :id :mu :init [0.0 0.0])`. Its value is
θ, the latents flattened in declared order, as a vector of doubles.
The target includes every factor the latents touch: their priors and the
observations that depend on them, which must then not be observed again as
sites of their own. A block without `:sample` cannot be drawn from; start
it at an `:init`. `:sample` need not draw from the target (it cannot know
its normalizer); a block drawn from under importance sampling or SMC, or
proposed from by single-site MH, also needs `:sample-log-density`, the log
density of what `:sample` draws, which weighs the draw:
log target(θ) − log sample-density(θ).
Constrained latents (`:support :positive` or `[:interval a b]`) need the
block to declare `:block/coordinates :constrained` and give its
capabilities in those natural coordinates (σ itself, p itself): `block`
then works in unconstrained θ — log σ, logit of p — adding each
transform's log-Jacobian to the density and the chain rule to the
gradient, so HMC and NUTS move freely. θ is what the trace holds;
`constrain` maps it back. Without the declaration, a block's capabilities
are in unconstrained coordinates already and every latent must be `:real`.(block description capabilities)A block from its description and capabilities (see the namespace).
A block from its `description` and `capabilities` (see the namespace).
(block-dist b inputs)Block b at inputs, as the distribution of its site.
Block `b` at `inputs`, as the distribution of its site.
(capability b k)The capability k of block b, or nil.
The capability `k` of block `b`, or nil.
(constrain b theta)The latents' values at θ: θ mapped back through each latent's transform
(identity for :real), as a vector in θ's order.
The latents' values at θ: θ mapped back through each latent's transform (identity for `:real`), as a vector in θ's order.
(latent b theta name)The latent name of θ: a double for a scalar, a vector otherwise.
The latent `name` of θ: a double for a scalar, a vector otherwise.
(value+grad dist theta)[log target, gradient] of dist (a block-dist) at θ, both from the
block's :value+grad; the gradient as a vector.
[log target, gradient] of `dist` (a `block-dist`) at θ, both from the block's `:value+grad`; the gradient as a vector.
(with-numeric-gradient capabilities)(with-numeric-gradient {:keys [log-density] :as capabilities}
{:keys [h] :or {h 1.0E-6}})capabilities with a :value+grad computed from :log-density by
central differences of step h (default 1e-6 times the coordinate's
scale): 2n evaluations per gradient, accurate to about 1e-8 relative on a
smooth density. An explicit opt-in for small blocks written in plain
Clojure — foerster never differentiates numerically behind your back; for
a compiled gradient, write the block with foerster-raster.
`capabilities` with a `:value+grad` computed from `:log-density` by central differences of step `h` (default 1e-6 times the coordinate's scale): 2n evaluations per gradient, accurate to about 1e-8 relative on a smooth density. An explicit opt-in for small blocks written in plain Clojure — foerster never differentiates numerically behind your back; for a compiled gradient, write the block with foerster-raster.
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 |