Skip to content

Commit 58ebafb

Browse files
committed
ENH: Add support for filling existing arrays
Add support for core distributions for filling existing arrays to allow parallel generation using multithreading. closes #68
1 parent 3e75060 commit 58ebafb

File tree

8 files changed

+173
-57
lines changed

8 files changed

+173
-57
lines changed

README.md

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,13 @@ w = rnd.standard_normal(10000, method='zig')
4444

4545
**Note**: There are _no_ plans to extend the alternative precision
4646
generation to all random number types.
47-
48-
49-
5047

48+
* Support for filling existing arrays using `out` keyword argument. Currently
49+
supported in (both 32- and 64-bit outputs)
50+
51+
* Uniforms (`random_sample`)
52+
* Exponentials (`standard_exponential`)
53+
* Normals (`standard_normal`)
5154

5255
## Included Pseudo Random Number Generators
5356

@@ -98,9 +101,9 @@ the RNG._
98101
`mt19937` generator is identical to `numpy.random.RandomState`, and
99102
will produce an identical sequence of random numbers for a given seed.
100103
* Builds and passes all tests on:
101-
* Linux 32/64 bit, Python 2.7, 3.4, 3.5 (should work on 2.6 and 3.3)
104+
* Linux 32/64 bit, Python 2.7, 3.4, 3.5, 3.6 (probably works on 2.6 and 3.3)
102105
* PC-BSD (FreeBSD) 64-bit, Python 2.7
103-
* OSX 64-bit, Python 2.7
106+
* OSX 64-bit, Python 2.7
104107
* Windows 32/64 bit (only tested on Python 2.7 and 3.5, but should
105108
work on 3.3/3.4)
106109

@@ -124,9 +127,9 @@ need to be smoothed.
124127
## Requirements
125128
Building requires:
126129

127-
* Python (2.7, 3.4, 3.5)
128-
* NumPy (1.9, 1.10, 1.11)
129-
* Cython (0.22, 0.23, 0.24)
130+
* Python (2.7, 3.4, 3.5, 3.6)
131+
* NumPy (1.9, 1.10, 1.11, 1.12)
132+
* Cython (0.22, 0.23, 0.24, 0.25)
130133
* tempita (0.5+), if not provided by Cython
131134

132135
Testing requires nose (1.3+).

README.rst

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
randomstate
22
===========
33

4-
|Travis Build Status| |Appveyor Build Status|
4+
|Travis Build Status| |Appveyor Build Status| |PyPI version|
55

66
This is a library and generic interface for alternative random
77
generators in Python and NumPy.
@@ -45,6 +45,13 @@ change.
4545
**Note**: There are *no* plans to extend the alternative precision
4646
generation to all random number types.
4747

48+
- Support for filling existing arrays using ``out`` keyword argument.
49+
Currently supported in (both 32- and 64-bit outputs)
50+
51+
- Uniforms (``random_sample``)
52+
- Exponentials (``standard_exponential``)
53+
- Normals (``standard_normal``)
54+
4855
Included Pseudo Random Number Generators
4956
----------------------------------------
5057

