8000 Merge pull request #13 from bashtage/legacy-experiments · mattip/numpy@2557203 · GitHub
[go: up one dir, main page]

Skip to content

Commit 2557203

Browse files
authored
Merge pull request #13 from bashtage/legacy-experiments
ENH: Add integrated legacy generator
2 parents 3d5f1f3 + 27a27ca commit 2557203

File tree

5 files changed

+210
-56
lines changed

5 files changed

+210
-56
lines changed

_randomgen/doc/source/legacy.rst

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Legacy Random Generation
22
------------------------
33
The :class:`~randomgen.legacy.LegacyGenerator` provides access to
4-
some legacy generators. These all depend on Box-Muller normals or
4+
legacy generators. These all depend on Box-Muller normals or
55
inverse CDF exponentials or gammas. This class should only be used
66
if it is essential to have randoms that are identical to what
77
would have been produced by NumPy.
@@ -12,36 +12,22 @@ are produced in pairs. It is important to use
1212
:attr:`~randomgen.legacy.LegacyGenerator.state`
1313
when accessing the state so that these extra values are saved.
1414

15-
.. warning::
16-
17-
:class:`~randomgen.legacy.LegacyGenerator` only contains functions
18-
that have changed. Since it does not contain other functions, it
19-
is not direclty possible to replace :class:`~numpy.random.RandomState`.
20-
In order to full replace :class:`~numpy.random.RandomState`, it is
21-
necessary to use both :class:`~randomgen.legacy.LegacyGenerator`
22-
and :class:`~randomgen.generator.RandomGenerator` both driven
23-
by the same basic RNG. Methods present in :class:`~randomgen.legacy.LegacyGenerator`
24-
must be called from :class:`~randomgen.legacy.LegacyGenerator`. Other Methods
25-
should be called from :class:`~randomgen.generator.RandomGenerator`.
26-
27-
2815
.. code-block:: python
2916
30-
from randomgen import RandomGenerator, MT19937
17+
from randomgen import MT19937
3118
from randomgen.legacy import LegacyGenerator
3219
from numpy.random import RandomState
3320
# Use same seed
3421
rs = RandomState(12345)
3522
mt19937 = MT19937(12345)
36-
rg = RandomGenerator(mt19937)
3723
lg = LegacyGenerator(mt19937)
3824
3925
# Identical output
4026
rs.standard_normal()
4127
lg.standard_normal()
4228
4329
rs.random_sample()
44-
rg.random_sample()
30+
lg.random_sample()
4531
4632
rs.standard_exponential()
4733
lg.standard_exponential()
@@ -65,31 +51,62 @@ Simple random data
6551
.. autosummary::
6652
:toctree: generated/
6753

54+
~LegacyGenerator.rand
6855
~LegacyGenerator.randn
56+
~LegacyGenerator.randint
57+
~LegacyGenerator.random_integers
58+
~LegacyGenerator.random_sample
59+
~LegacyGenerator.choice
60+
~LegacyGenerator.bytes
61+
~LegacyGenerator.random_uintegers
62+
~LegacyGenerator.random_raw
63+
64+
Permutations
65+
============
66+
.. autosummary::
67+
:toctree: generated/
68+
69+
~LegacyGenerator.shuffle
70+
~LegacyGenerator.permutation
6971

7072
Distributions
7173
=============
7274
.. autosummary::
7375
:toctree: generated/
7476

