RandLib 2.0.0
Probability distributions, fitting, and heavy-tail tools for C++20
Loading...
Searching...
No Matches
laplace.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <span>
4
10
12
13namespace randlib
14{
15
20template <typename RealType>
22{
23 RealType variate(detail::EngineRef g) const override { return (*this)(g); }
24 void sample(detail::EngineRef g, std::span<RealType> outputData) const override;
25
26public:
27 AsymmetricLaplaceDistribution(double shift = 0, double scale = 1, double asymmetry = 1);
28
29protected:
30 void ChangeLocation();
31
32public:
33 void SetScale(double scale);
34
35 [[nodiscard]] inline double GetAsymmetry() const noexcept { return this->kappa; }
36
37 [[nodiscard]] double f(const RealType &x) const override;
38 [[nodiscard]] double logf(const RealType &x) const override;
39 [[nodiscard]] double F(const RealType &x) const override;
40 [[nodiscard]] double S(const RealType &x) const override;
41
42 template <std::uniform_random_bit_generator G>
43 [[nodiscard]] RealType operator()(G &g) const;
44
45private:
46 RealType quantileImpl(double p) const override;
47 RealType quantileImpl1m(double p) const override;
48
49 std::complex<double> CFImpl(double t) const override;
50
51public:
52 [[nodiscard]] long double Entropy() const;
53
54 RANDLIB_DEPRECATED_FIT_MEMBER void FitScale(std::span<const RealType> sample);
55};
56
63template <typename RealType>
65 public ExponentialFamily<RealType, DoublePair>
66{
67public:
68 CenteredAsymmetricLaplaceRand(double scale = 1, double asymmetry = 1)
69 : AsymmetricLaplaceDistribution<RealType>(0.0, scale, asymmetry)
70 {
71 }
72 [[nodiscard]] std::string Name() const override;
73
75
77 DoublePair SufficientStatistic(RealType x) const override;
78 DoublePair SourceParameters() const override;
79 DoublePair SourceToNatural(DoublePair sourceParameters) const override;
80 double LogNormalizer(DoublePair theta) const override;
81 DoublePair LogNormalizerGradient(DoublePair theta) const override;
82 double CarrierMeasure(RealType) const override;
83};
84
89template <typename RealType>
91{
92public:
93 ShiftedAsymmetricLaplaceDistribution(double shift = 0, double scale = 1, double asymmetry = 1)
94 : AsymmetricLaplaceDistribution<RealType>(shift, scale, asymmetry)
95 {
96 }
98 [[nodiscard]] inline double GetShift() const noexcept { return this->m; }
99
100 RANDLIB_DEPRECATED_FIT_MEMBER void FitShift(std::span<const RealType> sample);
101
102protected:
103 RANDLIB_DEPRECATED_FIT_MEMBER void FitShiftAndScale(std::span<const RealType> sample);
104};
105
116template <typename RealType>
118{
119public:
120 AsymmetricLaplaceRand(double shift = 0, double scale = 1, double asymmetry = 1)
121 : ShiftedAsymmetricLaplaceDistribution<RealType>(shift, scale, asymmetry)
122 {
123 }
124 [[nodiscard]] std::string Name() const override;
125 void SetAsymmetry(double asymmetry);
126 template <std::uniform_random_bit_generator G>
127 [[nodiscard]] static RealType StandardVariate(double asymmetry, G &g);
128
130
131private:
132 DoublePair getOneSidedSums(std::span<const RealType> sample);
133
134public:
135 RANDLIB_DEPRECATED_FIT_MEMBER void FitAsymmetry(std::span<const RealType> sample);
136};
137
148template <typename RealType>
150{
151public:
152 LaplaceRand(double shift = 0, double scale = 1) : ShiftedAsymmetricLaplaceDistribution<RealType>(shift, scale, 1.0)
153 {
154 }
155 [[nodiscard]] std::string Name() const override;
156 template <std::uniform_random_bit_generator G>
157 [[nodiscard]] static RealType StandardVariate(G &g);
158 RANDLIB_DEPRECATED_FIT_MEMBER void Fit(std::span<const RealType> sample) { this->FitShiftAndScale(sample); }
159};
160
161} // namespace randlib
162
165
166namespace randlib
167{
168
169template <typename RealType>
171 : GeneralGeometricStableDistribution<RealType>(2.0, 0.0, scale, 0.0, shift)
172{
175}
176
177template <typename RealType>
179{
180 this->SetLocation((1.0 - this->kappaSq) * this->gamma * this->kappaInv);
181}
182
183template <typename RealType>
185{
186 if (!(scale > 0.0))
187 throw std::invalid_argument("Laplace distribution: scale should be positive, but it's equal to " +
188 std::to_string(scale));
190 ChangeLocation();
191}
192
193template <typename RealType>
194double AsymmetricLaplaceDistribution<RealType>::f(const RealType &x) const
195{
196 return this->pdfLaplace(x - this->m);
197}
198
199template <typename RealType>
201{
202 return this->logpdfLaplace(x - this->m);
203}
204
205template <typename RealType>
206double AsymmetricLaplaceDistribution<RealType>::F(const RealType &x) const
207{
208 return this->cdfLaplace(x - this->m);
209}
210
211template <typename RealType>
212double AsymmetricLaplaceDistribution<RealType>::S(const RealType &x) const
213{
214 return this->cdfLaplaceCompl(x - this->m);
215}
216
217template <typename RealType>
218template <std::uniform_random_bit_generator G>
220{
221 RealType X = (this->kappa == 1) ? LaplaceRand<RealType>::StandardVariate(g)
223 return this->m + this->gamma * X;
224}
225
226template <typename RealType>
227void AsymmetricLaplaceDistribution<RealType>::sample(detail::EngineRef g, std::span<RealType> outputData) const
228{
229 if (this->kappa == 1) {
230 for (RealType &var : outputData)
231 var = this->m + this->gamma * LaplaceRand<RealType>::StandardVariate(g);
232 } else {
233 for (RealType &var : outputData)
234 var = this->m + this->gamma * AsymmetricLaplaceRand<RealType>::StandardVariate(this->kappa, g);
235 }
236}
237
238template <typename RealType>
239std::complex<double> AsymmetricLaplaceDistribution<RealType>::CFImpl(double t) const
240{
241 double bt = this->gamma * t;
242 double btSq = bt * bt;
243 double denominator = (1 + this->kappaSq * btSq) * (1 + btSq / this->kappaSq);
244 std::complex<double> y(std::cos(this->m * t), std::sin(this->m * t));
245 std::complex<double> x(1, -this->kappa * bt), z(1, bt * this->kappaInv);
246 return x * y * z / denominator;
247}
248
249template <typename RealType>
250RealType AsymmetricLaplaceDistribution<RealType>::quantileImpl(double p) const
251{
252 return this->quantileLaplace(p);
253}
254
255template <typename RealType>
256RealType AsymmetricLaplaceDistribution<RealType>::quantileImpl1m(double p) const
257{
258 return this->quantileLaplace1m(p);
259}
260
261template <typename RealType>
263{
264 double y = this->kappaInv + this->kappa;
265 return 1.0 + std::log(this->gamma * y);
266}
267
268template <typename RealType>
269void AsymmetricLaplaceDistribution<RealType>::FitScale(std::span<const RealType> sample)
270{
271 double deviation = 0.0;
272 for (const RealType &x : sample) {
273 if (x > this->m)
274 deviation += this->kappa * (x - this->m);
275 else
276 deviation -= (x - this->m) / this->kappa;
277 }
278 deviation /= sample.size();
279 SetScale(deviation);
280}
281
282template <typename RealType>
284{
287 double minVar = *std::min_element(sample.begin(), sample.end());
288 double maxVar = *std::max_element(sample.begin(), sample.end());
289 if (minVar == maxVar) {
290 this->SetShift(minVar);
291 return;
292 }
293 double median = math::findRoot(
294 [this, sample](double med) {
295 double y = 0.0;
296 for (const RealType &x : sample) {
297 if (x > med)
298 y -= this->kappaSq;
299 else if (x < med)
300 ++y;
301 }
302 return y;
303 },
304 minVar, maxVar);
305
306 this->SetShift(median);
307}
308
309template <typename RealType>
311{
312 this->FitShift(sample);
313 this->FitScale(sample);
314}
315template <typename RealType>
317{
318 return "Centered Asymmetric-Laplace(" + this->toStringWithPrecision(this->GetScale()) + ", " +
319 this->toStringWithPrecision(this->GetAsymmetry()) + ")";
320}
321
322template <typename RealType>
324{
325 return {std::max<double>(x, 0.0), std::max<double>(-x, 0.0)};
326}
327
328template <typename RealType>
330{
331 return {this->GetScale(), this->GetAsymmetry()};
332}
333
334template <typename RealType>
336{
337 double scale = sourceParameters.first, asymmetry = sourceParameters.second;
338 return {-asymmetry / scale, -1.0 / (scale * asymmetry)};
339}
340
341template <typename RealType>
343{
344 return std::log(-1.0 / theta.first - 1.0 / theta.second);
345}
346
347template <typename RealType>
349{
350 double sum = -1.0 / theta.first - 1.0 / theta.second;
351 return {1.0 / (theta.first * theta.first * sum), 1.0 / (theta.second * theta.second * sum)};
352}
353
354template <typename RealType>
356{
357 return 0.0;
358}
359template <typename RealType>
361{
362 return "Asymmetric-Laplace(" + this->toStringWithPrecision(this->GetShift()) + ", " +
363 this->toStringWithPrecision(this->GetScale()) + ", " + this->toStringWithPrecision(this->GetAsymmetry()) +
364 ")";
365}
366
367template <typename RealType>
369{
371 this->ChangeLocation();
372}
373
374template <typename RealType>
375template <std::uniform_random_bit_generator G>
377{
378 RealType x = detail::standardExponential<RealType>(g) / asymmetry;
379 RealType y = detail::standardExponential<RealType>(g) * asymmetry;
380 return x - y;
381}
382
383template <typename RealType>
384DoublePair AsymmetricLaplaceRand<RealType>::getOneSidedSums(std::span<const RealType> sample)
385{
386 double xPlus = 0.0, xMinus = 0.0;
387 for (const RealType &x : sample) {
388 if (x < this->m)
389 xMinus += (x - this->m);
390 else
391 xPlus += (x - this->m);
392 }
393 return DoublePair(xPlus, xMinus);
394}
395
396template <typename RealType>
397void AsymmetricLaplaceRand<RealType>::FitAsymmetry(std::span<const RealType> sample)
398{
399 auto [xPlus, xMinus] = getOneSidedSums(sample);
400 double gammaN = this->gamma * sample.size();
401 double asymmetry = 0;
402 if (xPlus == xMinus)
403 asymmetry = 1.0;
404 else if (xPlus == 0)
405 asymmetry = -xMinus / gammaN;
406 else if (xMinus == 0) {
407 asymmetry = gammaN / xPlus;
408 } else {
410 double a = xPlus / gammaN;
411 double c = (xPlus + xMinus) / gammaN;
412 double e = xMinus / gammaN;
414 double ae = a * e;
415 double delta1 = 2 * std::pow(c, 3) + 9 * c + 27 * (a + e) - 72 * ae * c;
416 double delta0 = c * c + 3 + 12 * ae;
417 double p2 = delta1 + std::sqrt(delta1 * delta1 - 4 * std::pow(delta0, 3));
418 double Q = std::cbrt(0.5 * p2);
419 double temp = 8 * a * a;
420 double p = (8 * a * c - 3.0) / temp;
421 double q = (-1.0 + 4 * a * c + temp) / (a * temp);
422 double S = 0.5 * std::sqrt(-2.0 / 3 * p + (Q + delta0 / Q) / (3 * a));
424 double c1 = -0.25 / a, b1 = -4 * S * S - 2 * p, b2 = q / S;
425 if (b1 + b2 > 0)
426 asymmetry = c1 + S + 0.5 * std::sqrt(b1 + b2);
427 else
428 asymmetry = c1 - S + 0.5 * std::sqrt(b1 - b2);
429 }
430
431 SetAsymmetry(asymmetry);
432}
433template <typename RealType>
435{
436 return "Laplace(" + this->toStringWithPrecision(this->GetShift()) + ", " +
437 this->toStringWithPrecision(this->GetScale()) + ")";
438}
439
440template <typename RealType>
441template <std::uniform_random_bit_generator G>
443{
445 return BernoulliRand::StandardVariate(g) ? W : -W;
446}
447
448} // namespace randlib
449
The AsymmetricLaplaceDistribution class Abstract parent class for general Laplace and Asymmetric La...
Definition laplace.hpp:22
double GetAsymmetry() const noexcept
Definition laplace.hpp:35
void SetScale(double scale)
Definition laplace.hpp:184
double F(const RealType &x) const override
Definition laplace.hpp:206
void FitScale(std::span< const RealType > sample)
Definition laplace.hpp:269
void ChangeLocation()
Definition laplace.hpp:178
long double Entropy() const
Definition laplace.hpp:262
double logf(const RealType &x) const override
Definition laplace.hpp:200
double S(const RealType &x) const override
Definition laplace.hpp:212
AsymmetricLaplaceDistribution(double shift=0, double scale=1, double asymmetry=1)
Definition laplace.hpp:170
RealType operator()(G &g) const
Definition laplace.hpp:219
double f(const RealType &x) const override
Definition laplace.hpp:194
The AsymmetricLaplaceRand class Asymmetric Laplace distribution.
Definition laplace.hpp:118
static RealType StandardVariate(double asymmetry, G &g)
Definition laplace.hpp:376
void FitAsymmetry(std::span< const RealType > sample)
Definition laplace.hpp:397
std::string Name() const override
Definition laplace.hpp:360
void SetAsymmetry(double asymmetry)
Definition laplace.hpp:368
AsymmetricLaplaceRand(double shift=0, double scale=1, double asymmetry=1)
Definition laplace.hpp:120
static int StandardVariate(G &g)
Definition bernoulli.hpp:106
The CenteredAsymmetricLaplaceRand class Centered Laplace distribution.
Definition laplace.hpp:66
DoublePair SourceParameters() const override
Definition laplace.hpp:329
DoublePair LogNormalizerGradient(DoublePair theta) const override
Definition laplace.hpp:348
double LogNormalizer(DoublePair theta) const override
Definition laplace.hpp:342
DoublePair SufficientStatistic(RealType x) const override
Sufficient statistic (max(x, 0), max(-x, 0)) with natural parameters (-κ/γ, -1/(γκ))
Definition laplace.hpp:323
double CarrierMeasure(RealType) const override
Definition laplace.hpp:355
std::string Name() const override
Definition laplace.hpp:316
DoublePair SourceToNatural(DoublePair sourceParameters) const override
Definition laplace.hpp:335
CenteredAsymmetricLaplaceRand(double scale=1, double asymmetry=1)
Definition laplace.hpp:68
Definition exponential_family.hpp:14
The GeneralGeometricStableDistribution class Abstract class for general interface of Asymmetric Lapla...
Definition geometric_stable.hpp:19
void SetAsymmetry(double asymmetry)
Definition geometric_stable.hpp:260
double m
shift m
Definition geometric_stable.hpp:30
void SetShift(double shift)
Definition geometric_stable.hpp:242
double kappa
parameters for α = 2
Definition geometric_stable.hpp:35
void SetScale(double scale)
Definition geometric_stable.hpp:250
The LaplaceRand class Laplace distribution.
Definition laplace.hpp:150
LaplaceRand(double shift=0, double scale=1)
Definition laplace.hpp:152
void Fit(std::span< const RealType > sample)
Definition laplace.hpp:158
std::string Name() const override
Definition laplace.hpp:434
static RealType StandardVariate(G &g)
Definition laplace.hpp:442
The ShiftedAsymmetricLaplaceDistribution class Abstract parent class for shifted Laplace and Asymme...
Definition laplace.hpp:91
void FitShiftAndScale(std::span< const RealType > sample)
Definition laplace.hpp:310
void FitShift(std::span< const RealType > sample)
Definition laplace.hpp:283
ShiftedAsymmetricLaplaceDistribution(double shift=0, double scale=1, double asymmetry=1)
Definition laplace.hpp:93
double GetShift() const noexcept
Definition laplace.hpp:98
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
Real standardNormal(G &g)
Ziggurat algorithm of Marsaglia and Tsang with 256 layers and an explicit sign bit.
Definition variates.hpp:45
Real findRoot(const F &f, std::type_identity_t< Real > a, std::type_identity_t< Real > b, int maxIterations=200)
Definition roots.hpp:205
Definition bivariate_normal.hpp:10
Pair< double > DoublePair
Definition fwd.hpp:39