Skip to content

Commit 2b70909

Browse files
authored
Merge pull request #73 from bashtage/add-out-std-gamma
ENH: Add output support to standard_gamma
2 parents d0eea87 + b383072 commit 2b70909

9 files changed

+165
-76
lines changed

README.md

+7-3
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ w = rnd.standard_normal(10000, method='zig')
5151
* Uniforms (`random_sample`)
5252
* Exponentials (`standard_exponential`)
5353
* Normals (`standard_normal`)
54+
* Standard Gammas (via `standard_gamma`)
5455

5556
## Included Pseudo Random Number Generators
5657

@@ -78,9 +79,12 @@ The RNGs include:
7879
support an additional `method` keyword argument which can be `bm` or
7980
`zig` where `bm` corresponds to the current method using the Box-Muller
8081
transformation and `zig` uses the much faster (100%+) ziggurat method.
81-
* `random_sample` can produce either single precision (`np.float32`) or
82-
double precision (`np.float64`, the default) using an the optional
83-
keyword argument `dtype`.
82+
* Core random number generators can produce either single precision
83+
(`np.float32`) or double precision (`np.float64`, the default) using
84+
an the optional keyword argument `dtype`
85+
* Core random number generators can fill existin arrays using the
86+
`out` keyword argument
87+
8488

8589
### New Functions
8690

README.rst

+5-2
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ generation to all random number types.
5151
- Uniforms (``random_sample``)
5252
- Exponentials (``standard_exponential``)
5353
- Normals (``standard_normal``)
54+
- Standard Gammas (via ``standard_gamma``)
5455

5556
Included Pseudo Random Number Generators
5657
----------------------------------------
@@ -82,9 +83,11 @@ New Features
8283
argument which can be ``bm`` or ``zig`` where ``bm`` corresponds to
8384
the current method using the Box-Muller transformation and ``zig``
8485
uses the much faster (100%+) ziggurat method.
85-
- ``random_sample`` can produce either single precision
86+
- Core random number generators can produce either single precision
8687
(``np.float32``) or double precision (``np.float64``, the default)
87-
using an the optional keyword argument ``dtype``.
88+
using an the optional keyword argument ``dtype``
89+
- Core random number generators can fill existin arrays using the
90+
``out`` keyword argument
8891

8992
New Functions
9093
~~~~~~~~~~~~~

doc/source/change-log.rst

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Changes since 1.11.4
1212
* Uniforms (:meth:`~randomstate.prng.mt19937.random_sample`)
1313
* Normals (:meth:`~randomstate.prng.mt19937.standard_normal`)
1414
* Standard Exponentials (:meth:`~randomstate.prng.mt19937.standard_exponential`)
15+
* Standard Gammas (:meth:`~randomstate.prng.mt19937.standard_gamma`)
1516

1617
Version 1.11.4
1718
--------------

doc/source/index.rst

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ What's New or Different
4040

4141
* Uniforms (:meth:`~randomstate.prng.mt19937.random_sample`)
4242
* Normals (:meth:`~randomstate.prng.mt19937.standard_normal`)
43+
* Standard Gammas (:meth:`~randomstate.prng.mt19937.standard_gamma`)
4344
* Standard Exponentials (:meth:`~randomstate.prng.mt19937.standard_exponential`)
4445

4546
This allows mulththreading to fill large arrays in chunks using suitable

randomstate/array_fillers.pxi.in

+1-9
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,8 @@ cdef object {{ctype}}_fill(aug_state* state, void *func, object size, object loc
2222
return fout
2323

2424
if out is not None:
25+
check_output(out, np.{{nptype}}, size)
2526
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_CARRAY) or
30-
np.PyArray_CHKFLAGS(out_array, np.NPY_FARRAY)):
31-
raise ValueError('Supplied output array is not contiguous, writable or aligned.')
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')
3527
else:
3628
out_array = <np.ndarray>np.empty(size, np.{{nptype}})
3729

randomstate/array_utilities.pxi

+63-20
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,20 @@ cdef np.npy_intp compute_numel(size):
2727
n = size
2828
return n
2929

