25double factorial(
unsigned k)
28 for (
unsigned i = 2; i <= k; ++i) r *= static_cast<double>(i);
45double truncated_exponential_raw_moment(
double lambda,
double a,
double b,
48 const double aa = std::max(a, 0.0);
49 if (std::isfinite(b)) {
50 if (b <= aa)
return kNaN;
52 const double e_a = std::exp(-lambda * aa);
53 const double e_b = std::exp(-lambda * b);
54 const double Z = e_a - e_b;
55 if (Z < 1e-12)
return kNaN;
56 if (k == 0)
return 1.0;
64 std::vector<double> J(k + 1, 0.0);
66 for (
unsigned m = 1; m <= k; ++m) {
67 const double endpoint = std::pow(aa,
static_cast<double>(m)) * e_a
68 - std::pow(b,
static_cast<double>(m)) * e_b;
69 J[m] = endpoint / lambda + (m / lambda) * J[m - 1];
71 return lambda * J[k] / Z;
76 for (
unsigned i = 0; i <= k; ++i) {
78 * std::pow(aa,
static_cast<double>(k - i))
79 * fact_i / std::pow(lambda,
static_cast<double>(i));
89 const DistributionFamily &family()
const override;
90 double mean()
const override {
return 1.0 / p1_; }
91 double variance()
const override {
return 1.0 / (p1_ * p1_); }
92 double rawMoment(
unsigned k)
const override {
93 if (k == 0)
return 1.0;
94 if (k == 1)
return mean();
95 return factorial(k) / std::pow(p1_,
static_cast<double>(k));
97 double pdf(
double c)
const override {
98 if (!(p1_ > 0.0))
return kNaN;
99 return (c < 0.0) ? 0.0 : p1_ * std::exp(-p1_ * c);
101 double cdf(
double c)
const override {
102 return (c <= 0.0) ? 0.0 : -std::expm1(-p1_ * c);
104 DistSupport support()
const override {
return {0.0,
kInf}; }
105 bool integrationRange(
double &lo,
double &hi)
const override {
106 if (!(p1_ > 0.0))
return false;
111 std::pair<double, double> plotRange(
double trunc_lo,
double trunc_hi)
const override {
112 double lo = trunc_lo, hi = trunc_hi;
113 if (!std::isfinite(lo)) lo = 0.0;
114 if (!std::isfinite(hi)) hi = 6.0 / p1_;
117 double sample(std::mt19937_64 &rng)
const override {
118 std::exponential_distribution<double> d(p1_);
121 std::optional<double> quantile(
double p)
const override {
122 if (!(p1_ > 0.0))
return std::nullopt;
124 return -std::log1p(-p) / p1_;
126 std::optional<double> truncatedRawMoment(
double lo,
double hi,
127 unsigned k)
const override {
128 const double r = truncated_exponential_raw_moment(p1_, lo, hi, k);
129 if (std::isnan(r))
return std::nullopt;
132 std::optional<std::vector<double>> sampleTruncated(
133 std::mt19937_64 &rng,
double lo,
double hi,
unsigned n)
const override {
134 const double lambda = p1_;
135 if (!(lambda > 0.0))
return std::nullopt;
136 std::vector<double> out;
138 if (std::isinf(hi)) {
142 std::exponential_distribution<double> E(lambda);
143 for (
unsigned i = 0; i < n; ++i) out.push_back(lo + E(rng));
149 const double F_lo = -std::expm1(-lambda * lo);
150 const double F_hi = -std::expm1(-lambda * hi);
151 if (!(F_lo < F_hi))
return std::nullopt;
152 std::uniform_real_distribution<double> U01(0.0, 1.0);
153 for (
unsigned i = 0; i < n; ++i) {
154 const double u = F_lo + U01(rng) * (F_hi - F_lo);
155 out.push_back(-std::log1p(-u) / lambda);
159 std::optional<double> iidOrderStatMean(std::size_t n,
160 bool isMax)
const override {
161 const double lambda = p1_;
165 return 1.0 / (
static_cast<double>(n) * lambda);
167 for (std::size_t k = 1; k <= n; ++k)
168 H += 1.0 /
static_cast<double>(k);
171 std::unique_ptr<Distribution> affine(
double a,
double b)
const override {
175 if (!(a > 0.0) || b != 0.0)
return nullptr;
176 return std::make_unique<ExponentialDistribution>(p1_ / a, 0.0);
178 std::string serialise()
const override {
184 "exponential", 1,
"Exp", {
"λ",
nullptr},
185 +[](
double p1,
double p2) -> std::unique_ptr<Distribution> {
186 return std::make_unique<ExponentialDistribution>(p1, p2);
191 return exponential_family;
195double exponentialPairLess(
const Distribution &X,
const Distribution &Y)
197 const double lx = X.p1(), ly = Y.p1();
198 if (!(lx > 0.0 && ly > 0.0))
return kNaN;
199 return lx / (lx + ly);
202[[maybe_unused]]
const ComparatorRuleRegistrar exponential_less_rule(
203 "exponential",
"exponential", &exponentialPairLess);
209bool exponentialRateConjugateUpdate(
double &k,
double &lambda,
210 const DistributionTemplate &,
double d)
212 if (!(k > 0.0) || !(lambda > 0.0) || !(d >= 0.0))
return false;
218double exponentialRateLogPredictive(
double k,
double lambda,
219 const DistributionTemplate &,
double d)
221 if (!(k > 0.0) || !(lambda > 0.0) || !(d >= 0.0))
return kNaN;
222 return std::log(k) + k * std::log(lambda)
223 - (k + 1.0) * std::log(lambda + d);
226[[maybe_unused]]
const ConjugateRuleRegistrar exponential_rate_conjugate(
227 "exponential", 0,
"gamma",
228 {&exponentialRateConjugateUpdate, &exponentialRateLogPredictive});
230[[maybe_unused]]
const DistributionFamilyRegistrar exponential_family_registrar(
Internal helpers shared by the per-family Distribution implementations under src/distributions/.
Base holding the two parameters; subclasses add closed forms.
BaseDistribution(double p1, double p2)
double binomial_coeff(unsigned n, unsigned k)
C(n, k) as a double (exact for the small moment orders used here).
std::string double_to_text(double v)
Format a double back into the canonical text form used by gate_value extras and gate_rv distribution ...
A registered family's descriptor: its complete identity.