7577
~LegacyGenerator.beta
78+
~LegacyGenerator.binomial
7679
~LegacyGenerator.chisquare
80+
~LegacyGenerator.complex_normal
7781
~LegacyGenerator.dirichlet
7882
~LegacyGenerator.exponential
7983
~LegacyGenerator.f
8084
~LegacyGenerator.gamma
85+
~LegacyGenerator.geometric
86+
~LegacyGenerator.gumbel
87+
~LegacyGenerator.hypergeometric
88+
~LegacyGenerator.laplace
89+
~LegacyGenerator.logistic
8190
~LegacyGenerator.lognormal
91+
~LegacyGenerator.logseries
92+
~LegacyGenerator.multinomial
8293
~LegacyGenerator.multivariate_normal
8394
~LegacyGenerator.negative_binomial
8495
~LegacyGenerator.noncentral_chisquare
8596
~LegacyGenerator.noncentral_f
8697
~LegacyGenerator.normal
8798
~LegacyGenerator.pareto
99+
~LegacyGenerator.poisson
88100
~LegacyGenerator.power
101+
~LegacyGenerator.rayleigh
89102
~LegacyGenerator.standard_cauchy
90103
~LegacyGenerator.standard_exponential
91104
~LegacyGenerator.standard_gamma
92105
~LegacyGenerator.standard_normal
93106
~LegacyGenerator.standard_t
107+
~LegacyGenerator.triangular
108+
~LegacyGenerator.uniform
109+
~LegacyGenerator.vonmises
94110
~LegacyGenerator.wald
95111
~LegacyGenerator.weibull
112+
~LegacyGenerator.zipf

_randomgen/randomgen/legacy/legacy.pyx renamed to _randomgen/randomgen/legacy/_legacy.pyx

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,23 +22,23 @@ import randomgen.pickle
2222

2323
np.import_array()
2424

