37 const DistributionFamily &family()
const override;
38 double mean()
const override {
39 return std::exp(p1_ + 0.5 * p2_ * p2_);
41 double variance()
const override {
42 const double s2 = p2_ * p2_;
43 return std::expm1(s2) * std::exp(2.0 * p1_ + s2);
45 double rawMoment(
unsigned k)
const override {
46 if (k == 0)
return 1.0;
48 const double kd =
static_cast<double>(k);
49 return std::exp(kd * p1_ + 0.5 * kd * kd * p2_ * p2_);
51 double pdf(
double c)
const override {
52 if (!(p2_ > 0.0))
return kNaN;
53 if (c <= 0.0)
return 0.0;
54 const double z = (std::log(c) - p1_) / p2_;
55 return std::exp(-0.5 * z * z)
56 / (c * p2_ * std::sqrt(2.0 * M_PI));
58 double cdf(
double c)
const override {
59 if (!(p2_ > 0.0))
return kNaN;
60 if (c <= 0.0)
return 0.0;
61 return Phi((std::log(c) - p1_) / p2_);
63 DistSupport support()
const override {
return {0.0,
kInf}; }
64 bool integrationRange(
double &lo,
double &hi)
const override {
65 if (!(p2_ > 0.0))
return false;
72 hi = std::exp(p1_ + 8.0 * p2_);
75 std::pair<double, double> plotRange(
double trunc_lo,
double trunc_hi)
const override {
76 double lo = trunc_lo, hi = trunc_hi;
77 if (!std::isfinite(lo)) lo = 0.0;
78 if (!std::isfinite(hi)) hi = std::exp(p1_ + 3.0 * p2_);
81 double sample(std::mt19937_64 &rng)
const override {
82 std::lognormal_distribution<double> d(p1_, p2_);
85 std::optional<double> quantile(
double p)
const override {
86 if (!(p2_ > 0.0))
return std::nullopt;
89 for (
int i = 0; i < 2; ++i) {
90 const double d =
phi(z);
91 if (!(d > 0.0))
break;
92 z -= (
Phi(z) - p) / d;
94 return std::exp(p1_ + p2_ * z);
96 std::optional<double> truncatedRawMoment(
double lo,
double hi,
97 unsigned k)
const override {
98 if (!(p2_ > 0.0))
return std::nullopt;
103 if (std::isfinite(hi) && hi <= 0.0)
return std::nullopt;
104 const double alpha = (std::isfinite(lo) && lo > 0.0)
105 ? (std::log(lo) - p1_) / p2_ : -
kInf;
106 const double beta = std::isfinite(hi)
107 ? (std::log(hi) - p1_) / p2_ :
kInf;
108 const double Phi_a = std::isfinite(alpha) ?
Phi(alpha) : 0.0;
109 const double Phi_b = std::isfinite(beta) ?
Phi(beta) : 1.0;
110 const double mass = Phi_b - Phi_a;
111 if (mass < 1e-12)
return std::nullopt;
112 if (k == 0)
return 1.0;
113 const double kd =
static_cast<double>(k);
114 const double shifted_a =
115 std::isfinite(alpha) ?
Phi(alpha - kd * p2_) : 0.0;
116 const double shifted_b =
117 std::isfinite(beta) ?
Phi(beta - kd * p2_) : 1.0;
118 return std::exp(kd * p1_ + 0.5 * kd * kd * p2_ * p2_)
119 * (shifted_b - shifted_a) / mass;
121 std::optional<std::vector<double>> sampleTruncated(
122 std::mt19937_64 &rng,
double lo,
double hi,
unsigned n)
const override {
123 if (!(p2_ > 0.0))
return std::nullopt;
127 if (std::isfinite(hi) && hi <= 0.0)
return std::nullopt;
128 const double alpha = (std::isfinite(lo) && lo > 0.0)
129 ? (std::log(lo) - p1_) / p2_ : -
kInf;
130 const double beta = std::isfinite(hi)
131 ? (std::log(hi) - p1_) / p2_ :
kInf;
132 const double Phi_a = std::isfinite(alpha) ?
Phi(alpha) : 0.0;
133 const double Phi_b = std::isfinite(beta) ?
Phi(beta) : 1.0;
134 if (!(Phi_a < Phi_b))
return std::nullopt;
135 static constexpr double EPS = 1e-15;
136 std::uniform_real_distribution<double> U01(0.0, 1.0);
137 std::vector<double> out;
139 for (
unsigned i = 0; i < n; ++i) {
140 double u = Phi_a + U01(rng) * (Phi_b - Phi_a);
141 if (u < EPS) u = EPS;
142 if (u > 1.0 - EPS) u = 1.0 - EPS;
143 out.push_back(std::exp(p1_ + p2_ *
inv_phi(u)));
147 std::unique_ptr<Distribution> affine(
double a,
double b)
const override {
151 if (!(a > 0.0) || b != 0.0)
return nullptr;
152 return std::make_unique<LogNormalDistribution>(p1_ + std::log(a), p2_);
154 std::string serialise()
const override {
157 std::optional<double> asDirac()
const override {
158 if (p2_ == 0.0)
return std::exp(p1_);
164 "lognormal", 2,
"LogN", {
"μ",
"σ"},
165 +[](
double p1,
double p2) -> std::unique_ptr<Distribution> {
166 return std::make_unique<LogNormalDistribution>(p1, p2);
171 return lognormal_family;
177double lognormalPairLess(
const Distribution &X,
const Distribution &Y)
180 std::sqrt(X.p2() * X.p2() + Y.p2() * Y.p2());
181 return Phi((Y.p1() - X.p1()) / denom);
184[[maybe_unused]]
const ComparatorRuleRegistrar lognormal_less_rule(
185 "lognormal",
"lognormal", &lognormalPairLess);
189std::unique_ptr<Distribution>
190lognormalProductRule(
const std::vector<const Distribution *> &factors)
192 double mu = 0.0, var = 0.0;
193 for (
const Distribution *f : factors) {
195 var += f->p2() * f->p2();
197 return std::make_unique<LogNormalDistribution>(mu, std::sqrt(var));
200[[maybe_unused]]
const ProductRuleRegistrar lognormal_product_rule(
201 "lognormal",
"lognormal", &lognormalProductRule);
207std::unique_ptr<Distribution> expOfNormal(
const Distribution &x)
209 return std::make_unique<LogNormalDistribution>(x.p1(), x.p2());
212std::unique_ptr<Distribution> lnOfLogNormal(
const Distribution &x)
215 return normal ? normal->factory(x.p1(), x.p2()) :
nullptr;
218[[maybe_unused]]
const TransformRuleRegistrar exp_normal_rule(
219 "exp",
"normal", &expOfNormal);
220[[maybe_unused]]
const TransformRuleRegistrar ln_lognormal_rule(
221 "ln",
"lognormal", &lnOfLogNormal);
228bool lognormalMuConjugateUpdate(
double &mu,
double &sigma,
229 const DistributionTemplate &lik,
double d)
231 const double s = lik.p2.literal;
232 if (!(s > 0.0) || !(sigma > 0.0) || !(d > 0.0))
return false;
233 const double ld = std::log(d);
234 const double tau0 = 1.0 / (sigma * sigma);
235 const double taul = 1.0 / (s * s);
236 mu = (tau0 * mu + taul * ld) / (tau0 + taul);
237 sigma = std::sqrt(1.0 / (tau0 + taul));
241double lognormalMuLogPredictive(
double mu,
double sigma,
242 const DistributionTemplate &lik,
double d)
244 const double s = lik.p2.literal;
245 if (!(s > 0.0) || !(sigma > 0.0) || !(d > 0.0))
return kNaN;
246 const double v = sigma * sigma + s * s;
247 const double ld = std::log(d);
248 return -0.5 * ((ld - mu) * (ld - mu) / v + std::log(2.0 * M_PI * v)) - ld;
251[[maybe_unused]]
const ConjugateRuleRegistrar lognormal_mu_conjugate(
252 "lognormal", 0,
"normal",
253 {&lognormalMuConjugateUpdate, &lognormalMuLogPredictive});
255[[maybe_unused]]
const DistributionFamilyRegistrar lognormal_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 phi(double z)
Standard normal pdf φ(z) = exp(-z²/2)/√(2π).
double Phi(double z)
Standard normal CDF Φ(z) = ½(1 + erf(z/√2)).
const DistributionFamily * lookupDistributionFamily(const std::string &name)
Look up a family by its on-disk name token.
double inv_phi(double p)
Inverse standard-normal CDF, Beasley-Springer-Moro (1995).
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.