On this page:
1.1 Disrupt
flip
query
observe!
sample
in-pmf
pmf?
region?
1.2 Bayesian Network
1.3 Dice
1.4 Bentham
flip
observe!
reward!
expected-utility
9.2

1 Examples🔗ℹ

Roulette enables programmers to build languages that require the use of inference engines. This section gives examples of languages built on top of Roulette to show concretely how this works.

1.1 Disrupt🔗ℹ

 #lang roulette/example/disrupt package: roulette
 #lang roulette/example/disrupt/safe

Disrupt is an example discrete probabilistic programming language built on top of the RSDD inference engine.
> (define first-coin (flip 0.5))
> first-coin

┌─────┬───────────┐

│Value│Probability│

├─────┼───────────┤

│#t   │0.5        

│#f   │0.5        

└─────┴───────────┘

The flip construct returns a Boolean where #t has the given probability. Evaluating first-coin at the REPL prints out its probability distribution.
> (define second-coin (flip 0.5))
> (define both-heads (and first-coin second-coin))
> both-heads

┌─────┬───────────┐

│Value│Probability│

├─────┼───────────┤

│#t   │0.25       

│#f   │0.75       

└─────┴───────────┘

Now, both-heads is a Boolean that takes on the value #t when both coins are also #t.
> (observe! (not both-heads))
> first-coin

┌─────┬───────────┐

│Value│Probability│

├─────┼───────────┤

│#t   │0.5        

│#f   │0.5        

└─────┴───────────┘

Observing both-heads, specifically that it is #f, changes the probability of first-coin. Conditional on both-heads being #f, the probability first-coin being #t is 1/3.

procedure

