diff --git a/examples/pos/probabilistic.effekt b/examples/pos/probabilistic.effekt index 3ee7cce46..f63469c50 100644 --- a/examples/pos/probabilistic.effekt +++ b/examples/pos/probabilistic.effekt @@ -2,9 +2,9 @@ module examples/pos/probabilistic import immutable/list -/// -/// Bayesian inference for random variables (RVs) with boolean values -/// + +// Bayesian inference for random variables (RVs) with boolean values +// ----------------------------------------------------------------- // set up a heap for storing RVs effect IndexOutOfBounds[A](): A @@ -58,35 +58,36 @@ def heap[R] { prog: => R / Heap } = { } -// effects for expressing some constructs on RVs - -/// Cond expresses a RV which depends on a some other RVs listed in listRef. -/// The list listProbabilities contains the probabilities with which the -/// expressed RV is true given that the RVs in listRef are true/false: -/// if there are n RVs in listRef, then there are supposed to be 2^n -/// elements in listProbabilities, one for each combination of true/false -/// for the elements in listRef. -/// The order in listProbabilities goes from all RVs in listRef are false to -/// all of them are true in ascending order, when false is read -/// as 0 and true as 1 and the list is interpreted as binary number. -/// That is, if, e.g., there are 3 elements in listRef, then the first element -/// in listProbabilities corresponds to [false,false,false] (interpreted as 000), -/// the second to [false,false,true] (or 001) then [false,true,false] -/// (or 010) and so on up to [true,true,true] (or 111). +// Effects for expressing some constructs on RVs +// --------------------------------------------- + +// Cond expresses a RV which depends on a some other RVs listed in listRef. +// The list listProbabilities contains the probabilities with which the +// expressed RV is true given that the RVs in listRef are true/false: +// if there are n RVs in listRef, then there are supposed to be 2^n +// elements in listProbabilities, one for each combination of true/false +// for the elements in listRef. +// The order in listProbabilities goes from all RVs in listRef are false to +// all of them are true in ascending order, when false is read +// as 0 and true as 1 and the list is interpreted as binary number. +// That is, if, e.g., there are 3 elements in listRef, then the first element +// in listProbabilities corresponds to [false,false,false] (interpreted as 000), +// the second to [false,false,true] (or 001) then [false,true,false] +// (or 010) and so on up to [true,true,true] (or 111). effect Cond(listRef: List[Ref], listProbabilities: List[Double]): Ref -/// Conj and Disj express the conjunction and disjunction of -/// RVs x and y, i.e., Conj(x, y) is true iff x and y are both true, -/// where Disj(x, y) is false iff x and y are both false. +// Conj and Disj express the conjunction and disjunction of +// RVs x and y, i.e., Conj(x, y) is true iff x and y are both true, +// where Disj(x, y) is false iff x and y are both false. effect Conj(x: Ref, y: Ref): Ref effect Disj(x: Ref, y: Ref): Ref -/// Prior expresses that the prior belief that x is true is prob. +// Prior expresses that the prior belief that x is true is prob. effect Prior(x: Ref, prob: Double): Unit -/// Flip expresses a RV which is true with probability prob. -/// It can be written as a special case of Cond with no dependencies, -/// so it is no additional effect but just a function using Cond. +// Flip expresses a RV which is true with probability prob. +// It can be written as a special case of Cond with no dependencies, +// so it is no additional effect but just a function using Cond. def Flip(prob: Double) = do Cond([], [prob]) @@ -99,7 +100,8 @@ effect Fork(): Bool effect Fail[A](): A -// some functions for handling the probabilistic constructs +// Some functions for handling the probabilistic constructs +// -------------------------------------------------------- def pow(base: Int, exp: Int): Int = { if (exp < 0) { 0 } @@ -107,15 +109,15 @@ def pow(base: Int, exp: Int): Int = { else { base * pow(base, exp - 1) } } -/// Observe a RV, failing when the same RV -/// is observed again with a different result. +// Observe a RV, failing when the same RV +// is observed again with a different result. def observe(ref: Ref, b: Bool) = do get(ref) match { case Unobserved() => do put(ref, Observed(b)) case Observed(y) => if (b == y) { () } else { do Fail() } } -/// Observe each (if the Bool list is long enough) RV in a list. +// Observe each (if the Bool list is long enough) RV in a list. def observeList(listRef: List[Ref], listBool: List[Bool]): Unit / { Heap, Fail } = { listRef match { case Nil() => () @@ -128,15 +130,17 @@ def observeList(listRef: List[Ref], listBool: List[Bool]): Unit / { Heap, Fail } } } -/// Convert a non-negative number to a list of booleans with specified length. -/// The conversion proceeds according to the interpretation of positions -/// in a list as described above for the Cond effect. If the number is too large -/// to fit into the list of the specified length, the higher bits are truncated. -/// Example: given that the length is 3, the correspondence is -/// decimal number binary number bool list -/// 0 000 [false,false,false] -/// 1 001 [false,false,true] -/// 5 101 [true,false,true] +/** + * Convert a non-negative number to a list of booleans with specified length. + * The conversion proceeds according to the interpretation of positions + * in a list as described above for the Cond effect. If the number is too large + * to fit into the list of the specified length, the higher bits are truncated. + * Example: given that the length is 3, the correspondence is + * decimal number binary number bool list + * 0 000 [false,false,false] + * 1 001 [false,false,true] + * 5 101 [true,false,true] + */ def toBoolList(length: Int, number: Int): List[Bool] = try { def toBoolListReverse(length: Int, number: Int): List[Bool] = { if (number < 0) { do Fail() } @@ -155,12 +159,14 @@ def toBoolList(length: Int, number: Int): List[Bool] = try { reverse(toBoolListReverse(length, number)) } with Fail[A] { () => println("toBoolList: Unexpected negative number."); [] } -/// Observe the possibilities of true/false combinations for the RVs -/// in listRef corresponding to the above-described interpretation of -/// positions in a list up to position index, and score in each case -/// with the probability at the corresponding position in listProbabilities. -/// Usually this function is called with index = (2^n - 1) where n is the -/// length of listRef, to observe all possibilitites. +/** + * Observe the possibilities of true/false combinations for the RVs + * in listRef corresponding to the above-described interpretation of + * positions in a list up to position index, and score in each case + * with the probability at the corresponding position in listProbabilities. + * Usually this function is called with index = (2^n - 1) where n is the + * length of listRef, to observe all possibilitites. + */ def observeAndScore(index: Int, listRef: List[Ref], listProbabilities: List[Double]): Unit / { IndexOutOfBounds, Heap, Fail, Fork, Score } = { if (index < 0) () @@ -185,11 +191,13 @@ def choose3[R] { x: => R } { y: => R } { z: => R } : R / Fork = def fresh(): Ref / Heap = do empty(Unobserved()) -/// Handler for effects for probabilistic constructs: -/// run the program prog forwards collecting all effects for probabilistic -/// constructs, then observe the result RV in prog to be as expected and -/// then go backwards through the program enumerating all possible ways -/// how the result could have come about from the input RV. +/* + * Handler for effects for probabilistic constructs: + * run the program prog forwards collecting all effects for probabilistic + * constructs, then observe the result RV in prog to be as expected and + * then go backwards through the program enumerating all possible ways + * how the result could have come about from the input RV. + */ def handleLang(expected: Bool) { prog: Ref => Ref / { Cond, Conj, Disj, Prior }}: Var / { IndexOutOfBounds, Score, Fail, Fork } = heap { val input = fresh(); try { do put(prog(input), Observed(expected)) } @@ -257,12 +265,14 @@ def handleLang(expected: Bool) { prog: Ref => Ref / { Cond, Conj, Disj, Prior }} type Weighted[R] { MkWeighted(weight: Double, value: R) } -/// Handler for scoring, forking and failing: -/// start with current probability 1, run the program prog -/// and handle Score by updating the current probability -/// accordingly and handle Fork by resuming with both -/// possibilities false and true appending the results. -/// This yields a list of weighted ways through the program. +/* + * Handler for scoring, forking and failing: + * start with current probability 1, run the program prog + * and handle Score by updating the current probability + * accordingly and handle Fork by resuming with both + * possibilities false and true appending the results. + * This yields a list of weighted ways through the program. + */ def handleProbabilities[R] { prog: => R / { Score, Fork, Fail } } = { val empty: List[Weighted[R]] = Nil(); try { @@ -275,8 +285,11 @@ def handleProbabilities[R] { prog: => R / { Score, Fork, Fail } } = { with Fail[A] { () => empty } } -// functions for calculating the normalized posterior from a + +// Functions for calculating the normalized posterior from a // list of weighted ways through a program + + def collectProbabilities(b: Bool, l: List[Weighted[Var]]): Double = { l match { case Nil() => 0.0 @@ -305,8 +318,9 @@ def posterior(result: List[Weighted[Var]]): List[Weighted[Bool]] / Fail = { } -// some examples, in particular three versions of -// the famous sprinkler-rain-grass example +// Some examples +// ------------- +// In particular three versions of the famous sprinkler-rain-grass example def test() = { if (do Fork()) {