Skip to content

Commit 50d358e

Browse files
authored
Merge pull request #97 from bashtage/add-sfmt
ENH: Add SFMT PRNG
2 parents 511d7ba + bf8b98e commit 50d358e

26 files changed

+4105
-11
lines changed

randomstate/distributions.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ typedef int bool;
4040
#include "interface/mlfg-1279-861/mlfg-1279-861-shim.h"
4141
#elif defined(RS_DSFMT)
4242
#include "interface/dSFMT/dSFMT-shim.h"
43+
#elif defined(RS_SFMT)
44+
#include "interface/sfmt/sfmt-shim.h"
4345
#else
4446
#error Unknown RNG!!! Unknown RNG!!! Unknown RNG!!!
4547
#endif

randomstate/interface/pcg-64/pcg-64-shim.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,5 @@ inline void entropy_init(aug_state* state)
6565

6666
inline double random_double(aug_state* state)
6767
{
68-
uint64_t rn;
69-
rn = random_uint64(state);
70-
return (rn >> 11) * (1.0 / 9007199254740992.0);
68+
return (random_uint64(state) >> 11) * (1.0 / 9007199254740992.0);
7169
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#include "sfmt-shim.h"
2+
3+
extern inline uint32_t random_uint32(aug_state *state);
4+
5+
extern inline uint64_t random_uint64(aug_state *state);
6+
7+
extern inline double random_double(aug_state *state);
8+
9+
extern inline uint64_t random_raw_values(aug_state *state);
10+
11+
void reset_buffer(aug_state *state) {
12+
int i = 0;
13+
for (i = 0; i < (2 * SFMT_N); i++) {
14+
state->buffered_uint64[i] = 0ULL;
15+
}
16+
state->buffer_loc = 2 * SFMT_N;
17+
}
18+
19+
extern void set_seed_by_array(aug_state *state, uint32_t init_key[],
20+
int key_length) {
21+
reset_buffer(state);
22+
sfmt_init_by_array(state->rng, init_key, key_length);
23+
}
24+
25+
void set_seed(aug_state *state, uint32_t seed) {
26+
reset_buffer(state);
27+
sfmt_init_gen_rand(state->rng, seed);
28+
}
29+
30+
void entropy_init(aug_state *state) {
31+
uint32_t seeds[1];
32+
entropy_fill((void *)seeds, sizeof(seeds));
33+
set_seed(state, seeds[0]);
34+
}
35+
36+
// void jump_state(aug_state* state)
37+
//{
38+
// dSFMT_jump(state->rng, poly_128);
39+
//}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#ifdef _WIN32
2+
#include "../../src/common/stdint.h"
3+
#define inline __forceinline
4+
#else
5+
#include <stdint.h>
6+
#endif
7+
8+
#include "../../src/common/binomial.h"
9+
#include "../../src/common/entropy.h"
10+
#include "../../src/sfmt/sfmt.h"
11+
12+
typedef struct s_aug_state {
13+
sfmt_t *rng;
14+
binomial_t *binomial;
15+
16+
int has_gauss, has_gauss_float, shift_zig_random_int, has_uint32;
17+
float gauss_float;
18+
double gauss;
19+
uint32_t uinteger;
20+
uint64_t zig_random_int;
21+
22+
uint64_t *buffered_uint64;
23+
int buffer_loc;
24+
} aug_state;
25+
26+
static inline uint64_t random_uint64_from_buffer(aug_state *state) {
27+
uint64_t out;
28+
if (state->buffer_loc >= (2 * SFMT_N)) {
29+
state->buffer_loc = 0;
30+
sfmt_fill_array64(state->rng, state->buffered_uint64, 2 * SFMT_N);
31+
}
32+
out = state->buffered_uint64[state->buffer_loc];
33+
state->buffer_loc++;
34+
return out;
35+
}
36+
37+
static inline uint32_t random_uint32(aug_state *state) {
38+
uint64_t d;
39+
if (state->has_uint32) {
40+
state->has_uint32 = 0;
41+
return state->uinteger;
42+
}
43+
d = random_uint64_from_buffer(state);
44+
state->uinteger = (uint32_t)(d >> 32);
45+
state->has_uint32 = 1;
46+
return (uint32_t)(d & 0xFFFFFFFFUL);
47+
}
48+
49+
static inline uint64_t random_uint64(aug_state *state) {
50+
return random_uint64_from_buffer(state);
51+
}
52+
53+
static inline double random_double(aug_state *state) {
54+
return (random_uint64_from_buffer(state) >> 11) * (1.0 / 9007199254740992.0);
55+
}
56+
57+
static inline uint64_t random_raw_values(aug_state *state) {
58+
return random_uint64_from_buffer(state);
59+
}
60+
61+
extern void entropy_init(aug_state *state);
62+
63+
extern void set_seed_by_array(aug_state *state, uint32_t init_key[],
64+
int key_length);
65+
66+
extern void set_seed(aug_state *state, uint32_t seed);
67+
68+
// extern void jump_state(aug_state* state);

randomstate/interface/sfmt/sfmt.pxi

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
DEF RS_RNG_NAME = u'sfmt'
2+
DEF SFMT_MEXP = 19937
3+
DEF SFMT_N = 156 # (SFMT_MEXP / 128 + 1)
4+
DEF RS_SEED_NBYTES = 1
5+
DEF RS_SEED_ARRAY_BITS = 32
6+
7+
ctypedef uint32_t rng_state_t
8+
9+
cdef extern from "distributions.h":
10+
11+
cdef union W128_T:
12+
uint32_t u[4]
13+
uint64_t u64[2]
14+
15+
ctypedef W128_T w128_t;
16+
17+
cdef struct SFMT_T:
18+
w128_t state[SFMT_N];
19+
int idx;
20+
21+
ctypedef SFMT_T sfmt_t;
22+
23+
cdef struct s_aug_state:
24+
sfmt_t *rng
25+
binomial_t *binomial
26+
27+
int has_gauss, shift_zig_random_int, has_uint32, has_gauss_float
28+
float gauss_float
29+
double gauss
30+
uint64_t zig_random_int
31+
uint32_t uinteger
32+
33+
uint64_t *buffered_uint64
34+
int buffer_loc
35+
36+
ctypedef s_aug_state aug_state
37+
38+
cdef void set_seed(aug_state* state, uint32_t seed)
39+
40+
cdef void set_seed_by_array(aug_state* state, uint32_t init_key[], int key_length)
41+
42+
cdef void jump_state(aug_state* state)
43+
44+
45+
ctypedef sfmt_t rng_t
46+
47+
cdef object _get_state(aug_state state):
48+
cdef uint32_t [::1] key = np.zeros(4 * SFMT_N, dtype=np.uint32)
49+
cdef uint64_t [::1] buf = np.zeros(2 * SFMT_N, dtype=np.uint64)
50+
cdef Py_ssize_t i, j, key_loc = 0
51+
cdef w128_t state_val
52+
for i in range(SFMT_N):
53+
state_val = state.rng.state[i]
54+
for j in range(4):
55+
key[key_loc] = state_val.u[j]
56+
key_loc += 1
57+
for i in range(2 * SFMT_N):
58+
buf[i] = state.buffered_uint64[i]
59+
60+
return (np.asarray(key), state.rng.idx,
61+
np.asarray(buf), state.buffer_loc)
62+
63+
cdef object _set_state(aug_state *state, object state_info):
64+
cdef Py_ssize_t i, j, key_loc = 0
65+
cdef uint32_t [::1] key = state_info[0]
66+
state.rng.idx = state_info[1]
67+
68+
for i in range(SFMT_N):
69+
for j in range(4):
70+
state.rng.state[i].u[j] = key[key_loc]
71+
key_loc += 1
72+
73+
state.buffer_loc = <int>state_info[3]
74+
for i in range(2 * SFMT_N):
75+
state.buffered_uint64[i] = state_info[2][i]
76+
77+
78+
DEF CLASS_DOCSTRING = u"""
79+
RandomState(seed=None)
80+
81+
Container for the SIMD-based Mersenne Twister pseudo-random number generator.
82+
83+
``dSFMT.RandomState`` exposes a number of methods for generating random
84+
numbers drawn from a variety of probability distributions [1]_ . In addition
85+
to the distribution-specific arguments, each method takes a keyword argument
86+
`size` that defaults to ``None``. If `size` is ``None``, then a single
87+
value is generated and returned. If `size` is an integer, then a 1-D
88+
array filled with generated values is returned. If `size` is a tuple,
89+
then an array with that shape is filled and returned.
90+
91+
**No Compatibility Guarantee**
92+
93+
``dSFMT.RandomState`` does not make a guarantee that a fixed seed and a
94+
fixed series of calls to ``dSFMT.RandomState`` methods using the same
95+
parameters will always produce the same results. This is different from
96+
``numpy.random.RandomState`` guarantee. This is done to simplify improving
97+
random number generators. To ensure identical results, you must use the
98+
same release version.
99+
100+
Parameters
101+
----------
102+
seed : {None, int, array_like}, optional
103+
Random seed initializing the pseudo-random number generator.
104+
Can be an integer in [0, 2**32-1], array of integers in
105+
[0, 2**32-1] or ``None`` (the default). If `seed` is ``None``,
106+
then ``dSFMT.RandomState`` will try to read entropy from
107+
``/dev/urandom`` (or the Windows analog) if available to
108+
produce a 64-bit seed. If unavailable, the a 64-bit hash of the time
109+
and process ID is used.
110+
111+
Notes
112+
-----
113+
The Python stdlib module "random" also contains a Mersenne Twister
114+
pseudo-random number generator with a number of methods that are similar
115+
to the ones available in ``RandomState``. The `RandomState` object, besides
116+
being NumPy-aware, also has the advantage that it provides a much larger
117+
number of probability distributions to choose from.
118+
119+
>>> from randomstate.entropy import random_entropy
120+
>>> import randomstate.prng.sfmt as rnd
121+
>>> seed = random_entropy()
122+
>>> rs = [rnd.RandomState(seed) for _ in range(10)]
123+
124+
**State and Seeding**
125+
126+
The ``sfmt.RandomState`` state vector consists of a 624 element array of
127+
32-bit unsigned integers plus a single integer value between 0 and 624
128+
indicating the current position within the main array. The implementation
129+
used here augments this with a array of 64-bit unsigned integers which are used
130+
to efficiently access the random numbers produced by the SFMT generator.
131+
132+
``sfmt.RandomState`` is seeded using either a single 32-bit unsigned integer
133+
or a vector of 32-bit unsigned integers. In either case, the input seed is
134+
used as an input (or inputs) for a hashing function, and the output of the
135+
hashing function is used as the initial state. Using a single 32-bit value
136+
for the seed can only initialize a small range of the possible initial
137+
state values.
138+
139+
.. [1] Mutsuo Saito and Makoto Matsumoto, "SIMD-oriented Fast Mersenne
140+
Twister: a 128-bit Pseudorandom Number Generator." Monte Carlo
141+
and Quasi-Monte Carlo Methods 2006, Springer, pp. 607 -- 622, 2008.
142+
"""

randomstate/interface/xorshift1024/xorshift1024-shim.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,7 @@ inline uint64_t random_raw_values(aug_state* state)
4747

4848
inline double random_double(aug_state* state)
4949
{
50-
uint64_t rn;
51-
rn = random_uint64(state);
52-
return (rn >> 11) * (1.0 / 9007199254740992.0);
50+
return (random_uint64(state) >> 11) * (1.0 / 9007199254740992.0);
5351
}
5452

5553
extern void set_seed(aug_state* state, uint64_t seed);

randomstate/interface/xorshift128/xorshift128-shim.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,7 @@ inline uint64_t random_raw_values(aug_state* state)
4747

4848
inline double random_double(aug_state* state)
4949
{
50-
uint64_t rn;
51-
rn = random_uint64(state);
52-
return (rn >> 11) * (1.0 / 9007199254740992.0);
50+
return (random_uint64(state) >> 11) * (1.0 / 9007199254740992.0);
5351
}
5452

5553
extern void set_seed(aug_state* state, uint64_t seed);

randomstate/performance.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
scale_64 = 2
2424

2525
RNGS = ['mlfg_1279_861', 'mrg32k3a', 'pcg64', 'pcg32', 'mt19937', 'xorshift128', 'xorshift1024',
26-
'xoroshiro128plus', 'dsfmt', 'random']
26+
'xoroshiro128plus', 'dsfmt', 'sfmt', 'random']
2727

2828

2929
def timer(code, setup):
@@ -41,6 +41,7 @@ def run_timer(dist, command, numpy_command=None, setup='', random_type=''):
4141

4242
res = {}
4343
for rng in RNGS:
44+
print(str(rng))
4445
mod = 'randomstate.prng' if rng != 'random' else 'numpy'
4546
key = '-'.join((mod, rng, dist)).replace('"', '')
4647
command = numpy_command if 'numpy' in mod else command

randomstate/prng/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from .pcg32 import pcg32
88
from .pcg64 import pcg64
99
from .dsfmt import dsfmt
10+
from .sfmt import sfmt
1011

1112
def __generic_ctor(mod_name='mt19937'):
1213
"""
@@ -46,6 +47,8 @@ def __generic_ctor(mod_name='mt19937'):
4647
mod = xorshift1024
4748
elif mod_name == 'dsfmt':
4849
mod = dsfmt
50+
elif mod_name == 'sfmt':
51+
mod = sfmt
4952
else:
5053
raise ValueError(str(mod_name) + ' is not a known PRNG module.')
5154

randomstate/prng/sfmt/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .sfmt import *

0 commit comments

Comments
 (0)