Skip to content

Commit d14e00f

Browse files
committed
ENH: Add jumt to MT19937
Add jump function to MT19937 generator using Horner method closes #84
1 parent fd9c111 commit d14e00f

22 files changed

+904
-14
lines changed

.travis.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ matrix:
2121
- env:
2222
- PYTHON=3.4
2323
- NUMPY=1.10
24-
- CYTHON=0.23
24+
- CYTHON=0.24
2525
- env:
2626
- PYTHON=3.5
2727
- env:

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ Building requires:
144144

145145
* Python (2.7, 3.4, 3.5, 3.6)
146146
* NumPy (1.9, 1.10, 1.11, 1.12)
147-
* Cython (0.22, 0.23, 0.24, 0.25)
147+
* Cython (0.22, **not** 0.23, 0.24, 0.25)
148148
* tempita (0.5+), if not provided by Cython
149149

150150
Testing requires pytest (3.0+).

README.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ Building requires:
160160

161161
- Python (2.7, 3.4, 3.5, 3.6)
162162
- NumPy (1.9, 1.10, 1.11, 1.12)
163-
- Cython (0.22, 0.23, 0.24, 0.25)
163+
- Cython (0.22, **not** 0.23, 0.24, 0.25)
164164
- tempita (0.5+), if not provided by Cython
165165

166166
Testing requires pytest (3.0+).

doc/source/change-log.rst

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ Since Version 1.13
3333
* Add SIMD-oriented Fast Mersenne Twister
3434
(`SFMT <http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/>`_) generator.
3535
* Add complex normal (:meth:`~randomstate.prng.mt19937.complex_normal`)
36+
* Added support for jumping the MT19937 generator
3637

3738
Version 1.13
3839
------------

doc/source/index.rst

+3-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,9 @@ The main innovation is the inclusion of a number of alternative pseudo-random nu
8888
generators, 'in addition' to the standard PRNG in NumPy. The included PRNGs are:
8989

9090
* MT19937 - The standard NumPy generator. Produces identical results to NumPy
91-
using the same seed/state. See `NumPy's documentation`_.
91+
using the same seed/state. Adds a jump function that advances the generator
92+
as-if 2**128 draws have been made (:meth:`randomstate.prng.mt19937.jump`).
93+
See `NumPy's documentation`_.
9294
* dSFMT - A SSE2 enables version of the MT19937 generator. Theoretically the
9395
same, but with a different state and so it is not possible to produce a
9496
sequence identical to MT19937. See the `dSFMT authors' page`_.

doc/source/mt19937.rst

+6
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ Random generator
1616
get_state
1717
set_state
1818

19+
Parallel generation
20+
===================
21+
.. autosummary::
22+
:toctree: generated/
23+
24+
jump
1925

2026
Simple random data
2127
==================

doc/source/parallel.rst

+2
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ are listed below.
5454
+-----------------+-------------------------+-------------------------+-------------------------+
5555
| xorshift1024 | :math:`2^{1024}` | :math:`2^{512}` | 64 |
5656
+-----------------+-------------------------+-------------------------+-------------------------+
57+
| mt19937 | :math:`2^{19937}` | :math:`2^{128}` | 32 |
58+
+-----------------+-------------------------+-------------------------+-------------------------+
5759

5860
``jump`` can be used to produce long blocks which should be long enough to not
5961
overlap.

pyproject.toml

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[build-system]
2+
requires = ["numpy>=1.09", "cython>=0.24", "setuptools", "wheel"]

randomstate/interface/random-kit/random-kit-poly.h

+207
Large diffs are not rendered by default.

randomstate/interface/random-kit/random-kit-shim.c

+7
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
#include "random-kit-shim.h"
2+
#include "random-kit-poly.h"
3+
24

35
extern inline uint32_t random_uint32(aug_state* state);
46

@@ -23,4 +25,9 @@ void entropy_init(aug_state* state)
2325
uint32_t seeds[1];
2426
entropy_fill((void*) seeds, sizeof(seeds));
2527
set_seed(state, seeds[0]);
28+
}
29+
30+
void jump_state(aug_state* state)
31+
{
32+
randomkit_jump(state->rng, poly);
2633
}

