RandLib 2.0.0
Probability distributions, fitting, and heavy-tail tools for C++20
Loading...
Searching...
No Matches
marchenko_pastur.hpp
Go to the documentation of this file.
1#pragma once
2
8
9namespace randlib
10{
11
18template <typename RealType>
19class MarchenkoPasturRand final : public ContinuousDistribution<RealType>
20{
21 RealType variate(detail::EngineRef g) const override { return (*this)(g); }
22 void sample(detail::EngineRef g, std::span<RealType> outputData) const override;
23
24 double lambda = 1;
25 double sigmaSq = 1;
26 double a = 0;
27 double b = 4;
28 double logLambda = 0;
29
30 BetaRand<RealType> BetaRV{0.5, 1.5, 0, 4};
31 double M = 1.0;
32
33public:
34 MarchenkoPasturRand(double ratio = 1, double scale = 1);
35 [[nodiscard]] std::string Name() const override;
36
37 [[nodiscard]] Support SupportType() const override { return Support::Finite; }
38 [[nodiscard]] RealType MinValue() const override { return (lambda < 1) ? sigmaSq * a : 0; }
39 [[nodiscard]] RealType MaxValue() const override { return sigmaSq * b; }
40
41 void SetParameters(double ratio, double scale);
42 [[nodiscard]] double GetRatio() const noexcept { return lambda; }
43 [[nodiscard]] double GetScale() const noexcept { return sigmaSq; }
44
45 [[nodiscard]] double f(const RealType &x) const override;
46 [[nodiscard]] double logf(const RealType &x) const override;
47
48private:
53 double ccdfForLargeRatio(const RealType &x) const;
58 double cdfForSmallRatio(const RealType &x) const;
59
60public:
61 [[nodiscard]] double F(const RealType &x) const override;
62 [[nodiscard]] double S(const RealType &x) const override;
63
64private:
65 enum GENERATOR_ID
66 {
67 TINY_RATIO,
68 SMALL_RATIO,
69 LARGE_RATIO,
70 HUGE_RATIO
71 };
72
73 GENERATOR_ID getIdOfUsedGenerator() const
74 {
75 if (lambda < 0.3)
76 return TINY_RATIO;
77 if (lambda <= 1.0)
78 return SMALL_RATIO;
79 return (lambda > 3.3) ? HUGE_RATIO : LARGE_RATIO;
80 }
81
82 template <std::uniform_random_bit_generator G>
83 RealType variateForTinyRatio(G &g) const;
84 template <std::uniform_random_bit_generator G>
85 RealType variateForSmallRatio(G &g) const;
86 template <std::uniform_random_bit_generator G>
87 RealType variateForLargeRatio(G &g) const;
88 template <std::uniform_random_bit_generator G>
89 RealType variateForHugeRatio(G &g) const;
90
91public:
92 template <std::uniform_random_bit_generator G>
93 [[nodiscard]] RealType operator()(G &g) const;
94
95private:
96 [[nodiscard]] long double Moment(int n) const;
97
98public:
99 [[nodiscard]] long double Mean() const override;
100 [[nodiscard]] long double Variance() const override;
101 [[nodiscard]] RealType Mode() const override;
102 [[nodiscard]] long double Skewness() const override;
103 [[nodiscard]] long double ExcessKurtosis() const override;
104
105private:
106 RealType quantileImpl(double p) const override;
107 RealType quantileImpl1m(double p) const override;
108 std::complex<double> CFImpl(double t) const override;
109};
110
111} // namespace randlib
112
114
115namespace randlib
116{
117
118template <typename RealType>
120{
121 SetParameters(ratio, scale);
122}
123
124template <typename RealType>
126{
127 return "Marchenko-Pastur(" + this->toStringWithPrecision(GetRatio()) + ", " +
128 this->toStringWithPrecision(GetScale()) + ")";
129}
130
131template <typename RealType>
132void MarchenkoPasturRand<RealType>::SetParameters(double ratio, double scale)
133{
134 if (!(ratio > 0.0))
135 throw std::invalid_argument("Marchenko-Pastur distribution: ratio parameter should be positive");
136 if (!(scale > 0.0))
137 throw std::invalid_argument("Marchenko-Pastur distribution: scale should be positive");
138 lambda = ratio;
139 double sqrtLambda = std::sqrt(lambda);
140 a = 1.0 - sqrtLambda;
141 a *= a;
142 b = 1.0 + sqrtLambda;
143 b *= b;
144
145 sigmaSq = scale;
146 logLambda = std::log(lambda);
147
148 GENERATOR_ID genId = getIdOfUsedGenerator();
149 if (genId == TINY_RATIO || genId == HUGE_RATIO) {
150 BetaRV.SetShapes(1.5, 1.5);
151 BetaRV.SetSupport(a, b);
152 M = std::min(a / lambda, a);
153 } else {
154 BetaRV.SetShapes(0.5, 1.5);
155 BetaRV.SetSupport(0, b);
156 M = 0.25 * b / sqrtLambda;
157 if (lambda > 1)
158 M /= lambda * lambda;
159 }
160}
161
162template <typename RealType>
163double MarchenkoPasturRand<RealType>::f(const RealType &x) const
164{
165 if (x == 0.0)
166 return (lambda < 1) ? 0.0 : INFINITY;
167 double xSt = x / sigmaSq;
168 if (xSt < a || xSt > b)
169 return 0.0;
170 double y = 0.5 * std::sqrt((b - xSt) * (xSt - a));
171 y /= (lambda * xSt);
172 y /= (randlib::consts::pi * sigmaSq);
173 return y;
174}
175
176template <typename RealType>
177double MarchenkoPasturRand<RealType>::logf(const RealType &x) const
178{
179 if (x == 0.0)
180 return (lambda < 1) ? -INFINITY : INFINITY;
181 double xSt = x / sigmaSq;
182 if (xSt < a || xSt > b)
183 return -INFINITY;
184 double y = 0.5 * std::log((b - xSt) * (xSt - a));
185 y -= randlib::consts::ln2 + randlib::consts::lnpi + logLambda + std::log(x);
186 return y;
187}
188
189template <typename RealType>
190double MarchenkoPasturRand<RealType>::ccdfForLargeRatio(const RealType &x) const
191{
192 double y1 = 1.0 - x + lambda;
193 double lambdam1 = lambda - 1.0;
194 double lambdap1 = lambda + 1.0;
195 double temp = std::sqrt(4 * lambda - y1 * y1);
196 if (temp != 0.0) {
197 y1 /= temp;
198 y1 = lambdap1 * math::atan(y1);
199 } else
200 y1 = lambdap1 * math::sign(y1) * randlib::consts::half_pi;
201 double y2 = x * lambdap1;
202 y2 -= lambdam1 * lambdam1;
203 y2 /= temp * lambdam1;
204 y2 = lambdam1 * math::atan(y2);
205 double y = randlib::consts::pi - temp + y1 + y2;
206 y /= randlib::consts::pi * lambda;
207 return 0.5 * y;
208}
209
210template <typename RealType>
211double MarchenkoPasturRand<RealType>::cdfForSmallRatio(const RealType &x) const
212{
213 double y1 = 1.0 - x + lambda;
214 double temp = std::sqrt(4 * lambda - y1 * y1);
215 double lambdam1 = lambda - 1.0;
216 double lambdap1 = lambda + 1.0;
217 if (temp != 0.0) {
218 y1 /= temp;
219 y1 = lambdap1 * math::atan(y1);
220 } else
221 y1 = lambdap1 * math::sign(y1) * randlib::consts::half_pi;
222 double y2 = 0.0;
223 if (lambdam1 != 0) {
224 y2 = x * lambdap1;
225 y2 -= lambdam1 * lambdam1;
226 y2 /= -temp * lambdam1;
227 y2 = lambdam1 * math::atan(y2);
228 }
229 double y = randlib::consts::pi * lambda + temp - y1 + y2;
230 y /= randlib::consts::pi * lambda;
231 return 0.5 * y;
232}
233
234template <typename RealType>
235double MarchenkoPasturRand<RealType>::F(const RealType &x) const
236{
237 double xSt = x / sigmaSq;
238 if (xSt < 0.0)
239 return 0.0;
240 if (xSt >= b)
241 return 1.0;
242 if (lambda > 1.0)
243 return (xSt > a) ? 1.0 - ccdfForLargeRatio(xSt) : 1.0 - 1.0 / lambda;
244 return (xSt > a) ? cdfForSmallRatio(xSt) : 0.0;
245}
246
247template <typename RealType>
248double MarchenkoPasturRand<RealType>::S(const RealType &x) const
249{
250 double xSt = x / sigmaSq;
251 if (xSt < 0.0)
252 return 1.0;
253 if (xSt >= b)
254 return 0.0;
255 if (lambda > 1.0)
256 return (xSt > a) ? ccdfForLargeRatio(xSt) : 1.0 / lambda;
257 return (xSt > a) ? 1.0 - cdfForSmallRatio(xSt) : 1.0;
258}
259
260template <typename RealType>
261template <std::uniform_random_bit_generator G>
263{
264 size_t iter = 0;
265 do {
266 double X = BetaRV(g);
267 double U = detail::uniform01<RealType>(g);
268 if (U < M / X)
269 return X;
270 } while (++iter <= ProbabilityDistribution<RealType>::MAX_ITER_REJECTION);
271 throw std::runtime_error("Marchenko-Pastur distribution: sampling failed");
272}
273
274template <typename RealType>
275template <std::uniform_random_bit_generator G>
276RealType MarchenkoPasturRand<RealType>::variateForSmallRatio(G &g) const
277{
278 size_t iter = 0;
279 do {
280 double X = BetaRV(g);
281 double U = detail::uniform01<RealType>(g);
282 double ratio = M * (1.0 - a / X);
283 if (U * U < ratio)
284 return X;
285 } while (++iter <= ProbabilityDistribution<RealType>::MAX_ITER_REJECTION);
286 throw std::runtime_error("Marchenko-Pastur distribution: sampling failed");
287}
288
289template <typename RealType>
290template <std::uniform_random_bit_generator G>
291RealType MarchenkoPasturRand<RealType>::variateForLargeRatio(G &g) const
292{
293 return (detail::uniform01<RealType>(g) > 1.0 / lambda) ? 0.0 : variateForSmallRatio(g);
294}
295
296template <typename RealType>
297template <std::uniform_random_bit_generator G>
298RealType MarchenkoPasturRand<RealType>::variateForHugeRatio(G &g) const
299{
300 return (detail::uniform01<RealType>(g) > 1.0 / lambda) ? 0.0 : variateForTinyRatio(g);
301}
302
303template <typename RealType>
304template <std::uniform_random_bit_generator G>
306{
307 switch (getIdOfUsedGenerator()) {
308 case TINY_RATIO:
309 return sigmaSq * variateForTinyRatio(g);
310 case SMALL_RATIO:
311 return sigmaSq * variateForSmallRatio(g);
312 case LARGE_RATIO:
313 return sigmaSq * variateForLargeRatio(g);
314 case HUGE_RATIO:
315 return sigmaSq * variateForHugeRatio(g);
316 default:
317 throw std::runtime_error("Marchenko-Pastur distribution: invalid generator id");
318 }
319}
320
321template <typename RealType>
322void MarchenkoPasturRand<RealType>::sample(detail::EngineRef g, std::span<RealType> outputData) const
323{
324 switch (getIdOfUsedGenerator()) {
325 case TINY_RATIO:
326 for (RealType &var : outputData)
327 var = sigmaSq * variateForTinyRatio(g);
328 break;
329 case SMALL_RATIO:
330 for (RealType &var : outputData)
331 var = sigmaSq * variateForSmallRatio(g);
332 break;
333 case LARGE_RATIO:
334 for (RealType &var : outputData)
335 var = sigmaSq * variateForLargeRatio(g);
336 break;
337 case HUGE_RATIO:
338 for (RealType &var : outputData)
339 var = sigmaSq * variateForHugeRatio(g);
340 break;
341 default:
342 return;
343 }
344}
345
346template <typename RealType>
347long double MarchenkoPasturRand<RealType>::Moment(int n) const
348{
349 if (!(n >= 0))
350 throw std::invalid_argument("Marchenko-Pastur distribution: degree n should be non-negative");
351 switch (n) {
352 case 0:
353 return 1.0;
354 case 1:
355 return sigmaSq;
356 case 2:
357 return sigmaSq * sigmaSq * lambda;
358 default: {
359 long double sum = 0.0;
360 long double temp = math::lfact(n) + math::lfact(n - 1);
361 long double nlogSigmaSq = n * std::log(sigmaSq);
362 for (int k = 0; k != n; ++k) {
363 long double term = temp;
364 int kp1 = k + 1;
365 term -= 2 * math::lfact(k);
366 term -= math::lfact(n - k);
367 term -= math::lfact(n - kp1);
368 term += k * logLambda;
369 term += nlogSigmaSq;
370 term = std::exp(term) / kp1;
371 sum += term;
372 }
373 return sum;
374 }
375 }
376}
377
378template <typename RealType>
380{
381 return sigmaSq;
382}
383
384template <typename RealType>
386{
387 return sigmaSq * sigmaSq * lambda;
388}
389
390template <typename RealType>
392{
393 if (lambda > 1)
394 return 0.0;
395 RealType mode = lambda - 1.0;
396 mode *= mode;
397 mode /= lambda + 1.0;
398 return sigmaSq * mode;
399}
400
401template <typename RealType>
403{
404 long double mu = Mean();
405 long double var = Variance();
406 long double skewness = Moment(3);
407 skewness -= std::pow(mu, 3);
408 skewness -= 3 * mu * var;
409 skewness /= std::pow(var, 1.5);
410 return skewness;
411}
412
413template <typename RealType>
415{
416 long double mu = Mean();
417 long double var = Variance();
418 long double moment3 = Moment(3);
419 long double kurtosis = Moment(4);
420 long double muSq = mu * mu;
421 kurtosis -= 4 * mu * moment3;
422 kurtosis += 3 * muSq * muSq;
423 kurtosis += 6 * muSq * var;
424 kurtosis /= var * var;
425 return kurtosis - 3.0;
426}
427
428template <typename RealType>
429RealType MarchenkoPasturRand<RealType>::quantileImpl(double p) const
430{
431 if (p <= 1.0 - 1.0 / lambda)
432 return 0.0;
433 RealType minInitValue = sigmaSq * a;
434 RealType maxInitValue = sigmaSq * b;
435 RealType initValue = minInitValue + p * (maxInitValue - minInitValue);
437}
438
439template <typename RealType>
440RealType MarchenkoPasturRand<RealType>::quantileImpl1m(double p) const
441{
442 if (p >= 1.0 / lambda)
443 return 0.0;
444 RealType minInitValue = sigmaSq * a;
445 RealType maxInitValue = sigmaSq * b;
446 RealType initValue = maxInitValue - p * (maxInitValue - minInitValue);
448}
449
450template <typename RealType>
451std::complex<double> MarchenkoPasturRand<RealType>::CFImpl(double t) const
452{
453 if (lambda < 1)
456 if (lambda == 1) {
458 double re = this->ExpectedValue([t](double x) { return std::cos(t * x) - 1.0; }, 0, 4 * sigmaSq);
459 double im = this->ExpectedValue([t](double x) { return std::sin(t * x); }, 0, 4 * sigmaSq);
460 return std::complex<double>(1.0 + re, im);
461 }
463 double re = this->ExpectedValue([t](double x) { return std::cos(t * x); }, sigmaSq * a, sigmaSq * b);
464 double im = this->ExpectedValue([t](double x) { return std::sin(t * x); }, sigmaSq * a, sigmaSq * b);
465 return std::complex<double>(1.0 - 1.0 / lambda + re, im);
466}
467
468} // namespace randlib
The BetaRand class Beta distribution.
Definition continuous/beta.hpp:246
The ContinuousDistribution class Abstract class for all continuous distributions.
Definition continuous.hpp:22
RealType quantileImpl(double p, RealType initValue) const override
Definition continuous.hpp:140
RealType quantileImpl1m(double p, RealType initValue) const override
Definition continuous.hpp:152
The MarchenkoPasturRand class Marchenko-Pastur distribution.
Definition marchenko_pastur.hpp:20
MarchenkoPasturRand(double ratio=1, double scale=1)
Definition marchenko_pastur.hpp:119
std::string Name() const override
Definition marchenko_pastur.hpp:125
double GetScale() const noexcept
Definition marchenko_pastur.hpp:43
double F(const RealType &x) const override
Definition marchenko_pastur.hpp:235
double f(const RealType &x) const override
Definition marchenko_pastur.hpp:163
Support SupportType() const override
Definition marchenko_pastur.hpp:37
RealType operator()(G &g) const
Definition marchenko_pastur.hpp:305
double logf(const RealType &x) const override
Definition marchenko_pastur.hpp:177
long double Skewness() const override
Definition marchenko_pastur.hpp:402
long double Variance() const override
Definition marchenko_pastur.hpp:385
double GetRatio() const noexcept
Definition marchenko_pastur.hpp:42
void SetParameters(double ratio, double scale)
Definition marchenko_pastur.hpp:132
RealType MinValue() const override
Definition marchenko_pastur.hpp:38
RealType MaxValue() const override
Definition marchenko_pastur.hpp:39
long double ExcessKurtosis() const override
Definition marchenko_pastur.hpp:414
RealType Mode() const override
Mode is the value, which has the largest probability to happen, or, in the case of continuous distrib...
Definition marchenko_pastur.hpp:391
double S(const RealType &x) const override
Definition marchenko_pastur.hpp:248
long double Mean() const override
Definition marchenko_pastur.hpp:379
virtual std::complex< double > CFImpl(double t) const
Definition univariate.hpp:365
The EngineRef class Non-owning, type-erased reference to any uniform random bit generator.
Definition engine/uniform.hpp:116
constexpr long double ln2
Definition constants.hpp:18
constexpr long double half_pi
Definition constants.hpp:23
constexpr long double pi
Definition constants.hpp:22
constexpr long double lnpi
Definition constants.hpp:21
Real standardNormal(G &g)
Ziggurat algorithm of Marsaglia and Tsang with 256 layers and an explicit sign bit.
Definition variates.hpp:45
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
int sign(Real x)
Definition functions.hpp:232
Real atan(Real x)
Definition functions.hpp:238
Definition bivariate_normal.hpp:10
Support
Shape of the support of a univariate distribution.
Definition univariate.hpp:15