30+
cdef check_output(object out, object dtype, object size):
31+
if out is None:
32+
return
33+
cdef np.ndarray out_array = <np.ndarray>out
34+
if not (np.PyArray_CHKFLAGS(out_array, np.NPY_CARRAY) or
35+
np.PyArray_CHKFLAGS(out_array, np.NPY_FARRAY)):
36+
raise ValueError('Supplied output array is not contiguous, writable or aligned.')
37+
if out_array.dtype != dtype:
38+
raise TypeError('Supplied output array has the wrong type. '
39+
'Expected {0}, got {0}'.format(dtype, out_array.dtype))
40+
if size is not None:
41+
# TODO: enable this !!! if tuple(size) != out_array.shape:
42+
raise ValueError('size and out cannot be simultaneously used')
43+
3044
cdef double POISSON_LAM_MAX = <double>np.iinfo('l').max - np.sqrt(np.iinfo('l').max)*10
3145

3246
cdef enum ConstraintType:
@@ -101,7 +115,8 @@ cdef int check_constraint(double val, object name, constraint_type cons) except
101115
return 0
102116

103117
cdef object cont_broadcast_1(aug_state* state, void* func, object size, object lock,
104-
np.ndarray a_arr, object a_name, constraint_type a_constraint):
118+
np.ndarray a_arr, object a_name, constraint_type a_constraint,
119+
object out):
105120

