simsam 0.1.0

Sample from custom discrete and continuous distributions (SciPy-like API)
Documentation

simsam

Sim(ple)sam(ple) — a Rust library for sampling from custom discrete and continuous distributions.

Define distributions by PDF or CDF (closures, histograms, location-scale transforms, truncation, or simsym symbolic expressions), then draw samples via inverse transform sampling — similar to SciPy rv_continuous.

Features

Sampling

  • sample() / sample_with_rng() — inverse transform (default: bisection + Newton)
  • BuildOptions::with_hermite(n) — fast PPF table (SciPy stats.sampling-style numerical inversion)
  • BuildOptions::with_tdr() — transformed density rejection (TDR); automatic numerical dPDF when only PDF is given
  • from_pdf_dpdf_fn — explicit dPDF for TDR (log-concave hat construction)
  • rand::distr::Distribution integration
  • Multivariate sampling: rejection sampling, Metropolis–Hastings (MH), Gibbs, HMC
  • Multivariate CDF approximation via Monte Carlo
  • Gaussian copula for correlated samples from arbitrary 1D marginals

Choosing a 1D sampler

Goal Method
Default / need accurate ppf BuildOptions::default() (inverse transform)
Many samples, smooth unimodal CDF BuildOptions::default().with_hermite(128)
Have PDF (+ optional dPDF), rejection-friendly density BuildOptions::default().with_tdr()

Note: ppf, mean, and other statistics always use numerical inverse CDF even when TDR is selected for sample().

Distribution API (SciPy-like)

Method Description
pdf, logpdf Density (when PDF is available)
cdf, logcdf Cumulative distribution
sf, logsf, isf Survival functions
ppf Percent point function (inverse CDF)
mean, var, std, median Summary statistics
entropy, expect, interval Entropy, E[f(X)], confidence interval

Constructors

simsam SciPy analogue
from_pdf_fn / from_cdf_fn Subclass rv_continuous
from_pdf_dpdf_fn TDR with explicit dPDF
from_histogram rv_histogram
from_pdf_loc_scale loc / scale parameters
Truncated Truncate to sub-interval
SymbolicContinuous Custom _pdf + symbolic CAS (via simsym)
DiscreteSampler::from_pmf rv_discrete(values=...)

Cargo features

  • Default: no symbolic dependency
  • symbolic: enables SymbolicContinuous and SymbolicPdfNd

Examples

PDF + statistics

use simsam::{from_pdf_fn, Interval};

let support = Interval::new(0.0, 1.0).unwrap();
let dist = from_pdf_fn(|x| 3.0 * x * x, support).unwrap();
let x = dist.sample().unwrap();
let m = dist.mean().unwrap();   // 0.75
let v = dist.var().unwrap();
let (lo, hi) = dist.interval(0.9).unwrap();

Fast Hermite sampling

use simsam::{from_pdf_fn_with_options, BuildOptions, Interval};

let opts = BuildOptions::default().with_hermite(128);
let dist = from_pdf_fn_with_options(|x| 2.0 * x, support, opts).unwrap();
for _ in 0..10_000 {
    let _ = dist.sample().unwrap();
}

TDR sampling

use simsam::{from_pdf_fn_with_options, BuildOptions, Interval, TdrBuildConfig, TdrTransform};

let support = Interval::new(-1.0, 1.0).unwrap();
let dist = from_pdf_fn_with_options(
    |x| 1.0 - x * x,
    support,
    BuildOptions::default().with_tdr_config(TdrBuildConfig {
        transform: TdrTransform::InvSqrt,
        ..TdrBuildConfig::default()
    }),
)
.unwrap();
cargo run --example tdr_quadratic
cargo run --example sampling_methods

Histogram

use simsam::{from_histogram, BuildOptions};

let edges = vec![0.0, 1.0, 2.0];
let counts = vec![1.0, 3.0];
let dist = from_histogram(edges, counts, false, BuildOptions::default()).unwrap();

Location-scale

use simsam::{from_pdf_loc_scale, PdfFn, BuildOptions, Interval};

let base = Interval::new(0.0, 1.0).unwrap();
let dist = from_pdf_loc_scale(PdfFn::new(|_| 1.0, base), 10.0, 2.0, BuildOptions::default()).unwrap();
// Y = 10 + 2 * Uniform(0, 1) on [10, 12]

Symbolic (simsym)

use simsam::{BuildOptions, Interval, SymbolicContinuous};
use simsym::prelude::*;

let x = symbol("x");
let pdf = rational(2, 1) * x;
let sym = SymbolicContinuous::with_defaults(pdf, x, Interval::new(0.0, 1.0).unwrap()).unwrap();
let dist = sym.sampler(BuildOptions::default()).unwrap();

Run (symbolic examples are gated):

cargo run --example symbolic --features symbolic

Multivariate: rejection sampling (2D uniform)

cargo run --example multivar_rejection_uniform2d

Multivariate: Metropolis–Hastings (2D uniform)

cargo run --example multivar_mh_uniform2d

Multivariate: HMC (2D truncated Gaussian; numeric gradient)

cargo run --example multivar_hmc_gaussian2d

Multivariate: Gibbs (independent uniforms via conditionals)

cargo run --example multivar_gibbs_independent_uniforms

Multivariate: CDF approximation (Monte Carlo)

cargo run --example multivar_cdf_mc_uniform2d

Copula: Gaussian copula with custom marginals

cargo run --example multivar_copula_gaussian

Conditional factorization sampler

cargo run --example multivar_factorization

Multivariate symbolic HMC (symbolic gradient)

cargo run --example multivar_symbolic_hmc --features symbolic

Python

Python bindings live in python/ as a separate maturin/PyO3 crate. The root Rust crate has no PyO3 dependency, so cargo publish from the repo root is unchanged.

Setup

cd python
uv sync
uv run maturin develop --release
uv run pytest

Usage

import simsam

dist = simsam.from_pdf(lambda x: 2.0 * x, simsam.Interval(0.0, 1.0))
print(dist.mean(), dist.sample_n(10_000))

opts = simsam.BuildOptions.hermite(128)
dist = simsam.from_pdf_with_options(lambda x: 3.0 * x * x, simsam.Interval(0, 1), opts)

mh = simsam.metropolis_hastings(
    lambda x, y: 1.0,
    simsam.HyperRect([0.0, 0.0], [1.0, 1.0]),
)
print(mh.sample_n(1000))

Optional simsym symbolic PDFs (requires building with the symbolic Cargo feature):

uv run maturin develop --release --features symbolic,pyo3/extension-module
dist = simsam.symbolic_continuous("3 * x^2", "x", simsam.Interval(0.0, 1.0))

See python/README.md for the full Python API.

Limitations

  • Finite support required; use a wide interval + Truncated for partial ranges.
  • Unimodal CDF assumed for inverse transform on continuous distributions.
  • No built-in catalog of named distributions (use rand_distr / statrs), multivariate laws, KDE, or fit(data).

License

BSD-3-Clause — see LICENSE.