RandLib 2.0.0
Probability distributions, fitting, and heavy-tail tools for C++20
Loading...
Searching...
No Matches
von_mises.hpp
Go to the documentation of this file.
1#pragma once
2
7
8namespace randlib
9{
10
17template <typename RealType>
18class VonMisesRand final : public CircularDistribution<RealType>
19{
20 RealType variate(detail::EngineRef g) const override { return (*this)(g); }
21
22 double k = 1;
23 double logI0k = 1;
25 int p = 12;
26 static constexpr double CK = 10.5;
27
28public:
29 VonMisesRand(double location = 0, double concentration = 1);
30
31 [[nodiscard]] std::string Name() const override;
32
33 void SetConcentration(double concentration);
34 [[nodiscard]] inline double GetConcentration() const noexcept { return k; }
35
36private:
37 double cdfSeries(double x) const;
40 double lowerTail(double d) const;
41 double cdfErfcAux(RealType x) const;
42 double cdfErfc(RealType x) const;
43 double ccdfErfc(RealType x) const;
44
45public:
46 [[nodiscard]] double f(const RealType &x) const override;
47 [[nodiscard]] double logf(const RealType &x) const override;
48 [[nodiscard]] double F(const RealType &x) const override;
49 [[nodiscard]] double S(const RealType &x) const override;
50 template <std::uniform_random_bit_generator G>
51 [[nodiscard]] RealType operator()(G &g) const;
52
53 [[nodiscard]] long double CircularMean() const override;
54 [[nodiscard]] long double CircularVariance() const override;
55 [[nodiscard]] RealType Median() const override;
56 [[nodiscard]] RealType Mode() const override;
57
58private:
59 std::complex<double> CFImpl(double t) const override;
60};
61
62} // namespace randlib
63
65
66namespace randlib
67{
68
69template <typename RealType>
70VonMisesRand<RealType>::VonMisesRand(double location, double concentration) : CircularDistribution<RealType>(location)
71{
72 SetConcentration(concentration);
73}
74
75template <typename RealType>
77{
78 return "von Mises(" + this->toStringWithPrecision(this->GetLocation()) + ", " +
79 this->toStringWithPrecision(this->GetConcentration()) + ")";
80}
81
82template <typename RealType>
84{
85 if (!(concentration > 0.0))
86 throw std::invalid_argument("von Mises distribution: concentration parameter should be positive");
87 k = concentration;
88 logI0k = math::logBesselI(0, k);
89 s = (k > 1.3) ? 1.0 / std::sqrt(k) : randlib::consts::pi * std::exp(-k);
90 if (k < CK) {
92 static constexpr double a[] = {28.0, 0.5, 100.0, 5.0};
93 p = std::ceil(a[0] + a[1] * k - a[2] / (k + a[3]));
94 }
95}
96
97template <typename RealType>
98double VonMisesRand<RealType>::cdfSeries(double x) const
99{
101 double sinX = std::sin(x), cosX = std::cos(x);
102 double px = p * x;
103 double sn = std::sin(px), cn = std::cos(px);
104 double R = 0, V = 0;
105 for (int n = p - 1; n > 0; --n) {
106 double temp = sn;
107 sn *= cosX;
108 sn -= cn * sinX;
109 cn *= cosX;
110 cn += temp * sinX;
111 R += 2 * n / k;
112 R = 1.0 / R;
113 V += sn / n;
114 V *= R;
115 }
117 V += 0.5 * x / randlib::consts::pi;
118 V += 0.5;
119 return V;
120}
121
122template <typename RealType>
123double VonMisesRand<RealType>::cdfErfcAux(RealType x) const
124{
126 double c = 24.0 * k;
127 double v = c - 56.0;
128 double r = std::sqrt((54.0 / (347.0 / v + 26.0 - c) - 6.0 + c) / 12.0);
129 double z = -std::sin(0.5 * x) * r;
130 double twoZSq = 2.0 * z * z;
131 v -= twoZSq - 3.0;
132 double y = (c - 2 * twoZSq - 16.0) / 3.0;
133 y = ((twoZSq + 1.75) * twoZSq + 83.5) / v - y;
134 y *= y;
135 return z * (1.0 - twoZSq / y);
136}
137
138template <typename RealType>
139double VonMisesRand<RealType>::cdfErfc(RealType x) const
140{
141 double arg = cdfErfcAux(x);
142 return 0.5 * std::erfc(arg);
143}
144
145template <typename RealType>
146double VonMisesRand<RealType>::ccdfErfc(RealType x) const
147{
148 double arg = cdfErfcAux(x);
149 return 0.5 * std::erfc(-arg);
150}
151
152template <typename RealType>
153double VonMisesRand<RealType>::f(const RealType &x) const
154{
155 return (x < this->MinValue() || x > this->MaxValue()) ? 0.0 : std::exp(logf(x));
156}
157
158template <typename RealType>
159double VonMisesRand<RealType>::logf(const RealType &x) const
160{
161 if (x < this->MinValue() || x > this->MaxValue())
162 return -INFINITY;
163 double y = k * std::cos(x - this->loc) - logI0k;
165 return y;
166}
167
168template <typename RealType>
169double VonMisesRand<RealType>::F(const RealType &x) const
170{
171 if (x <= this->MinValue())
172 return 0.0;
173 if (x >= this->MaxValue())
174 return 1.0;
175 double xAdj = x - this->loc;
176 if (k >= CK)
177 return cdfErfc(xAdj);
179 return (xAdj <= 0) ? lowerTail(xAdj) : 1.0 - lowerTail(-xAdj);
180}
181
182template <typename RealType>
183double VonMisesRand<RealType>::lowerTail(double d) const
184{
185 static constexpr double SMALL_TAIL = 1e-3;
186 const double series = cdfSeries(d);
187 if (series >= SMALL_TAIL)
188 return series;
189 return math::integrate([this](double t) { return f(this->loc + t); }, -randlib::consts::pi, d);
190}
191
192template <typename RealType>
193double VonMisesRand<RealType>::S(const RealType &x) const
194{
195 if (x <= this->MinValue())
196 return 1.0;
197 if (x >= this->MaxValue())
198 return 0.0;
199 double xAdj = x - this->loc;
200 if (k >= CK)
201 return ccdfErfc(xAdj);
202 return (xAdj >= 0) ? lowerTail(-xAdj) : 1.0 - lowerTail(xAdj);
203}
204
205template <typename RealType>
206template <std::uniform_random_bit_generator G>
208{
211 size_t iter = 0;
212 do {
213 RealType U = detail::uniform01<RealType>(g);
214 RealType V = 2 * detail::uniform01<RealType>(g) - 1;
215 RealType theta = s * V / U;
216 if ((std::fabs(theta) <= randlib::consts::pi) &&
217 ((k * theta * theta < 4.0 * (1.0 - U)) || (k * std::cos(theta) >= 2 * std::log(U) + k)))
218 return this->loc + theta;
220 throw std::runtime_error("von Mises distribution: sampling failed");
221}
222
223template <typename RealType>
225{
226 return this->loc;
227}
228
229template <typename RealType>
231{
232 double var = math::logBesselI(1, k);
233 var -= logI0k;
234 return -std::expm1l(var);
235}
236
237template <typename RealType>
238std::complex<double> VonMisesRand<RealType>::CFImpl(double t) const
239{
241 if (t != std::round(t))
243 double tloc = t * this->loc;
244 double cosTloc = std::cos(tloc), sinTloc = std::sin(tloc);
245 std::complex<double> y(cosTloc, sinTloc);
246 double z = math::logBesselI(t, k) - logI0k;
247 return y * std::exp(z);
248}
249
250template <typename RealType>
252{
253 return this->loc;
254}
255
256template <typename RealType>
258{
259 return this->loc;
260}
261
262} // namespace randlib
The CircularDistribution class Abstract class for all continuous circular distributions.
Definition circular.hpp:19
The ProbabilityDistribution class Abstract class for all probability distributions.
Definition distribution.hpp:28
virtual std::complex< double > CFImpl(double t) const
Definition univariate.hpp:365
The VonMisesRand class Von-Mises distribution.
Definition von_mises.hpp:19
double S(const RealType &x) const override
Definition von_mises.hpp:193
long double CircularMean() const override
Definition von_mises.hpp:224
RealType operator()(G &g) const
Definition von_mises.hpp:207
VonMisesRand(double location=0, double concentration=1)
Definition von_mises.hpp:70
double logf(const RealType &x) const override
Definition von_mises.hpp:159
double GetConcentration() const noexcept
Definition von_mises.hpp:34
double F(const RealType &x) const override
Definition von_mises.hpp:169
long double CircularVariance() const override
Definition von_mises.hpp:230
double f(const RealType &x) const override
Definition von_mises.hpp:153
RealType Median() const override
Definition von_mises.hpp:251
void SetConcentration(double concentration)
Definition von_mises.hpp:83
std::string Name() const override
Definition von_mises.hpp:76
RealType Mode() const override
Mode is the value, which has the largest probability to happen, or, in the case of continuous distrib...
Definition von_mises.hpp:257
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 pi
Definition constants.hpp:22
constexpr long double e
Definition constants.hpp:15
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 logBesselI(std::type_identity_t< Real > nu, Real x)
Calculates logarithm of modified Bessel function of the 1st kind.
Definition functions.hpp:577
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