RandLib 2.0.0
Probability distributions, fitting, and heavy-tail tools for C++20
Loading...
Searching...
No Matches
poisson.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <span>
4
10
12
13namespace randlib
14{
15
24template <typename IntType>
25class PoissonRand final : public DiscreteDistribution<IntType>, public ExponentialFamily<IntType, double>
26{
27 IntType variate(detail::EngineRef g) const override { return (*this)(g); }
28 void sample(detail::EngineRef g, std::span<IntType> outputData) const override;
29
30 double lambda = 1;
31 double logLambda = 0;
32 double Fmu = 2 * randlib::consts::inv_e;
33 double Pmu = randlib::consts::inv_e;
34
35 double mu = 1, delta = 6;
36 double zeta{};
37 long double c1{}, c2{}, c3{}, c4{}, c{};
38 double sqrtMu = 1, sqrtMupHalfDelta = 2;
39 double lfactMu = 0;
40
41public:
42 explicit PoissonRand(double rate = 1.0);
43 [[nodiscard]] std::string Name() const override;
44 [[nodiscard]] Support SupportType() const override { return Support::RightSemiFinite; }
45 [[nodiscard]] IntType MinValue() const override { return 0; }
46 [[nodiscard]] IntType MaxValue() const override { return std::numeric_limits<IntType>::max(); }
47
48private:
49 void SetGeneratorConstants();
50
51public:
52 void SetRate(double rate);
53 [[nodiscard]] inline double GetRate() const noexcept { return lambda; }
54
55 double SufficientStatistic(IntType x) const override;
56 double SourceParameters() const override;
57 double SourceToNatural(double sourceParameters) const override;
58 double NaturalParameters() const override;
59 double LogNormalizer(double theta) const override;
60 double LogNormalizerGradient(double theta) const override;
61 double CarrierMeasure(IntType x) const override;
62 double CrossEntropyAdjusted(double parameters) const override;
63 double EntropyAdjusted() const override;
64
65 [[nodiscard]] double logP(const IntType &k) const override;
66 [[nodiscard]] double F(const IntType &k) const override;
67 [[nodiscard]] double S(const IntType &k) const override;
68
69private:
70 double acceptanceFunction(IntType X) const;
71 bool generateByInversion() const;
72 template <std::uniform_random_bit_generator G>
73 IntType variateRejection(G &g) const;
74 template <std::uniform_random_bit_generator G>
75 IntType variateInversion(G &g) const;
76
77public:
78 template <std::uniform_random_bit_generator G>
79 [[nodiscard]] IntType operator()(G &g) const;
80
81 template <std::uniform_random_bit_generator G>
82 [[nodiscard]] static IntType Variate(double rate, G &g);
83
84 [[nodiscard]] long double Mean() const override;
85 [[nodiscard]] long double Variance() const override;
86 [[nodiscard]] IntType Median() const override;
87 [[nodiscard]] IntType Mode() const override;
88 [[nodiscard]] long double Skewness() const override;
89 [[nodiscard]] long double ExcessKurtosis() const override;
90
91private:
92 std::complex<double> CFImpl(double t) const override;
93
94public:
99 RANDLIB_DEPRECATED_FIT_MEMBER void Fit(std::span<const IntType> sample);
106 RANDLIB_DEPRECATED_FIT_MEMBER void Fit(std::span<const IntType> sample, DoublePair &confidenceInterval,
107 double significanceLevel);
115 RANDLIB_DEPRECATED_FIT_MEMBER GammaRand<> FitBayes(std::span<const IntType> sample,
116 const GammaDistribution<> &priorDistribution, bool MAP = false);
117};
118
119} // namespace randlib
120
124
125namespace randlib
126{
127
128namespace detail
129{
130
140template <typename IntType, std::uniform_random_bit_generator G>
142{
143 if (rate < 10) {
144 const double u = uniform01<double>(g);
145 double probability = std::exp(-rate), cumulative = probability;
146 IntType k = 0;
148 while (u > cumulative && k < 1000) {
149 ++k;
150 probability *= rate / k;
151 cumulative += probability;
152 }
153 return k;
154 }
155 const double sqrtRate = std::sqrt(rate), halfLog2Pi = 0.5 * std::log(2 * randlib::consts::pi);
156 const double b = 0.931 + 2.53 * sqrtRate, a = -0.059 + 0.02483 * b;
157 const double logInverseAlpha = std::log(1.1239 + 1.1328 / (b - 3.4)), squeeze = 0.9277 - 3.6224 / (b - 2);
158 const double largest = static_cast<double>(std::numeric_limits<IntType>::max());
159 for (;;) {
160 const double u = uniform01<double>(g) - 0.5, v = uniform01<double>(g);
161 const double us = 0.5 - std::fabs(u);
162 const double x = std::floor((2 * a / us + b) * u + rate + 0.43);
163 if (us >= 0.07 && v <= squeeze)
164 return (x < largest) ? static_cast<IntType>(x) : std::numeric_limits<IntType>::max();
165 if (x < 0 || (us < 0.013 && v > us))
166 continue;
168 const double logProbability = (x + 1 - rate) + x * std::log1p((rate - x - 1) / (x + 1)) -
169 0.5 * std::log(x + 1) - halfLog2Pi -
170 math::stirlingError<double>(static_cast<std::size_t>(std::min(x, largest)) + 1);
171 if (std::log(v) + logInverseAlpha - std::log(a / (us * us) + b) <= logProbability)
172 return (x < largest) ? static_cast<IntType>(x) : std::numeric_limits<IntType>::max();
173 }
174}
175
176} // namespace detail
177
178template <typename IntType>
180{
181 SetRate(rate);
182}
183
184template <typename IntType>
185std::string PoissonRand<IntType>::Name() const
186{
187 return "Poisson(" + this->toStringWithPrecision(GetRate()) + ")";
188}
189
190template <typename IntType>
192{
193 delta = std::round(std::sqrt(2 * mu * std::log(randlib::consts::inv_pi * 128 * mu)));
194 delta = std::max(6.0, std::min(mu, delta));
195
196 c1 = std::sqrt(0.5 * randlib::consts::pi * mu * randlib::consts::e);
197
198 c2 = 1.0 / (2 * mu + delta);
199 c2 += 0.5 * (randlib::consts::lnpi - randlib::consts::ln2 + std::log(mu + 0.5 * delta));
200 c2 = c1 + std::exp(c2);
201
202 c3 = c2 + 1.0;
203 c4 = c3 + 1.0;
204
205 zeta = (4.0 * mu) / delta + 2;
206 c = std::exp(-(2.0 + delta) / zeta);
207 c *= zeta;
208 c += c4;
209
210 sqrtMu = std::sqrt(mu);
211 sqrtMupHalfDelta = std::sqrt(mu + 0.5 * delta);
212 lfactMu = math::lfact(mu);
213}
214
215template <typename IntType>
217{
218 if (!(rate > 0.0))
219 throw std::invalid_argument("Poisson distribution: rate should be positive, but it's equal to " +
220 std::to_string(rate));
221 lambda = rate;
222
223 logLambda = std::log(lambda);
224 mu = std::floor(lambda);
225 Fmu = F(mu);
226 Pmu = this->P(mu);
227
228 if (!generateByInversion())
229 SetGeneratorConstants();
230}
231
232template <typename IntType>
234{
235 return x;
236}
237
238template <typename IntType>
240{
241 return lambda;
242}
243
244template <typename IntType>
245double PoissonRand<IntType>::SourceToNatural(double sourceParameters) const
246{
247 return std::log(sourceParameters);
248}
249
250template <typename IntType>
252{
253 return logLambda;
254}
255
256template <typename IntType>
257double PoissonRand<IntType>::LogNormalizer(double theta) const
258{
259 return std::exp(theta);
260}
261
262template <typename IntType>
264{
265 return std::exp(theta);
266}
267
268template <typename IntType>
270{
271 return -math::lfact(x);
272}
273
274template <typename IntType>
275double PoissonRand<IntType>::CrossEntropyAdjusted(double parameters) const
276{
277 return parameters - lambda * std::log(parameters);
278}
279
280template <typename IntType>
282{
283 return lambda - lambda * logLambda;
284}
285
286template <typename IntType>
287double PoissonRand<IntType>::logP(const IntType &k) const
288{
289 if (k < 0)
290 return -INFINITY;
291 if (k == 0)
292 return -lambda;
294 return -math::stirlingError(k) - math::bd0(k, lambda) - 0.5 * std::log(2 * randlib::consts::pi * k);
295}
296
297template <typename IntType>
298double PoissonRand<IntType>::F(const IntType &k) const
299{
300 return (k >= 0.0) ? math::qgamma(k + 1, lambda, logLambda) : 0.0;
301}
302
303template <typename IntType>
304double PoissonRand<IntType>::S(const IntType &k) const
305{
306 return (k >= 0.0) ? math::pgamma(k + 1, lambda, logLambda) : 1.0;
307}
308
309template <typename IntType>
310double PoissonRand<IntType>::acceptanceFunction(IntType X) const
311{
312 if (X == 0)
313 return 0.0;
314 double q = X * logLambda;
315 q += lfactMu;
316 q -= math::lfact(X + mu);
317 return q;
318}
319
320template <typename IntType>
321bool PoissonRand<IntType>::generateByInversion() const
322{
325 return lambda < 10;
326}
327
328template <typename IntType>
329template <std::uniform_random_bit_generator G>
330IntType PoissonRand<IntType>::variateRejection(G &g) const
331{
332 size_t iter = 0;
333 IntType X = 0;
334 do {
335 bool reject = false;
336 float W = 0.0;
337 float U = c * detail::uniform01<float>(g);
338 if (U <= c1) {
340 float Y = -std::fabs(N) * sqrtMu;
341 X = std::floor(Y);
342 if (X < -mu) {
343 reject = true;
344 } else {
345 W = -0.5 * (N * N - 1.0);
346 }
347 } else if (U <= c2) {
349 float Y = 1.0 + std::fabs(N) * sqrtMupHalfDelta;
350 X = std::ceil(Y);
351 if (X > delta) {
352 reject = true;
353 } else {
354 W = Y * (2.0 - Y) / (2.0 * mu + delta);
355 }
356 } else if (U <= c3) {
357 return mu;
358 } else if (U <= c4) {
359 X = 1;
360 } else {
362 float Y = delta + V * zeta;
363 X = std::ceil(Y);
364 W = -(2.0 + Y) / zeta;
365 }
366
367 if (!reject && W - detail::standardExponential<float>(g) <= acceptanceFunction(X)) {
368 return X + mu;
369 }
370
371 } while (++iter < ProbabilityDistribution<IntType>::MAX_ITER_REJECTION);
372 throw std::runtime_error("Poisson distribution: sampling failed");
373}
374
375template <typename IntType>
376template <std::uniform_random_bit_generator G>
377IntType PoissonRand<IntType>::variateInversion(G &g) const
378{
379 double U = detail::uniform01<double>(g);
380 IntType k = mu;
381 double s = Fmu, p = Pmu;
382 if (s < U) {
383 do {
384 ++k;
385 p *= lambda / k;
386 s += p;
387 } while (s < U && p > 0);
388 } else {
389 s -= p;
390 while (k > 0 && s > U) {
391 p /= lambda / k;
392 --k;
393 s -= p;
394 }
395 }
396 return k;
397}
398
399template <typename IntType>
400template <std::uniform_random_bit_generator G>
402{
403 return generateByInversion() ? variateInversion(g) : variateRejection(g);
404}
405
406template <typename IntType>
407template <std::uniform_random_bit_generator G>
408IntType PoissonRand<IntType>::Variate(double rate, G &g)
409{
410 const PoissonRand<IntType> distribution(rate);
411 return distribution(g);
412}
413
414template <typename IntType>
415void PoissonRand<IntType>::sample(detail::EngineRef g, std::span<IntType> outputData) const
416{
417 if (generateByInversion()) {
418 for (IntType &var : outputData)
419 var = variateInversion(g);
420 } else {
421 for (IntType &var : outputData)
422 var = variateRejection(g);
423 }
424}
425
426template <typename IntType>
427long double PoissonRand<IntType>::Mean() const
428{
429 return lambda;
430}
431
432template <typename IntType>
434{
435 return lambda;
436}
437
438template <typename IntType>
439std::complex<double> PoissonRand<IntType>::CFImpl(double t) const
440{
441 std::complex<double> y(std::cos(t) - 1.0, std::sin(t));
442 return std::exp(lambda * y);
443}
444
445template <typename IntType>
447{
449 return std::max(std::floor(lambda + 1.0 / 3 - 0.02 / lambda), 0.0);
450}
451
452template <typename IntType>
454{
455 if (math::detail::areClose(mu, lambda)) {
456 return (Pmu < this->P(mu + 1)) ? mu + 1 : mu;
457 }
458 return mu;
459}
460
461template <typename IntType>
463{
464 return 1.0 / std::sqrt(lambda);
465}
466
467template <typename IntType>
469{
470 return 1.0 / lambda;
471}
472
473template <typename IntType>
474void PoissonRand<IntType>::Fit(std::span<const IntType> sample)
475{
476 if (!this->allElementsAreNonNegative(sample))
477 throw this->fitError(FitErrorCode::WrongSample, this->NON_NEGATIVITY_VIOLATION);
478 SetRate(this->GetSampleMean(sample));
479}
480
481template <typename IntType>
482void PoissonRand<IntType>::Fit(std::span<const IntType> sample, DoublePair &confidenceInterval,
483 double significanceLevel)
484{
485 size_t n = sample.size();
486
487 if (significanceLevel <= 0 || significanceLevel > 1)
488 throw this->fitError(FitErrorCode::WrongLevel,
489 "Alpha is equal to " + this->toStringWithPrecision(significanceLevel));
490
491 Fit(sample);
492
493 double halfAlpha = 0.5 * significanceLevel;
494 ErlangRand<double> ErlangRV(n);
495 confidenceInterval.first = ErlangRV.Quantile(halfAlpha);
496 ErlangRV.SetShape(n + 1);
497 confidenceInterval.second = ErlangRV.Quantile1m(halfAlpha);
498}
499
500template <typename IntType>
501GammaRand<> PoissonRand<IntType>::FitBayes(std::span<const IntType> sample,
502 const GammaDistribution<> &priorDistribution, bool MAP)
503{
504 if (!this->allElementsAreNonNegative(sample))
505 throw this->fitError(FitErrorCode::WrongSample, this->NON_NEGATIVITY_VIOLATION);
506 double alpha = priorDistribution.GetShape();
507 double beta = priorDistribution.GetRate();
508 GammaRand<> posteriorDistribution(alpha + this->GetSampleSum(sample), beta + sample.size());
509 SetRate(MAP ? posteriorDistribution.Mode() : posteriorDistribution.Mean());
510 return posteriorDistribution;
511}
512
513} // namespace randlib
514
The DiscreteDistribution class Abstract class for all discrete distributions.
Definition discrete.hpp:21
The ErlangRand class Erlang distibution.
Definition continuous/gamma.hpp:375
void SetShape(size_t shape)
Definition continuous/gamma.hpp:1100
Definition exponential_family.hpp:14
The GammaDistribution class Abstract class for Gamma distribution.
Definition continuous/gamma.hpp:31
double GetShape() const noexcept
Definition continuous/gamma.hpp:78
long double Mean() const override
Definition continuous/gamma.hpp:693
double GetRate() const noexcept
Definition continuous/gamma.hpp:86
RealType Mode() const override
Mode is the value, which has the largest probability to happen, or, in the case of continuous distrib...
Definition continuous/gamma.hpp:717
The GammaRand class Gamma distribution.
Definition continuous/gamma.hpp:295
The PoissonRand class Poisson distribution.
Definition poisson.hpp:26
double LogNormalizerGradient(double theta) const override
Definition poisson.hpp:263
void Fit(std::span< const IntType > sample, DoublePair &confidenceInterval, double significanceLevel)
Fit.
Definition poisson.hpp:482
long double Variance() const override
Definition poisson.hpp:433
double F(const IntType &k) const override
Definition poisson.hpp:298
double CrossEntropyAdjusted(double parameters) const override
Definition poisson.hpp:275
GammaRand FitBayes(std::span< const IntType > sample, const GammaDistribution<> &priorDistribution, bool MAP=false)
fit rate λ via Bayes estimation
Definition poisson.hpp:501
IntType Median() const override
Definition poisson.hpp:446
IntType operator()(G &g) const
Definition poisson.hpp:401
Support SupportType() const override
Definition poisson.hpp:44
long double ExcessKurtosis() const override
Definition poisson.hpp:468
double SufficientStatistic(IntType x) const override
Definition poisson.hpp:233
long double Mean() const override
Definition poisson.hpp:427
IntType MaxValue() const override
Definition poisson.hpp:46
void SetRate(double rate)
Definition poisson.hpp:216
double CarrierMeasure(IntType x) const override
Definition poisson.hpp:269
double GetRate() const noexcept
Definition poisson.hpp:53
IntType MinValue() const override
Definition poisson.hpp:45
double S(const IntType &k) const override
Definition poisson.hpp:304
double EntropyAdjusted() const override
Definition poisson.hpp:281
std::string Name() const override
Definition poisson.hpp:185
double LogNormalizer(double theta) const override
Definition poisson.hpp:257
long double Skewness() const override
Definition poisson.hpp:462
IntType Mode() const override
Mode is the value, which has the largest probability to happen, or, in the case of continuous distrib...
Definition poisson.hpp:453
void Fit(std::span< const IntType > sample)
fit rate λ via maximum-likelihood method
Definition poisson.hpp:474
static IntType Variate(double rate, G &g)
Definition poisson.hpp:408
double SourceToNatural(double sourceParameters) const override
Definition poisson.hpp:245
double logP(const IntType &k) const override
Definition poisson.hpp:287
double SourceParameters() const override
Definition poisson.hpp:239
PoissonRand(double rate=1.0)
Definition poisson.hpp:179
double NaturalParameters() const override
Definition poisson.hpp:251
T Variate() const
Uses the engine of this object, so it is not thread-safe for one object; use operator() with one engi...
Definition distribution.hpp:133
T Quantile(double p) const
Definition univariate.hpp:307
T Quantile1m(double p) const
Definition univariate.hpp:324
The EngineRef class Non-owning, type-erased reference to any uniform random bit generator.
Definition engine/uniform.hpp:116
#define RANDLIB_DETAIL_SUPPRESS_DEPRECATED_END
Definition fwd.hpp:26
#define RANDLIB_DETAIL_SUPPRESS_DEPRECATED_BEGIN
Definition fwd.hpp:25
#define RANDLIB_DEPRECATED_FIT_MEMBER
Definition fwd.hpp:9
constexpr long double inv_pi
Definition constants.hpp:24
constexpr long double ln2
Definition constants.hpp:18
constexpr long double pi
Definition constants.hpp:22
constexpr long double e
Definition constants.hpp:15
constexpr long double inv_e
Definition constants.hpp:35
constexpr long double lnpi
Definition constants.hpp:21
IntType poissonVariate(double rate, G &g)
Poisson variate for a rate that changes from call to call, as in mixtures: the setup is a few element...
Definition poisson.hpp:141
Real standardNormal(G &g)
Ziggurat algorithm of Marsaglia and Tsang with 256 layers and an explicit sign bit.
Definition variates.hpp:45
bool areClose(RealType a, RealType b, RealType eps=1e-6)
Definition functions.hpp:38
Real qgamma(std::type_identity_t< Real > a, Real x, std::type_identity_t< Real > logA, std::type_identity_t< Real > lgammaA)
Calculate upper regularized incomplete gamma function, accelerated by using precalculated values of l...
Definition math/gamma.hpp:938
Real lfact(size_t n)
Calculate log(n!) using table values for small n <= 255 and built-in lgamma function for large n > 25...
Definition math/gamma.hpp:591
Real bd0(std::type_identity_t< Real > x, Real m)
Deviance term of the saddle-point expansions of Poisson and binomial probabilities (C.
Definition math/gamma.hpp:620
Real stirlingError(size_t n)
Definition math/gamma.hpp:598
Real pgamma(std::type_identity_t< Real > a, Real x, std::type_identity_t< Real > logA, std::type_identity_t< Real > lgammaA)
Calculate lower regularized incomplete gamma function, accelerated by using precalculated value of lo...
Definition math/gamma.hpp:896
Definition bivariate_normal.hpp:10
@ WrongSample
the sample could not have been drawn from this distribution
@ WrongLevel
the significance level is not in (0, 1)
Support
Shape of the support of a univariate distribution.
Definition univariate.hpp:15
@ RightSemiFinite
[a, ∞)
Pair< double > DoublePair
Definition fwd.hpp:39