RandLib 2.0.0
Probability distributions, fitting, and heavy-tail tools for C++20
Loading...
Searching...
No Matches
log_normal.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <span>
4
10
11namespace randlib
12{
13
23template <typename RealType>
24class LogNormalRand final : public ContinuousDistribution<RealType>
25{
26 RealType variate(detail::EngineRef g) const override { return (*this)(g); }
27
29 double expMu = 1;
30 double expHalfSigmaSq = 1.6487212707;
31
32public:
33 LogNormalRand(double location = 0, double squaredScale = 1);
34
40 [[nodiscard]] static LogNormalRand FromLogMeanLogStd(double logMean, double logStandardDeviation)
41 {
42 if (!(logStandardDeviation > 0.0))
43 throw std::invalid_argument("Log-Normal distribution: scale should be positive");
44 return LogNormalRand(logMean, logStandardDeviation * logStandardDeviation);
45 }
46
52 [[nodiscard]] static LogNormalRand FromLogMeanLogVariance(double logMean, double logVariance)
53 {
54 return LogNormalRand(logMean, logVariance);
55 }
56
57 [[nodiscard]] std::string Name() const override;
58 [[nodiscard]] Support SupportType() const override { return Support::RightSemiFinite; }
59 [[nodiscard]] RealType MinValue() const override { return 0; }
60 [[nodiscard]] RealType MaxValue() const override { return INFINITY; }
61
62 void SetLocation(double location);
63 void SetScale(double scale);
64 [[nodiscard]] inline double GetLocation() const { return X.Mean(); }
65 [[nodiscard]] inline double GetScale() const { return X.GetScale(); }
66
67 [[nodiscard]] double f(const RealType &x) const override;
68 [[nodiscard]] double logf(const RealType &x) const override;
69 [[nodiscard]] double F(const RealType &x) const override;
70 [[nodiscard]] double S(const RealType &x) const override;
71
72 template <std::uniform_random_bit_generator G>
73 [[nodiscard]] RealType operator()(G &g) const;
74
75 template <std::uniform_random_bit_generator G>
76 [[nodiscard]] static RealType StandardVariate(G &g);
77
78 [[nodiscard]] long double Mean() const override;
79 [[nodiscard]] long double Variance() const override;
80 [[nodiscard]] RealType Median() const override;
81 [[nodiscard]] RealType Mode() const override;
82 [[nodiscard]] long double Skewness() const override;
83 [[nodiscard]] long double ExcessKurtosis() const override;
84
85private:
86 RealType quantileImpl(double p) const override;
87 RealType quantileImpl1m(double p) const override;
88 std::complex<double> CFImpl(double t) const override;
89
90public:
95 RANDLIB_DEPRECATED_FIT_MEMBER void FitLocation(std::span<const RealType> sample);
100 RANDLIB_DEPRECATED_FIT_MEMBER void FitScale(std::span<const RealType> sample);
105 RANDLIB_DEPRECATED_FIT_MEMBER void Fit(std::span<const RealType> sample);
106
115 FitLocationBayes(std::span<const RealType> sample, const NormalRand<RealType> &priorDistribution, bool MAP = false);
116
125 FitScaleBayes(std::span<const RealType> sample, const InverseGammaRand<RealType> &priorDistribution,
126 bool MAP = false);
127
136 FitBayes(std::span<const RealType> sample, const NormalInverseGammaRand<RealType> &priorDistribution,
137 bool MAP = false);
138};
139
140} // namespace randlib
141
142namespace randlib
143{
144
145template <typename RealType>
146LogNormalRand<RealType>::LogNormalRand(double location, double squaredScale)
147{
148 if (!(squaredScale > 0.0))
149 throw std::invalid_argument("Log-Normal distribution: squared scale should be positive");
150 SetLocation(location);
151 SetScale(std::sqrt(squaredScale));
152}
153
154template <typename RealType>
156{
157 return "Log-Normal(" + this->toStringWithPrecision(GetLocation()) + ", " + this->toStringWithPrecision(GetScale()) +
158 ")";
159}
160
161template <typename RealType>
163{
164 if (!std::isfinite(location))
165 throw std::invalid_argument("Log-Normal distribution: location should be finite");
166 X.SetLocation(location);
167 expMu = std::exp(X.GetLocation());
168}
169
170template <typename RealType>
172{
173 if (!(scale > 0.0))
174 throw std::invalid_argument("Log-Normal distribution: scale should be positive");
175 X.SetScale(scale);
176 expHalfSigmaSq = std::exp(0.5 * X.Variance());
177}
178
179template <typename RealType>
180double LogNormalRand<RealType>::f(const RealType &x) const
181{
182 return (x > 0.0) ? std::exp(logf(x)) : 0.0;
183}
184
185template <typename RealType>
186double LogNormalRand<RealType>::logf(const RealType &x) const
187{
188 if (x <= 0.0)
189 return -INFINITY;
190 double logX = std::log(x);
191 double y = X.logf(logX);
192 return y - logX;
193}
194
195template <typename RealType>
196double LogNormalRand<RealType>::F(const RealType &x) const
197{
198 return (x > 0.0) ? X.F(std::log(x)) : 0.0;
199}
200
201template <typename RealType>
202double LogNormalRand<RealType>::S(const RealType &x) const
203{
204 return (x > 0.0) ? X.S(std::log(x)) : 1.0;
205}
206
207template <typename RealType>
208template <std::uniform_random_bit_generator G>
210{
211 return std::exp(X(g));
212}
213
214template <typename RealType>
215template <std::uniform_random_bit_generator G>
217{
218 return std::exp(detail::standardNormal<RealType>(g));
219}
220
221template <typename RealType>
223{
224 return expMu * expHalfSigmaSq;
225}
226
227template <typename RealType>
229{
230 double y = expMu * expHalfSigmaSq;
231 return y * y * std::expm1l(X.Variance());
232}
233
234template <typename RealType>
235RealType LogNormalRand<RealType>::quantileImpl(double p) const
236{
237 return std::exp(X.Quantile(p));
238}
239
240template <typename RealType>
241RealType LogNormalRand<RealType>::quantileImpl1m(double p) const
242{
243 return std::exp(X.Quantile1m(p));
244}
245
246template <typename RealType>
247std::complex<double> LogNormalRand<RealType>::CFImpl(double t) const
248{
253 static constexpr double NORMAL_RANGE = 10;
254 const double sigma = X.GetScale(), sigmaSq = sigma * sigma;
255 const std::complex<double> w = math::W0Lambert(std::complex<double>(0, -t * sigmaSq * expMu));
256 const std::complex<double> scaled = w / sigmaSq;
257 auto integrand = [sigma, scaled](double u) {
258 const double v = sigma * u;
260 double excess = 0;
261 if (std::fabs(v) < 0.5) {
262 double term = v;
263 for (int k = 2; k < 30 && std::fabs(term) > std::numeric_limits<double>::epsilon() * std::fabs(excess);
264 ++k) {
265 term *= v / k;
266 excess += term;
267 }
268 } else {
269 excess = std::expm1(v) - v;
270 }
271 return std::exp(-0.5 * u * u - scaled * excess);
272 };
273 const double re =
274 math::integrate([&integrand](double u) { return integrand(u).real(); }, -NORMAL_RANGE, NORMAL_RANGE);
275 const double im =
276 math::integrate([&integrand](double u) { return integrand(u).imag(); }, -NORMAL_RANGE, NORMAL_RANGE);
277 const std::complex<double> expectation =
278 std::complex<double>(re, im) / static_cast<double>(randlib::consts::sqrt2pi);
279 const std::complex<double> value = std::exp(-(w * w + 2.0 * w) / (2 * sigmaSq)) * expectation;
280 if (std::isfinite(value.real()) && std::isfinite(value.imag()))
281 return value;
282 return this->integrateCharacteristicFunction(t);
283}
284
285template <typename RealType>
287{
288 return expMu;
289}
290
291template <typename RealType>
293{
294 return expMu / (expHalfSigmaSq * expHalfSigmaSq);
295}
296
297template <typename RealType>
299{
300 double y = std::expm1l(X.Variance());
301 return (expHalfSigmaSq * expHalfSigmaSq + 2) * std::sqrt(y);
302}
303
304template <typename RealType>
306{
307 double temp = expHalfSigmaSq * expHalfSigmaSq;
308 double c = temp * temp;
309 double b = c * temp;
310 double a = c * c;
311 return a + 2 * b + 3 * c - 6;
312}
313
314template <typename RealType>
315void LogNormalRand<RealType>::FitLocation(std::span<const RealType> sample)
316{
318 if (!this->allElementsArePositive(sample))
319 throw this->fitError(FitErrorCode::WrongSample, this->POSITIVITY_VIOLATION);
320 this->SetLocation(this->GetSampleLogMean(sample));
321}
322
323template <typename RealType>
324void LogNormalRand<RealType>::FitScale(std::span<const RealType> sample)
325{
327 if (!this->allElementsArePositive(sample))
328 throw this->fitError(FitErrorCode::WrongSample, this->POSITIVITY_VIOLATION);
329 double mu = X.GetLocation();
330 this->SetScale(std::sqrt(this->GetSampleLogVariance(sample, mu)));
331}
332
333template <typename RealType>
334void LogNormalRand<RealType>::Fit(std::span<const RealType> sample)
335{
337 if (!this->allElementsArePositive(sample))
338 throw this->fitError(FitErrorCode::WrongSample, this->POSITIVITY_VIOLATION);
339 size_t n = sample.size();
340 long double logMean = 0.0L;
341 long double logSqDev = 0.0L;
342 for (double var : sample) {
343 double logVar = std::log(var);
344 logMean += logVar;
345 logSqDev += logVar * logVar;
346 }
347 logMean /= n;
348 logSqDev /= n;
349 logSqDev -= logMean * logMean;
350
351 SetLocation(logMean);
352 SetScale(std::sqrt(logSqDev));
353}
354
355template <typename RealType>
357 const NormalRand<RealType> &priorDistribution, bool MAP)
358{
360 if (!this->allElementsArePositive(sample))
361 throw this->fitError(FitErrorCode::WrongSample, this->POSITIVITY_VIOLATION);
362 size_t n = sample.size();
363 double mu0 = priorDistribution.GetLocation();
364 double tau0 = priorDistribution.GetPrecision();
365 double tau = X.GetPrecision();
366 double numerator = n * this->GetSampleLogMean(sample) * tau + tau0 * mu0;
367 double denominator = n * tau + tau0;
368 NormalRand<RealType> posteriorDistribution(numerator / denominator, 1.0 / denominator);
369 SetLocation(MAP ? posteriorDistribution.Mode() : posteriorDistribution.Mean());
370 return posteriorDistribution;
371}
372
373template <typename RealType>
375 const InverseGammaRand<RealType> &priorDistribution,
376 bool MAP)
377{
379 if (!this->allElementsArePositive(sample))
380 throw this->fitError(FitErrorCode::WrongSample, this->POSITIVITY_VIOLATION);
381 size_t n = sample.size();
382 double alpha = priorDistribution.GetShape();
383 double beta = priorDistribution.GetRate();
384 double newAlpha = alpha + 0.5 * n;
385 double mu = X.GetLocation();
386 double newBeta = beta + 0.5 * n * this->GetSampleLogVariance(sample, mu);
387 InverseGammaRand<RealType> posteriorDistribution(newAlpha, newBeta);
388 SetScale(std::sqrt(MAP ? posteriorDistribution.Mode() : posteriorDistribution.Mean()));
389 return posteriorDistribution;
390}
391
392template <typename RealType>
394LogNormalRand<RealType>::FitBayes(std::span<const RealType> sample,
395 const NormalInverseGammaRand<RealType> &priorDistribution, bool MAP)
396{
398 if (!this->allElementsArePositive(sample))
399 throw this->fitError(FitErrorCode::WrongSample, this->POSITIVITY_VIOLATION);
400 size_t n = sample.size();
401 double alpha = priorDistribution.GetShape();
402 double beta = priorDistribution.GetRate();
403 double mu0 = priorDistribution.GetLocation();
404 double lambda = priorDistribution.GetPrecision();
405 DoublePair logStats = this->GetSampleLogMeanAndVariance(sample);
406 double average = logStats.first, sum = n * average;
407 double newLambda = lambda + n;
408 double newMu0 = (lambda * mu0 + sum) / newLambda;
409 double newAlpha = alpha + 0.5 * n;
410 double variance = logStats.second;
411 double aux = mu0 - average;
412 double newBeta = beta + 0.5 * n * (variance + lambda / newLambda * aux * aux);
413 NormalInverseGammaRand<RealType> posteriorDistribution(newMu0, newLambda, newAlpha, newBeta);
414 DoublePair newParams = MAP ? static_cast<DoublePair>(posteriorDistribution.Mode())
415 : static_cast<DoublePair>(posteriorDistribution.Mean());
416 SetLocation(newParams.first);
417 SetScale(std::sqrt(newParams.second));
418 return posteriorDistribution;
419}
420
421} // namespace randlib
The ContinuousDistribution class Abstract class for all continuous distributions.
Definition continuous.hpp:22
The InverseGammaRand class Inverse-Gamma distribution.
Definition inverse_gamma.hpp:23
double GetRate() const noexcept
Definition inverse_gamma.hpp:49
double GetShape() const noexcept
Definition inverse_gamma.hpp:48
long double Mean() const override
Definition inverse_gamma.hpp:146
RealType Mode() const override
Mode is the value, which has the largest probability to happen, or, in the case of continuous distrib...
Definition inverse_gamma.hpp:168
The LogNormalRand class Log-Normal distribution.
Definition log_normal.hpp:25
InverseGammaRand< RealType > FitScaleBayes(std::span< const RealType > sample, const InverseGammaRand< RealType > &priorDistribution, bool MAP=false)
Set scale, using bayesian inference.
Definition log_normal.hpp:374
static LogNormalRand FromLogMeanLogStd(double logMean, double logStandardDeviation)
Definition log_normal.hpp:40
long double ExcessKurtosis() const override
Definition log_normal.hpp:305
RealType MaxValue() const override
Definition log_normal.hpp:60
double F(const RealType &x) const override
Definition log_normal.hpp:196
void FitLocation(std::span< const RealType > sample)
Fit location via maximum-likelihood.
Definition log_normal.hpp:315
static LogNormalRand FromLogMeanLogVariance(double logMean, double logVariance)
Definition log_normal.hpp:52
void FitScale(std::span< const RealType > sample)
Fit scale via maximum-likelihood.
Definition log_normal.hpp:324
LogNormalRand(double location=0, double squaredScale=1)
Definition log_normal.hpp:146
NormalInverseGammaRand< RealType > FitBayes(std::span< const RealType > sample, const NormalInverseGammaRand< RealType > &priorDistribution, bool MAP=false)
Set parameters, using bayesian inference.
Definition log_normal.hpp:394
long double Variance() const override
Definition log_normal.hpp:228
long double Mean() const override
Definition log_normal.hpp:222
double logf(const RealType &x) const override
Definition log_normal.hpp:186
void Fit(std::span< const RealType > sample)
Fit parameters via maximum-likelihood.
Definition log_normal.hpp:334
void SetLocation(double location)
Definition log_normal.hpp:162
double f(const RealType &x) const override
Definition log_normal.hpp:180
double S(const RealType &x) const override
Definition log_normal.hpp:202
double GetScale() const
Definition log_normal.hpp:65
NormalRand< RealType > FitLocationBayes(std::span< const RealType > sample, const NormalRand< RealType > &priorDistribution, bool MAP=false)
Set location, using bayesian inference.
Definition log_normal.hpp:356
RealType Median() const override
Definition log_normal.hpp:286
void SetScale(double scale)
Definition log_normal.hpp:171
RealType Mode() const override
Mode is the value, which has the largest probability to happen, or, in the case of continuous distrib...
Definition log_normal.hpp:292
RealType operator()(G &g) const
Definition log_normal.hpp:209
Support SupportType() const override
Definition log_normal.hpp:58
RealType MinValue() const override
Definition log_normal.hpp:59
double GetLocation() const
Definition log_normal.hpp:64
static RealType StandardVariate(G &g)
Definition log_normal.hpp:216
long double Skewness() const override
Definition log_normal.hpp:298
std::string Name() const override
Definition log_normal.hpp:155
The NormalInverseGammaRand class Normal-inverse-gamma distribution.
Definition normal_inverse_gamma.hpp:24
double GetLocation() const noexcept
Definition normal_inverse_gamma.hpp:38
double GetPrecision() const noexcept
Definition normal_inverse_gamma.hpp:39
double GetShape() const noexcept
Definition normal_inverse_gamma.hpp:40
double GetRate() const noexcept
Definition normal_inverse_gamma.hpp:41
The NormalRand class Normal distribution.
Definition normal.hpp:31
double GetPrecision() const
Definition normal.hpp:78
virtual void sample(detail::EngineRef g, std::span< T > outputData) const
Fills the output with variate(g); distributions with a faster bulk algorithm override it.
Definition distribution.hpp:66
RealType Mode() const override
Mode is the value, which has the largest probability to happen, or, in the case of continuous distrib...
Definition stable.hpp:1806
double GetLocation() const noexcept
Definition stable.hpp:168
long double Mean() const override
Definition stable.hpp:1790
The EngineRef class Non-owning, type-erased reference to any uniform random bit generator.
Definition engine/uniform.hpp:116
#define RANDLIB_DEPRECATED_FIT_MEMBER
Definition fwd.hpp:9
constexpr long double sqrt2pi
Definition constants.hpp:32
Real standardNormal(G &g)
Ziggurat algorithm of Marsaglia and Tsang with 256 layers and an explicit sign bit.
Definition variates.hpp:45
Real W0Lambert(Real x, std::type_identity_t< Real > epsilon=Real{1e-11})
Definition functions.hpp:689
Real integrate(const F &f, std::type_identity_t< Real > a, std::type_identity_t< Real > b, std::type_identity_t< Real > tolerance=1e-11, int maxSubdivisions=512)
Global adaptive Gauss-Kronrod quadrature (QUADPACK QAG).
Definition quadrature.hpp:226
Definition bivariate_normal.hpp:10
@ WrongSample
the sample could not have been drawn from this distribution
Support
Shape of the support of a univariate distribution.
Definition univariate.hpp:15
@ RightSemiFinite
[a, ∞)
Pair< double > DoublePair
Definition fwd.hpp:39