@@ -101,7 +108,8 @@ Status
101108
and will produce an identical sequence of random numbers for a given
102109
seed.
103110
- Builds and passes all tests on:
104-
- Linux 32/64 bit, Python 2.7, 3.4, 3.5 (should work on 2.6 and 3.3)
111+
- Linux 32/64 bit, Python 2.7, 3.4, 3.5, 3.6 (probably works on 2.6 and
112+
3.3)
105113
- PC-BSD (FreeBSD) 64-bit, Python 2.7
106114
- OSX 64-bit, Python 2.7
107115
- Windows 32/64 bit (only tested on Python 2.7 and 3.5, but should work
@@ -134,9 +142,9 @@ Requirements
134142

135143
Building requires:
136144

137-
- Python (2.7, 3.4, 3.5)
138-
- NumPy (1.9, 1.10, 1.11)
139-
- Cython (0.22, 0.23, 0.24)
145+
- Python (2.7, 3.4, 3.5, 3.6)
146+
- NumPy (1.9, 1.10, 1.11, 1.12)
147+
- Cython (0.22, 0.23, 0.24, 0.25)
140148
- tempita (0.5+), if not provided by Cython
141149

142150
Testing requires nose (1.3+).
@@ -264,3 +272,5 @@ NumPy's mt19937.
264272
:target: https://travis-ci.org/bashtage/ng-numpy-randomstate
265273
.. |Appveyor Build Status| image:: https://ci.appveyor.com/api/projects/status/odc5c4ukhru5xicl/branch/master?svg=true
266274
:target: https://ci.appveyor.com/project/bashtage/ng-numpy-randomstate/branch/master
275+
.. |PyPI version| image:: https://badge.fury.io/py/randomstate.svg
276+
:target: https://badge.fury.io/py/randomstate

doc/source/change-log.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,17 @@
22

33
Change Log
44
==========
5+
6+
Changes since 1.11.4
7+
--------------------
8+
* Add ``out`` argument which allows filling existing arrays. This feature was
9+
added to facilitate multithreaded filling of arrays using parallel random
10+
number generators.
11+
12+
* Uniforms (:meth:`~randomstate.prng.mt19937.random_sample`)
13+
* Normals (:meth:`~randomstate.prng.mt19937.standard_normal`)
14+
* Standard Exponentials (:meth:`~randomstate.prng.mt19937.standard_exponential`)
15+
516
Version 1.11.4
617
--------------
718
* Fix for error in Ziggurat implementation of Normal

doc/source/conf.py

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
import sys
1717
import os
1818
import shlex
19-
import sphinx_bootstrap_theme
19+
import guzzle_sphinx_theme
2020

2121
# If extensions (or modules to document with autodoc) are in another directory,
2222
# add these directories to sys.path here. If the directory is relative to the
@@ -118,22 +118,20 @@
118118

119119
# The theme to use for HTML and HTML Help pages. See the documentation for
120120
# a list of builtin themes.
121-
html_theme = 'bootstrap'
122-
html_theme_path = sphinx_bootstrap_theme.get_html_theme_path()
123121

124-
#on_rtd = os.environ.get('READTHEDOCS', None) == 'True'
125-
#
126-
#if not on_rtd: # only import and set the theme if we're building docs locally
127-
# import sphinx_rtd_theme
128-
# html_theme = 'sphinx_rtd_theme'
129-
# html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
130-
131-
# Theme options are theme-specific and customize the look and feel of a theme
132-
# further. For a list of options available for each theme, see the
133-
# documentation.
134-
html_theme_options = {'bootswatch_theme': 'yeti',
135-
# Render the next and previous page links in navbar. (Default: true)
136-
'navbar_sidebarrel': False,}
122+
# Adds an HTML table visitor to apply Bootstrap table classes
123+
html_translator_class = 'guzzle_sphinx_theme.HTMLTranslator'
124+
html_theme_path = guzzle_sphinx_theme.html_theme_path()
125+
html_theme = 'guzzle_sphinx_theme'
126+
127+
# Register the theme as an extension to generate a sitemap.xml
128+
extensions.append("guzzle_sphinx_theme")
129+
130+
# Guzzle theme options (see theme.conf for more information)
131+
html_theme_options = {
132+
# Set the name of the project to appear in the sidebar
133+
"project_nav_name": "randomstate",
134+
}
137135

138136
# Add any paths that contain custom themes here, relative to this directory.
139137
#html_theme_path = []

doc/source/index.rst

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,24 @@ What's New or Different
3535
rs.seed(0)
3636
rs.random_sample(3, dtype=np.float32)
3737
38+
* Optional ``out`` argument that allows existing arrays to be filled for
39+
select distributions
40+
41+
* Uniforms (:meth:`~randomstate.prng.mt19937.random_sample`)
42+
* Normals (:meth:`~randomstate.prng.mt19937.standard_normal`)
43+
* Standard Exponentials (:meth:`~randomstate.prng.mt19937.standard_exponential`)
3844

45+
This allows mulththreading to fill large arrays in chunks using suitable
46+
PRNGs in parallel.
47+
48+
.. ipython:: python
49+
50+
import numpy as np
51+
import randomstate as rs
52+
existing = np.zeros(4)
53+
rs.seed(0)
54+
rs.random_sample(out=existing[:2])
55+
print(existing)
3956
4057
* For changes since the previous release, see the :ref:`change-log`
4158

randomstate/array_fillers.pxi.in

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,37 @@ ctypes = (('float64', 'double'),
99
{{for nptype, ctype in ctypes}}
1010

1111

12-
cdef object {{ctype}}_fill(aug_state* state, void *func, object size, object lock):
12+
cdef object {{ctype}}_fill(aug_state* state, void *func, object size, object lock, object out):
1313
cdef random_{{ctype}}_fill f = <random_{{ctype}}_fill>func
14-
cdef {{ctype}} out
14+
cdef {{ctype}} fout
1515
cdef {{ctype}} *out_array_data
1616
cdef np.ndarray out_array
1717
cdef np.npy_intp n
1818

19-
if size is None:
19+
if size is None and out is None:
2020
with lock:
21-
f(state, 1, &out)
22-
return out
21+
f(state, 1, &fout)
22+
return fout
23+
24+
if out is not None:
25+
out_array = <np.ndarray>out
26+
if out_array.dtype != np.{{nptype}}:
27+
raise TypeError('Supplied output array has the wrong type.'
28+
'Expected {{nptype}}, got {0}'.format(out_array.dtype))
29+
if not (np.PyArray_CHKFLAGS(out_array, np.NPY_C_CONTIGUOUS) or
30+
np.PyArray_CHKFLAGS(out_array, np.NPY_F_CONTIGUOUS)):
31+
raise ValueError('Supplied output array is not C contiguous')
32+
if size is not None:
33+
# TODO: enable this !!! if tuple(size) != out_array.shape:
34+
raise ValueError('size and shape of the supplied output are different')
2335
else:
2436
out_array = <np.ndarray>np.empty(size, np.{{nptype}})
25-
n = np.PyArray_SIZE(out_array)
26-
out_array_data = <{{ctype}} *>np.PyArray_DATA(out_array)
27-
with lock, nogil:
28-
f(state, n, out_array_data)
29-
return out_array
37+
38+
n = np.PyArray_SIZE(out_array)
39+
out_array_data = <{{ctype}} *>np.PyArray_DATA(out_array)
40+
with lock, nogil:
41+
f(state, n, out_array_data)
42+
return out_array
3043

3144
{{endfor}}
3245

randomstate/randomstate.pyx

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -662,9 +662,9 @@ cdef class RandomState:
662662
self.get_state())
663663

664664
# Basic distributions:
665-
def random_sample(self, size=None, dtype=np.float64):
665+
def random_sample(self, size=None, dtype=np.float64, out=None):
666666
"""
667-
random_sample(size=None, dtype='d')
667+
random_sample(size=None, dtype='d', out=None)
668668
669669
Return random floats in the half-open interval [0.0, 1.0).
670670
@@ -684,6 +684,10 @@ cdef class RandomState:
684684
Desired dtype of the result. All dtypes are determined by their
685685
name, either 'float64' or 'float32'. The default value is
686686
'float64'.
687+
out : ndarray, optional
688+
Alternative output array in which to place the result. If size is not None,
689+
it must have the same shape as the provided size and must match the type of
690+
the output values.
687691
688692
Returns
689693
-------
@@ -710,9 +714,9 @@ cdef class RandomState:
710714
"""
711715
key = np.dtype(dtype).name
712716
if key == 'float64':
713-
return double_fill(&self.rng_state, &random_uniform_fill_double, size, self.lock)
717+
return double_fill(&self.rng_state, &random_uniform_fill_double, size, self.lock, out)
714718
elif key == 'float32':
715-
return float_fill(&self.rng_state, &random_uniform_fill_float, size, self.lock)
719+
return float_fill(&self.rng_state, &random_uniform_fill_float, size, self.lock, out)
716720
else:
717721
raise TypeError('Unsupported dtype "%s" for random_sample' % key)
718722

@@ -1403,9 +1407,10 @@ cdef class RandomState:
14031407

14041408

14051409
# Complicated, continuous distributions:
1406-
def standard_normal(self, size=None, dtype=np.float64, method=__normal_method):
1410+
def standard_normal(self, size=None, dtype=np.float64, method=__normal_method,
1411+
out=None):
14071412
"""
1408-
standard_normal(size=None, dtype='d', method='bm')
1413+
standard_normal(size=None, dtype='d', method='bm', out=None)
14091414
14101415
Draw samples from a standard Normal distribution (mean=0, stdev=1).
14111416
@@ -1422,6 +1427,10 @@ cdef class RandomState:
14221427
method : str, optional
14231428
Either 'bm' or 'zig'. 'bm' uses the default Box-Muller transformations
14241429
method. 'zig' uses the much faster Ziggurat method of Marsaglia and Tsang.
1430+
out : ndarray, optional
1431+
Alternative output array in which to place the result. If size is not None,
1432+
it must have the same shape as the provided size and must match the type of
1433+
the output values.
14251434
14261435
Returns
14271436
-------
@@ -1440,26 +1449,22 @@ cdef class RandomState:
14401449
>>> s.shape
14411450
(3, 4, 2)
14421451
1443-
Notes
1444-
-----
1445-
float32 normals are only available using the Box-Muller transformation
1446-
14471452
"""
14481453
key = np.dtype(dtype).name
14491454
if key == 'float64':
14501455
if method == u'zig':
14511456
return double_fill(&self.rng_state, &random_gauss_zig_double_fill,
1452-
size, self.lock)
1457+
size, self.lock, out)
14531458
else:
14541459
return double_fill(&self.rng_state, &random_gauss_fill,
1455-
size, self.lock)
1460+
size, self.lock, out)
14561461
elif key == 'float32':
14571462
if method == u'zig':
14581463
return float_fill(&self.rng_state, &random_gauss_zig_float_fill,
1459-
size, self.lock)
1464+
size, self.lock, out)
14601465
else:
14611466
return float_fill(&self.rng_state, &random_gauss_fill_float,
1462-
size, self.lock)
1467+
size, self.lock, out)
14631468
else:
14641469
raise TypeError('Unsupported dtype "%s" for standard_normal' % key)
14651470