randomstate/interface/random-kit/random-kit-shim.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
#include "../../src/common/binomial.h"
99
#include "../../src/common/entropy.h"
10+
#include "../../src/random-kit/random-kit-jump.h"
1011
#include "../../src/random-kit/random-kit.h"
1112

1213
typedef struct s_aug_state {
@@ -47,5 +48,4 @@ extern void set_seed_by_array(aug_state* state, uint32_t *init_key, int key_leng
4748

4849
extern void set_seed(aug_state* state, uint32_t seed);
4950

50-
51-
51+
extern void jump_state(aug_state* state);

randomstate/interface/random-kit/random-kit.pxi

+10
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
DEF RS_RNG_NAME = u'mt19937'
2+
DEF RS_RNG_JUMPABLE = 1
23
DEF RS_NORMAL_METHOD = u'bm'
34
DEF RK_STATE_LEN = 624
45
DEF RS_SEED_NBYTES = 1
@@ -29,6 +30,8 @@ cdef extern from "distributions.h":
2930

3031
cdef void set_seed_by_array(aug_state* state, uint32_t *init_key, int key_length)
3132

33+
cdef void jump_state(aug_state* state)
34+
3235
ctypedef randomkit_state rng_t
3336

3437
cdef object _get_state(aug_state state):
@@ -69,6 +72,13 @@ was made will be noted in the relevant docstring. Extension of existing
6972
parameter ranges and the addition of new parameters is allowed as long the
7073
previous behavior remains unchanged.
7174
75+
``mt19937.RandomState`` can be used in parallel applications by
76+
calling the method ``jump`` which advances the state as-if :math:`2^{128}`
77+
random numbers have been generated [2]_. This allows the original sequence to
78+
be split so that distinct segments can be used in each worker process. All
79+
generators should be initialized with the same seed to ensure that the
80+
segments come from the same sequence.
81+
7282
Parameters
7383
----------
7484
seed : {None, int, array_like}, optional

randomstate/setup-single-rng.py

+26-6
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,61 @@
1+
"""
2+
Build a single PRNG for testing/development
3+
4+
Usage:
5+
6+
python setup-single-rng.py build_ext --in-place -f
7+
"""
8+
import glob
19
import os
210
import sys
311
from distutils.core import setup
412
from distutils.extension import Extension
513
from os import getcwd, name
614
from os.path import join
715

16+
import Cython.Tempita as tempita
817
import numpy
918
from Cython.Build import cythonize
1019

1120
rngs = ['RNG_PCG32', 'RNG_PCG64',
1221
'RNG_MT19937', 'RNG_XORSHIFT128',
1322
'RNG_XORSHIFT1024', 'RNG_MRG32K3A', 'RNG_MLFG_1279_861']
1423

24+
files = glob.glob('./*.in')
25+
for templated_file in files:
26+
output_file_name = os.path.splitext(templated_file)[0]
27+
if os.path.exists(output_file_name):
28+
if os.path.getmtime(templated_file) < os.path.getmtime(output_file_name):
29+
continue
30+
with open(templated_file, 'r') as source_file:
31+
template = tempita.Template(source_file.read())
32+
with open(output_file_name, 'w') as output_file:
33+
output_file.write(template.substitute())
34+
1535
with open('config.pxi', 'w') as config:
1636
config.write('# Autogenerated\n\n')
17-
config.write("DEF RS_RNG_MOD_NAME='xorshift128'\n")
37+
config.write("DEF RS_RNG_MOD_NAME='mt19937'\n")
1838

1939
pwd = getcwd()
2040

2141
sources = [join(pwd, 'randomstate.pyx'),
2242
join(pwd, 'src', 'common', 'entropy.c'),
2343
join(pwd, 'distributions.c')]
2444

25-
sources += [join(pwd, 'src', 'xorshift128', p) for p in ('xorshift128.c',)]
26-
sources += [join(pwd, 'interface', 'xorshift128', 'xorshift128-shim.c')]
45+
sources += [join(pwd, 'src', 'random-kit', p) for p in ('random-kit.c','random-kit-jump.c')]
46+
sources += [join(pwd, 'interface', 'random-kit', 'random-kit-shim.c')]
2747

28-
defs = [('XORSHIFT128_RNG', '1')]
48+
defs = [('RS_RANDOMKIT', '1')]
2949

3050
include_dirs = [pwd] + [numpy.get_include()]
3151
if os.name == 'nt' and sys.version_info < (3, 5):
3252
include_dirs += [join(pwd, 'src', 'common')]
33-
include_dirs += [join(pwd, 'src', 'xorshift128')]
53+
include_dirs += [join(pwd, 'src', 'random-kit')]
3454

3555
extra_link_args = ['Advapi32.lib'] if name == 'nt' else []
3656
extra_compile_args = [] if name == 'nt' else ['-std=c99']
3757

38-
setup(ext_modules=cythonize([Extension("core_rng",
58+
setup(ext_modules=cythonize([Extension("randomstate.randomstate",
3959
sources=sources,
4060
include_dirs=include_dirs,
4161
define_macros=defs,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
/* Compiling
2+
*
3+
* g++ minipoly_mt19937.c -o minipoly -lntl
4+
*
5+
*/
6+
7+
#include <NTL/GF2X.h>
8+
#include <NTL/vec_GF2.h>
9+
#include <cstdlib>
10+
#include <fstream>
11+
12+
NTL_CLIENT
13+
14+
/* parameters for MT19937 */
15+
#define N 624
16+
#define M 397
17+
#define MATRIX_A 0x9908b0dfUL /* constant vector a */
18+
#define UPPER_MASK 0x80000000UL /* most significant w-r bits */
19+
#define LOWER_MASK 0x7fffffffUL /* least significant r bits */
20+
21+
static unsigned long mt[N]; /* the array for the state vector */
22+
static int mti = N + 1; /* mti==N+1 means mt[N] is not initialized */
23+
24+
/* Parameter for computing the minimal polynomial */
25+
#define MEXP 19937 /* the dimension of the state space */
26+
27+
GF2X phi; /* phi is the minimal polynomial */
28+
GF2X g; /* g(t) is used to store t^J mod phi(t) */
29+
ZZ ntl_jump_step;
30+
31+
/* initializes mt[N] with a seed */
32+
void init_genrand(unsigned long s) {
33+
mt[0] = s & 0xffffffffUL;
34+
for (mti = 1; mti < N; mti++) {
35+
mt[mti] = (1812433253UL * (mt[mti - 1] ^ (mt[mti - 1] >> 30)) + mti);
36+
/* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */
37+
/* In the previous versions, MSBs of the seed affect */
38+
/* only MSBs of the array mt[]. */
39+
/* 2002/01/09 modified by Makoto Matsumoto */
40+
mt[mti] &= 0xffffffffUL;
41+
/* for >32 bit machines */
42+
}
43+
}
44+
45+
/* initialize by an array with array-length */
46+
/* init_key is the array for initializing keys */
47+
/* key_length is its length */
48+
/* slight change for C++, 2004/2/26 */
49+
void init_by_array(unsigned long init_key[], int key_length) {
50+
int i, j, k;
51+
init_genrand(19650218UL);
52+
i = 1;
53+
j = 0;
54+
k = (N > key_length ? N : key_length);
55+
for (; k; k--) {
56+
mt[i] = (mt[i] ^ ((mt[i - 1] ^ (mt[i - 1] >> 30)) * 1664525UL)) +
57+
init_key[j] + j; /* non linear */
58+
mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
59+
i++;
60+
j++;
61+
if (i >= N) {
62+
mt[0] = mt[N - 1];
63+
i = 1;
64+
}
65+
if (j >= key_length)
66+
j = 0;
67+
}
68+
for (k = N - 1; k; k--) {
69+
mt[i] = (mt[i] ^ ((mt[i - 1] ^ (mt[i - 1] >> 30)) * 1566083941UL)) -
70+
i; /* non linear */
71+
mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
72+
i++;
73+
if (i >= N) {
74+
mt[0] = mt[N - 1];
75+
i = 1;
76+
}
77+
}
78+
79+
mt[0] = 0x80000000UL; /* MSB is 1; assuring non-zero initial array */
80+
}
81+
82+
/* generates a random number on [0,0xffffffff]-interval */
83+
unsigned long genrand_int32(void) {
84+
unsigned long y;
85+
static unsigned long mag01[2] = {0x0UL, MATRIX_A};
86+
/* mag01[x] = x * MATRIX_A for x=0,1 */
87+
88+
if (mti >= N) { /* generate N words at one time */
89+
int kk;
90+
91+
if (mti == N + 1) /* if init_genrand() has not been called, */
92+
init_genrand(5489UL); /* a default initial seed is used */
93+
94+
for (kk = 0; kk < N - M; kk++) {
95+
y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK);
96+
mt[kk] = mt[kk + M] ^ (y >> 1) ^ mag01[y & 0x1UL];
97+
}
98+
for (; kk < N - 1; kk++) {
99+
y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK);
100+
mt[kk] = mt[kk + (M - N)] ^ (y >> 1) ^ mag01[y & 0x1UL];
101+
}
102+
y = (mt[N - 1] & UPPER_MASK) | (mt[0] & LOWER_MASK);
103+
mt[N - 1] = mt[M - 1] ^ (y >> 1) ^ mag01[y & 0x1UL];
104+
105+
mti = 0;
106+
}
107+
108+
y = mt[mti++];
109+
110+
/* Tempering */
111+
y ^= (y >> 11);
112+
y ^= (y << 7) & 0x9d2c5680UL;
113+
y ^= (y << 15) & 0xefc60000UL;
114+
y ^= (y >> 18);
115+
116+
return y;
117+
}
118+
119+
/* computes the minimal polynomial of the linear recurrence */
120+
void comp_mini_poly(void) {
121+
int i;
122+
vec_GF2 v(INIT_SIZE, 2 * MEXP);
123+
124+
for (i = 0; i < 2 * MEXP; i++)
125+
v[i] = genrand_int32() & 0x01ul;
126+
127+
MinPolySeq(phi, v, MEXP);
128+
}
129+
130+
/* computes the t^J mod phi(t) */
131+
void comp_jump_rem(ZZ jump_step) /*(long jump_step)*/
132+
{
133+
/* changed by saito 2013.1.25 */
134+
// GF2X f;
135+
// SetCoeff (f, jump_step, 1);
136+
// g = f % phi;
137+
PowerXMod(g, jump_step, phi);
138+
/* changed by saito 2013.1.25 */
139+
}
140+
141+
/* computes the t^J mod phi(t) */
142+
void comp_jump_rem_ulong(unsigned long jump_step) /*(long jump_step)*/
143+
{
144+
/* changed by saito 2013.1.25 */
145+
// GF2X f;
146+
// SetCoeff (f, jump_step, 1);
147+
// g = f % phi;
148+
PowerXMod(g, jump_step, phi);
149+
/* changed by saito 2013.1.25 */
150+
}
151+
152+
int main(void) {
153+
int i, a = 0;
154+
long jump_step = 2147483647; /* the number of steps of jumping ahead */
155+
unsigned long init[4] = {0x123, 0x234, 0x345, 0x456}, length = 4;
156+
ofstream fout;
157+
158+
init_by_array(init, length);
159+
160+
comp_mini_poly();
161+
conv(ntl_jump_step, "340282366920938463463374607431768211456"); /* 2 ^ 128 */
162+
comp_jump_rem(ntl_jump_step);
163+
// comp_jump_rem_ulong(jump_step);
164+
fout.open("clist_mt19937.txt", ios::out);
165+
if (!fout)
166+
return -1;
167+
168+
for (i = MEXP - 1; i > -1; i--)
169+
fout << coeff(g, i);
170+
171+
fout.close();
172+
173+
return 0;
174+
}

randomstate/src/random-kit/mt19937_polynomial_coefs_2_128.txt

+1
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)