Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

A Field Guide to Distributions

A distribution is a shape and a rule for drawing from it. The shape is its density — where values are likely to land. The rule is its sampler — how a stream of random numbers becomes draws. Every distribution below is one note in fugue's vocabulary; pick one and hear it.

The blue curve is the exact law. The green bars are samples piling up. Watch them converge — that is the law of large numbers, live.

Things to try

  1. Start on Normal and drag σ up to 3 — the bell flattens and spreads, but the green samples still trace it.
  2. Switch to Beta and set α and β both below 1 — the density turns into a U, piling mass at the two edges.
  3. Open Cauchy. Its mean and variance readouts both read : the tails are so heavy that neither integral converges. The samples wander wildly.
  4. Pick Bernoulli and read the sample → badge: it says bool. Fugue gives you a real boolean, not a float you have to compare against 1.0.
  5. Drag Uniform's high below its low. The canvas turns red — this is exactly the Err that Uniform::new returns for an invalid interval.
  6. On any distribution, drop the seed back to a value you already used. The green histogram redraws the identical run: a seeded stream is a replayable trace.

What you just saw

For a continuous distribution the blue curve is the probability density f(x). It is not a probability — it is a density, so it can exceed 1. What integrates to 1 is the area:

For a discrete distribution the blue stems are the probability mass P(x), one bar per outcome, and the bars sum to 1:

The green bars are an empirical estimate of that same law built from samples. As the sample count grows they converge on the blue — the law of large numbers. The coral line is a single query: it reports at the x you drag to. Inference works in log-space because a product of thousands of these densities underflows to zero in ordinary floating point; a sum of their logs does not.

The markers name three summaries of the shape: the mean (the balance point), the median (half the mass on each side), and the mode (the peak). For a skewed law like LogNormal they sit in different places; for a symmetric one they coincide.

Fugue's type story

Most PPLs make every draw a f64, so a coin flip comes back as 1.0 and you compare floats; a count comes back as 4.0 and you round it. Fugue draws return their natural type, checked at compile time.

use fugue::*;
use rand::thread_rng;

fn main() {
    let mut rng = thread_rng();

    // Bernoulli -> bool. The coral query line is `log_prob` on one outcome.
    let coin = Bernoulli::new(0.5).unwrap();
    let heads: bool = coin.sample(&mut rng);
    let lp_true: f64 = coin.log_prob(&true); // ln(0.5)
    if heads { /* no `== 1.0`; it's already a bool */ }

    // Poisson -> u64. Counts are counts, not rounded floats.
    let arrivals: u64 = Poisson::new(4.0).unwrap().sample(&mut rng);
    let total_wait = arrivals * 10; // integer arithmetic, no cast

    // Categorical -> usize. Safe to index an array with, by construction.
    let pick: usize = Categorical::new(vec![0.5, 0.3, 0.2]).unwrap().sample(&mut rng);
    let labels = ["a", "b", "c"];
    let chosen = labels[pick];

    // Continuous laws return f64, as expected.
    let x: f64 = Normal::new(0.0, 1.0).unwrap().sample(&mut rng);
    let density: f64 = Normal::new(0.0, 1.0).unwrap().log_prob(&x);

    println!("{heads} {arrivals} {total_wait} {chosen} {x:.3} {density:.3} {lp_true:.3}");
}

Every constructor is fallible: Normal::new, Beta::new, Uniform::new and the rest return Result, so an invalid parameter (a negative σ, a low ≥ high) is an Err you handle, never a silent NaN. That is the red screen in try #5.

Inside a model, a draw is a sample at a named address; its distribution rides along and fugue scores it for you:

#![allow(unused)]
fn main() {
use fugue::*;

// Estimate a coin's bias, then predict the next flip's count over 10 tosses.
let model = prob!(
    let p <- sample(addr!("bias"), Beta::new(2.0, 2.0).unwrap());
    let hits <- sample(addr!("hits"), Binomial::new(10, p).unwrap());
    pure(hits)
);
}

The full field guide

Every distribution in fugue. The sample → column is the natural return type.

Continuous

DistributionSupportParameterssample →Reach for it when
Normal(−∞, ∞)mu, sigma > 0f64you want a symmetric bell — noise, error, a CLT limit.
Uniform[low, high)low < highf64you want a flat prior on a bounded interval.
LogNormal(0, ∞)mu, sigma > 0f64a positive, right-skewed quantity whose log is Normal.
Exponential[0, ∞)rate > 0f64the wait until the next memoryless event; mean = 1/rate.
Beta[0, 1]alpha, beta > 0f64a probability about a probability — the coin-bias prior.
Gamma(0, ∞)shape, rate > 0f64positive quantities; rate-parameterized, mean = shape/rate.
StudentT(−∞, ∞)df, loc, scale > 0f64a heavier-tailed Normal that tolerates outliers.
Cauchy(−∞, ∞)loc, scale > 0f64pathological tails — no mean, no variance. StudentT(df=1).
Laplace(−∞, ∞)loc, scale > 0f64a sharp peak with exponential tails; the L1 / lasso prior.
Weibull[0, ∞)shape, scale > 0f64time-to-failure and survival modeling.
ChiSquared(0, ∞)k > 0f64sums of squared Normals; = Gamma(k/2, ½).
InverseGamma(0, ∞)shape, rate > 0f64the conjugate prior for a Normal's variance.

Discrete

DistributionSupportParameterssample →Reach for it when
Bernoulli{0, 1}p ∈ [0, 1]boolone yes/no trial — and you want a real bool.
Categorical{0 … K−1}probs sum to 1usizepicking one of K labels; index arrays safely.
Binomial{0 … n}n, p ∈ [0, 1]u64successes in n independent trials.
Poisson{0, 1, 2, …}lambda > 0u64rare-event counts; mean = variance = λ.
DiscreteUniform{low … high}lowhighi64a fair die over an integer range.

Gamma is rate-parameterized

Fugue's Gamma::new(shape, rate) uses rate (λ), not scale, so the mean is shape / rate. Exponential, InverseGamma, and ChiSquared follow the same rate convention. If a value looks inverted, check whether you meant scale = 1/rate.

Go deeper


Next: Explorables