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.
Summary
Add two distributions whose CDFs require
erf/erfcfor normalisation. Both can use the existingvector_erfSIMD 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.Batch CDF calls
vector_erf(x / (σ√2))— a scalar_multiply thenvector_erf, exactly the same pipeline asGaussianDistributionCDF.Batch PDF and LogPDF use the Gaussian log-space pipeline with a constant offset.
TruncatedNormalDistribution
Parametrisation: μ, σ > 0, lower bound a, upper bound b (a < b; either may be ±∞). Scipy:
truncnorm.The normalisation constant Z is computed once via
std::erfat construction time (or on parameter change). Batch PDF/LogPDF and CDF routes through the Gaussian SIMD batch path, divided by the cached scalar Z.Implementation notes
vector_erfaccuracy, which is already validated at < 1 ULP on all x86 backends and ~2.29 ULP on NEON. The erf accuracy story is pre-existing.TruncatedNormalDistributioncaches α, β, and Z on construction; these must be invalidated when μ, σ, a, or b change.strategy_profileafter implementation.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.