Current section
Files
Jump to
Current section
Files
src/probly.gleam
import gleam/list
import gleam/pair
/// The probability of an event occuring. Within [0, 1].
pub type Prob =
Float
/// Represents the outcome of a probabilistic event as a collection of
/// all possible values, tagged with their likelihood.
pub type Dist(a) =
List(#(a, Prob))
/// Functions that "spread" values across distributions by assigning them
/// with probabilities. Examples include `uniform` and `binomial`.
pub type Spread(a) =
fn(List(a)) -> Dist(a)
/// Match an entry in a Dist to a given value of type `a`.
pub type Event(a) =
fn(a) -> Bool
/// Retrieve the probability of an Event within a Dist occuring.
pub fn probability_of_event(event: Event(a), dist: Dist(a)) -> Prob {
dist
|> list.filter(fn(e) { event(e.0) })
|> list.map(fn(e) { e.1 })
|> sum
}
/// Accumulate probabilities for events, presumably of the same `a` value.
fn sum(xs: List(Prob)) -> Prob {
list.fold(xs, 0.0, fn(x, acc) { x +. acc })
}
/// Combine distributions, assuming independence.
pub fn combine_dist(dist1: Dist(a), dist2: Dist(b)) -> Dist(#(a, b)) {
dist1
|> list.flat_map(fn(e1: #(a, Prob)) {
let val1 = pair.first(e1)
let prob1 = pair.second(e1)
dist2
|> list.map(fn(e2: #(b, Prob)) {
let val2 = pair.first(e2)
let prob2 = pair.second(e2)
#(#(val1, val2), prob1 *. prob2)
})
})
}
/// Merge duplicates in a distribution by summing probabilities.
/// "normalize" is a bit overloaded, this function does not ensure
/// that the sum of all probabilities across a Dist is 1.
pub fn normalize(dist: Dist(a)) -> Dist(a) {
list.fold(dist, [], fn(acc: Dist(a), event: #(a, Prob)) {
insert_or_update(event, acc)
})
}
/// A helper to insert or update `event`s in a given `Dist`.
fn insert_or_update(event: #(a, Prob), acc: Dist(a)) -> Dist(a) {
let val = pair.first(event)
let prob = pair.second(event)
case acc {
[] -> [#(val, prob)]
[#(existing_val, existing_prob), ..tail] ->
case existing_val == val {
True -> [#(existing_val, existing_prob +. prob), ..tail]
False -> [
#(existing_val, existing_prob),
..insert_or_update(event, tail)
]
}
}
}
/// Normalize the combinination two `Dist`s.
pub fn combine_dist_normalized(dist1: Dist(a), dist2: Dist(b)) -> Dist(#(a, b)) {
combine_dist(dist1, dist2)
|> normalize
}