### 4Derived Stochastic Forms and Functions

This section describes features derived from the primitive stochastic functions.

#### 4.1Sampling Convenience Functions

The stochastic functions in this section, sometimes called elementary stochastic procedures (ERPs), are convenience functions equivalent to calling sample on an appropriate distribution.

 procedure(bernoulli [p]) → (or/c 1 0) p : (real-in 0 1) = 1/2
 procedure(binomial count p) → exact-nonnegative-integer? count : exact-nonnegative-integer? p : (real-in 0 1)
 procedure(categorical weights) → exact-nonnegative-integer? weights : (vectorof (>=/c 0))
 procedure n : exact-positive-integer?
 procedure p : (real-in 0 1) = 1/2
 procedure mean : (>/c 0)
Equivalent to the following, respectively:
 (sample (bernoulli-dist p)) (sample (binomial-dist count p)) (sample (categorical-dist weights)) (sample (categorical-dist (make-vector n (/ n)))) (sample (geometric-dist p)) (sample (poisson-dist mean))

 procedure(beta a b) → real? a : (>/c 0) b : (>/c 0)
 procedure(cauchy mode [scale]) → real? mode : real? scale : (>/c 0) = 1
 procedure(exponential mean) → real? mean : (>/c 0)
 procedure(gamma [shape scale]) → real? shape : (>/c 0) = 1 scale : (>/c 0) = 1
 procedure(logistic [mean scale]) → real? mean : real? = 0 scale : (>/c 0) = 1
 procedure(normal [mean stddev]) → real? mean : real? = 0 stddev : (>/c 0) = 1
 procedure(pareto scale shape) → real? scale : (>/c 0) shape : (>/c 0)
 procedure(uniform) → real? (uniform hi) → real? hi : real? (uniform lo hi) → real? lo : real? hi : real?
Equivalent to the following, respectively:
 (sample (beta-dist a b)) (sample (cauchy-dist mode scale)) (sample (exponential-dist mean)) (sample (gamma-dist shape scale)) (sample (logistic-dist mean scale)) (sample (normal-dist mean stddev)) (sample (pareto-dist scale shape)) (sample (uniform-dist lo hi))

 procedure(dirichlet alpha) → (vectorof (>/c 0)) alpha : (vectorof (>/c 0))
Equivalent to (sample (dirichlet-dist alpha)).

 procedure(multi-normal mean cov) → col-matrix? mean : col-matrix? cov : square-matrix?
 procedure n : real? V : square-matrix?
 procedure(inverse-wishart n Vinv) → square-matrix? n : real? Vinv : square-matrix?
Equivalent to the following, respectively:
 (sample (multi-normal-dist mean cov)) (sample (wishart-dist n V)) (sample (inverse-wishart-dist n Vinv))

 procedure(discrete weighted-vals) → any/c weighted-vals : (listof (cons/c any/c (>=/c 0)))

procedure

(discrete* vals [weights])  any/c

vals : (non-empty-listof any/c)
 weights : (non-empty-listof (>/c 0)) = (make-vector (vector-length vals) 1)
Equivalent to the following, respectively:
 (sample (make-discrete-dist weighted-vals)) (sample (make-discrete-dist* vals weights))

 procedure(flip [p]) → boolean? p : (real-in 0 1) = 1/2
Equivalent to (= 1 (sample (bernoulli-dist p))).

#### 4.2Observations

 syntax(observe observable-expr value-expr)
Like observe-sample, except that instead of a distribution, the observation conditions the result of evaluating observable-expr. The observable-expr must evaluate to a call to sample (either explicitly or implicitly through one of the random procedures below) in an observable context; the call to sample is replaced with a call to observe-sample with a suitably adjusted value.

The observe form raises an error if observable-expr does not contain a call to sample in a suitable position and if observable-expr produces a value not equal to value-expr. See also observe/fail.

A observable context (OC) is (currently) defined as follows:

 OC = [ ] | (+ expr ... OC) | (cons OC expr) | (cons expr OC) | (reverse OC)

Support for other invertible built-in functions will be added in the future.

Thus, for example, the following are valid and successful:
> (observe (+ 10 (normal 0 1)) 11.5)

11.5

 > (observe (cons (bernoulli) (normal 0 1)) (cons 0 0.2))