25-
cdef class LegacyGenerator:
25+
cdef class _LegacyGenerator:
2626
"""
27-
LegacyGenerator(brng=None)
27+
_LegacyGenerator(brng=None)
2828
2929
Container providing legacy generators.
3030
31-
``LegacyGenerator`` exposes a number of methods for generating random
31+
``_LegacyGenerator`` exposes a number of methods for generating random
3232
numbers for a set of distributions where the method used to produce random
33-
samples has changed. Three core generators have changed: normals, exponentials
34-
and gammas. These have been replaced by fster Ziggurat-based methds in
35-
``RadnomGenerator``. ``LegacyGenerator`` retains the slower methods
33+
samples has changed. Three core generators have changed: normal, exponential
34+
and gamma. These have been replaced by faster Ziggurat-based methods in
35+
``RadnomGenerator``. ``_LegacyGenerator`` retains the slower methods
3636
to produce samples from these distributions as well as from distributions
3737
that depend on these such as the Chi-square, power or Weibull.
3838
3939
**No Compatibility Guarantee**
4040
41-
``LegacyGenerator`` is evolving and so it isn't possible to provide a
41+
``_LegacyGenerator`` is evolving and so it isn't possible to provide a
4242
compatibility guarantee like NumPy does. In particular, better algorithms
4343
have already been added. This will change once ``RandomGenerator``
4444
stabilizes.
@@ -53,15 +53,15 @@ cdef class LegacyGenerator:
5353
Examples
5454
--------
5555
Exactly reproducing a NumPy stream requires both a ``RandomGenerator``
56-
and a ``LegacyGenerator``. These must share a common ``MT19937`` basic
56+
and a ``_LegacyGenerator``. These must share a common ``MT19937`` basic
5757
RNG. Functions that are available in LegacyGenerator must be called
58-
from ``LegacyGenerator``, and other functions must be called from
58+
from ``_LegacyGenerator``, and other functions must be called from
5959
``RandomGenerator``.
6060
6161
>>> from randomgen import RandomGenerator, MT19937
62-
>>> from randomgen.legacy import LegacyGenerator
62+
>>> from randomgen.legacy._legacy import _LegacyGenerator
6363
>>> mt = MT19937(12345)
64-
>>> lg = LegacyGenerator(mt)
64+
>>> lg = _LegacyGenerator(mt)
6565
>>> rg = RandomGenerator(mt)
6666
>>> x = lg.standard_normal(10)
6767
>>> rg.shuffle(x)
@@ -118,7 +118,7 @@ cdef class LegacyGenerator:
118118
self.state = state
119119

120120
def __reduce__(self):
121-
return (randomgen.pickle.__generator_ctor,
121+
return (randomgen.pickle.__legacy_ctor,
122122
(self.state['brng'],),
123123
self.state)
124124

_randomgen/randomgen/legacy/legacy.py

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
from randomgen.generator import RandomGenerator
2+
from randomgen.mt19937 import MT19937
3+
from randomgen.legacy._legacy import _LegacyGenerator
4+
import randomgen.pickle
5+
6+
7+
class LegacyGenerator(RandomGenerator):
8+
"""
9+
LegacyGenerator(brng=None)
10+
11+
Container providing legacy generators.
12+
13+
``LegacyGenerator`` exposes a number of methods for generating random
14+
numbers for a set of distributions where the method used to produce random
15+
samples has changed. Three core generators have changed: normal, exponential
16+
and gamma. These have been replaced by faster Ziggurat-based methods in
17+
``RadnomGenerator``. ``LegacyGenerator`` retains the slower methods
18+
to produce samples from these distributions as well as from distributions
19+
that depend on these such as the Chi-square, power or Weibull.
20+
21+
**No Compatibility Guarantee**
22+
23+
``LegacyGenerator`` is evolving and so it isn't possible to provide a
24+
compatibility guarantee like NumPy does. In particular, better algorithms
25+
have already been added. This will change once ``RandomGenerator``
26+
stabilizes.
27+
28+
Parameters
29+
----------
30+
brng : Basic RNG, optional
31+
Basic RNG to use as the core generator. If none is provided, uses
32+
MT19937.
33+
34+
35+
Examples
36+
--------
37+
Exactly reproducing a NumPy stream requires using ``MT19937`` as
38+
the Basic RNG.
39+
40+
>>> from randomgen import MT19937
41+
>>> lg = LegacyGenerator(MT19937(12345))
42+
>>> x = lg.standard_normal(10)
43+
>>> lg.shuffle(x)
44+
>>> x[0]
45+
0.09290787674371767
46+
>>> lg.standard_exponential()
47+
1.6465621229906502
48+
49+
The equivalent commands from NumPy produce identical output.
50+
51+
>>> from numpy.random import RandomState
52+
>>> rs = RandomState(12345)
53+
>>> x = rs.standard_normal(10)
54+
>>> rs.shuffle(x)
55+
>>> x[0]
56+
0.09290787674371767
57+
>>> rs.standard_exponential()
58+
1.6465621229906502
59+
"""
60+
_LEGACY_ATTRIBUTES = tuple(a for a in dir(
61+
_LegacyGenerator) if not a.startswith('_'))
62+
63+
def __init__(self, brng=None):
64+
if brng is None:
65+
brng = MT19937()
66+
super(LegacyGenerator, self).__init__(brng)
67+
self.__legacy = _LegacyGenerator(brng)
68+
69+
def __getattribute__(self, name):
70+
if name in LegacyGenerator._LEGACY_ATTRIBUTES:
71+
return self.__legacy.__getattribute__(name)
72+
return object.__getattribute__(self, name)
73+
74+
# Pickling support:
75+
def __getstate__(self):
76+
return self.state
77+
78+
def __setstate__(self, state):
79+
self.state = state
80+
81+
def __reduce__(self):
82+
return (randomgen.pickle._experiment_ctor,
83+
(self.state['brng'],),
84+
self.state)

_randomgen/randomgen/pickle.py

Lines changed: 78 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,24 @@
11
from .generator import RandomGenerator
22
from .dsfmt import DSFMT
33
from .mt19937 import MT19937
4-
from .pcg32 import PCG32
5-
from .pcg64 import PCG64
6-
from .philox import Philox
7-
from .threefry import ThreeFry
8-
from .threefry32 import ThreeFry32
9-
from .xoroshiro128 import Xoroshiro128
10-
from .xorshift1024 import Xorshift1024
11-
12-
PRNGS = {'MT19937': MT19937,
13-
'DSFMT': DSFMT,
14-
'PCG32': PCG32,
15-
'PCG64': PCG64,
16-
'Philox': Philox,
17-
'ThreeFry': ThreeFry,
18-
'ThreeFry32': ThreeFry32,
19-
'Xorshift1024': Xorshift1024,
20-
'Xoroshiro128': Xoroshiro128}
4+
from randomgen.pcg32 import PCG32
5+
from randomgen.pcg64 import PCG64
6+
from randomgen.philox import Philox
7+
from randomgen.threefry import ThreeFry
8+
from randomgen.threefry32 import ThreeFry32
9+
from randomgen.xoroshiro128 import Xoroshiro128
10+
from randomgen.xorshift1024 import Xorshift1024
11+
from randomgen.legacy import LegacyGenerator
12+
13+
BasicRNGS = {'MT19937': MT19937,
14+
'DSFMT': DSFMT,
15+
'PCG32': PCG32,
16+
'PCG64': PCG64,
17+
'Philox': Philox,
18+
'ThreeFry': ThreeFry,
19+
'ThreeFry32': ThreeFry32,
20+
'Xorshift1024': Xorshift1024,
21+
'Xoroshiro128': Xoroshiro128}
2122

2223

2324
def __generator_ctor(brng_name='mt19937'):
@@ -27,21 +28,21 @@ def __generator_ctor(brng_name='mt19937'):
2728
Parameters
2829
----------
2930
brng_name: str
30-
String containing the core PRNG
31+
String containing the core BasicRNG
3132
3233
Returns
3334
-------
3435
rg: RandomGenerator
35-
RandomGenerator using the named core PRNG
36+
RandomGenerator using the named core BasicRNG
3637
"""
3738
try:
3839
brng_name = brng_name.decode('ascii')
3940
except AttributeError:
4041
pass
41-
if brng_name in PRNGS:
42-
brng = PRNGS[brng_name]
42+
if brng_name in BasicRNGS:
43+
brng = BasicRNGS[brng_name]
4344
else:
44-
raise ValueError(str(brng_name) + ' is not a known PRNG module.')
45+
raise ValueError(str(brng_name) + ' is not a known BasicRNG module.')
4546

