RandLib 2.0.0
Probability distributions, fitting, and heavy-tail tools for C++20
Loading...
Searching...
No Matches
categorical.hpp
Go to the documentation of this file.
1#pragma once
2
7
8namespace randlib
9{
10
23template <typename IntType>
24class CategoricalRand final : public DiscreteDistribution<IntType>
25{
26 IntType variate(detail::EngineRef g) const override { return (*this)(g); }
27
28 std::vector<double> prob{1.0};
29 IntType K = 1;
30 std::vector<double> lower{1.0};
31 std::vector<double> upper{1.0};
32 std::vector<IntType> guide{0};
33
34public:
35 explicit CategoricalRand(std::vector<double> &&probabilities = {1.0});
36 [[nodiscard]] std::string Name() const override;
37 [[nodiscard]] Support SupportType() const override { return Support::Finite; }
38 [[nodiscard]] IntType MinValue() const override { return 0; }
39 [[nodiscard]] IntType MaxValue() const override { return K - 1; }
40
41 void SetProbabilities(std::vector<double> &&probabilities);
42 [[nodiscard]] std::vector<double> GetProbabilities() { return prob; }
43
44 [[nodiscard]] double P(const IntType &k) const override;
45 [[nodiscard]] double logP(const IntType &k) const override;
46 [[nodiscard]] double F(const IntType &k) const override;
47 [[nodiscard]] double S(const IntType &k) const override;
48 template <std::uniform_random_bit_generator G>
49 [[nodiscard]] IntType operator()(G &g) const;
50
51 [[nodiscard]] long double Mean() const override;
52 [[nodiscard]] long double Variance() const override;
53 [[nodiscard]] IntType Mode() const override;
54
55private:
56 IntType quantileImpl(double p) const override;
57 IntType quantileImpl1m(double p) const override;
58 std::complex<double> CFImpl(double t) const override;
59};
60
61} // namespace randlib
62
64
65namespace randlib
66{
67
68template <typename IntType>
69CategoricalRand<IntType>::CategoricalRand(std::vector<double> &&probabilities)
70{
71 SetProbabilities(std::move(probabilities));
72}
73
74template <typename IntType>
76{
77 std::string str = "Categorical(";
78 for (int i = 0; i != K - 1; ++i)
79 str += this->toStringWithPrecision(prob[i]) + ", ";
80 return str + this->toStringWithPrecision(prob[K - 1]) + ")";
81}
82
83template <typename IntType>
84void CategoricalRand<IntType>::SetProbabilities(std::vector<double> &&probabilities)
85{
86 if (probabilities.empty())
87 throw std::invalid_argument("Categorical distribution: there should be at least one probability");
88 for (double probability : probabilities) {
89 if (!(probability >= 0.0))
90 throw std::invalid_argument("Categorical distribution: probabilities should be non-negative");
91 }
92 double sum = std::accumulate(probabilities.begin(), probabilities.end(), 0.0);
93 if (!(std::fabs(sum - 1.0) <= 1e-12 * probabilities.size()))
94 throw std::invalid_argument("Categorical distribution: probability parameters should sum to 1");
95 prob = std::move(probabilities);
96 K = prob.size();
97
100 lower.resize(K);
101 upper.resize(K);
102 double partial = 0.0;
103 for (IntType k = 0; k != K; ++k)
104 lower[k] = (partial += prob[k]);
105 partial = 0.0;
106 for (IntType k = K - 1; k >= 0; --k)
107 upper[k] = (partial += prob[k]);
108 guide.resize(K);
109 IntType k = 0;
110 for (IntType j = 0; j != K; ++j) {
111 const double level = static_cast<double>(j) / K;
112 while (k < K - 1 && lower[k] < level)
113 ++k;
114 guide[j] = k;
115 }
116}
117
118template <typename IntType>
119double CategoricalRand<IntType>::P(const IntType &k) const
120{
121 return (k < 0 || k >= K) ? 0.0 : prob[k];
122}
123
124template <typename IntType>
125double CategoricalRand<IntType>::logP(const IntType &k) const
126{
127 return std::log(P(k));
128}
129
130template <typename IntType>
131double CategoricalRand<IntType>::F(const IntType &k) const
132{
133 if (k < 0)
134 return 0.0;
135 if (k >= K - 1)
136 return 1.0;
137 return (2 * k <= K) ? lower[k] : 1.0 - upper[k + 1];
138}
139
140template <typename IntType>
141double CategoricalRand<IntType>::S(const IntType &k) const
142{
143 if (k < 0)
144 return 1.0;
145 if (k >= K - 1)
146 return 0.0;
147 return (2 * k <= K) ? 1.0 - lower[k] : upper[k + 1];
148}
149
150template <typename IntType>
151template <std::uniform_random_bit_generator G>
153{
154 double U = detail::uniform01<double>(g);
155 return quantileImpl(U);
156}
157
158template <typename IntType>
160{
161 long double sum = 0.0;
162 for (int i = 1; i != K; ++i)
163 sum += i * prob[i];
164 return sum;
165}
166
167template <typename IntType>
169{
170 long double mean = 0.0, secMom = 0.0;
171 for (int i = 1; i != K; ++i) {
172 long double aux = i * prob[i];
173 mean += aux;
174 secMom += i * aux;
175 }
176 return secMom - mean * mean;
177}
178
179template <typename IntType>
181{
182 auto maxProbIt = std::max_element(prob.begin(), prob.end());
183 return std::distance(prob.begin(), maxProbIt);
184}
185
186template <typename IntType>
187IntType CategoricalRand<IntType>::quantileImpl(double p) const
188{
190 const IntType j = std::min(static_cast<IntType>(p * K), K - 1);
191 IntType k = guide[j];
192 while (k > 0 && lower[k - 1] >= p)
193 --k;
194 while (k < K - 1 && lower[k] < p)
195 ++k;
196 return k;
197}
198
199template <typename IntType>
200IntType CategoricalRand<IntType>::quantileImpl1m(double p) const
201{
203 IntType low = 0, high = K;
204 while (high - low > 1) {
205 const IntType middle = low + (high - low) / 2;
206 if (upper[middle] > p)
207 low = middle;
208 else
209 high = middle;
210 }
211 return low;
212}
213
214template <typename IntType>
215std::complex<double> CategoricalRand<IntType>::CFImpl(double t) const
216{
217 double re = 0.0;
218 double im = 0.0;
219 for (int i = 0; i != K; ++i) {
220 re += prob[i] * std::cos(t * i);
221 im += prob[i] * std::sin(t * i);
222 }
223 return std::complex<double>(re, im);
224}
225
226} // namespace randlib
The CategoricalRand class
Definition categorical.hpp:25
void SetProbabilities(std::vector< double > &&probabilities)
Definition categorical.hpp:84
IntType MinValue() const override
Definition categorical.hpp:38
long double Variance() const override
Definition categorical.hpp:168
IntType Mode() const override
Mode is the value, which has the largest probability to happen, or, in the case of continuous distrib...
Definition categorical.hpp:180
IntType MaxValue() const override
Definition categorical.hpp:39
std::vector< double > GetProbabilities()
Definition categorical.hpp:42
long double Mean() const override
Definition categorical.hpp:159
Support SupportType() const override
Definition categorical.hpp:37
IntType operator()(G &g) const
Definition categorical.hpp:152
double P(const IntType &k) const override
Definition categorical.hpp:119
std::string Name() const override
Definition categorical.hpp:75
double logP(const IntType &k) const override
Definition categorical.hpp:125
double S(const IntType &k) const override
Definition categorical.hpp:141
double F(const IntType &k) const override
Definition categorical.hpp:131
CategoricalRand(std::vector< double > &&probabilities={1.0})
Definition categorical.hpp:69
The DiscreteDistribution class Abstract class for all discrete distributions.
Definition discrete.hpp:21
The EngineRef class Non-owning, type-erased reference to any uniform random bit generator.
Definition engine/uniform.hpp:116
Real standardNormal(G &g)
Ziggurat algorithm of Marsaglia and Tsang with 256 layers and an explicit sign bit.
Definition variates.hpp:45
Definition bivariate_normal.hpp:10
Support
Shape of the support of a univariate distribution.
Definition univariate.hpp:15