RandLib 2.0.0
Probability distributions, fitting, and heavy-tail tools for C++20
Loading...
Searching...
No Matches
bivariate_normal.hpp
Go to the documentation of this file.
1#pragma once
2
8
9namespace randlib
10{
11
18template <typename RealType>
20 : public ContinuousBivariateDistribution<NormalRand<RealType>, NormalRand<RealType>, RealType>
21{
22 Pair<RealType> variate(detail::EngineRef g) const override { return (*this)(g); }
23
24 double mu1 = 0;
25 double mu2 = 0;
26 double sigma1 = 1;
27 double sigma2 = 1;
28 double rho = 0;
29 double pdfCoef = 1 + randlib::consts::ln2 + randlib::consts::lnpi;
30 double sqrt1mroSq = 1;
31
32public:
33 BivariateNormalRand(double location1, double location2, double scale1, double scale2, double correlation);
34 [[nodiscard]] std::string Name() const override;
35
36 void SetLocations(double location1, double location2);
37 void SetCovariance(double scale1, double scale2, double correlation);
38 [[nodiscard]] inline DoublePair GetLocation() { return this->Mean(); }
39 [[nodiscard]] inline double GetFirstLocation() const noexcept { return mu1; }
40 [[nodiscard]] inline double GetSecondLocation() const noexcept { return mu2; }
41 [[nodiscard]] inline double GetFirstScale() const noexcept { return sigma1; }
42 [[nodiscard]] inline double GetSecondScale() const noexcept { return sigma2; }
43 [[nodiscard]] inline double GetCorrelation() const noexcept { return rho; }
44
45 [[nodiscard]] double f(const Pair<RealType> &point) const override;
46 [[nodiscard]] double logf(const Pair<RealType> &point) const override;
47 [[nodiscard]] double F(const Pair<RealType> &point) const override;
48 template <std::uniform_random_bit_generator G>
49 [[nodiscard]] Pair<RealType> operator()(G &g) const;
50
51 long double Correlation() const override;
52
53 [[nodiscard]] Pair<RealType> Mode() const override;
54};
55
56} // namespace randlib
57
58namespace randlib
59{
60
61template <typename RealType>
62BivariateNormalRand<RealType>::BivariateNormalRand(double location1, double location2, double scale1, double scale2,
63 double correlation)
64{
65 SetLocations(location1, location2);
66 SetCovariance(scale1, scale2, correlation);
67}
68
69template <typename RealType>
71{
72 return "Bivariate Normal( (" + this->toStringWithPrecision(GetFirstLocation()) + ", " +
73 this->toStringWithPrecision(GetSecondLocation()) + "), (" + this->toStringWithPrecision(GetFirstScale()) +
74 ", " + this->toStringWithPrecision(GetSecondScale()) + ", " + this->toStringWithPrecision(GetCorrelation()) +
75 ") )";
76}
77
78template <typename RealType>
79void BivariateNormalRand<RealType>::SetLocations(double location1, double location2)
80{
81 mu1 = location1;
82 mu2 = location2;
83 this->X.SetLocation(mu1);
84 this->Y.SetLocation(mu2);
85}
86
87template <typename RealType>
88void BivariateNormalRand<RealType>::SetCovariance(double scale1, double scale2, double correlation)
89{
90 if (!(scale1 > 0.0 && scale2 > 0.0))
91 throw std::invalid_argument(
92 "Scales of Bivariate-Normal distribution should be positive, but they're equal to " +
93 std::to_string(scale1) + " and " + std::to_string(scale2) + " respectively");
94 if (!(std::fabs(correlation) <= 1.0))
95 throw std::invalid_argument(
96 "Correlation of Bivariate-Normal distribution should be in interval [-1, 1], but it's equal to " +
97 std::to_string(correlation));
98 sigma1 = scale1;
99 sigma2 = scale2;
100 rho = correlation;
101
102 this->X.SetScale(sigma1);
103 this->Y.SetScale(sigma2);
104
105 sqrt1mroSq = 0.5 * std::log1pl(-rho * rho);
106 pdfCoef = this->X.GetLogScale() + this->Y.GetLogScale() + sqrt1mroSq + randlib::consts::ln2 + randlib::consts::lnpi;
107 sqrt1mroSq = std::exp(sqrt1mroSq);
108}
109
110template <typename RealType>
112{
113 RealType xAdj = (point.first - mu1) / sigma1, yAdj = (point.second - mu2) / sigma2;
114 if (rho == 1.0)
115 return (xAdj - yAdj == 0) ? INFINITY : 0.0;
116 if (rho == -1.0)
117 return (xAdj + yAdj == 0) ? INFINITY : 0.0;
118 return std::exp(logf(point));
119}
120
121template <typename RealType>
123{
124 if (rho == 0.0)
125 return this->X.logf(point.first) + this->Y.logf(point.second);
126 RealType xAdj = (point.first - mu1) / sigma1, yAdj = (point.second - mu2) / sigma2;
127 if (rho == 1.0)
128 return (xAdj - yAdj == 0) ? INFINITY : -INFINITY;
129 if (rho == -1.0)
130 return (xAdj + yAdj == 0) ? INFINITY : -INFINITY;
131 RealType z = xAdj * xAdj - 2 * rho * xAdj * yAdj + yAdj * yAdj;
132 return -(pdfCoef + 0.5 * z / (sqrt1mroSq * sqrt1mroSq));
133}
134
135template <typename RealType>
137{
138 if (rho == 0.0)
139 return this->X.F(point.first) * this->Y.F(point.second);
142 double p1, p2 = 0.0;
143 RealType x = point.first, y = point.second;
144 RealType xAdj = (x - mu1) / sigma1, yAdj = (y - mu2) / sigma2;
145 if (rho > 0)
146 p1 = (xAdj < yAdj) ? this->X.F(x) : this->Y.F(y);
147 else {
148 double standardComplement = 0.5 * std::erfc(yAdj * randlib::consts::inv_sqrt2);
149 p1 = std::max(this->X.F(x) - standardComplement, 0.0);
150 }
151 if (std::fabs(rho) < 1.0) {
152 RealType lowLimit = std::asin(rho);
153 RealType highLimit = math::sign(rho) * randlib::consts::half_pi;
154 p2 = math::integrate(
155 [xAdj, yAdj](double theta) {
157 RealType cosTheta = std::cos(theta);
158 RealType tanTheta = std::tan(theta);
159 RealType integrand = xAdj * tanTheta - yAdj / cosTheta;
160 integrand *= integrand;
161 integrand += xAdj * xAdj;
162 integrand = std::exp(-0.5 * integrand);
163 return integrand;
164 },
165 lowLimit, highLimit);
166 }
167 return p1 - 0.5 * p2 / randlib::consts::pi;
168}
169
170template <typename RealType>
171template <std::uniform_random_bit_generator G>
173{
174 RealType Z1 = detail::standardNormal<RealType>(g);
175 RealType Z2 = detail::standardNormal<RealType>(g);
176 RealType x = mu1 + sigma1 * Z1;
177 RealType y = mu2 + sigma2 * (rho * Z1 + sqrt1mroSq * Z2);
178 return std::make_pair(x, y);
179}
180
181template <typename RealType>
183{
184 return rho;
185}
186
187template <typename RealType>
189{
190 return std::make_pair(mu1, mu2);
191}
192
193} // namespace randlib
virtual LongDoublePair Mean() const final
Definition bivariate.hpp:50
The BivariateNormalRand class Bivariate Gaussian (normal) distribution.
Definition bivariate_normal.hpp:21
double GetSecondLocation() const noexcept
Definition bivariate_normal.hpp:40
Pair< RealType > operator()(G &g) const
Definition bivariate_normal.hpp:172
double F(const Pair< RealType > &point) const override
Definition bivariate_normal.hpp:136
double GetFirstScale() const noexcept
Definition bivariate_normal.hpp:41
long double Correlation() const override
Definition bivariate_normal.hpp:182
double logf(const Pair< RealType > &point) const override
Definition bivariate_normal.hpp:122
DoublePair GetLocation()
Definition bivariate_normal.hpp:38
double GetFirstLocation() const noexcept
Definition bivariate_normal.hpp:39
double f(const Pair< RealType > &point) const override
Definition bivariate_normal.hpp:111
std::string Name() const override
Definition bivariate_normal.hpp:70
double GetCorrelation() const noexcept
Definition bivariate_normal.hpp:43
double GetSecondScale() const noexcept
Definition bivariate_normal.hpp:42
Pair< RealType > Mode() const override
Definition bivariate_normal.hpp:188
void SetCovariance(double scale1, double scale2, double correlation)
Definition bivariate_normal.hpp:88
BivariateNormalRand(double location1, double location2, double scale1, double scale2, double correlation)
Definition bivariate_normal.hpp:62
void SetLocations(double location1, double location2)
Definition bivariate_normal.hpp:79
The ContinuousBivariateDistribution class Abstract class for all bivariate probability distribution...
Definition continuous_bivariate.hpp:18
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 inv_sqrt2
Definition constants.hpp:28
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
int sign(Real x)
Definition functions.hpp:232
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
std::pair< T, T > Pair
Definition fwd.hpp:33
Pair< double > DoublePair
Definition fwd.hpp:39