A C++17 library for modelling probability spaces, random variables, and joint
distributions from first principles. Built without STL containers — every data
structure (Vector, String, BitSet, HeterogeneousContainer, …) and every
numerical routine (trapezoidal integration, Lanczos approximation of
cd source
make # produces ./main
make run # builds and runs
make cleanRequires only g++ with C++17 support. No external dependencies, no CMake.
source/main.cpp is a scratchpad of test1..test18 demonstrations; main()
currently calls test17 (correlated bivariate Bernoulli) and test18
(bivariate Uniform) — edit it to exercise other distributions.
The codebase is organised bottom-up in six layers:
data_structures → events + sigma_algebra → functions
↓
distributions
↓
independence
Generic primitives written from scratch:
-
Vector<T>,Pair<T,U>,String,HeterogeneousContainer<T>(aT**that preserves dynamic type viaclone()). -
BitSet— compact set of non-negative integers;|=,&=,%=are union / intersection / set-difference (note:%=is difference, not modulo). -
Interval— closed$[a, b]$ with union and intersection. -
Integral(abstract) +TrapezoidalRuleIntegral— numerical integration with$n$ sub-intervals (default$10^6$ , seeConstants.h). -
special_functions/Gamma—$\Gamma(x)$ and$\ln\Gamma(x)$ via the Lanczos approximation with reflection formula for$x < 0.5$ . -
combinatorics/—FactorielandKSelection(combinations, variations, permutations, with/without repetition).
-
ElementaryEvent— atomic outcome with auto-generated unique id. -
Event— subset of$\Omega$ , backed by both aVector<ElementaryEvent>and aBitSetfor$O(1)$ id-membership tests.|and&are real set operations. -
Omega— distinguished event representing the whole sample space. -
FullGroupOfEvents— a partition of$\Omega$ (hypotheses). -
SigmaAlgebra+SigmaAlgebraFactory— generates a$\sigma$ -algebra from anOmegausing aSigmaAlgebraPattern(Trivial${\emptyset, \Omega}$ orPowerSet$2^\Omega$ , capped atPOWER_SET_MAX_ELEMENTS = 20). -
ProbabilitySpace— the triple$(\Omega, \Sigma, P)$ .
A templated callable hierarchy rooted at Function<T, U>:
-
ProbabilityFunction : Function<Event, double>— probability measure$P: \Sigma \to [0,1]$ , stored as aVector<Pair<Event, double>>. -
ConditionalProbabilityFunction— extends the above with$P(A\mid B) = P(A \cap B) / P(B)$ . -
FullProbabilityFormula— composes both into$P(A) = \sum_i P(A \mid H_i), P(H_i)$ . -
DensityFunction : Function<double, double>and its specialisations:UniformDensityFunction,NormalDensityFunction,ExponentialDensityFunction,GammaDensityFunction,ChiSquaredDensityFunction,StudentTDensityFunction. Heavy PDFs use a log-trick throughlogGammafor numerical stability.
Random variables built on top of the function layer.
RandomVariable<T> exposes calculateProbability, getExpectation,
getVariance, getOverallType. It splits into:
Discrete (DiscreteRandomVariable<T>) |
Continuous (ContinuousRandomVariable<Interval>) |
|---|---|
Bernoulli(p) — |
Uniform(a,b) — |
Binomial(n,p) — |
Normal(\mu,\sigma^2) — |
Geometric(p) — |
Exponential(\lambda) — |
NegativeBinomial(r,p) — |
Gamma(\alpha,\beta) — |
Poisson(\lambda) — |
ChiSquared(k) — |
HyperGeometric(N,K,n) — |
StudentT(\nu) — |
Bernoulli, Binomial, Geometric, and NegativeBinomial share the
BernoulliSchemeRandomVariable<T> base. Continuous distributions evaluate
Constants.h
(NORMAL_SIGMA_CUTOFF_MULTIPLIER, EXPONENTIAL_CUTOFF_MULTIPLIER, …).
Joint distributions live in distributions/joint_distributions/:
JointDiscreteDistribution<T>— marginals + per-variable supports + either an explicit PMF table or a functor. ExposesjointProbability,marginal,conditional,covariance,correlation,areIndependent.JointContinuousDistribution— marginals + supports + PDF functor. Uses tensor trapezoidal integration over each dimension.
The mode of construction is captured by JointDistributionMode
(Independent / TableGeneral / FunctorGeneral).
-
EventsIndependence— pairwise:$|P(A \cap B) - P(A)P(B)| \le \varepsilon$ . -
TotalityEventsIndependence— mutual independence over$k$ events; enumerates all non-trivial subsets via bitmasks and verifies$P\bigl(\bigcap_{i \in S} A_i\bigr) = \prod_{i \in S} P(A_i)$ .
All numerical knobs live in source/Constants.h:
| Constant | Purpose |
|---|---|
EPS, INDEPENDENCE_CHECK_TOLERANCE
|
Floating-point comparison tolerances |
COUNT_OF_SUB_INTERVALS |
Trapezoidal rule resolution ( |
POWER_SET_MAX_ELEMENTS, SIGMA_ALGEBRA_SIZE_LIMIT
|
Combinatorial guards |
*_CUTOFF_MULTIPLIER |
How far past the mean to truncate unbounded supports |
JOINT_CONTINUOUS_STEPS_PER_DIMENSION |
Resolution of tensor integration |
ContinuousRandomVariable<double>* normal = new Normal(0, 1);
std::cout << normal->getExpectation() << "\n"; // 0
std::cout << normal->getVariance() << "\n"; // 1
std::cout << normal->calculateProbability(Interval(-1, 1)); // ≈ 0.6827
delete normal;See source/main.cpp for further worked examples covering every distribution
and both joint distribution flavours.