xref: /aosp_15_r20/external/abseil-cpp/absl/random/distributions.h (revision 9356374a3709195abf420251b3e825997ff56c0f)
1 // Copyright 2017 The Abseil Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15 // -----------------------------------------------------------------------------
16 // File: distributions.h
17 // -----------------------------------------------------------------------------
18 //
19 // This header defines functions representing distributions, which you use in
20 // combination with an Abseil random bit generator to produce random values
21 // according to the rules of that distribution.
22 //
23 // The Abseil random library defines the following distributions within this
24 // file:
25 //
26 //   * `absl::Uniform` for uniform (constant) distributions having constant
27 //     probability
28 //   * `absl::Bernoulli` for discrete distributions having exactly two outcomes
29 //   * `absl::Beta` for continuous distributions parameterized through two
30 //     free parameters
31 //   * `absl::Exponential` for discrete distributions of events occurring
32 //     continuously and independently at a constant average rate
33 //   * `absl::Gaussian` (also known as "normal distributions") for continuous
34 //     distributions using an associated quadratic function
35 //   * `absl::LogUniform` for discrete distributions where the log to the given
36 //     base of all values is uniform
37 //   * `absl::Poisson` for discrete probability distributions that express the
38 //     probability of a given number of events occurring within a fixed interval
39 //   * `absl::Zipf` for discrete probability distributions commonly used for
40 //     modelling of rare events
41 //
42 // Prefer use of these distribution function classes over manual construction of
43 // your own distribution classes, as it allows library maintainers greater
44 // flexibility to change the underlying implementation in the future.
45 
46 #ifndef ABSL_RANDOM_DISTRIBUTIONS_H_
47 #define ABSL_RANDOM_DISTRIBUTIONS_H_
48 
49 #include <limits>
50 #include <type_traits>
51 
52 #include "absl/base/config.h"
53 #include "absl/base/internal/inline_variable.h"
54 #include "absl/meta/type_traits.h"
55 #include "absl/random/bernoulli_distribution.h"
56 #include "absl/random/beta_distribution.h"
57 #include "absl/random/exponential_distribution.h"
58 #include "absl/random/gaussian_distribution.h"
59 #include "absl/random/internal/distribution_caller.h"  // IWYU pragma: export
60 #include "absl/random/internal/traits.h"
61 #include "absl/random/internal/uniform_helper.h"  // IWYU pragma: export
62 #include "absl/random/log_uniform_int_distribution.h"
63 #include "absl/random/poisson_distribution.h"
64 #include "absl/random/uniform_int_distribution.h"  // IWYU pragma: export
65 #include "absl/random/uniform_real_distribution.h"  // IWYU pragma: export
66 #include "absl/random/zipf_distribution.h"
67 
68 namespace absl {
69 ABSL_NAMESPACE_BEGIN
70 
71 ABSL_INTERNAL_INLINE_CONSTEXPR(IntervalClosedClosedTag, IntervalClosedClosed,
72                                {});
73 ABSL_INTERNAL_INLINE_CONSTEXPR(IntervalClosedClosedTag, IntervalClosed, {});
74 ABSL_INTERNAL_INLINE_CONSTEXPR(IntervalClosedOpenTag, IntervalClosedOpen, {});
75 ABSL_INTERNAL_INLINE_CONSTEXPR(IntervalOpenOpenTag, IntervalOpenOpen, {});
76 ABSL_INTERNAL_INLINE_CONSTEXPR(IntervalOpenOpenTag, IntervalOpen, {});
77 ABSL_INTERNAL_INLINE_CONSTEXPR(IntervalOpenClosedTag, IntervalOpenClosed, {});
78 
79 // -----------------------------------------------------------------------------
80 // absl::Uniform<T>(tag, bitgen, lo, hi)
81 // -----------------------------------------------------------------------------
82 //
83 // `absl::Uniform()` produces random values of type `T` uniformly distributed in
84 // a defined interval {lo, hi}. The interval `tag` defines the type of interval
85 // which should be one of the following possible values:
86 //
87 //   * `absl::IntervalOpenOpen`
88 //   * `absl::IntervalOpenClosed`
89 //   * `absl::IntervalClosedOpen`
90 //   * `absl::IntervalClosedClosed`
91 //
92 // where "open" refers to an exclusive value (excluded) from the output, while
93 // "closed" refers to an inclusive value (included) from the output.
94 //
95 // In the absence of an explicit return type `T`, `absl::Uniform()` will deduce
96 // the return type based on the provided endpoint arguments {A lo, B hi}.
97 // Given these endpoints, one of {A, B} will be chosen as the return type, if
98 // a type can be implicitly converted into the other in a lossless way. The
99 // lack of any such implicit conversion between {A, B} will produce a
100 // compile-time error
101 //
102 // See https://en.wikipedia.org/wiki/Uniform_distribution_(continuous)
103 //
104 // Example:
105 //
106 //   absl::BitGen bitgen;
107 //
108 //   // Produce a random float value between 0.0 and 1.0, inclusive
109 //   auto x = absl::Uniform(absl::IntervalClosedClosed, bitgen, 0.0f, 1.0f);
110 //
111 //   // The most common interval of `absl::IntervalClosedOpen` is available by
112 //   // default:
113 //
114 //   auto x = absl::Uniform(bitgen, 0.0f, 1.0f);
115 //
116 //   // Return-types are typically inferred from the arguments, however callers
117 //   // can optionally provide an explicit return-type to the template.
118 //
119 //   auto x = absl::Uniform<float>(bitgen, 0, 1);
120 //
121 template <typename R = void, typename TagType, typename URBG>
122 typename absl::enable_if_t<!std::is_same<R, void>::value, R>  //
Uniform(TagType tag,URBG && urbg,R lo,R hi)123 Uniform(TagType tag,
124         URBG&& urbg,  // NOLINT(runtime/references)
125         R lo, R hi) {
126   using gen_t = absl::decay_t<URBG>;
127   using distribution_t = random_internal::UniformDistributionWrapper<R>;
128 
129   auto a = random_internal::uniform_lower_bound(tag, lo, hi);
130   auto b = random_internal::uniform_upper_bound(tag, lo, hi);
131   if (!random_internal::is_uniform_range_valid(a, b)) return lo;
132 
133   return random_internal::DistributionCaller<gen_t>::template Call<
134       distribution_t>(&urbg, tag, lo, hi);
135 }
136 
137 // absl::Uniform<T>(bitgen, lo, hi)
138 //
139 // Overload of `Uniform()` using the default closed-open interval of [lo, hi),
140 // and returning values of type `T`
141 template <typename R = void, typename URBG>
142 typename absl::enable_if_t<!std::is_same<R, void>::value, R>  //
Uniform(URBG && urbg,R lo,R hi)143 Uniform(URBG&& urbg,  // NOLINT(runtime/references)
144         R lo, R hi) {
145   using gen_t = absl::decay_t<URBG>;
146   using distribution_t = random_internal::UniformDistributionWrapper<R>;
147   constexpr auto tag = absl::IntervalClosedOpen;
148 
149   auto a = random_internal::uniform_lower_bound(tag, lo, hi);
150   auto b = random_internal::uniform_upper_bound(tag, lo, hi);
151   if (!random_internal::is_uniform_range_valid(a, b)) return lo;
152 
153   return random_internal::DistributionCaller<gen_t>::template Call<
154       distribution_t>(&urbg, lo, hi);
155 }
156 
157 // absl::Uniform(tag, bitgen, lo, hi)
158 //
159 // Overload of `Uniform()` using different (but compatible) lo, hi types. Note
160 // that a compile-error will result if the return type cannot be deduced
161 // correctly from the passed types.
162 template <typename R = void, typename TagType, typename URBG, typename A,
163           typename B>
164 typename absl::enable_if_t<std::is_same<R, void>::value,
165                            random_internal::uniform_inferred_return_t<A, B>>
Uniform(TagType tag,URBG && urbg,A lo,B hi)166 Uniform(TagType tag,
167         URBG&& urbg,  // NOLINT(runtime/references)
168         A lo, B hi) {
169   using gen_t = absl::decay_t<URBG>;
170   using return_t = typename random_internal::uniform_inferred_return_t<A, B>;
171   using distribution_t = random_internal::UniformDistributionWrapper<return_t>;
172 
173   auto a = random_internal::uniform_lower_bound<return_t>(tag, lo, hi);
174   auto b = random_internal::uniform_upper_bound<return_t>(tag, lo, hi);
175   if (!random_internal::is_uniform_range_valid(a, b)) return lo;
176 
177   return random_internal::DistributionCaller<gen_t>::template Call<
178       distribution_t>(&urbg, tag, static_cast<return_t>(lo),
179                       static_cast<return_t>(hi));
180 }
181 
182 // absl::Uniform(bitgen, lo, hi)
183 //
184 // Overload of `Uniform()` using different (but compatible) lo, hi types and the
185 // default closed-open interval of [lo, hi). Note that a compile-error will
186 // result if the return type cannot be deduced correctly from the passed types.
187 template <typename R = void, typename URBG, typename A, typename B>
188 typename absl::enable_if_t<std::is_same<R, void>::value,
189                            random_internal::uniform_inferred_return_t<A, B>>
Uniform(URBG && urbg,A lo,B hi)190 Uniform(URBG&& urbg,  // NOLINT(runtime/references)
191         A lo, B hi) {
192   using gen_t = absl::decay_t<URBG>;
193   using return_t = typename random_internal::uniform_inferred_return_t<A, B>;
194   using distribution_t = random_internal::UniformDistributionWrapper<return_t>;
195 
196   constexpr auto tag = absl::IntervalClosedOpen;
197   auto a = random_internal::uniform_lower_bound<return_t>(tag, lo, hi);
198   auto b = random_internal::uniform_upper_bound<return_t>(tag, lo, hi);
199   if (!random_internal::is_uniform_range_valid(a, b)) return lo;
200 
201   return random_internal::DistributionCaller<gen_t>::template Call<
202       distribution_t>(&urbg, static_cast<return_t>(lo),
203                       static_cast<return_t>(hi));
204 }
205 
206 // absl::Uniform<unsigned T>(bitgen)
207 //
208 // Overload of Uniform() using the minimum and maximum values of a given type
209 // `T` (which must be unsigned), returning a value of type `unsigned T`
210 template <typename R, typename URBG>
211 typename absl::enable_if_t<!std::numeric_limits<R>::is_signed, R>  //
Uniform(URBG && urbg)212 Uniform(URBG&& urbg) {  // NOLINT(runtime/references)
213   using gen_t = absl::decay_t<URBG>;
214   using distribution_t = random_internal::UniformDistributionWrapper<R>;
215 
216   return random_internal::DistributionCaller<gen_t>::template Call<
217       distribution_t>(&urbg);
218 }
219 
220 // -----------------------------------------------------------------------------
221 // absl::Bernoulli(bitgen, p)
222 // -----------------------------------------------------------------------------
223 //
224 // `absl::Bernoulli` produces a random boolean value, with probability `p`
225 // (where 0.0 <= p <= 1.0) equaling `true`.
226 //
227 // Prefer `absl::Bernoulli` to produce boolean values over other alternatives
228 // such as comparing an `absl::Uniform()` value to a specific output.
229 //
230 // See https://en.wikipedia.org/wiki/Bernoulli_distribution
231 //
232 // Example:
233 //
234 //   absl::BitGen bitgen;
235 //   ...
236 //   if (absl::Bernoulli(bitgen, 1.0/3721.0)) {
237 //     std::cout << "Asteroid field navigation successful.";
238 //   }
239 //
240 template <typename URBG>
Bernoulli(URBG && urbg,double p)241 bool Bernoulli(URBG&& urbg,  // NOLINT(runtime/references)
242                double p) {
243   using gen_t = absl::decay_t<URBG>;
244   using distribution_t = absl::bernoulli_distribution;
245 
246   return random_internal::DistributionCaller<gen_t>::template Call<
247       distribution_t>(&urbg, p);
248 }
249 
250 // -----------------------------------------------------------------------------
251 // absl::Beta<T>(bitgen, alpha, beta)
252 // -----------------------------------------------------------------------------
253 //
254 // `absl::Beta` produces a floating point number distributed in the closed
255 // interval [0,1] and parameterized by two values `alpha` and `beta` as per a
256 // Beta distribution. `T` must be a floating point type, but may be inferred
257 // from the types of `alpha` and `beta`.
258 //
259 // See https://en.wikipedia.org/wiki/Beta_distribution.
260 //
261 // Example:
262 //
263 //   absl::BitGen bitgen;
264 //   ...
265 //   double sample = absl::Beta(bitgen, 3.0, 2.0);
266 //
267 template <typename RealType, typename URBG>
Beta(URBG && urbg,RealType alpha,RealType beta)268 RealType Beta(URBG&& urbg,  // NOLINT(runtime/references)
269               RealType alpha, RealType beta) {
270   static_assert(
271       std::is_floating_point<RealType>::value,
272       "Template-argument 'RealType' must be a floating-point type, in "
273       "absl::Beta<RealType, URBG>(...)");
274 
275   using gen_t = absl::decay_t<URBG>;
276   using distribution_t = typename absl::beta_distribution<RealType>;
277 
278   return random_internal::DistributionCaller<gen_t>::template Call<
279       distribution_t>(&urbg, alpha, beta);
280 }
281 
282 // -----------------------------------------------------------------------------
283 // absl::Exponential<T>(bitgen, lambda = 1)
284 // -----------------------------------------------------------------------------
285 //
286 // `absl::Exponential` produces a floating point number representing the
287 // distance (time) between two consecutive events in a point process of events
288 // occurring continuously and independently at a constant average rate. `T` must
289 // be a floating point type, but may be inferred from the type of `lambda`.
290 //
291 // See https://en.wikipedia.org/wiki/Exponential_distribution.
292 //
293 // Example:
294 //
295 //   absl::BitGen bitgen;
296 //   ...
297 //   double call_length = absl::Exponential(bitgen, 7.0);
298 //
299 template <typename RealType, typename URBG>
300 RealType Exponential(URBG&& urbg,  // NOLINT(runtime/references)
301                      RealType lambda = 1) {
302   static_assert(
303       std::is_floating_point<RealType>::value,
304       "Template-argument 'RealType' must be a floating-point type, in "
305       "absl::Exponential<RealType, URBG>(...)");
306 
307   using gen_t = absl::decay_t<URBG>;
308   using distribution_t = typename absl::exponential_distribution<RealType>;
309 
310   return random_internal::DistributionCaller<gen_t>::template Call<
311       distribution_t>(&urbg, lambda);
312 }
313 
314 // -----------------------------------------------------------------------------
315 // absl::Gaussian<T>(bitgen, mean = 0, stddev = 1)
316 // -----------------------------------------------------------------------------
317 //
318 // `absl::Gaussian` produces a floating point number selected from the Gaussian
319 // (ie. "Normal") distribution. `T` must be a floating point type, but may be
320 // inferred from the types of `mean` and `stddev`.
321 //
322 // See https://en.wikipedia.org/wiki/Normal_distribution
323 //
324 // Example:
325 //
326 //   absl::BitGen bitgen;
327 //   ...
328 //   double giraffe_height = absl::Gaussian(bitgen, 16.3, 3.3);
329 //
330 template <typename RealType, typename URBG>
331 RealType Gaussian(URBG&& urbg,  // NOLINT(runtime/references)
332                   RealType mean = 0, RealType stddev = 1) {
333   static_assert(
334       std::is_floating_point<RealType>::value,
335       "Template-argument 'RealType' must be a floating-point type, in "
336       "absl::Gaussian<RealType, URBG>(...)");
337 
338   using gen_t = absl::decay_t<URBG>;
339   using distribution_t = typename absl::gaussian_distribution<RealType>;
340 
341   return random_internal::DistributionCaller<gen_t>::template Call<
342       distribution_t>(&urbg, mean, stddev);
343 }
344 
345 // -----------------------------------------------------------------------------
346 // absl::LogUniform<T>(bitgen, lo, hi, base = 2)
347 // -----------------------------------------------------------------------------
348 //
349 // `absl::LogUniform` produces random values distributed where the log to a
350 // given base of all values is uniform in a closed interval [lo, hi]. `T` must
351 // be an integral type, but may be inferred from the types of `lo` and `hi`.
352 //
353 // I.e., `LogUniform(0, n, b)` is uniformly distributed across buckets
354 // [0], [1, b-1], [b, b^2-1] .. [b^(k-1), (b^k)-1] .. [b^floor(log(n, b)), n]
355 // and is uniformly distributed within each bucket.
356 //
357 // The resulting probability density is inversely related to bucket size, though
358 // values in the final bucket may be more likely than previous values. (In the
359 // extreme case where n = b^i the final value will be tied with zero as the most
360 // probable result.
361 //
362 // If `lo` is nonzero then this distribution is shifted to the desired interval,
363 // so LogUniform(lo, hi, b) is equivalent to LogUniform(0, hi-lo, b)+lo.
364 //
365 // See https://en.wikipedia.org/wiki/Reciprocal_distribution
366 //
367 // Example:
368 //
369 //   absl::BitGen bitgen;
370 //   ...
371 //   int v = absl::LogUniform(bitgen, 0, 1000);
372 //
373 template <typename IntType, typename URBG>
374 IntType LogUniform(URBG&& urbg,  // NOLINT(runtime/references)
375                    IntType lo, IntType hi, IntType base = 2) {
376   static_assert(random_internal::IsIntegral<IntType>::value,
377                 "Template-argument 'IntType' must be an integral type, in "
378                 "absl::LogUniform<IntType, URBG>(...)");
379 
380   using gen_t = absl::decay_t<URBG>;
381   using distribution_t = typename absl::log_uniform_int_distribution<IntType>;
382 
383   return random_internal::DistributionCaller<gen_t>::template Call<
384       distribution_t>(&urbg, lo, hi, base);
385 }
386 
387 // -----------------------------------------------------------------------------
388 // absl::Poisson<T>(bitgen, mean = 1)
389 // -----------------------------------------------------------------------------
390 //
391 // `absl::Poisson` produces discrete probabilities for a given number of events
392 // occurring within a fixed interval within the closed interval [0, max]. `T`
393 // must be an integral type.
394 //
395 // See https://en.wikipedia.org/wiki/Poisson_distribution
396 //
397 // Example:
398 //
399 //   absl::BitGen bitgen;
400 //   ...
401 //   int requests_per_minute = absl::Poisson<int>(bitgen, 3.2);
402 //
403 template <typename IntType, typename URBG>
404 IntType Poisson(URBG&& urbg,  // NOLINT(runtime/references)
405                 double mean = 1.0) {
406   static_assert(random_internal::IsIntegral<IntType>::value,
407                 "Template-argument 'IntType' must be an integral type, in "
408                 "absl::Poisson<IntType, URBG>(...)");
409 
410   using gen_t = absl::decay_t<URBG>;
411   using distribution_t = typename absl::poisson_distribution<IntType>;
412 
413   return random_internal::DistributionCaller<gen_t>::template Call<
414       distribution_t>(&urbg, mean);
415 }
416 
417 // -----------------------------------------------------------------------------
418 // absl::Zipf<T>(bitgen, hi = max, q = 2, v = 1)
419 // -----------------------------------------------------------------------------
420 //
421 // `absl::Zipf` produces discrete probabilities commonly used for modelling of
422 // rare events over the closed interval [0, hi]. The parameters `v` and `q`
423 // determine the skew of the distribution. `T`  must be an integral type, but
424 // may be inferred from the type of `hi`.
425 //
426 // See http://mathworld.wolfram.com/ZipfDistribution.html
427 //
428 // Example:
429 //
430 //   absl::BitGen bitgen;
431 //   ...
432 //   int term_rank = absl::Zipf<int>(bitgen);
433 //
434 template <typename IntType, typename URBG>
435 IntType Zipf(URBG&& urbg,  // NOLINT(runtime/references)
436              IntType hi = (std::numeric_limits<IntType>::max)(), double q = 2.0,
437              double v = 1.0) {
438   static_assert(random_internal::IsIntegral<IntType>::value,
439                 "Template-argument 'IntType' must be an integral type, in "
440                 "absl::Zipf<IntType, URBG>(...)");
441 
442   using gen_t = absl::decay_t<URBG>;
443   using distribution_t = typename absl::zipf_distribution<IntType>;
444 
445   return random_internal::DistributionCaller<gen_t>::template Call<
446       distribution_t>(&urbg, hi, q, v);
447 }
448 
449 ABSL_NAMESPACE_END
450 }  // namespace absl
451 
452 #endif  // ABSL_RANDOM_DISTRIBUTIONS_H_
453