@@ -1663,9 +1668,9 @@ cdef class RandomState:
16631668
0.0, '', CONS_NONE,
16641669
0.0, '', CONS_NONE)
16651670

1666-
def standard_exponential(self, size=None, dtype=np.float64):
1671+
def standard_exponential(self, size=None, dtype=np.float64, out=None):
16671672
"""
1668-
standard_exponential(size=None, dtype=np.float64)
1673+
standard_exponential(size=None, dtype=np.float64, out=None)
16691674
16701675
Draw samples from the standard exponential distribution.
16711676
@@ -1682,6 +1687,10 @@ cdef class RandomState:
16821687
Desired dtype of the result. All dtypes are determined by their
16831688
name, either 'float64' or 'float32'. The default value is
16841689
'float64'.
1690+
out : ndarray, optional
1691+
Alternative output array in which to place the result. If size is not None,
1692+
it must have the same shape as the provided size and must match the type of
1693+
the output values.
16851694
16861695
Returns
16871696
-------
@@ -1699,11 +1708,11 @@ cdef class RandomState:
16991708
if key == 'float64':
17001709
return double_fill(&self.rng_state,
17011710
&random_standard_exponential_fill_double,
1702-
size, self.lock)
1711+
size, self.lock, out)
17031712
elif key == 'float32':
17041713
return float_fill(&self.rng_state,
17051714
&random_standard_exponential_fill_float,
1706-
size, self.lock)
1715+
size, self.lock, out)
17071716
else:
17081717
raise TypeError('Unsupported dtype "%s" for standard_exponential'
17091718
% key)

0 commit comments

Comments
 (0)