RandLib 2.0.0
Probability distributions, fitting, and heavy-tail tools for C++20
Loading...
Searching...
No Matches
generalized_extreme_value.hpp
Go to the documentation of this file.
1#pragma once
2
7
8#include <cmath>
9#include <limits>
10#include <string>
11
12namespace randlib
13{
14
15template <typename RealType>
17{
18 RealType variate(detail::EngineRef g) const override { return (*this)(g); }
19
20 double shape = 0;
21 double scale = 1;
22 double location = 0;
23 double logScale = 0;
24
25 [[nodiscard]] double standardized(const RealType &x) const { return (x - location) / scale; }
26 [[nodiscard]] double supportTerm(const RealType &x) const { return std::fma(shape, standardized(x), 1.0); }
27 [[nodiscard]] double supportLog(const RealType &x) const
28 {
29 const double product = shape * standardized(x);
30 return std::fabs(product) < 0.5 ? std::log1p(product) : std::log(supportTerm(x));
31 }
32
33public:
34 GeneralizedExtremeValueRand(double shapeParameter = 0, double scaleParameter = 1, double locationParameter = 0)
35 {
36 SetParameters(shapeParameter, scaleParameter, locationParameter);
37 }
38
39 void SetParameters(double newShape, double newScale, double newLocation)
40 {
41 if (!std::isfinite(newShape))
42 throw std::invalid_argument("Generalized extreme-value distribution: shape should be finite");
43 if (!(newScale > 0) || !std::isfinite(newScale))
44 throw std::invalid_argument("Generalized extreme-value distribution: scale should be positive and finite");
45 if (!std::isfinite(newLocation))
46 throw std::invalid_argument("Generalized extreme-value distribution: location should be finite");
47 shape = newShape;
48 scale = newScale;
49 location = newLocation;
50 logScale = std::log(scale);
51 }
52
53 [[nodiscard]] double GetShape() const noexcept { return shape; }
54 [[nodiscard]] double GetScale() const noexcept { return scale; }
55 [[nodiscard]] double GetLocation() const noexcept { return location; }
56
57 [[nodiscard]] std::string Name() const override
58 {
59 return "GeneralizedExtremeValue(" + this->toStringWithPrecision(shape) + ", " +
60 this->toStringWithPrecision(scale) + ", " + this->toStringWithPrecision(location) + ")";
61 }
62
63 [[nodiscard]] Support SupportType() const override
64 {
65 if (shape > 0)
67 if (shape < 0)
69 return Support::Infinite;
70 }
71
72 [[nodiscard]] RealType MinValue() const override
73 {
74 return shape > 0 ? static_cast<RealType>(location - scale / shape) : -std::numeric_limits<RealType>::infinity();
75 }
76
77 [[nodiscard]] RealType MaxValue() const override
78 {
79 return shape < 0 ? static_cast<RealType>(location - scale / shape) : std::numeric_limits<RealType>::infinity();
80 }
81
82 [[nodiscard]] double logf(const RealType &x) const override
83 {
84 if (shape == 0) {
85 const double z = standardized(x);
86 return -logScale - z - std::exp(-z);
87 }
88 const double support = supportTerm(x);
89 if (!(support > 0)) {
90 if (support == 0 && shape < 0) {
91 if (shape < -1)
92 return INFINITY;
93 if (shape == -1)
94 return -logScale;
95 }
96 return -INFINITY;
97 }
98 const double logSupport = supportLog(x);
99 return -logScale - (1 / shape + 1) * logSupport - std::exp(-logSupport / shape);
100 }
101
102 [[nodiscard]] double f(const RealType &x) const override { return std::exp(logf(x)); }
103
104 [[nodiscard]] double F(const RealType &x) const override
105 {
106 if (shape > 0 && x <= MinValue())
107 return 0;
108 if (shape < 0 && x >= MaxValue())
109 return 1;
110 const double exponent = shape == 0 ? std::exp(-standardized(x)) : std::exp(-supportLog(x) / shape);
111 return std::exp(-exponent);
112 }
113
114 [[nodiscard]] double S(const RealType &x) const override
115 {
116 if (shape > 0 && x <= MinValue())
117 return 1;
118 if (shape < 0 && x >= MaxValue())
119 return 0;
120 const double exponent = shape == 0 ? std::exp(-standardized(x)) : std::exp(-supportLog(x) / shape);
121 return -std::expm1(-exponent);
122 }
123
124 template <std::uniform_random_bit_generator G>
125 [[nodiscard]] RealType operator()(G &g) const
126 {
127 const RealType exponential = detail::standardExponential<RealType>(g);
128 if (shape == 0)
129 return static_cast<RealType>(location - scale * std::log(exponential));
130 return static_cast<RealType>(location + scale * std::expm1(-shape * std::log(exponential)) / shape);
131 }
132
133 [[nodiscard]] long double Mean() const override
134 {
135 if (shape >= 1)
136 return INFINITY;
137 if (shape == 0)
138 return location + scale * randlib::consts::egamma;
139 if (std::fabs(shape) < SMALL_SHAPE)
140 return location + scale * standardMean();
141 return location + scale * (std::tgamma(1 - shape) - 1) / shape;
142 }
143
144 [[nodiscard]] long double Variance() const override
145 {
146 if (shape >= 0.5)
147 return INFINITY;
148 if (shape == 0) {
149 const long double value = randlib::consts::pi * scale;
150 return value * value / 6;
151 }
152 if (std::fabs(shape) < SMALL_SHAPE) {
154 const long double scaled = std::exp(2 * logGammaOneMinusShape()) * std::expm1(logGammaExcess(2)) / shape;
155 return scale * scale * scaled / shape;
156 }
157 const long double first = std::tgamma(1 - shape);
158 return scale * scale * (std::tgamma(1 - 2 * shape) - first * first) / (shape * shape);
159 }
160
161 [[nodiscard]] RealType Median() const override { return quantileImpl(0.5); }
162
163 [[nodiscard]] RealType Mode() const override
164 {
165 if (shape <= -1)
166 return MaxValue();
167 if (shape == 0)
168 return static_cast<RealType>(location);
169 return static_cast<RealType>(location + scale * std::expm1(-shape * std::log1p(shape)) / shape);
170 }
171
172 [[nodiscard]] long double Skewness() const override
173 {
174 if (shape >= 1.0 / 3)
175 return INFINITY;
176 if (shape == 0) {
179 }
180 if (std::fabs(shape) < SMALL_SHAPE) {
181 const long double mean = standardMean();
182 return standardCentralMoment(3, mean) / std::pow(standardCentralMoment(2, mean), 1.5L);
183 }
184 const long double first = std::tgamma(1 - shape);
185 const long double second = std::tgamma(1 - 2 * shape);
186 const long double third = std::tgamma(1 - 3 * shape);
187 const long double variance = second - first * first;
188 const long double centralThird = third - 3 * first * second + 2 * first * first * first;
189 return centralThird / std::pow(variance, 1.5L) * (shape > 0 ? 1 : -1);
190 }
191
192 [[nodiscard]] long double ExcessKurtosis() const override
193 {
194 if (shape >= 0.25)
195 return INFINITY;
196 if (shape == 0)
197 return 2.4L;
198 if (std::fabs(shape) < SMALL_SHAPE) {
199 const long double mean = standardMean();
200 const long double variance = standardCentralMoment(2, mean);
201 return standardCentralMoment(4, mean) / (variance * variance) - 3;
202 }
203 const long double first = std::tgamma(1 - shape);
204 const long double second = std::tgamma(1 - 2 * shape);
205 const long double third = std::tgamma(1 - 3 * shape);
206 const long double fourth = std::tgamma(1 - 4 * shape);
207 const long double variance = second - first * first;
208 const long double centralFourth =
209 fourth - 4 * first * third + 6 * first * first * second - 3 * first * first * first * first;
210 return centralFourth / (variance * variance) - 3;
211 }
212
213 [[nodiscard]] long double Entropy() const { return logScale + randlib::consts::egamma * (1 + shape) + 1; }
214
215private:
217 static constexpr double SMALL_SHAPE = 0.05;
218
220 [[nodiscard]] long double zetaPowerSeries(int m, int subtracted) const
221 {
222 long double sum = 0, power = shape;
223 for (int k = 2; k < 64; ++k) {
224 power *= shape;
225 const long double term = math::riemannZeta(static_cast<long double>(k)) * power *
226 (std::pow(static_cast<long double>(m), k) - subtracted) / k;
227 sum += term;
228 if (std::fabs(term) <= std::numeric_limits<long double>::epsilon() * std::fabs(sum))
229 break;
230 }
231 return sum;
232 }
233
235 [[nodiscard]] long double logGammaExcess(int m) const { return zetaPowerSeries(m, m); }
236
238 [[nodiscard]] long double logGammaOneMinusShape() const
239 {
240 return randlib::consts::egamma * shape + zetaPowerSeries(1, 0);
241 }
242
244 [[nodiscard]] long double standardMean() const { return std::expm1(logGammaOneMinusShape()) / shape; }
245
247 [[nodiscard]] long double standardCentralMoment(int order, long double mean) const
248 {
249 const double xi = shape, center = static_cast<double>(mean);
250 return math::integrate(
251 [xi, center, order](double logExponential) {
252 const double density = std::exp(logExponential - std::exp(logExponential));
253 return density == 0 ? 0.0 : std::pow(std::expm1(-xi * logExponential) / xi - center, order) * density;
254 },
255 -INFINITY, INFINITY);
256 }
257
258 RealType quantileImpl(double p) const override
259 {
260 const double logExponential = std::log(-std::log(p));
261 if (shape == 0)
262 return static_cast<RealType>(location - scale * logExponential);
263 return static_cast<RealType>(location + scale * std::expm1(-shape * logExponential) / shape);
264 }
265
266 RealType quantileImpl1m(double p) const override
267 {
268 const double logExponential = std::log(-std::log1p(-p));
269 if (shape == 0)
270 return static_cast<RealType>(location - scale * logExponential);
271 return static_cast<RealType>(location + scale * std::expm1(-shape * logExponential) / shape);
272 }
273};
274
275} // namespace randlib
The ContinuousDistribution class Abstract class for all continuous distributions.
Definition continuous.hpp:22
Definition generalized_extreme_value.hpp:17
GeneralizedExtremeValueRand(double shapeParameter=0, double scaleParameter=1, double locationParameter=0)
Definition generalized_extreme_value.hpp:34
double F(const RealType &x) const override
Definition generalized_extreme_value.hpp:104
double f(const RealType &x) const override
Definition generalized_extreme_value.hpp:102
long double Skewness() const override
Definition generalized_extreme_value.hpp:172
long double Variance() const override
Definition generalized_extreme_value.hpp:144
double GetScale() const noexcept
Definition generalized_extreme_value.hpp:54
double logf(const RealType &x) const override
Definition generalized_extreme_value.hpp:82
RealType Mode() const override
Mode is the value, which has the largest probability to happen, or, in the case of continuous distrib...
Definition generalized_extreme_value.hpp:163
void SetParameters(double newShape, double newScale, double newLocation)
Definition generalized_extreme_value.hpp:39
long double Entropy() const
Definition generalized_extreme_value.hpp:213
double GetLocation() const noexcept
Definition generalized_extreme_value.hpp:55
RealType Median() const override
Definition generalized_extreme_value.hpp:161
RealType MinValue() const override
Definition generalized_extreme_value.hpp:72
Support SupportType() const override
Definition generalized_extreme_value.hpp:63
double S(const RealType &x) const override
Definition generalized_extreme_value.hpp:114
double GetShape() const noexcept
Definition generalized_extreme_value.hpp:53
long double Mean() const override
Definition generalized_extreme_value.hpp:133
RealType MaxValue() const override
Definition generalized_extreme_value.hpp:77
long double ExcessKurtosis() const override
Definition generalized_extreme_value.hpp:192
RealType operator()(G &g) const
Definition generalized_extreme_value.hpp:125
std::string Name() const override
Definition generalized_extreme_value.hpp:57
std::string toStringWithPrecision(const double a_value, const int n=6) const
Definition distribution.hpp:181
The EngineRef class Non-owning, type-erased reference to any uniform random bit generator.
Definition engine/uniform.hpp:116
constexpr long double egamma
Definition constants.hpp:36
constexpr long double apery
Definition constants.hpp:37
constexpr long double pi
Definition constants.hpp:22
constexpr long double pi_sq
Definition constants.hpp:39
constexpr long double sqrt2
Definition constants.hpp:27
constexpr long double sqrt3
Definition constants.hpp:29
Real standardNormal(G &g)
Ziggurat algorithm of Marsaglia and Tsang with 256 layers and an explicit sign bit.
Definition variates.hpp:45
Real riemannZeta(Real s)
Definition special_functions.hpp:340
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
Support
Shape of the support of a univariate distribution.
Definition univariate.hpp:15
@ LeftSemiFinite
(-∞, b]
@ RightSemiFinite
[a, ∞)
@ Infinite
(-∞, ∞)