4647
return RandomGenerator(brng())
4748

@@ -64,9 +65,61 @@ def __brng_ctor(brng_name='mt19937'):
6465
brng_name = brng_name.decode('ascii')
6566
except AttributeError:
6667
pass
67-
if brng_name in PRNGS:
68-
brng = PRNGS[brng_name]
68+
if brng_name in BasicRNGS:
69+
brng = BasicRNGS[brng_name]
6970
else:
70-
raise ValueError(str(brng_name) + ' is not a known PRNG module.')
71+
raise ValueError(str(brng_name) + ' is not a known BasicRNG module.')
7172

7273
return brng()
74+
75+
76+
def __legacy_ctor(brng_name='mt19937'):
77+
"""
78+
Pickling helper function that returns a LegacyGenerator object
79+
80+
Parameters
81+
----------
82+
brng_name: str
83+
String containing the core BasicRNG
84+
85+
Returns
86+
-------
87+
lg: LegacyGenerator
88+
LegacyGenerator using the named core BasicRNG
89+
"""
90+
try:
91+
brng_name = brng_name.decode('ascii')
92+
except AttributeError:
93+
pass
94+
if brng_name in BasicRNGS:
95+
brng = BasicRNGS[brng_name]
96+
else:
97+
raise ValueError(str(brng_name) + ' is not a known BasicRNG module.')
98+
99+
return LegacyGenerator(brng())
100+
101+
102+
def _experiment_ctor(brng_name='mt19937'):
103+
"""
104+
Pickling helper function that returns a LegacyGenerator object
105+
106+
Parameters
107+
----------
108+
brng_name: str
109+
String containing the name of the Basic RNG
110+
111+
Returns
112+
-------
113+
brng: BasicRNG
114+
Basic RNG instance
115+
"""
116+
try:
117+
brng_name = brng_name.decode('ascii')
118+
except AttributeError:
119+
pass
120+
if brng_name in BasicRNGS:
121+
brng = BasicRNGS[brng_name]
122+
else:
123+
raise ValueError(str(brng_name) + ' is not a known BasicRNG module.')
124+
125+
return LegacyGenerator(brng())

0 commit comments

Comments
 (0)
0