106121
cdef np.ndarray randoms
107122
cdef double a_val
@@ -113,10 +128,12 @@ cdef object cont_broadcast_1(aug_state* state, void* func, object size, object l
113128
if a_constraint != CONS_NONE:
114129
check_array_constraint(a_arr, a_name, a_constraint)
115130

116-
if size is not None:
131+
if size is not None and out is None:
117132
randoms = <np.ndarray>np.empty(size, np.double)
118-
else:
133+
elif out is None:
119134
randoms = np.PyArray_SimpleNew(np.PyArray_NDIM(a_arr), np.PyArray_DIMS(a_arr), np.NPY_DOUBLE)
135+
else:
136+
randoms = <np.ndarray>out
120137

121138
randoms_data = <double *>np.PyArray_DATA(randoms)
122139
n = np.PyArray_SIZE(randoms)
@@ -214,11 +231,13 @@ cdef object cont_broadcast_3(aug_state* state, void* func, object size, object l
214231
cdef object cont(aug_state* state, void* func, object size, object lock, int narg,
215232
object a, object a_name, constraint_type a_constraint,
216233
object b, object b_name, constraint_type b_constraint,
217-
object c, object c_name, constraint_type c_constraint):
234+
object c, object c_name, constraint_type c_constraint,
235+
object out):
218236

219237
cdef np.ndarray a_arr, b_arr, c_arr
220238
cdef double _a = 0.0, _b = 0.0, _c = 0.0
221239
cdef bint is_scalar = True
240+
check_output(out, np.float64, size)
222241
if narg > 0:
223242
a_arr = <np.ndarray>np.PyArray_FROM_OTF(a, np.NPY_DOUBLE, np.NPY_ALIGNED)
224243
is_scalar = is_scalar and np.PyArray_NDIM(a_arr) == 0
@@ -232,7 +251,8 @@ cdef object cont(aug_state* state, void* func, object size, object lock, int nar
232251
if not is_scalar:
233252
if narg == 1:
234253
return cont_broadcast_1(state, func, size, lock,
235-
a_arr, a_name, a_constraint)
254+
a_arr, a_name, a_constraint,
255+
out)
236256
elif narg == 2:
237257
return cont_broadcast_2(state, func, size, lock,
238258
a_arr, a_name, a_constraint,
@@ -256,7 +276,7 @@ cdef object cont(aug_state* state, void* func, object size, object lock, int nar
256276
if c_constraint != CONS_NONE and is_scalar:
257277
check_constraint(_c, c_name, c_constraint)
258278

259-
if size is None:
279+
if size is None and out is None:
260280
with lock:
261281
if narg == 0:
262282
return (<random_double_0>func)(state)
@@ -267,8 +287,15 @@ cdef object cont(aug_state* state, void* func, object size, object lock, int nar
267287
elif narg == 3:
268288
return (<random_double_3>func)(state, _a, _b, _c)
269289

270-
cdef np.npy_intp i, n = compute_numel(size)
271-
cdef np.ndarray randoms = np.empty(n, np.double)
290+
cdef np.npy_intp i, n
291+
cdef np.ndarray randoms
292+
if out is None:
293+
n = compute_numel(size)
294+
randoms = np.empty(n, np.double)
295+
else:
296+
randoms = <np.ndarray>out
297+
n = np.PyArray_SIZE(randoms)
298+
272299
cdef double *randoms_data = <double *>np.PyArray_DATA(randoms)
273300
cdef random_double_0 f0;
274301
cdef random_double_1 f1;
@@ -293,7 +320,10 @@ cdef object cont(aug_state* state, void* func, object size, object lock, int nar
293320
for i in range(n):
294321
randoms_data[i] = f3(state, _a, _b, _c)
295322

296-
return np.asarray(randoms).reshape(size)
323+
if out is None:
324+
return np.asarray(randoms).reshape(size)
325+
else:
326+
return out
297327

298328
cdef object discrete_broadcast_d(aug_state* state, void* func, object size, object lock,
299329
np.ndarray a_arr, object a_name, constraint_type a_constraint):
@@ -602,7 +632,8 @@ cdef object disc(aug_state* state, void* func, object size, object lock,
602632

603633

604634
cdef object cont_broadcast_float_1(aug_state* state, void* func, object size, object lock,
605-
np.ndarray a_arr, object a_name, constraint_type a_constraint):
635+
np.ndarray a_arr, object a_name, constraint_type a_constraint,
636+
object out):
606637

607638
cdef np.ndarray randoms
608639
cdef float a_val
@@ -614,12 +645,14 @@ cdef object cont_broadcast_float_1(aug_state* state, void* func, object size, ob
614645
if a_constraint != CONS_NONE:
615646
check_array_constraint(a_arr, a_name, a_constraint)
616647

617-
if size is not None:
648+
if size is not None and out is None:
618649
randoms = <np.ndarray>np.empty(size, np.float32)
619-
else:
650+
elif out is None:
620651
randoms = np.PyArray_SimpleNew(np.PyArray_NDIM(a_arr),
621652
np.PyArray_DIMS(a_arr),
622653
np.NPY_FLOAT32)
654+
else:
655+
randoms = <np.ndarray>out
623656

624657
randoms_data = <float *>np.PyArray_DATA(randoms)
625658
n = np.PyArray_SIZE(randoms)
@@ -635,36 +668,46 @@ cdef object cont_broadcast_float_1(aug_state* state, void* func, object size, ob
635668
return randoms
636669

637670
cdef object cont_float(aug_state* state, void* func, object size, object lock,
638-
object a, object a_name, constraint_type a_constraint):
671+
object a, object a_name, constraint_type a_constraint,
672+
object out):
639673

640674
cdef np.ndarray a_arr, b_arr, c_arr
641675
cdef float _a
642676
cdef bint is_scalar = True
643677
cdef int requirements = np.NPY_ALIGNED | np.NPY_FORCECAST
644-
678+
check_output(out, np.float32, size)
645679
a_arr = <np.ndarray>np.PyArray_FROMANY(a, np.NPY_FLOAT32, 0, 0, requirements)
646680
# a_arr = <np.ndarray>np.PyArray_FROM_OTF(a, np.NPY_FLOAT32, np.NPY_ALIGNED)
647681
is_scalar = np.PyArray_NDIM(a_arr) == 0
648682

649683
if not is_scalar:
650-
return cont_broadcast_float_1(state, func, size, lock, a_arr, a_name, a_constraint)
684+
return cont_broadcast_float_1(state, func, size, lock, a_arr, a_name, a_constraint, out)
651685

652686
_a = <float>PyFloat_AsDouble(a)
653687
if a_constraint != CONS_NONE:
654688
check_constraint(_a, a_name, a_constraint)
655689

656-
if size is None:
690+
if size is None and out is None:
657691
with lock:
658692
return (<random_float_1>func)(state, _a)
659693

660-
cdef np.npy_intp i, n = compute_numel(size)
661-
cdef np.ndarray randoms = np.empty(n, np.float32)
694+
cdef np.npy_intp i, n
695+
cdef np.ndarray randoms
696+
if out is None:
697+
n = compute_numel(size)
698+
randoms = np.empty(n, np.float32)
699+
else:
700+
randoms = <np.ndarray>out
701+
n = np.PyArray_SIZE(randoms)
702+
662703
cdef float *randoms_data = <float *>np.PyArray_DATA(randoms)
663704
cdef random_float_1 f1 = <random_float_1>func;
664705

665706
with lock, nogil:
666707
for i in range(n):
667708
randoms_data[i] = f1(state, _a)
668709

669-
return np.asarray(randoms).reshape(size)
670-
710+
if out is None:
711+
return np.asarray(randoms).reshape(size)
712+
else:
713+
return out

0 commit comments

Comments
 (0)