30 const DistributionFamily &family()
const override;
31 double mean()
const override {
return p1_ / p2_; }
32 double variance()
const override {
return p1_ / (p2_ * p2_); }
33 double rawMoment(
unsigned k)
const override {
34 if (k == 0)
return 1.0;
35 if (k == 1)
return mean();
39 for (
unsigned i = 0; i < k; ++i) rising *= (s + static_cast<double>(i));
40 return rising / std::pow(p2_,
static_cast<double>(k));
42 double pdf(
double c)
const override {
43 const double s = p1_, lambda = p2_;
44 if (s < 1.0 || s != std::floor(s) || !(lambda > 0.0))
return kNaN;
45 if (c < 0.0)
return 0.0;
46 const unsigned long k =
static_cast<unsigned long>(s);
48 for (
unsigned long i = 2; i < k; ++i) fact *= static_cast<double>(i);
49 return std::pow(lambda,
static_cast<double>(k))
50 * std::pow(c,
static_cast<double>(k - 1))
51 * std::exp(-lambda * c) / fact;
53 double cdf(
double c)
const override {
54 const double s = p1_, lambda = p2_;
55 if (s < 1.0 || s != std::floor(s))
return kNaN;
56 if (c <= 0.0)
return 0.0;
57 const unsigned long k =
static_cast<unsigned long>(s);
58 const double lc = lambda * c;
59 double term = 1.0, sum = 1.0;
60 for (
unsigned long n = 1; n < k; ++n) {
61 term *= lc /
static_cast<double>(n);
64 return 1.0 - std::exp(-lc) * sum;
66 DistSupport support()
const override {
return {0.0,
kInf}; }
67 bool integrationRange(
double &lo,
double &hi)
const override {
68 if (!(p1_ >= 1.0 && p2_ > 0.0))
return false;
70 hi = (p1_ + 12.0 * std::sqrt(p1_)) / p2_;
73 std::pair<double, double> plotRange(
double trunc_lo,
double trunc_hi)
const override {
74 double lo = trunc_lo, hi = trunc_hi;
75 if (!std::isfinite(lo)) lo = 0.0;
76 if (!std::isfinite(hi)) hi = std::max(2.0 * p1_ / p2_, 6.0 / p2_);
79 double sample(std::mt19937_64 &rng)
const override {
81 std::gamma_distribution<double> d(p1_, 1.0 / p2_);
84 std::unique_ptr<Distribution> affine(
double a,
double b)
const override {
86 if (!(a > 0.0) || b != 0.0)
return nullptr;
89 if (p1_ < 1.0 || p1_ != std::floor(p1_))
return nullptr;
90 return std::make_unique<ErlangDistribution>(p1_, p2_ / a);
92 std::string serialise()
const override {
95 if (p1_ >= 1.0 && p1_ == std::floor(p1_))
96 return "erlang:" + std::to_string(
static_cast<unsigned long>(p1_))
103 "erlang", 2,
"Erl", {
"k",
"λ"},
104 +[](
double p1,
double p2) -> std::unique_ptr<Distribution> {
105 return std::make_unique<ErlangDistribution>(p1, p2);
110 return erlang_family;
117std::unique_ptr<Distribution>
118erlangSumRule(
const std::vector<ClosureTerm> &terms)
120 double lambda =
kNaN;
121 unsigned long total_shape = 0;
122 for (
const auto &t : terms) {
123 if (!t.dist)
return nullptr;
124 if (t.a != 1.0 || t.b != 0.0)
return nullptr;
126 unsigned long w_shape;
127 if (std::strcmp(t.dist->family().name,
"exponential") == 0) {
128 w_lambda = t.dist->p1();
133 if (t.dist->p1() < 1.0 || t.dist->p1() != std::floor(t.dist->p1()))
135 w_lambda = t.dist->p2();
136 w_shape =
static_cast<unsigned long>(t.dist->p1());
138 if (std::isnan(lambda)) lambda = w_lambda;
139 else if (lambda != w_lambda)
return nullptr;
140 total_shape += w_shape;
142 return std::make_unique<ErlangDistribution>(
143 static_cast<double>(total_shape), lambda);
146[[maybe_unused]]
const ClosureRuleRegistrar erlang_sum_rules[] = {
147 {
"exponential",
"exponential", &erlangSumRule},
148 {
"exponential",
"erlang", &erlangSumRule},
149 {
"erlang",
"exponential", &erlangSumRule},
150 {
"erlang",
"erlang", &erlangSumRule},
159bool erlangRateConjugateUpdate(
double &k,
double &lambda,
160 const DistributionTemplate &lik,
double d)
162 const double k0 = lik.p1.literal;
163 if (k0 < 1.0 || k0 != std::floor(k0))
return false;
164 if (!(k > 0.0) || !(lambda > 0.0) || !(d > 0.0))
return false;
170double erlangRateLogPredictive(
double k,
double lambda,
171 const DistributionTemplate &lik,
double d)
173 const double k0 = lik.p1.literal;
174 if (k0 < 1.0 || k0 != std::floor(k0))
return kNaN;
175 if (!(k > 0.0) || !(lambda > 0.0) || !(d > 0.0))
return kNaN;
176 return std::lgamma(k + k0) - std::lgamma(k) - std::lgamma(k0)
177 + k * std::log(lambda) + (k0 - 1.0) * std::log(d)
178 - (k + k0) * std::log(lambda + d);
181[[maybe_unused]]
const ConjugateRuleRegistrar erlang_rate_conjugate(
182 "erlang", 1,
"gamma",
183 {&erlangRateConjugateUpdate, &erlangRateLogPredictive});
185[[maybe_unused]]
const DistributionFamilyRegistrar erlang_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)
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.