Skip to content

feat: add HalfNormalDistribution and TruncatedNormalDistribution — erf/erfc normalisation family #57

Description

@OldCrow

Summary

Add two distributions whose CDFs require erf/erfc for normalisation. Both can use the existing vector_erf SIMD path once the normalisation constants are computed at construction time.

HalfNormalDistribution

Parametrisation: scale σ > 0. The Half-Normal is the distribution of |X| where X ~ Normal(0, σ). Scipy: halfnorm.

PDF(x) = √(2/π) / σ · exp(-x²/(2σ²))   for x ≥ 0
CDF(x) = erf(x / (σ√2))
log PDF = 0.5·log(2/π) - log(σ) - x²/(2σ²)

Batch CDF calls vector_erf(x / (σ√2)) — a scalar_multiply then vector_erf, exactly the same pipeline as GaussianDistribution CDF.
Batch PDF and LogPDF use the Gaussian log-space pipeline with a constant offset.

  • MLE: σ̂ = √(Σxᵢ²/n)
  • Mean = σ√(2/π), Variance = σ²(1 - 2/π)
  • Use cases: Bayesian scale priors (positive-constrained Normal), measurement error models, absolute-value transforms of symmetric data

TruncatedNormalDistribution

Parametrisation: μ, σ > 0, lower bound a, upper bound b (a < b; either may be ±∞). Scipy: truncnorm.

α = (a - μ) / σ,  β = (b - μ) / σ
Z = Φ(β) - Φ(α) = [erf(β/√2) - erf(α/√2)] / 2   (normalisation constant; scalar, computed at construction)
PDF(x) = φ((x-μ)/σ) / (σZ)
CDF(x) = [Φ((x-μ)/σ) - Φ(α)] / Z

The normalisation constant Z is computed once via std::erf at construction time (or on parameter change). Batch PDF/LogPDF and CDF routes through the Gaussian SIMD batch path, divided by the cached scalar Z.

  • MLE: involves truncated expectations; iterative but well-defined
  • Use cases: physical measurements with known bounds (e.g. reaction times > 0), truncated sampling models, Bayesian posterior computation for constrained parameters
  • Edge cases: a = -∞ gives Half-Normal (if μ = 0); a = -∞, b = +∞ gives Gaussian

Implementation notes

  • Both depend on vector_erf accuracy, which is already validated at < 1 ULP on all x86 backends and ~2.29 ULP on NEON. The erf accuracy story is pre-existing.
  • HalfNormal is simpler and should be implemented first; TruncatedNormal depends on the same erf infrastructure but requires more careful normalisation constant handling.
  • TruncatedNormalDistribution caches α, β, and Z on construction; these must be invalidated when μ, σ, a, or b change.
  • Dispatch thresholds: both follow the Gaussian PDF/CDF pattern once the scalar normalisation is divided out. Set to NEVER initially; calibrate with strategy_profile after implementation.
  • Skew-Normal (f(x) = 2φ(x)Φ(αx)) is a related erf-dependent distribution but requires Owen's T function — deferred to a separate issue due to higher implementation complexity.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions