Aluode/PerceptionLabPortable
0
1/*
2 Creation, 2020:
3 - New random number generator using a mersenne twister + tweaked lemire
4 postprocessor. This fixed a convergence issue on windows targets for
5 libsvm and liblinear.
6 Sylvain Marie, Schneider Electric
7 See <https://github.com/scikit-learn/scikit-learn/pull/13511#issuecomment-481729756>
8 */
9#ifndef _NEWRAND_H
10#define _NEWRAND_H
11
12#ifdef __cplusplus
13#include <random> // needed for cython to generate a .cpp file from newrand.h
14extern "C" {
15#endif
16
17// Scikit-Learn-specific random number generator replacing `rand()` originally
18// used in LibSVM / LibLinear, to ensure the same behaviour on windows-linux,
19// with increased speed
20// - (1) Init a `mt_rand` object
21std::mt19937 mt_rand(std::mt19937::default_seed);
22
23// - (2) public `set_seed()` function that should be used instead of `srand()` to set a new seed.
24void set_seed(unsigned custom_seed) {
25 mt_rand.seed(custom_seed);
26}
27
28// - (3) New internal `bounded_rand_int` function, used instead of rand() everywhere.
29inline uint32_t bounded_rand_int(uint32_t range) {
30 // "LibSVM / LibLinear Original way" - make a 31bit positive
31 // random number and use modulo to make it fit in the range
32 // return abs( (int)mt_rand()) % range;
33
34 // "Better way": tweaked Lemire post-processor
35 // from http://www.pcg-random.org/posts/bounded-rands.html
36 uint32_t x = mt_rand();
37 uint64_t m = uint64_t(x) * uint64_t(range);
38 uint32_t l = uint32_t(m);
39 if (l < range) {
40 uint32_t t = -range;
41 if (t >= range) {
42 t -= range;
43 if (t >= range)
44 t %= range;
45 }
46 while (l < t) {
47 x = mt_rand();
48 m = uint64_t(x) * uint64_t(range);
49 l = uint32_t(m);
50 }
51 }
52 return m >> 32;
53}
54
55#ifdef __cplusplus
56}
57#endif
58
59#endif /* _NEWRAND_H */
60 