Skip to content

Commit 2d40d6b

Browse files
committed
Add support for specifying the underlying PRNG
1 parent 72b6e0e commit 2d40d6b

File tree

6 files changed

+398
-91
lines changed

6 files changed

+398
-91
lines changed

lib/node_modules/@stdlib/random/base/t/README.md

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,10 +86,24 @@ r = rand( 3.14 );
8686

8787
The function accepts the following `options`:
8888

89+
- **prng**: pseudorandom number generator for generating uniformly distributed pseudorandom numbers on the interval `[0,1)`. If provided, the function **ignores** both the `state` and `seed` options. In order to seed the returned pseudorandom number generator, one must seed the provided `prng` (assuming the provided `prng` is seedable).
8990
- **seed**: pseudorandom number generator seed.
9091
- **state**: a [`Uint32Array`][@stdlib/array/uint32] containing pseudorandom number generator state. If provided, the function ignores the `seed` option.
9192
- **copy**: `boolean` indicating whether to copy a provided pseudorandom number generator state. Setting this option to `false` allows sharing state between two or more pseudorandom number generators. Setting this option to `true` ensures that a returned generator has exclusive control over its internal state. Default: `true`.
9293

94+
To use a custom PRNG as the underlying source of uniformly distributed pseudorandom numbers, set the `prng` option.
95+
96+
```javascript
97+
var minstd = require( '@stdlib/random/base/minstd' );
98+
99+
var rand = rt.factory({
100+
'prng': minstd.normalized
101+
});
102+
103+
var r = rand( 3.0 );
104+
// returns <number>
105+
```
106+
93107
To seed a pseudorandom number generator, set the `seed` option.
94108

95109
```javascript
@@ -175,6 +189,19 @@ for ( i = 0; i < 100; i++ ) {
175189
}
176190
```
177191

192+
If provided a PRNG for uniformly distributed numbers, this value is `null`.
193+
194+
<!-- eslint-disable stdlib/no-builtin-math -->
195+
196+
```javascript
197+
var rand = rt.factory({
198+
'prng': Math.random
199+
});
200+
201+
var seed = rand.seed;
202+
// returns null
203+
```
204+
178205
#### t.seedLength
179206

180207
Length of generator seed.
@@ -184,6 +211,19 @@ var len = t.seedLength;
184211
// returns <number>
185212
```
186213

214+
If provided a PRNG for uniformly distributed numbers, this value is `null`.
215+
216+
<!-- eslint-disable stdlib/no-builtin-math -->
217+
218+
```javascript
219+
var rand = rt.factory({
220+
'prng': Math.random
221+
});
222+
223+
var len = rand.seedLength;
224+
// returns null
225+
```
226+
187227
#### t.state
188228

189229
Writable property for getting and setting the generator state.
@@ -220,6 +260,19 @@ r = t( 10.0 );
220260
// ...
221261
```
222262

263+
If provided a PRNG for uniformly distributed numbers, this value is `null`.
264+
265+
<!-- eslint-disable stdlib/no-builtin-math -->
266+
267+
```javascript
268+
var rand = rt.factory({
269+
'prng': Math.random
270+
});
271+
272+
var state = rand.state;
273+
// returns null
274+
```
275+
223276
#### t.stateLength
224277

225278
Length of generator state.
@@ -229,6 +282,19 @@ var len = t.stateLength;
229282
// returns <number>
230283
```
231284

285+
If provided a PRNG for uniformly distributed numbers, this value is `null`.
286+
287+
<!-- eslint-disable stdlib/no-builtin-math -->
288+
289+
```javascript
290+
var rand = rt.factory({
291+
'prng': Math.random
292+
});
293+
294+
var len = rand.stateLength;
295+
// returns null
296+
```
297+
232298
#### t.byteLength
233299

234300
Size (in bytes) of generator state.
@@ -238,6 +304,19 @@ var sz = t.byteLength;
238304
// returns <number>
239305
```
240306

307+
If provided a PRNG for uniformly distributed numbers, this value is `null`.
308+
309+
<!-- eslint-disable stdlib/no-builtin-math -->
310+
311+
```javascript
312+
var rand = rt.factory({
313+
'prng': Math.random
314+
});
315+
316+
var sz = rand.byteLength;
317+
// returns null
318+
```
319+
241320
#### t.toJSON()
242321