'(0 . 0.2)

 > (observe (build-list 3 (lambda (i) (bernoulli))) '(1 1 0))

'(1 1 0)

The following are statically rejected because they can be shown to not call a sampling function in an observable context (even though the values produced might be the same!):
 > (observe 3 3) eval:6:0: observe: expression is not observable; it does not sample in an observable context at: 3 in: (observe 3 3) > (observe (+ (normal 0 1) 10) 11.5) eval:7:0: observe: expression is not observable; it does not sample in an observable context at: (+ (normal 0 1) 10) in: (observe (+ (normal 0 1) 10) 11.5)
The following raises a dynamic error because the observation fails (but it is not statically rejected because of limitations in the analysis):
 > (observe ((values (lambda _ 1)) 2) 3) observe: observation failed expected: 3 got: 1
The following observation fails because the observed value is not in the support of the distribution. Unlike the others, however, it would not raise an error if it occurred in a sampler, because the observation was successfully propagated to a sampling function.
 > (observe (uniform 0 1) 2) fail: failed reason: observation

The call to sample can occur in another function, as long as it occurs in an observable context with respect to the function’s body and the function call also occurs within an observable context. For example:
> (define (f x) (+ (* 3 x) 12 (normal 0 1)))
> (observe (f 9) 40)

40

Note: because of floating-point imprecision, the result of observable-expr may not be exactly equal to value-expr.

 procedure v : any/c (observe/fail v1 v2) → void? v1 : any/c v2 : any/c
Like observe, but uses fail to reject executions incompatible with the observation (as opposed to raising an error, as observe does).

Roughly equivalent to the following, respectively:
 (unless v (fail)) (unless (equal? v1 v2) (fail))

#### 4.3Laziness via Memoization

 syntax(pdelay body ...+)
Delays the body computation, producing a promise (ppromise?) that can be forced with pforce. The pdelay form uses mem internally, so the promises cooperate with the enclosing solver/sampler context.

Use pdelay to make a random choice lazy when it may not be relevant to all execution paths.

Examples:

 > (time ; eager, explores 2^10 possibilities (enumerate (define flips (for/list ([i 10]) (flip))) (andmap (lambda (x) x) flips)))

cpu time: 59 real time: 59 gc time: 41

(discrete-dist [#f 1023/1024] [#t 1/1024])

 > (time ; lazy, explores 11 possibilities (enumerate (define flips (for/list ([i 10]) (pdelay (flip)))) (andmap pforce flips)))

cpu time: 0 real time: 0 gc time: 0

(discrete-dist [#f 1023/1024] [#t 1/1024])

 procedure v : any/c
Returns #t if v is a promise produced by pdelay, #f otherwise.

 procedure(pforce p) → any p : ppromise?
Evaluates p’s body and caches the result, if p has not been previously forced, or returns the cached result otherwise.

 syntax(deflazy id expr)
Similar to (define id (pdelay expr)), except that when a reference to id is evaluated, it is automatically forced.

Examples:

> (deflazy x (begin (printf "flipping!\n") (flip)))
; flip hasn't occurred yet
> (if x 1 0)

flipping!

0

 syntax(defmem (fun-id arg-id ...) body ...+) (defmem (fun-id arg-id ... . rest-arg-id) body ...+)
Convenience form for defining memoized functions. Equivalent to the following, respectively:
 (define fun-id (mem (lambda (arg-id ...) body ...))) (define fun-id (mem (lambda (arg-id ... . rest-arg-id) body ...)))

#### 4.4Indexed Tables

syntax

(table ([var-id sequence-expr] ...+) maybe-lazy body ...+)

(table (var-id ...+) body ...+)

maybe-lazy =
| #:lazy

 sequence-expr : sequence?
Creates an indexed collection that acts like a function; the function takes as many arguments as there are var-ids.

In the first form, the table is finite, and it is eagerly populated with an entry for every combination of elements from each sequence-expr. (Each sequence-expr must be finite.) If a finite table is addressed with indexes that do not occur in sequence-expr, an error is raised.

Examples:

> (define F (table ([i 10] [j 20]) (flip)))
> (F 0 0)

#f

> (F 2 3)

#t

> (F 7 13)

#t

If the #:lazy keyword appears after the variable binding sequence, then the table’s entries are not evaluated until they are looked up (see also pdelay).

Examples:

> (define LF (table ([i 10] [j 20]) #:lazy (printf "flipping!\n") (flip)))
> (LF 0 0)

flipping!

#t

> (LF 0 0)

#t

In the second form, the table is conceptually infinite, and it is lazily populated as entries are requested. This form is equivalent to (mem (lambda (var-id ...) body ...)).