Skip to content

Commit 9b4e7fe

Browse files
committed
ENH: Add 32-bit normals using Box-Muller
Augment state to handle 32-bit float normals Implement pure-32-bit version of normal Re-factor standard_gamma Add 32-bit gamma in C
1 parent 50d36ab commit 9b4e7fe

24 files changed

+281
-109
lines changed

README.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,15 @@ w = rnd.standard_normal(10000, method='zig')
3131
```
3232

3333
* Preliminary support for 32-bit floating randoms for core generators.
34-
Currently only uniforms (`random_sample`) and exponentials
35-
(`standard_exponential`) have been implemented. Ultimately support
36-
should be avialable for:
34+
Currently only uniforms (`random_sample`), exponentials
35+
(`standard_exponential`) and normals (`standard_normal` but only
36+
using Box-Muller, so `method='bm'` is required) have been implemented.
37+
Ultimately support should be avialable for:
3738

3839
* Uniforms
3940
* Exponentials
4041
* Standard Gammas (via `standard_gamma`)
41-
* Normals (via `standard_normal`)
42+
* Normals (currently only implemented using Box-Muller transformation)
4243

4344
**WARNING**: The 32-bit generators are **experimental** and subjust
4445
to change.

README.rst

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,16 @@ Features
3232
w = rnd.standard_normal(10000, method='zig')
3333
3434
- Preliminary support for 32-bit floating randoms for core generators.
35-
Currently only uniforms (``random_sample``) and exponentials
36-
(``standard_exponential``) have been implemented. Ultimately support
37-
should be avialable for:
35+
Currently only uniforms (``random_sample``), exponentials
36+
(``standard_exponential``) and normals (``standard_normal`` but only
37+
using Box-Muller, so ``method='bm'`` is required) have been
38+
implemented. Ultimately support should be avialable for:
3839

3940
- Uniforms
4041
- Exponentials
4142
- Standard Gammas (via ``standard_gamma``)
42-
- Normals (via ``standard_normal``)
43+
- Normals (currently only implemented using Box-Muller
44+
transformation)
4345

4446
**WARNING**: The 32-bit generators are **experimental** and subjust to
4547
change.

randomstate/distributions.c

Lines changed: 154 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,36 @@ static inline double gauss(aug_state* state)
5353
}
5454
}
5555

56+
static inline float gauss_float(aug_state* state)
57+
{
58+
if (state->has_gauss_float)
59+
{
60+
const float temp = state->gauss_float;
61+
state->has_gauss_float = false;
62+
state->gauss_float = 0.0f;
63+
return temp;
64+
}
65+
else
66+
{
67+
float f, x1, x2, r2;
68+
69+
do {
70+
x1 = 2.0f * random_float(state) - 1.0f;
71+
x2 = 2.0f * random_float(state) - 1.0f;
72+
r2 = x1*x1 + x2*x2;
73+
}
74+
while (r2 >= 1.0 || r2 == 0.0);
75+
76+
/* Box-Muller transform */
77+
f = sqrtf(-2.0f * logf(r2)/r2);
78+
/* Keep for next call */
79+
state->gauss_float = f*x1;
80+
state->has_gauss_float = true;
81+
return f*x2;
82+
}
83+
}
84+
85+
5686
/*
5787
* Julia implementation of Ziggurat algo
5888
* MIT license
@@ -92,6 +122,116 @@ static inline double gauss_zig_julia(aug_state* state)
92122
}
93123
}
94124

125+
126+
static inline double standard_gamma(aug_state* state, double shape)
127+
{
128+
double b, c;
129+
double U, V, X, Y;
130+
131+
if (shape == 1.0)
132+
{
133+
return standard_exponential(state);
134+
}
135+
else if (shape < 1.0)
136+
{
137+
for (;;)
138+
{
139+
U = random_double(state);
140+
V = standard_exponential(state);
141+
if (U <= 1.0 - shape)
142+
{
143+
X = pow(U, 1./shape);
144+
if (X <= V)
145+
{
146+
return X;
147+
}
148+
}
149+
else
150+
{
151+
Y = -log((1-U)/shape);
152+
X = pow(1.0 - shape + shape*Y, 1./shape);
153+
if (X <= (V + Y))
154+
{
155+
return X;
156+
}
157+
}
158+
}
159+
}
160+
else
161+
{
162+
b = shape - 1./3.;
163+
c = 1./sqrt(9*b);
164+
for (;;)
165+
{
166+
do
167+
{
168+
X = gauss(state);
169+
V = 1.0 + c*X;
170+
} while (V <= 0.0);
171+
172+
V = V*V*V;
173+
U = random_double(state);
174+
if (U < 1.0 - 0.0331*(X*X)*(X*X)) return (b*V);
175+
if (log(U) < 0.5*X*X + b*(1. - V + log(V))) return (b*V);
176+
}
177+
}
178+
}
179+
180+
static inline float standard_gamma_float(aug_state* state, float shape)
181+
{
182+
float b, c;
183+
float U, V, X, Y;
184+
185+
if (shape == 1.0f)
186+
{
187+
return standard_exponential_float(state);
188+
}
189+
else if (shape < 1.0f)
190+
{
191+
for (;;)
192+
{
193+
U = random_float(state);
194+
V = standard_exponential_float(state);
195+
if (U <= 1.0f - shape)
196+
{
197+
X = powf(U, 1.0f/shape);
198+
if (X <= V)
199+
{
200+
return X;
201+
}
202+
}
203+
else
204+
{
205+
Y = -logf((1.0f-U)/shape);
206+
X = powf(1.0f - shape + shape*Y, 1.0f/shape);
207+
if (X <= (V + Y))
208+
{
209+
return X;
210+
}
211+
}
212+
}
213+
}
214+
else
215+
{
216+
b = shape - 1.0f/3.0f;
217+
c = 1.0f / sqrtf(9.0f*b);
218+
for (;;)
219+
{
220+
do
221+
{
222+
X = gauss_float(state);
223+
V = 1.0f + c*X;
224+
} while (V <= 0.0f);
225+
226+
V = V*V*V;
227+
U = random_float(state);
228+
if (U < 1.0f - 0.0331f * (X*X)*(X*X)) return (b*V);
229+
if (logf(U) < 0.5f * X*X + b*(1.0f - V + logf(V))) return (b*V);
230+
}
231+
}
232+
}
233+
234+
95235
/*
96236
*
97237
* RNGs for use in other code
@@ -186,59 +326,17 @@ void random_gauss_fill(aug_state* state, npy_intp count, double *out) {
186326
}
187327
}
188328

329+
void random_gauss_fill_float(aug_state* state, npy_intp count, float *out) {
189330

190-
double random_standard_gamma(aug_state* state, double shape)
191-
{
192-
double b, c;
193-
double U, V, X, Y;
194-
195-
if (shape == 1.0)
196-
{
197-
return random_standard_exponential(state);
198-
}
199-
else if (shape < 1.0)
200-
{
201-
for (;;)
202-
{
203-
U = random_double(state);
204-
V = random_standard_exponential(state);
205-
if (U <= 1.0 - shape)
206-
{
207-
X = pow(U, 1./shape);
208-
if (X <= V)
209-
{
210-
return X;
211-
}
212-
}
213-
else
214-
{
215-
Y = -log((1-U)/shape);
216-
X = pow(1.0 - shape + shape*Y, 1./shape);
217-
if (X <= (V + Y))
218-
{
219-
return X;
220-
}
221-
}
222-
}
331+
npy_intp i;
332+
for (i = 0; i < count; i++) {
333+
out[i] = gauss_float(state);
223334
}
224-
else
225-
{
226-
b = shape - 1./3.;
227-
c = 1./sqrt(9*b);
228-
for (;;)
229-
{
230-
do
231-
{
232-
X = random_gauss(state);
233-
V = 1.0 + c*X;
234-
} while (V <= 0.0);
335+
}
235336

236-
V = V*V*V;
237-
U = random_double(state);
238-
if (U < 1.0 - 0.0331*(X*X)*(X*X)) return (b*V);
239-
if (log(U) < 0.5*X*X + b*(1. - V + log(V))) return (b*V);
240-
}
241-
}
337+
double random_standard_gamma(aug_state* state, double shape)
338+
{
339+
return standard_gamma(state, shape);
242340
}
243341

244342

@@ -311,7 +409,12 @@ double random_uniform(aug_state *state, double lower, double range)
311409

312410
double random_gamma(aug_state *state, double shape, double scale)
313411
{
314-
return scale * random_standard_gamma(state, shape);
412+
return scale * standard_gamma(state, shape);
413+
}
414+
415+
float random_gamma_float(aug_state *state, float shape, float scale)
416+
{
417+
return scale * standard_gamma_float(state, shape);
315418
}
316419

317420
double random_beta(aug_state *state, double a, double b)

randomstate/distributions.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ extern double random_uniform(aug_state *state, double loc, double scale);
6868

6969
extern double random_gamma(aug_state *state, double shape, double scale);
7070

71+
extern float random_gamma_float(aug_state *state, float shape, float scale);
72+
7173
extern double random_beta(aug_state *state, double a, double b);
7274

7375
extern double random_chisquare(aug_state *state, double df);
@@ -150,4 +152,6 @@ extern void random_standard_exponential_fill_float(aug_state* state, npy_intp co
150152

151153
extern void random_gauss_fill(aug_state* state, npy_intp count, double *out);
152154

155+
extern void random_gauss_fill_float(aug_state* state, npy_intp count, float *out);
156+
153157
extern void random_gauss_zig_julia_fill(aug_state *state, npy_intp count, double *out);

randomstate/interface/dSFMT/dSFMT-shim.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ typedef struct s_aug_state {
1515
dsfmt_t *rng;
1616
binomial_t *binomial;
1717

18-
int has_gauss, shift_zig_random_int, has_uint32;
18+
int has_gauss, has_gauss_float, shift_zig_random_int, has_uint32;
19+
float gauss_float;
1920
double gauss;
2021
uint32_t uinteger;
2122
uint64_t zig_random_int;

randomstate/interface/dSFMT/dSFMT.pxi

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ cdef extern from "distributions.h":
2828
dsfmt_t *rng
2929
binomial_t *binomial
3030

31-
int has_gauss, shift_zig_random_int, has_uint32
31+
int has_gauss, shift_zig_random_int, has_uint32, has_gauss_float
32+
float gauss_float
3233
double gauss
3334
uint64_t zig_random_int
3435
uint32_t uinteger

randomstate/interface/mlfg-1279-861/mlfg-1279-861-shim.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ typedef struct s_aug_state {
1414
mlfg_state *rng;
1515
binomial_t *binomial;
1616

17-
int has_gauss, shift_zig_random_int, has_uint32;
17+
int has_gauss, has_gauss_float, shift_zig_random_int, has_uint32;
18+
float gauss_float;
1819
double gauss;
1920
uint32_t uinteger;
2021
uint64_t zig_random_int;

randomstate/interface/mlfg-1279-861/mlfg-1279-861.pxi

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ cdef extern from "distributions.h":
1313
mlfg_state *rng
1414
binomial_t *binomial
1515

16-
int has_gauss, shift_zig_random_int, has_uint32
16+
int has_gauss, shift_zig_random_int, has_uint32, has_gauss_float
17+
float gauss_float
1718
double gauss
1819
uint64_t zig_random_int
1920
uint32_t uinteger

randomstate/interface/mrg32k3a/mrg32k3a-shim.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ typedef struct s_aug_state {
1515
mrg32k3a_state *rng;
1616
binomial_t *binomial;
1717

18-
int has_gauss, shift_zig_random_int, has_uint32;
18+
int has_gauss, has_gauss_float, shift_zig_random_int, has_uint32;
19+
float gauss_float;
1920
double gauss;
2021
uint64_t zig_random_int;
2122
uint32_t uinteger;

randomstate/interface/mrg32k3a/mrg32k3a.pxi

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ cdef extern from "distributions.h":
1414
mrg32k3a_state *rng
1515
binomial_t *binomial
1616

17-
int has_gauss, shift_zig_random_int, has_uint32
17+
int has_gauss, shift_zig_random_int, has_uint32, has_gauss_float
18+
float gauss_float
1819
double gauss
1920
uint32_t uinteger
2021
uint64_t zig_random_int

0 commit comments

Comments
 (0)