(flip p [#:region reg])  boolean?

  p : (real-in 0 1)
  reg : (or/c region? #f) = #f
Returns a Boolean where #t has probability p and #f has probability (- 1 p). Optionally, flip can be given a region to place the newly created value. See query for details.
> (if (flip 1/2) 'a 'b)

┌─────┬───────────┐

│Value│Probability│

├─────┼───────────┤

│'b   │1/2        

│'a   │1/2        

└─────┴───────────┘

syntax

(query maybe-option ... body ...+)

 
maybe-option = 
  | #:samples iter
  | #:region reg-id
Returns the probability mass function (PMF) associated with e. In other words, query performs nested inference. See in-pmf for an example of how to use the result of this function. Observations are delimited to the dynamic extent of body. After body has finished, any observations executed during body are forgotten.
> (query (flip 1/2))

(pmf [#t 1/2] [#f 1/2])

The #:samples option runs body expression iter times, producing a probabilistic value according to the sampling distribution. The #:region option binds reg-id to the region associated with the query. A region is a first-class representation of dynamic extent. Each application of flip places its return value in a region. A query marginalizes only over variables in its region. After a region ends, the values associated with that region are considered discarded and their probability cannot be computed.
> (query (flip 1/2))

(pmf [#t 1/2] [#f 1/2])

> (let ([x (flip 1/2)]) (query x))

┌────────────┬───────────┐

│Value       │Probability│

├────────────┼───────────┤

│(pmf [#t 1])│1/2        

│(pmf [#f 1])│1/2        

└────────────┴───────────┘

> (query (query (flip 1/2)))

(pmf [(pmf [#t 1/2] [#f 1/2]) 1])

> (query (let ([x (flip 1/2)]) (query x)))

(pmf [(pmf [#f 1]) 1/2] [(pmf [#t 1]) 1/2])

> (query #:region r (query (flip 1/2 #:region r)))

(pmf [(pmf [#f 1]) 1/2] [(pmf [#t 1]) 1/2])

procedure

(observe! e)  void?

  e : boolean?
Conditions the current execution on e evaluating to #t.
> (define x (flip 1/2))
> (define y (flip 1/2))
> (and x y)

┌─────┬───────────┐

│Value│Probability│

├─────┼───────────┤

│#t   │1/4        

│#f   │3/4        

└─────┴───────────┘

> (observe! x)
> (and x y)

┌─────┬───────────┐

│Value│Probability│

├─────┼───────────┤

│#t   │1/4        

│#f   │3/4        

└─────┴───────────┘

procedure

(sample e)  any/c

  e : any/c
Samples a concrete value from the given probabilistic value.
> (sample (flip 1/2))

#f

procedure

(in-pmf e)  stream?

  e : pmf?
Sequence constructor for PMFs.
> (define-syntax-rule (expectation e)
    (for/all ([pmf (query e)])
      (for/sum ([(val prob) (in-pmf pmf)])
        (* val prob))))
> (expectation (if (flip 1/2) 5 10))

15/2

procedure

(pmf? e)  boolean?

  e : any/c
Predicate for PMFs.

procedure

(region? e)  boolean?

  e : any/c
Predicate for regions.

1.2 Bayesian Network🔗ℹ

 #lang roulette/example/bn package: roulette

A Bayesian network is a common kind of probabilistic graphical model that expresses conditional dependencies between variables. These networks are often described in the Bayesian Interchange Format (BIF). There are many such networks freely available online, including in the BNLearn repository. The BN language interprets Bayesian networks written in the BIF.

"cancer.rkt"

#lang roulette/example/bn

 

variable Pollution { type discrete [ 3 ] { low, medium, high }; }

variable Smoker { type discrete [ 2 ] { True, False }; }

variable Cancer { type discrete [ 2 ] { True, False }; }

variable Xray { type discrete [ 2 ] { positive, negative }; }

variable Dyspnoea { type discrete [ 2 ] { True, False }; }

 

probability ( Pollution ) {

  table 0.5, 0.4, 0.1;

}

probability ( Smoker ) {

  table 0.3, 0.7;

}

probability ( Cancer | Pollution, Smoker ) {

  (low, True) 0.03, 0.97;

  (medium, True) 0.03, 0.97;

  (high, True) 0.05, 0.95;

  (low, False) 0.001, 0.999;

  (medium, False) 0.001, 0.999;

  (high, False) 0.02, 0.98;

}

probability ( Xray | Cancer ) {

  (True) 0.9, 0.1;

  (False) 0.2, 0.8;

}

probability ( Dyspnoea | Cancer ) {

  (True) 0.65, 0.35;

  (False) 0.3, 0.7;

}

This Bayesian network is the famous "cancer" example that describes the relationship between several variables and whether a patient has lung cancer. Running this file in DrRacket allows one to explore the network with all the constructs from Disrupt in the interactions area. Additionally, the module exports all the variables, making them available to Disrupt programs via require.

> Cancer

┌──────┬────────────────────┐

│Value │Probability         

├──────┼────────────────────┤

│'True │0.011629999999999998│

│'False│0.98837             

└──────┴────────────────────┘

> (observe! (equal? Xray 'positive))
> Cancer

┌──────┬────────────────────┐

│Value │Probability         

├──────┼────────────────────┤

│'True │0.011629999999999998│

│'False│0.98837             

└──────┴────────────────────┘

This interaction shows the posterior probability that a patient has lung cancer given that they have a positive X-ray result. Notice that the posterior probability of Cancer only marginally increases with a positive test result. Many people are surprised by this phenomenon, which is known as the base rate fallacy.

1.3 Dice🔗ℹ

 #lang roulette/example/dice package: roulette

Dice is a small discrete probabilistic programming language. It was the first functional language to use knowledge-compilation-based probabilistic inference.

"noisy-or.rkt"

#lang roulette/example/dice

 

let n0  = flip 0.5 in

let n4  = flip 0.5 in

let n1  = if n0 then flip 0.8 else flip 0.1 in

let n21  = if n0 then flip 0.8 else flip 0.1 in

let n22  = if n4 then flip 0.8 else flip 0.1 in

let n33  = if n4 then flip 0.8 else flip 0.1 in

let n2  = (n21 || n22) in

let n31  = if n1 then flip 0.8 else flip 0.1 in

let n32  = if n2 then flip 0.8 else flip 0.1 in

let n3  = (n31  || (n32 || n33)) in

n3

Most programs should run out of the box, but there are a few limitations. A small number of operations are not supported. The implementation does not perform typechecking, so evaluating an ill-typed program is undefined behavior. Functions are not compiled modularly, and evaluation follows the "eager" strategy.

1.4 Bentham🔗ℹ

 #lang roulette/example/bentham package: roulette

Bentham is a more restrictive version of Disrupt with support for calculating expected utility.
> (define x (flip 1/2))
> (define y (flip 1/2))
> (when x (reward! 99))
> (expected-utility (not (and x y)))

33

If x holds, then the program accumulates 99 in utility via the reward! procedure. The query function returns the expected utility of the program conditional on (not (and x y)). From earlier, we know that the conditional probability of x given this expression is 1/3. Therefore, the expected utility is 33.

procedure

(flip p)  boolean?

  p : (real-in 0 1)

procedure

(observe! e)  void?

  e : boolean?
These procedures have the same meaning as they do in Disrupt.

procedure

(reward! e)  void?

  e : number?
Adds e to the amount of utility accumulated by the current program execution.

procedure

(expected-utility e)  number?

  e : any/c
Returns the expected utility given e.