Inference Syntax Reference

Introduction

The VentureScript inference language is the language in which the toplevel VentureScript program is written. It is actually the same language as the VentureScript modeling language, except with several additional predefined procedures and special forms.

VentureScript inference programs are effectively evaluated in a context where the underlying model trace is available as a reified object, on which built-in inference procedures can operate. [1]

Block Structure

Like JavaScript, the organizing syntactic element of VentureScript is the block: a list of statements surrounded by curly braces and separated by parentheses. VentureScript has the following kinds of statements:

let <var> = <expression>;

Bind the variable on the left hand side to the value of the expression on the right hand side for the remainder of the block.

Note 1: This is not assignment: if the variable was already in scope, it is shadowed, not mutated; at the end of the block, the old value will be visible again.

Note 2: If the expression produces an inference action, that action is not performed, but stored in the variable for later use. See <-.

<var> <- <action>;

Perform the inference action on the right hand side and bind the returned value to the variable on the left hand side.

Note 1: This is not assignment: if the variable was already in scope, it is shadowed, not mutated; at the end of the block, the old value will be visible again.

Note 2: It is an error for the expression to produce a value that is not an inference action. For binding the results of pure computations, see let.

let (<var1>, <var2>, ...) = <expression>;

Multiple value bind. The expression must return a list of ref s of the same length as the number of variables. See also values_list.

letrec <var1> = <expression1>;
and <var2> = <expression2>;
...

Mutually recursive binding. The contiguous chain of and statements are grouped together with the letrec statement at the beginning, and all the variables are in scope in all the defining expressions and the rest of the block, analogously to the same construct in Scheme. The name stands for “let, recursively”. This is useful for defining mutually recursive procedures:

letrec even = (n) -> { if (n == 0) { true  } else {  odd(n - 1) } };
   and odd  = (n) -> { if (n == 0) { false } else { even(n - 1) } };
<action>

Just evaluate the expression. If this is the last statement of the block, the return value of the whole block is the result of evaluating the expression.

Note that all of these affordances appear as expressions as well. In fact, the block structure in VentureScript is just syntactic sugar for nested binding expressions of various kinds.

Inference control with tags and subproblems

VentureScript permits the user to control the strategy and focus of inference activity with the notion of the subproblem. Any set of random choices in a VentureScript model defines a local subproblem, which is the problem of sampling from the posterior on those choices, conditioned on keeping the rest of the execution history fixed. Subproblem decomposition is useful because progress in distribution on a subproblem constitutes progress on the overall problem as well.

It is typical to prepare a model for inference control by tagging some expressions with tags that can later be referenced when selecting a subproblem to operate on. VentureScript provides syntactic sugar for tagging model expressions:

something #tag1
something_else #tag2:value

The first form just associates the something expression with the tag named tag1. The second form associates the something_else expression with the tag named tag2 with the value computed by the value expression.

For example, a typical simple Dirichlet process mixture model (over Bernoulli vectors) might be implemented and tagged like this:

assume assign   = make_crp(1);
assume z        = mem((i) ~> { assign() #clustering });     // The partition induced by the DP
assume pct      = mem((d) ~> { gamma(1.0, 1.0) #hyper:d }); // Per-dimension hyper prior
assume theta    = mem((z, d) ~> { beta(pct(d), pct(d)) #compt:d });   // Per-component latent
assume datum    = mem((i, d) ~> {{
  cmpt ~ z(i);
  weight ~ theta(cmpt, d);
  flip(weight)} #row:i #col:d });   // Per-datapoint random choices

VentureScript provides a path-like syntax for identifying random choices in a model on which inference might then be applied. The / (forward slash) operator is analogous to a UNIX directory separator: it means “apply the pattern on the right to subchoices of the choices selected on the left” (and a leading / means “search for the choices on right in the whole model”). By “subchoices” I mean the choices made in the dynamic extent of evaluating that one. For example:

/?hyper               // select all choices tagged with the "hyper" tag
/?row==3              // select all choices involved in the third row
/?row==3/?clustering  // select only the cluster assignment for the third row

Random choice selections can also be computed programmatically with the procedures beginning with by_, below.

A complete subproblem definition includes not only the random choices of interest, but also additional information, such as how to treat which of their consequences (likelihood-free choices have to be resampled, choices that can report likelihoods may be resampled or may be kept, contributing to the acceptance ratio, etc). VentureScript currently provides one operation to convert a selection of random choices into a subproblem, namely minimal_subproblem. That subproblem may then be passed to an inference operation like resimulation_mh to actually operate on those choices.

Randomized proposal targets may be obtained with the random_singleton procedure, which correctly accounts for the acceptance correction due to the probability of selecting that particular subproblem to work on.

Putting it all together, the inference tactic default_markov_chain might be defined as:

define default_markov_chain = (n) -> {
  repeat(n, resimulation_mh(minimal_subproblem(random_singleton(by_extent(by_top())))))}

For compatibility with previous versions of VentureScript, all the inference operators will also accept a tag and a tag value as two separate arguments, and treat them as minimal_subproblem(/?<tag>==<value>).

Note: The argument lists of the inference operators below come out of Sphinx a bit weird. The intent is that the “tag_value” argument is required if and only if the “subproblem” argument is actually a raw tag, and immediately follows it.

Note 2: The subproblem selection subsystem is not (yet) implemented in the Puma backend. If using Puma, use the tag/value invocation form.

Any given random choice in an execution history can be tagged by an arbitrary number of tags; but for each tag it has, it must be given a unique value (if any). The most locally assigned value wins.

Built-in Procedures for Inference

All the procedures available in the modeling language can be used in the inference language, too. In addition, the following inference procedures are available.

resimulation_mh(subproblem[, tag_value : object[, transitions : int]])
Return type:<inference_action returning <array <number>>>

Run a Metropolis-Hastings kernel, proposing by resimulating the prior.

The transitions argument specifies how many transitions of the chain to run.

Returns the average number of nodes touched per transition in each particle.

mh(subproblem[, tag_value : object[, transitions : int]])
Return type:<inference_action returning <array <number>>>

Run a Metropolis-Hastings kernel, proposing by resimulating the prior.

The transitions argument specifies how many transitions of the chain to run.

Returns the average number of nodes touched per transition in each particle.

func_mh(subproblem[, tag_value : object[, transitions : int]])
Return type:<inference_action returning <array <number>>>

Like mh, but functional.

To wit, represent the proposal with a new trace (sharing common structure) instead of modifying the existing particle in place.

Up to log factors, there is no asymptotic difference between this and mh, but the distinction is exposed for those who know what they are doing.

gibbs(subproblem[, tag_value : object[, transitions : int[, in_parallel : bool]]])
Return type:<inference_action returning ()>

Run a Gibbs sampler that computes the local conditional by enumeration.

All the random choices identified by the scope-block pair must be discrete.

The transitions argument specifies how many transitions of the chain to run.

The in-parallel argument, if supplied, toggles parallel evaluation of the local conditional. Parallel evaluation is only available in the Puma backend, and is on by default.

Returns the average number of nodes touched per transition in each particle.

emap(subproblem[, tag_value : object[, transitions : int[, in_parallel : bool]]])
Return type:<inference_action returning ()>

Deterministically move to the local conditional maximum (computed by enumeration).

All the random choices identified by the scope-block pair must be discrete.

The transitions argument specifies how many times to do this. Specifying more than one transition is redundant unless the block is one.

The in-parallel argument, if supplied, toggles parallel evaluation of the local conditional. Parallel evaluation is only available in the Puma backend, and is on by default.

Returns the average number of nodes touched per transition in each particle.

func_pgibbs(subproblem, tag_value : object[, particles : int[, transitions : int[, in_parallel : bool]]])
Return type:<inference_action returning ()>

Move to a sample of the local conditional by particle Gibbs.

The block must indicate a sequential grouping of the random choices in the scope. This can be done by supplying the keyword ordered as the block, or the value of calling ordered_range.

The particles argument specifies how many particles to use in the particle Gibbs filter.

The transitions argument specifies how many times to do this.

The in-parallel argument, if supplied, toggles per-particle parallelism. Parallel evaluation is only available in the Puma backend, and is on by default.

Returns the average number of nodes touched per transition in each particle.

pgibbs(subproblem, tag_value : object[, particles : int[, transitions : int[, in_parallel : bool]]])
Return type:<inference_action returning ()>

Like func_pgibbs but reuse a single trace instead of having several.

The performance is asymptotically worse in the sequence length, but does not rely on stochastic procedures being able to functionally clone their auxiliary state.

The only reason to use this is if you know you want to.

Returns the average number of nodes touched per transition in each particle.

func_pmap(subproblem, tag_value : object[, particles : int[, transitions : int[, in_parallel : bool]]])
Return type:<inference_action returning ()>

Like func_pgibbs, but deterministically select the maximum-likelihood particle at the end instead of sampling.

Iterated applications of func_pmap are guaranteed to grow in likelihood (and therefore do not converge to the conditional).

Returns the average number of nodes touched per transition in each particle.

meanfield(subproblem[, tag_value : object[, training_steps : int[, transitions : int]]])
Return type:<inference_action returning <array <number>>>

Sample from a mean-field variational approximation of the local conditional.

The mean-field approximation is optimized with gradient ascent. The training_steps argument specifies how many steps to take.

The transitions argument specifies how many times to do this.

Note: There is currently no way to save the result of training the variational approximation to be able to sample from it many times.

Returns the average number of nodes touched per transition in each particle.

Return type:<inference_action returning <array <number>>>

Print some statistics about the requested scaffold.

This may be useful as a diagnostic.

The transitions argument specifies how many times to do this; this is not redundant if the block argument is one.

Returns the average number of nodes touched per transition in each particle.

nesterov(subproblem[, tag_value : object[, step_size : number[, steps : int[, transitions : int]]]])
Return type:<inference_action returning <array <number>>>

Move deterministically toward the maximum of the local conditional by Nesterov-accelerated gradient ascent.

Not available in the Puma backend. Not all the builtin procedures support all the gradient information necessary for this.

The gradient is of the log conditional.

The presence of discrete random choices in the scope-block pair will not prevent this inference strategy, but none of the discrete choices will be moved by the gradient steps.

The step_size argument gives how far to move along the gradient at each point.

The steps argument gives how many steps to take.

The transitions argument specifies how many times to do this.

Note: the Nesterov acceleration is applied across steps within one transition, not across transitions.

Returns the average number of nodes touched per transition in each particle.

gradient_ascent(subproblem[, tag_value : object[, step_size : number[, steps : int[, transitions : int]]]])
Return type:<inference_action returning <array <number>>>

Move deterministically toward the maximum of the local conditional by gradient ascent.

Not available in the Puma backend. Not all the builtin procedures support all the gradient information necessary for this.

This is just like nesterov, except without the Nesterov correction.

Returns the average number of nodes touched per transition in each particle.

hmc(subproblem[, tag_value : object[, step_size : number[, steps : int[, transitions : int]]]])
Return type:<inference_action returning <array <number>>>

Run a Hamiltonian Monte Carlo transition kernel.

Not available in the Puma backend. Not all the builtin procedures support all the gradient information necessary for this.

The presence of discrete random choices in the scope-block pair will not prevent this inference strategy, but none of the discrete choices will be moved.

The step_size argument gives the step size of the integrator used by HMC.

The steps argument gives how many steps to take in each HMC trajectory.

The transitions argument specifies how many times to do this.

Returns the average number of nodes touched per transition in each particle.

rejection(subproblem[, tag_value : object[, density_bound : maybe number[, attempt_bound : number[, transitions : int]]]])
Return type:<inference_action returning <array <number>>>

Sample from the local conditional by rejection sampling.

Not available in the Puma backend. Not all the builtin procedures support all the density bound information necessary for this.

The density_bound argument, if supplied, indicates the maximum density the requested subproblem could obtain (in log space). If omitted or nil, Venture will attempt to compute this bound automatically, but this process may be somewhat brittle.

The attempt_bound argument, if supplied, indicates how many attempts to make. If no sample is accepted after that many trials, stop, and leave the local state as it was. Warning: bounded rejection is not a Bayes-sound inference algorithm. If attempt_bound is not given, keep trying until acceptance (possibly leaving the session unresponsive).

Note: Regardless of whether some arguments are omitted, the last optional argument given is taken to be the number of transitions, not the density or attempt bound.

The transitions argument specifies how many times to do this. Specifying more than 1 transition is redundant if the block is anything other than one.

Returns the average number of nodes touched per transition in each particle.

bogo_possibilize(subproblem[, tag_value : object[, transitions : int]])
Return type:<inference_action returning <array <number>>>

Initialize the local inference problem to a possible state.

If the current local likelihood is 0, resimulate the local prior until a non-zero likelihood state is found.

Notes:

  • If the current state is possible, do nothing.

  • This is different from rejection sampling because the distribution on results is not the conditional, but the prior conditioned on the likelihood being non-zero. As such, it is likely to complete faster.

  • This is different from likelihood weighting because a) it keeps trying automatically until it finds a possible state, and b) it does not modify the weight of the particle it is applied to (because if the scope and block are other than default all it is not clear what the weight should become).

  • Does not change the particle weight, because the right one is not obvious for general scaffolds, or for the case where the state was possible to begin with. If you’re using bogo_possibilize(default, all) for pure initialization from the prior, consider following it with:

    do(l <- global_log_likelihood,
       set_particle_log_weights(l))
    

The transitions argument specifies how many times to do this. Specifying more than 1 transition is redundant if the block is anything other than one.

Returns the average number of nodes touched per transition in each particle.

log_rejection_bound_at(subproblem[, tag_value : object[, transitions : int]])
Return type:<inference_action returning <array <number>>>

Compute an upper bound on the density obtainable in the given subproblem.

This is the same bound that rejection sampling on that subproblem would use as the acceptance criterion.

slice(subproblem[, tag_value : object[, w : number[, m : int[, transitions : int]]]])
Return type:<inference_action returning <array <number>>>

Slice sample from the local conditonal of the selected random choice.

The scope-block pair must identify a single random choice, which must be continuous and one-dimensional.

This kernel uses the stepping-out procedure to find the slice. The w and m arguments parameterize the slice sampler in the standard way.

The transitions argument specifies how many transitions of the chain to run.

Returns the average number of nodes touched per transition in each particle.

slice_doubling(subproblem[, tag_value : object[, w : number[, p : int[, transitions : int]]]])
Return type:<inference_action returning <array <number>>>

Slice sample from the local conditional of the selected random choice.

The scope-block pair must identify a single random choice, which must be continuous and one-dimensional.

This kernel uses the interval-doubling procedure to find the slice. The w and p arguments parameterize the slice sampler in the standard way.

The transitions argument specifies how many transitions of the chain to run.

Returns the average number of nodes touched per transition in each particle.

resample(particles : int)
Return type:<inference_action returning ()>

Perform an SMC-style resampling step.

The particles argument gives the number of particles to make. Subsequent modeling and inference commands will be applied to each result particle independently.

Future observations will have the effect of weighting the particles relative to each other by the relative likelihoods of observing those values in those particles. The resampling step respects those weights.

The new particles will be handled in series. See the next procedures for alternatives.

resample_multiprocess(particles : int[, max_processes : int])
Return type:<inference_action returning ()>

Like resample, but fork multiple OS processes to simulate the resulting particles in parallel.

The max_processes argument, if supplied, puts a cap on the number of processes to make. The particles are distributed evenly among the processes. If no cap is given, fork one process per particle.

Subtlety: Collecting results (and especially performing further resampling steps) requires inter-process communication, and therefore requires serializing and deserializing any state that needs transmitting. resample_multiprocess is therefore not a drop-in replacement for resample, as the former will handle internal states that cannot be serialized, whereas the latter will not.

resample_serializing(particles : int)
Return type:<inference_action returning ()>

Like resample, but performs serialization the same way resample_multiprocess does.

Use this to debug serialization problems without messing with actually spawning multiple processes.

resample_threaded(particles : int)
Return type:<inference_action returning ()>

Like resample_multiprocess but uses threads rather than actual processes, and does not serialize, transmitting objects in shared memory instead.

Python’s global interpreter lock is likely to prevent any speed gains this might have produced.

Might be useful for debugging concurrency problems without messing with serialization and multiprocessing, but we expect such problems to be rare.

resample_thread_ser(particles : int)
Return type:<inference_action returning ()>

Like resample_threaded, but serializes the same way resample_multiprocess does.

Python’s global interpreter lock is likely to prevent any speed gains this might have produced.

Might be useful for debugging concurrency+serialization problems without messing with actual multiprocessing, but then one is messing with multithreading.

likelihood_weight()
Return type:<inference_action returning ()>

Likelihood-weight the full particle set.

Resample all particles in the current set from the prior and reset their weights to the likelihood.

enumerative_diversify(scope : object, block : object)
Return type:<inference_action returning ()>

Diversify the current particle set to represent the local conditional exactly.

Specifically:

  1. Compute the local conditional by enumeration of all possible values in the given scope and block
  2. Fork every extant particle as many times are there are values
  3. Give each new particle a relative weight proportional to the relative weight of its ancestor particle times the conditional probability of the chosen value.

Unlike most inference SPs, this transformation is deterministic.

This is useful together with collapse_equal and collapse_equal_map for implementing certain kinds of dynamic programs in Venture.

collapse_equal(scope : object, block : object)
Return type:<inference_action returning ()>

Collapse the current particle set to represent the local conditional less redundantly.

Specifically:

  1. Bin all extant particles by the (joint) values they exhibit on all random variables in the given scope and block (specify a block of “none” to have one bin)
  2. Resample by relative weight within each bin, retaining one particle
  3. Set the relative weight of the retained particle to the sum of the weights of the particles that formed the bin

Viewed as an operation on only the random variables in the given scope and block, this is deterministic (the randomness only affects other values).

This is useful together with enumerative_diversify for implementing certain kinds of dynamic programs in Venture.

collapse_equal_map(scope : object, block : object)
Return type:<inference_action returning ()>

Like collapse_equal but deterministically retain the max-weight particle.

And leave its weight unaltered, instead of adding in the weights of all the other particles in the bin.

checkInvariants()
Return type:<inference_action returning ()>

Run a trace self-check looking for contract violations in derived data fields.

O(#nodes).

draw_scaffold(subproblem[, tag_value : object[, transitions : int]])
Return type:<inference_action returning <array <number>>>

Draw a visual representation of the scaffold indicated by the given scope and block.

This is useful for debugging. The transition count is ignored.

Returns the number of nodes in the drawing.

subsampled_mh(subproblem[, tag_value : object[, Nbatch : int[, k0 : int[, epsilon : number[, useDeltaKernels : bool[, deltaKernelArgs : number[, updateValues : bool[, transitions : int]]]]]]]])
Return type:<inference_action returning <array <number>>>

Run a subsampled Metropolis-Hastings kernel

per the Austerity MCMC paper.

Note: not all dependency structures that might occur in a scaffold are supported. See subsampled_mh_check_applicability.

Note: the resulting execution history may not actually be possible, so may confuse other transition kernels. See subsampled_mh_make_consistent and *_update.

Returns the average number of nodes touched per transition in each particle.

subsampled_mh_check_applicability(subproblem[, tag_value : object[, transitions : int]])
Return type:<inference_action returning <array <number>>>

Raise a warning if the given scope and block obviously do not admit subsampled MH

From the source:

# Raise three types of warnings:
# - SubsampledScaffoldNotEffectiveWarning: calling subsampled_mh will be the
#   same as calling mh.
# - SubsampledScaffoldNotApplicableWarning: calling subsampled_mh will cause
#   incorrect behavior.
# - SubsampledScaffoldStaleNodesWarning: stale node will affect the
#   inference of other random variables. This is not a critical
#   problem but requires one to call makeConsistent before other
#   random nodes are selected as principal nodes.
#
# This method cannot check all potential problems caused by stale nodes.
subsampled_mh_make_consistent(subproblem[, tag_value : object[, useDeltaKernels : bool[, deltaKernelArgs : number[, updateValues : bool[, transitions : int]]]]])
Return type:<inference_action returning <array <number>>>

Fix inconsistencies introduced by subsampled MH.

Returns the number of nodes touched.

mh_kernel_update(subproblem[, tag_value : object[, useDeltaKernels : bool[, deltaKernelArgs : number[, updateValues : bool[, transitions : int]]]]])
Return type:<inference_action returning <array <number>>>

Run a normal mh kernel, tolerating inconsistencies introduced by previous subsampled MH.

Returns the average number of nodes touched per transition in each particle.

gibbs_update(subproblem[, tag_value : object[, transitions : int[, in_parallel : bool]]])
Return type:<inference_action returning ()>

Run a normal gibbs kernel, tolerating inconsistencies introduced by previous subsampled MH.

Returns the average number of nodes touched per transition in each particle.

pgibbs_update(subproblem, tag_value : object[, particles : int[, transitions : int[, in_parallel : bool]]])
Return type:<inference_action returning ()>

Run a normal pgibbs kernel, tolerating inconsistencies introduced by previous subsampled MH.

Returns the average number of nodes touched per transition in each particle.

incorporate()
Return type:<inference_action returning ()>

Explicitly make the history consistent with observations.

Specifically, modify the execution history so that the values of variables that have been observed since the last incorporate match the given observations. If there are multiple particles, also adjust their relative weights by the relative likelihoods of the observations being incorporated.

This is done automatically at the end of every observe command, but is also provided explicitly in case is proves needful.

Note: In the future, VentureScript may implement various different incorporation algorithms, in which case explicit incorporation may become necessary again.

log_likelihood_at(scope : object[, block : object])
Return type:<inference_action returning <array <number>>>

Compute and return the value of the local log likelihood at the given scope and block.

If there are stochastic nodes in the conditional regeneration graph, reuses their current values. This could be viewed as a one-sample estimate of the local likelihood.

log_likelihood_at(default, all) is not the same as getGlobalLogScore because it does not count the scores of any nodes that cannot report likelihoods, or whose existence is conditional. log_likelihood_at also treats exchangeably coupled nodes correctly.

Compare log_joint_at.

log_joint_at(scope : object[, block : object])
Return type:<inference_action returning <array <number>>>

Compute and return the value of the local log joint density at the given scope and block.

The principal nodes must be able to assess. Otherwise behaves like log_likelihood_at, except that it includes the log densities of non-observed stochastic nodes.

particle_log_weights()
Return type:<inference_action returning <array <number>>>

Return the weights of all extant particles as an array of numbers (in log space).

equalize_particle_log_weights()
Return type:<inference_action returning <array <number>>>

Make all the particle weights equal, conserving their logsumexp.

Return the old particle weights.

set_particle_log_weights(<array <number>>)
Return type:<inference_action returning ()>

Set the weights of the particles to the given array. It is an error if the length of the array differs from the number of particles.

increment_particle_log_weights(<array <number>>)
Return type:<inference_action returning ()>

Add the given array to the weights of the particles pointwise. It is an error if the length of the array differs from the number of particles.

for_each_particle(<action>)
Return type:<inference_action returning <list>>

Run the given inference action once for each particle in the model. The inference action is evaluated independently for each particle, and is not allowed to contain modeling commands (assume, observe, predict, forget, freeze).

on_particle(<integer>, <action>)
Return type:<inference_action returning <object>>

Run the given inference action on the particle with the given index. The inference action is not allowed to contain modeling commands (assume, observe, predict, forget, freeze).

load_plugin(filename, ...)
Return type:<inference_action returning <object>>

Load the plugin located at <filename>.

Any additional arguments to load_plugin are passed to the plugin’s __venture_start__ function, whose result is returned.

XXX: Currently, extra arguments must be VentureSymbols, which are unwrapped to Python strings for the plugin.

_call_back(<object>, ...)
Return type:<inference_action returning <object>>

A helper function for implementing the eponymous inference macro.

Calling it directly is likely to be difficult and unproductive.

_assume(<symbol>, <expression>[, <label>])
Return type:<inference_action returning <object>>

A helper function for implementing the eponymous inference macro.

Calling it directly is likely to be difficult and unproductive.

_observe(<expression>, <object>[, <label>])
Return type:<inference_action returning <object>>

A helper function for implementing the eponymous inference macro.

Calling it directly is likely to be difficult and unproductive.

_force(<expression>, <object>)
Return type:<inference_action returning <object>>

A helper function for implementing the eponymous inference macro.

Calling it directly is likely to be difficult and unproductive.

_predict(<expression>[, <label>])
Return type:<inference_action returning <object>>

A helper function for implementing the eponymous inference macro.

Calling it directly is likely to be difficult and unproductive.

_predict_all(<expression>[, <label>])
Return type:<inference_action returning <object>>

A helper function for implementing the eponymous inference macro.

Calling it directly is likely to be difficult and unproductive.

_sample(<expression>)
Return type:<inference_action returning <object>>

A helper function for implementing the eponymous inference macro.

Calling it directly is likely to be difficult and unproductive.

_sample_all(<expression>)
Return type:<inference_action returning <object>>

A helper function for implementing the eponymous inference macro.

Calling it directly is likely to be difficult and unproductive.

_extract_stats(<expression>)
Return type:<inference_action returning <object>>

A helper function for implementing the eponymous inference macro.

Calling it directly is likely to be difficult and unproductive.

forget(<label>)
Return type:<inference_action returning <object>>

Forget an observation, prediction, or unused assumption.

Removes the directive indicated by the label argument from the model. If an assumption is forgotten, the symbol it binds disappears from scope; the behavior if that symbol was still referenced is unspecified.

If the directive being forgotten was an observation, returns an array containing the log probability of the observed value in each particle. Otherwise returns an array of zeroes of the same length as the particle set.

The particle weights are not modified, even if an observation is forgotten.

freeze(<label>)
Return type:<inference_action returning ()>

Freeze an assumption to its current sample.

Replaces the assumption indicated by the label argument with a constant whose value is that assumption’s current value (which may differ across particles). This has the effect of preventing future inference on that assumption, and decoupling it from its (former) dependecies, as well as reclaiming any memory of random choices that can no longer influence any toplevel value.

Together with forget, freeze makes it possible for particle filters in Venture to use model memory independent of the sequence length.

report(<label>)
Return type:<inference_action returning <object>>

Report the current value of the given directive.

The directive can be specified by label or by directive id.

endloop()
Return type:<inference_action returning <object>>

Stop any continuous inference that may be running.

ordered_range(<object>, ...)
Return type:<list>

deterministic ordered_range

assert(<bool>[, message])
Return type:<inference_action returning ()>

Check the given boolean condition and raise an error if it fails.

convert_model(<symbol>)
Return type:<inference_action returning <model>>

Convert the implicit model to a different tracing backend.

The symbol gives the name of the backend to use, either puma or lite.

new_model([<symbol>])
Return type:<inference_action returning <model>>

Create an new empty model.

The symbol, if supplied, gives the name of the backend to use, either puma or lite. If omitted, defaults to the same backend as the current implicit model.

This is an inference action rather than a pure operation due to implementation accidents. [It reads the Engine to determine the default backend to use and for the registry of bound foreign sps.]

fork_model([<symbol>])
Return type:<inference_action returning <model>>

Create an new model by copying the state of the current implicit model.

The symbol, if supplied, gives the name of the backend to use, either puma or lite. If omitted, defaults to the same backend as the current implicit model.

in_model(<model>, <action>)
Return type:<inference_action returning <pair <object> <model>>>

Run the given inference action against the given model.

Returns a pair consisting of the result of the action and the model, which is also mutated.

This is itself an inference action rather than a pure operation due to implementation accidents. [It invokes a method on the Engine to actually run the given action].

model_import_foreign(<name>)
Return type:<inference_action returning ()>

Import the named registered foregin SP into the current model.

This is typically only necessary in conjunction with new_model, because foreign SPs are automatically imported into the model that is ambient at the time the foreign SP is bound by the ripl (which is usually the toplevel model).

The name must refer to an SP that was previously registered with Venture via ripl.register_foreign_sp or ripl.bind_foreign_sp. Binds that symbol to that procedure in the current model.

select(scope : object[, block : object])
Return type:<inference_action returning subproblem>

Select the subproblem indicated by the given scope and block from the current model.

Does not interoperate with multiple particles.

detach(<subproblem>)
Return type:<inference_action returning <pair weight <rhoDB>>>

Detach the current model along the given subproblem.

Return the current likelihood at the fringe, and a database of the old values that is suitable for restoring the current state (e.g., for rejecting a proposal).

Does not interoperate with multiple particles, or with custom proposals.

regen(<subproblem>)
Return type:<inference_action returning weight>

Regenerate the current model along the given subproblem.

Return the new likelihood at the fringe.

Does not interoperate with multiple particles, or with custom proposals.

restore(<subproblem>, <rhoDB>)
Return type:<inference_action returning weight>

Restore a former state of the current model along the given subproblem.

Does not interoperate with multiple particles.

detach_for_proposal(<subproblem>)
Return type:<inference_action returning <pair weight <rhoDB>>>

Detach the current model along the given subproblem, returning the local posterior.

Differs from detach in that it includes the log densities of the principal nodes in the returned weight, so as to match regen_with_proposal. The principal nodes must be able to assess.

Return the current posterior at the fringe, and a database of the old values for restoring the current state.

Does not interoperate with multiple particles.

regen_with_proposal(<subproblem>, <list>)
Return type:<inference_action returning weight>

Regenerate the current model along the given subproblem from the given values.

Differs from regen in that it deterministically moves the principal nodes to the given values rather than resimulating them from the prior, and includes the log densities of those nodes in the returned weight. The principal nodes must be able to assess.

Return the new posterior at the fringe.

Does not interoperate with multiple particles.

get_current_values(<subproblem>)
Return type:<inference_action returning <list>>

Get the current values of the principal nodes of the given subproblem.

Does not interoperate with multiple particles.

num_blocks(scope : object)
Return type:<inference_action returning <array <number>>>

Report the number of blocks in the given scope in each particle.

draw_subproblem(<subproblem>)
Return type:<inference_action returning ()>

Draw a subproblem by printing out the source code of affected random choices.

save_model(<filename>)
Return type:<inference_action returning ()>

Save the current model to a file.

Note: save_model and load_model rely on Python’s pickle module.

load_model(<filename>)
Return type:<inference_action returning ()>

Load from a file created by save_model, clobbering the current model.

Note: save_model and load_model rely on Python’s pickle module.

pyexec(<code>)
Return type:<inference_action returning ()>

Execute the given string as Python code, via exec.

The code is executed in an environment where the RIPL is accessible via the name ripl. Values from the ambient inference program are not directly accessible. The environment against which pyexec is executed persists across invocations of pyexec and pyeval.

pyeval(<code>)
Return type:<inference_action returning <object>>

Evaluate the given string as a Python expression, via eval.

The code is executed in an environment where the RIPL is accessible via the name ripl. Values from the ambient inference program are not directly accessible. The environment against which pyeval is evaluated persists across invocations of pyexec and pyeval.

save_model_string(model)
Return type:<inference_action returning model_string>

Save a model to a string which can later be loaded with load_model_string.

load_model_string(model_string)
Return type:<inference_action returning model>

Load a model saved with save_model_string.

parallel_mapv_action(proc(a) -> b, <array a>[, parallelism])
Return type:<inference_action returning <array b>>
print(<object>, ...)
Return type:()

Print the given values to the terminal.

If you are trying to add a debugging print statement to a VentureScript expression that is not already an inference action, consider using debug, which does not require sequencing.

plot(<spec>, <dataset>)
Return type:()

Plot a data set according to a plot specification.

Example:

define d = empty()
assume x = normal(0, 1)
infer accumulate_dataset(1000,
          do(resimulation_mh(default, one, 1),
             collect(x)))
plot("c0s", d)

will do 1000 iterations of mh collecting some standard data and the value of x, and then show a plot of the x variable (which should be a scalar) against the iteration number (from 1 to 1000), colored according to the global log score. See collect for details on collecting and labeling data to be plotted.

The format specifications are inspired loosely by the classic printf. To wit, each individual plot that appears on a page is specified by some line noise consisting of format characters matching the following regex:

[<geom>]*(<stream>?<scale>?){1,3}

specifying

  • the geometric objects to draw the plot with, and
  • for each dimension (x, y, and color, respectively)
    • the data stream to use
    • the scale

The possible geometric objects are:

  • _p_oint,
  • _l_ine,
  • _b_ar, and
  • _h_istogram

The possible data streams are:

  • _<an integer>_ that column in the data set, 0-indexed,
  • _%_ the next column after the last used one
  • iteration _c_ounter,
  • _t_ime (wall clock, since the beginning of the Venture program), and
  • pa_r_ticle

The possible scales are:

  • _d_irect, and
  • _l_ogarithmic

If one stream is indicated for a 2-D plot (points or lines), the x axis is filled in with the iteration counter. If three streams are indicated, the third is mapped to color.

If the given specification is a list, make all those plots at once.

plotf(<spec>, <dataset>)
Return type:<inference_action returning ()>

Plot a data set according to a plot specification.

This is identical to plot, except it’s an inference action, so can participate in do blocks.

Example:

do(assume x, normal(0, 1),
   ...
   plotf("c0s", d))
plot_to_file(<basename>, <spec>, <dataset>)
Return type:()

Save plot(s) to file(s).

Like plot, but save the resulting plot(s) instead of displaying on screen. Just as <spec> may be either a single expression or a list, <basenames> may either be a single symbol or a list of symbols. The number of basenames must be the same as the number of specifications.

Examples:
plot_to_file(“basename”, “spec”, <expression> …) saves the plot specified by
the spec in the file “basename.png”
plot_to_file(quote(basename1, basename2), (quote(spec1, spec2)), <expression> …) saves
the spec1 plot in the file basename1.png, and the spec2 plot in basename2.png.
plotf_to_file(<basename>, <spec>, <dataset>)
Return type:<inference_action returning ()>

Save plot(s) to file(s).

Like plotf, but save the resulting plot(s) instead of displaying on screen. See plot_to_file.

empty()
Return type:<dataset>

Create an empty dataset into which further collect ed stuff may be merged.

into(<foreignblob>, <foreignblob>)
Return type:<inference_action returning ()>

Destructively merge the contents of the second argument into the first.

Right now only implemented on datasets created by empty and collect, but in principle generalizable to any monoid.

_collect(<object>, ...)
Return type:<inference_action returning <dataset>>

A helper function for implementing the eponymous inference macro.

Calling it directly is likely to be difficult and unproductive.

sweep(<dataset>)
Return type:<inference_action returning ()>

Print the iteration count.

Extracts the last row of the supplied inference Dataset and prints its iteration count.

printf(<dataset>)
Return type:<inference_action returning ()>

Print model values collected in a dataset.

This is a basic debugging facility.

p_p_plot_2samp(<array <number>>, <array <number>>)
Return type:()

Render a P-P plot comparing the two observed samples.

p_p_plot_2samp_to_file(<string>, <array <number>>, <array <number>>)
Return type:()

Render a P-P plot comparing the two observed samples and save it in the given file.

by_intersection(<foreignblob>, <foreignblob>)
Return type:<foreignblob>

Intersect the selected choices.

by_tag(<tag>)
Return type:<foreignblob>

Select the choices tagged by the given tag.

They remain keyed by their values, so that random_singleton will pick all the choices given by a random tag value, rather than a single choice at random from all choices under that tag.

by_tag_value(<tag>, <value>)
Return type:<foreignblob>

Select the choices tagged by the given tag at the given value.

by_walk(<foreignblob>, <edge>)
Return type:<foreignblob>

Walk along the given edge in the dependency graph pointwise.

Possible edges are

  • operator, for the operator position of an expression
  • source, for the expression a variable is bound to
  • request, for the request node corresponding to a procedure application
  • <an integer>, for that index subexpression of an expression
  • <a tag search expression>, for choices in the dynamic extent of a node that were given in that tag
by_extent(<foreignblob>)
Return type:<foreignblob>

Select the choices in the dynamic extent of the current selection.

by_union(<foreignblob>, ...)
Return type:<foreignblob>

Union the given selections.

by_top()
Return type:<foreignblob>

Select the “top” of the model, whose dynamic extent is all random choices.

by_star()
Return type:<foreignblob>

Walk to all the random choices in the dynamic extent of the source.

minimal_subproblem(<foreignblob>)
Return type:<foreignblob>

Construct the minimal subproblem from the given selection.

random_singleton(<foreignblob>)
Return type:<foreignblob>

Randomly select one component of the current selection.

Correctly account for the acceptance correction due to possible changes in the probability of selecting a particular subproblem to work on.

A “component” may be a single random choice, if the current selection is a set, or a set, if the current selection is a dictionary of tag values to sets of variables.

esr(<integer>)
Return type:<foreignblob>

Walk to the given requested result.

iterate(f : <inference action>, iterations : int)
Return type:<inference action>

Repeatedly apply the given action, suppressing the returned values.

repeat(iterations : int, f : <inference action>)
Return type:<inference action>

Repeatedly apply the given action, suppressing the returned values. This is the same as iterate, except for taking its arguments in the opposite order, as a convenience.

sequence(ks : list<inference action returning a>)
Return type:<inference action returning list<a>>

Apply the given list of actions in sequence, returning the values. This is Haskell’s sequence.

sequence(ks : list<inference action>)
Return type:<inference action>

Apply the given list of actions in sequence, discarding the values. This is Haskell’s sequence_.

mapM(act : proc(a) -> <inference action returning b>, objs : list<a>)
Return type:<inference action returning list<b>>

Apply the given action function to each given object and perform those actions in order. Return a list of the resulting values. The nomenclature is borrowed from Haskell.

imapM(act : proc(int, a) -> <inference action returning b>, objs : list<a>)
Return type:<inference action returning list<b>>

Apply the given action function to each given object and its index in the list and perform those actions in order. Return a list of the resulting values.

for_each(objs : list<a>, act : proc(a) -> <inference action>)
Return type:<inference action>

Apply the given action function to each given object and perform those actions in order. Discard the results.

for_each_indexed(objs : list<a>, act : proc(int, a) -> <inference action>)
Return type:<inference action>

Apply the given action function to each given object and its index in the list and perform those actions in order. Discard the results.

pass <inference action>

An inference action that does nothing and returns nil. Useful in the same sorts of situations as Python’s pass statement.

bind(<inference action returning a>, proc(a) -> <inference action returning b>)
Return type:<inference action returning b>

Chain two inference actions sequentially, passing the value of the first into the procedure computing the second. This is Haskell’s bind, specialized to inference actions.

bind_(<inference action>, proc() -> <inference action returning a>)
Return type:<inference action returning a>

Chain two inference actions sequentially, ignoring the value of the first. This is Haskell’s >> operator, specialized to inference actions.

Note that the second argument is a thunk that computes an inference action. This is important, because it defers computing the action to take until it is actually time to take it, preventing infinite loops in, e.g., unrolling the future action spaces of recursive procedures.

return(<object>)
Return type:<inference action returning <object>>

An inference action that does nothing and just returns the argument passed to return.

curry(proc(<a>, <b>) -> <c>, <a>)
Return type:proc(<b>) -> <c>

Curry a two-argument function into two one-argument stages. Supports the idiom (bind (collect …) (curry plotf (quote spec))).

curry3(proc(<a>, <b>, <c>) -> <d>, <a>, <b>)
Return type:proc(<c>) -> <d>

Curry a three-argument function into a two-argument stage and a one-argument stage. Supports the idiom (bind (collect …) (curry plotf_to_file (quote name) (quote spec))).

global_log_likelihood <inference action returning <array <number>>>

An inference action that computes and returns the global likelihood (in log space). Cost: O(size of trace).

global_log_joint <inference action returning <array <number>>>

An inference action that computes and returns the global joint density (in log space). Cost: O(size of trace).

conditional <inference action>

An inference action that sets each particle to an independent sample from the full conditional (with respect to currently incorporated observations).

This is implemented by global rejection sampling (generalized to continuous equality constraints), so may take a while for problems where the conditional is far from the prior in KL divergence.

join_datasets(datasets : list<dataset>)
Return type:<inference action returning <dataset>>

Merge all the given datasets into one.

accumulate_dataset(iterations : int, a : <inference action returning <dataset>>)
Return type:<inference action returning <dataset>>

Run the given inference action the given number of times, accumulating all the returned datasets into one.

For example:

accumulate_dataset(1000,
  do(default_markov_chain(10),
     collect(x)))

will return a dataset consisting of the values of x that occur at 10-step intervals in the history of a 10000-step default Markov chain on the current model.

duplicate_particle(<int>)
Return type:<inference action returning ()>

Duplicate the given particle, conserving its weight. The copy is initialized with zero weight (negative infinity in log space).

add_particle <inference action returning ()>

Add a new particle, sampled from the prior and weighted by its likelihood. In case different extant particles have different priors, as can happen if freeze was used, it’s the prior of particle 0.

run(<inference action returning a>)
Return type:a

Run the given inference action and return its value.

autorun(<object>)
Return type:<object>

If the argument is an inference action, run it and return the result. Otherwise, return the argument.

default_markov_chain(transitions : int)
Return type:<inference action>

Take the requested number of steps of the default Markov chain.

The default Markov chain is single-site resimulation M-H.

default_markov_chain(k)

is equivalent to

resimulation_mh(default, one, k)

See mh.

regeneration_local_proposal(<list>)
Return type:proc(<subproblem>) -> <inference action returning <result>>

Propose the given values for the given subproblem. Changes the underlying model to represent the proposed state, and returns a proposal result (see mh_correct).

mh_correct(<inference action returning <result>>)
Return type:<inference action>

Run the given proposal, and accept or reject it according to the Metropolis-Hastings acceptance ratio returned in its result.

The action must return a list of three items:

  • The local acceptance ratio (in log space)
  • An action to run to accept the proposal
  • An action to run to reject the proposal
symmetric_local_proposal(proc(<value>) -> <value>)
Return type:proc(<subproblem>) -> <inference action returning <result>>

Propose using the given kernel for the given subproblem. Changes the underlying model to represent the proposed state, and returns a proposal result (see mh_correct).

The kernel function must be symmetric, but need not be assessable.

on_subproblem(scope: object, block: object, proposal: proc(<subproblem>) -> <inference action returning <result>>)
Return type:<inference action returning <result>>

Select the subproblem indicated by the given scope and block, and apply the given proposal procedure to that subproblem. Returns a proposal result (see mh_correct).

The given proposal function must accept a subproblem and produce an action that returns a proposal result. The result returned by on_subproblem consists of modifying the acceptance ratio to account for any change in the probability of selecting the same subproblem from the given scope and block.

value_at(selector: object)
Return type:object

Return the value the current model has at the choice indicated by the given selector. If the selector identifies more than one random choice, return one of their values arbitrarily.

Does not interoperate with multiple particles (use on_particle if needed).

value_at2(scope: object, block:object)
Return type:object

Return the value the current model has at the choice indicated by the given scope and block. If the scope-block pair identifies more than one random choice, return one of their values arbitrarily.

Does not interoperate with multiple particles (use on_particle if needed).

set_value_at(selector: object, value: object)
Return type:<inference_action returning weight>

Set the value of the random choice at the given selector in the current model to the given object. Return the probability density thereof in the local posterior around that random choice. It is an error if the selector identifies more than one random choice.

Does not interoperate with multiple particles (use on_particle if needed).

set_value_at(scope: object, block: object, value: object)
Return type:<inference_action returning weight>

Set the value of the random choice at the given scope and block in the current model to the given object. Return the probability density thereof in the local posterior around that random choice. It is an error if the scope-block pair identifies more than one random choice.

Does not interoperate with multiple particles (use on_particle if needed).

Built-in Helpers

  • default: The default scope.

    The default scope contains all the random choices, each in its own block.

  • one: Mix over individual blocks in the scope.

    If given as a block keyword, one causes the inference procedure to uniformly choose one of the blocks in the scope on which it is invoked and apply to that.

  • all: Affect all choices in the scope.

    If given as a block keyword, all causes the inference procedure to apply to all random choices in the scope.

  • none: Affect no choices in the scope.

    If given as a block keyword, none causes the inference procedure to apply to no random choices. This is useful only for collapse_equal and collapse_equal_map.

  • ordered: Make particle Gibbs operate on all blocks in order of block ID.

  • ordered_range(<block>, <block>): Make particle Gibbs operate on a range of blocks in order of block ID.

    Specifically, all the blocks whose IDs lie between the given lower and upper bounds.

Special Forms

All the macros available in the modeling language can be used in the inference language, too. In addition, the following inference macros are available.

loop(<kernel>)

Run the given kernel continuously in a background thread.

Available in Lite and Puma.

Can only be used as the top level of the infer instruction: infer loop(something).

Execute the stop_continuous_inference instruction to stop.

(do <stmt> <stmt> ...)

Sequence actions that may return results.

Note: The above template is abstract syntax. do is what curly-delimited blocks expand to.

Each <stmt> except the last may be

  • an action, in which case it is performed and any value it returns is dropped, or
  • a binder of the form (<variable> <- <action>) in which case the action is performed and its value is made available to the remainder of the do form by being bound to the variable, or
  • a let binder of the form (let <variable> <expression>) in which case the value of the expression is just bound to the variable (without performing any actions), or
  • a multivalue binder of the form (let_values (<var1> <var2> ...) <expression>) in which case the expression is expected to return a list of ref s, whose values are unpacked into the variables, or
  • a letrec or mutrec binder of the form (letrec <var> <exp>) or (mutrec <var> <exp>). All mutrec binders headed by a single letrec form a block, which functions like letrec in scheme, with the rest of the do expression as its body.

The last <stmt> may not be a binder and must be an action. The whole do expression is then a single compound heterogeneous action, whose value is the value returned by the last <stmt>.

If you need an action that produces a value without doing anything, use return(<value>) (see return). If you need an action that evaluates an expression and returns its value, use action(<exp>) (see action). If you need an action that does nothing and produces no useful value, you can use pass.

For example, to make a kernel that does inference until some variable in the model becomes “true” (why would anyone want to do that?), you can write:

1 define my_strange_kernel = proc () {
2   do(finish <- sample(something_from_the_model),
3      if (finish) {
4         pass
5      } else {
6         do(default_markov_chain(1),
7            my_strange_kernel())
8      })}

Line 2 is a binder for the do, which makes finish a variable usable by the remainder of the procedure. The if starting on line 3 is an action, and is the last statement of the outer do. Line 6 is a non-binder statement for the inner do.

The nomenclature is borrowed from the (in)famous do notation of Haskell. If this helps you think about it, Venture’s do is exactly Haskell do, except there is only one monad, which is essentially State ModelHistory. Randomness and actual i/o are not treated monadically, but just executed, which we can get away with because VentureScript is strict and doesn’t aspire to complete functional purity.

begin(<kernel>, ...)

Perform the given kernels in sequence.

action(<exp>)

Wrap an object, usually a non-inference function like plotf, as an inference action, so it can be used inside a do(…) block.

The difference between action and return is that action defers evaluation of its argument until the action is performed, whereas return evaluates its argument eagerly.

call_back(<name>, <model-expression>, ...)

Invoke a user-defined callback.

Locate the callback registered under the name name and invoke it with

  • First, the Infer instance in which the present inference program is being run
  • Then, for each expression in the call_back form, a list of values for that expression, represented as stack dicts, sampled across all extant particles. The lists are parallel to each other.

Return the value returned by the callback, or Nil if the callback returned None.

To bind a callback, call the bind_callback method on the Ripl object:

ripl.bind_callback(<name>, <callable>):

Bind the given Python callable as a callback function that can be
referred to by `call_back` by the given name (which is a string).

There is an example in the source in test/inference_language/test_callback.py.

collect(<model-expression> ...)

Extract data from the underlying model during inference.

When a collect inference command is executed, the given expressions are sampled and their values are returned in a Dataset object. This is the way to get data into datasets; see accumulate_dataset and into for accumulating datasets, and print, plot, and plot_to_file for using them.

Each <model-expression> may optionally be given in the form labelled( <model-expression>, <name>), in which case the given name serves as the key in the returned table of data. Otherwise, the key defaults to a string representation of the given expression.

Note: The <model-expression> s are sampled in the model, not the inference program. For example, they may refer to variables assume d in the model, but may not refer to variables define d in the inference program. The <model-expression> s may be constructed programmatically: see unquote.

collect also automatically collects some standard items: the iteration count (maintained by merging datasets), the particle id, the wall clock time that passed since the VentureScript program began, the global log score, the particle weights in log space, and the normalized weights of the particles in direct space.

If you want to do something custom with the data, you will want to use the asPandas() method of the Dataset object from your callback or foreign inference sp.

[<label>:] assume <symbol> = <model-expression>;

Programmatically add an assumption.

Extend the underlying model by adding a new generative random variable, like the assume directive. The given model expression may be constructed programmatically – see unquote.

The <label>, if supplied, may be used to freeze or forget this directive.

assume_values (<symbol> ...) = <model-expression>;

Multiple-value assume.

The expression is expected to produce a list of references (see ref) of the same length as the list of symbols given to assume_values. assume_values binds those symbols to the deref s of the corresponding references.

For example:

(assume_values (a b) (list (ref (normal 0 1)) (ref (normal 1 2))))

is equivalent to:

(assume _some_name_432_ (list (ref (normal 0 1)) (ref (normal 1 2))))
(assume a (deref (first _some_name_432_)))
(assume b (deref (second _some_name_432_)))

which has the same effect as:

(assume a (normal 0 1))
(assume b (normal 0 1))

assume_values does not accept a custom label argument.

[<label>:] observe <model-expression> = <value>;

Programmatically add an observation.

Condition the underlying model by adding a new observation, like the observe directive. The given model expression may be constructed programmatically – see unquote. The given value is computed in the inference program, and may be stochastic. This corresponds to conditioning a model on randomly chosen data.

The <label>, if supplied, may be used to forget this observation.

This is an action returning an array containing the log probability of the observed value in each particle. The particle weights are incremented by this difference automatically.

[<label>:] predict <model-expression>;

Programmatically add a prediction.

Extend the underlying model by adding a new generative random variable, like the predict directive. The given model expression may be constructed programmatically – see unquote.

The <label>, if supplied, may be used to freeze or forget this directive.

predict_all(<model-expression>[, <label>])

Programmatically add a prediction and return results from all particles.

Extend the underlying model by adding a new generative random variable, predict. Unlike predict, return the values of the expression from all the particles, as a list.

The <label>, if supplied, may be used to freeze or forget this directive.

force <model-expression> = <value>;

Programatically force the state of the model.

Force the model to set the requested variable to the given value, without constraining it to stay that way. Implemented as an observe followed by a forget.

This is an action returning an array containing the log probability of the observed value in each particle. The particle weights are incremented by this difference automatically, even though the observation is immediately forgotten.

sample <model-expression>

Programmatically sample from the model.

Sample an expression from the underlying model by simulating a new generative random variable without adding it to the model, like the sample directive. If there are multiple particles, refers to the distinguished one.

The given model expression may be constructed programmatically – see unquote.

sample_all(<model-expression>)

Programmatically sample from the model in all particles.

Sample an expression from the underlying model by simulating a new generative random variable without adding it to the model, like the sample directive.

Unlike the sample directive, interacts with all the particles, and returns values from all of them as a list.

The given model expression may be constructed programmatically – see unquote.

extract_stats(<model-expression>)

Extract maintained statistics.

Specifically, sample the given model expression, like sample, but expect it to return a stochastic procedure and reify and return the statistics about its applications that it has collected.

The exact VentureScript-level representation of the returned statistics depends on the procedure in question. If the procedure does not collect statistics, return nil.

For example:

assume coin = make_beta_bernoulli(1, 1)
observe coin() = true
extract_stats(coin) --> list(1, 0)
unquote(<object>)

Programmatically construct part of a model expression.

All the <model-expression> s in the above special forms may be constructed programmatically. An undecorated expression is taken literally, but if unquote(...) appears in such an expression, the code inside the unquote is executed in the inference program, and its result is spliced in to the model program.

For example, suppose one wanted to observe every value in a data set, but allow the model to know the index of the observation (e.g., to select observation models). For this, every observed model expression needs to be different, but in a programmable manner. Here is a way to do that:

define data = ...
define observe_after = proc(i) {
  if (i < length(data)) {
     do(observe(obs_fun(unquote(i)), lookup(data, i)),  // (*)
        observe_after(i + 1))
  } else {
     pass
  }}
infer observe_after(0)

Note the use of unquote on the like marked (*) to construct different observe expressions for each data element. To the underlying model, this will look like:

observe obs_fun(0) = <val0>
observe obs_fun(1) = <val1>
observe obs_fun(2) = <val2>
...

Footnotes

[1]

For the interested, the way this is actually done is that each of the primitives documented here actually returns a procedure that accepts a reified object representing the sampled execution history of the model program, affects it, and returns a pair consisting of whatever value it wishes to return in the first slot, and the reified execution history in the second. This is relevant if you wish to define additional inference abstractions in Venture, or more complex combinations of them than the provided ones.

For those readers for whom this analogy is helpful, the above is exactly the type signature one would get by implementing a State monad on execution histories. This is why the do form is called do. The begin form is a simplification of do that drops all intermediate return values. For the analogues of runState, see in_model and run. As of this writing, there are no analogues of get or put.