243322
Serializes the pseudorandom number generator as a JSON object.
@@ -247,6 +326,19 @@ var o = t.toJSON();
247326
// returns { 'type': 'PRNG', 'name': '...', 'state': {...}, 'params': [] }
248327
```
249328

329+
If provided a PRNG for uniformly distributed numbers, this method returns `null`.
330+
331+
<!-- eslint-disable stdlib/no-builtin-math -->
332+
333+
```javascript
334+
var rand = rt.factory({
335+
'prng': Math.random
336+
});
337+
338+
var o = rand.toJSON();
339+
// returns null
340+
```
341+
250342
</section>
251343

252344
<!-- /.usage -->

lib/node_modules/@stdlib/random/base/t/docs/repl.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,13 @@
3838
options: Object (optional)
3939
Options.
4040

41+
options.prng: Function (optional)
42+
Pseudorandom number generator (PRNG) for generating uniformly
43+
distributed pseudorandom numbers on the interval `[0,1)`. If provided,
44+
the `state` and `seed` options are ignored. In order to seed the
45+
returned pseudorandom number generator, one must seed the provided
46+
`prng` (assuming the provided `prng` is seedable).
47+
4148
options.seed: integer|ArrayLikeObject<integer> (optional)
4249
Pseudorandom number generator seed. The seed may be either a positive
4350
unsigned 32-bit integer or, for arbitrary length seeds, an array-like

lib/node_modules/@stdlib/random/base/t/lib/factory.js

Lines changed: 98 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,16 @@ var isObject = require( '@stdlib/assert/is-plain-object' );
2929
var isUint32Array = require( '@stdlib/assert/is-uint32array' );
3030
var isBoolean = require( '@stdlib/assert/is-boolean' ).isPrimitive;
3131
var isnan = require( '@stdlib/math/base/assert/is-nan' );
32+
var isFunction = require( '@stdlib/assert/is-function' );
33+
var constantFunction = require( '@stdlib/utils/constant-function' );
34+
var noop = require( '@stdlib/utils/noop' );
3235
var chisquare = require( '@stdlib/random/base/chisquare' ).factory;
3336
var randn = require( '@stdlib/random/base/improved-ziggurat' ).factory;
3437
var gcopy = require( '@stdlib/blas/base/gcopy' );
3538
var Uint32Array = require( '@stdlib/array/uint32' );
3639
var copy = require( '@stdlib/utils/copy' );
3740
var typedarray2json = require( '@stdlib/array/to-json' );
38-
var t0 = require( './t.js' );
41+
var sqrt = require( '@stdlib/math/base/special/sqrt' );
3942

4043

4144
// MAIN //
@@ -45,17 +48,19 @@ var t0 = require( './t.js' );
4548
*
4649
* @param {PositiveNumber} [v] - degrees of freedom
4750
* @param {Options} [options] - function options
48-
* @param {(uinteger32|Collection<uinteger32>)} [options.seed] - pseudorandom number generator seed
49-
* @param {Uint32Array} [options.state] - pseudorandom number generator state
51+
* @param {PRNG} [options.prng] - pseudorandom number generator which generates uniformly distributed pseudorandom numbers
52+
* @param {PRNGSeedMT19937} [options.seed] - pseudorandom number generator seed
53+
* @param {PRNGStateMT19937} [options.state] - pseudorandom number generator state
5054
* @param {boolean} [options.copy=true] - boolean indicating whether to copy a provided pseudorandom number generator state
5155
* @throws {TypeError} `v` must be a positive number
5256
* @throws {TypeError} options argument must be an object
5357
* @throws {TypeError} must provide valid options
5458
* @throws {Error} must provide a valid state
55-
* @returns {Function} pseudorandom number generator
59+
* @returns {PRNG} pseudorandom number generator
5660
*
5761
* @example
5862
* var t = factory( 1.0 );
63+
*
5964
* var v = t();
6065
* // returns <number>
6166
*
@@ -93,17 +98,26 @@ function factory() {
9398
if ( hasOwnProp( opts, 'copy' ) && !isBoolean( opts.copy ) ) {
9499
throw new TypeError( 'invalid option. `copy` option must be a boolean. Option: `' + opts.copy + '`.' );
95100
}
96-
if ( hasOwnProp( opts, 'state' ) && !isUint32Array( opts.state ) ) {
97-
throw new TypeError( 'invalid option. `state` option must be a Uint32Array. Option: `' + opts.state + '`.' );
101+
if ( hasOwnProp( opts, 'prng' ) ) {
102+
if ( !isFunction( opts.prng ) ) {
103+
throw new TypeError( 'invalid option. `prng` option must be a pseudorandom number generator function. Option: `' + opts.prng + '`.' );
104+
}
105+
rnorm = randn({
106+
'prng': opts.prng
107+
});
108+
} else {
109+
if ( hasOwnProp( opts, 'state' ) && !isUint32Array( opts.state ) ) {
110+
throw new TypeError( 'invalid option. `state` option must be a Uint32Array. Option: `' + opts.state + '`.' );
111+
}
112+
opts = copy( opts, 1 );
113+
if ( opts.copy === false ) {
114+
FLG = false;
115+
} else if ( opts.state ) {
116+
opts.state = gcopy( opts.state.length, opts.state, 1, new Uint32Array( opts.state.length ), 1 ); // eslint-disable-line max-len
117+
}
118+
opts.copy = false;
119+
rnorm = randn( opts );
98120
}
99-
opts = copy( opts, 1 );
100-
if ( opts.copy === false ) {
101-
FLG = false;
102-
} else if ( opts.state ) {
103-
opts.state = gcopy( opts.state.length, opts.state, 1, new Uint32Array( opts.state.length ), 1 ); // eslint-disable-line max-len
104-
}
105-
opts.copy = false;
106-
rnorm = randn( opts );
107121
} else {
108122
v = arguments[ 0 ];
109123
if ( !isPositive( v ) ) {
@@ -126,51 +140,89 @@ function factory() {
126140
if ( hasOwnProp( opts, 'copy' ) && !isBoolean( opts.copy ) ) {
127141
throw new TypeError( 'invalid option. `copy` option must be a boolean. Option: `' + opts.copy + '`.' );
128142
}
129-
if ( hasOwnProp( opts, 'state' ) && !isUint32Array( opts.state ) ) {
130-
throw new TypeError( 'invalid option. `state` option must be a Uint32Array. Option: `' + opts.state + '`.' );
131-
}
132-
opts = copy( opts, 1 );
133-
if ( opts.copy === false ) {
134-
FLG = false;
135-
} else if ( opts.state ) {
136-
opts.state = gcopy( opts.state.length, opts.state, 1, new Uint32Array( opts.state.length ), 1 ); // eslint-disable-line max-len
143+
if ( hasOwnProp( opts, 'prng' ) ) {
144+
if ( !isFunction( opts.prng ) ) {
145+
throw new TypeError( 'invalid option. `prng` option must be a pseudorandom number generator function. Option: `' + opts.prng + '`.' );
146+
}
147+
rnorm = randn({
148+
'prng': opts.prng
149+
});
150+
} else {
151+
if ( hasOwnProp( opts, 'state' ) && !isUint32Array( opts.state ) ) {
152+
throw new TypeError( 'invalid option. `state` option must be a Uint32Array. Option: `' + opts.state + '`.' );
153+
}
154+
opts = copy( opts, 1 );
155+
if ( opts.copy === false ) {
156+
FLG = false;
157+
} else if ( opts.state ) {
158+
opts.state = gcopy( opts.state.length, opts.state, 1, new Uint32Array( opts.state.length ), 1 ); // eslint-disable-line max-len
159+
}
160+
opts.copy = false;
161+
rnorm = randn( opts );
137162
}
138-
opts.copy = false;
139-
rnorm = randn( opts );
140163
}
141-
if ( opts.state ) {
142-
STATE = opts.state;
164+
if ( opts && opts.prng ) {
165+
if ( v === void 0 ) {
166+
rchisq = chisquare({
167+
'prng': opts.prng
168+
});
169+
} else {
170+
rchisq = chisquare( v, {
171+
'prng': opts.prng
172+
});
173+
}
143174
} else {
144-
STATE = rnorm.state;
145-
rnorm.state = STATE; // updates the underlying PRNG to point to a shared state
175+
if ( opts.state ) {
176+
STATE = opts.state;
177+
} else {
178+
STATE = rnorm.state;
179+
rnorm.state = STATE; // updates the underlying PRNG to point to a shared state
180+
}
181+
if ( v === void 0 ) {
182+
rchisq = chisquare({
183+
'state': STATE,
184+
'copy': false
185+
});
186+
} else {
187+
rchisq = chisquare( v, {
188+
'state': STATE,
189+
'copy': false
190+
});
191+
}
146192
}
147-
rchisq = chisquare({
148-
'state': STATE,
149-
'copy': false
150-
});
151-
152-
rand = rnorm.PRNG;
153193
if ( v === void 0 ) {
154194
prng = t2;
155195
} else {
156196
prng = t1;
157197
}
198+
rand = rnorm.PRNG;
199+
158200
setReadOnly( prng, 'NAME', 't' );
159-
setReadOnlyAccessor( prng, 'seed', getSeed );
160-
setReadOnlyAccessor( prng, 'seedLength', getSeedLength );
161-
setReadWriteAccessor( prng, 'state', getState, setState );
162-
setReadOnlyAccessor( prng, 'stateLength', getStateLength );
163-
setReadOnlyAccessor( prng, 'byteLength', getStateSize );
164-
setReadOnly( prng, 'toJSON', toJSON );
165-
setReadOnly( prng, 'PRNG', rand );
166201

202+
// If we are provided an "external" PRNG, we don't support getting or setting PRNG state, as we'd need to check for compatible state value types, etc, entailing considerable complexity.
203+
if ( opts && opts.prng ) {
204+
setReadOnly( prng, 'seed', null );
205+
setReadOnly( prng, 'seedLength', null );
206+
setReadWriteAccessor( prng, 'state', constantFunction( null ), noop );
207+
setReadOnly( prng, 'stateLength', null );
208+
setReadOnly( prng, 'byteLength', null );
209+
setReadOnly( prng, 'toJSON', constantFunction( null ) );
210+
} else {
211+
setReadOnlyAccessor( prng, 'seed', getSeed );
212+
setReadOnlyAccessor( prng, 'seedLength', getSeedLength );
213+
setReadWriteAccessor( prng, 'state', getState, setState );
214+
setReadOnlyAccessor( prng, 'stateLength', getStateLength );
215+
setReadOnlyAccessor( prng, 'byteLength', getStateSize );
216+
setReadOnly( prng, 'toJSON', toJSON );
217+
}
218+
setReadOnly( prng, 'PRNG', rand );
167219
return prng;
168220

169221
/**
170222
* Returns the PRNG seed.
171223
*
172224
* @private
173-
* @returns {Uint32Array} seed
225+
* @returns {PRNGSeedMT19937} seed
174226
*/
175227
function getSeed() {
176228
return rand.seed;
@@ -210,7 +262,7 @@ function factory() {
210262
* Returns the current pseudorandom number generator state.
211263
*
212264
* @private
213-
* @returns {Uint32Array} current state
265+
* @returns {PRNGStateMT19937} current state
214266
*/
215267
function getState() {
216268
return rand.state;
@@ -220,7 +272,7 @@ function factory() {
220272
* Sets the pseudorandom number generator state.
221273
*
222274
* @private
223-
* @param {Uint32Array} s - generator state
275+
* @param {PRNGStateMT19937} s - generator state
224276
* @throws {TypeError} must provide a `Uint32Array`
225277
* @throws {Error} must provide a valid state
226278
*/
@@ -268,7 +320,7 @@ function factory() {
268320
* // returns <number>
269321
*/
270322
function t1() {
271-
return t0( rnorm, rchisq, v );
323+
return rnorm() / sqrt( rchisq() / v );
272324
}
273325

274326
/**
@@ -297,7 +349,7 @@ function factory() {
297349
) {
298350
return NaN;
299351
}
300-
return t0( rnorm, rchisq, v );
352+
return rnorm() / sqrt( rchisq( v ) / v );
301353
}
302354
}
303355

lib/node_modules/@stdlib/random/base/t/lib/main.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ var factory = require( './factory.js' );
2929
* Returns a pseudorandom number drawn from a Student's t-distribution with degrees of freedom `v`.
3030
*
3131
* @name t
32-
* @type {Function}
32+
* @type {PRNG}
3333
* @param {PositiveNumber} v - degrees of freedom
3434
* @returns {number} pseudorandom number
3535
*

0 commit comments

Comments
 (0)