factory.js (9663B)
1 /** 2 * @license Apache-2.0 3 * 4 * Copyright (c) 2018 The Stdlib Authors. 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 19 'use strict'; 20 21 // MODULES // 22 23 var setReadOnly = require( '@stdlib/utils/define-nonenumerable-read-only-property' ); 24 var setReadOnlyAccessor = require( '@stdlib/utils/define-nonenumerable-read-only-accessor' ); 25 var setReadWriteAccessor = require( '@stdlib/utils/define-nonenumerable-read-write-accessor' ); 26 var isObject = require( '@stdlib/assert/is-plain-object' ); 27 var isBoolean = require( '@stdlib/assert/is-boolean' ).isPrimitive; 28 var isFunction = require( '@stdlib/assert/is-function' ); 29 var hasOwnProp = require( '@stdlib/assert/has-own-property' ); 30 var constantFunction = require( '@stdlib/utils/constant-function' ); 31 var noop = require( '@stdlib/utils/noop' ); 32 var randn = require( './../../../base/improved-ziggurat' ).factory; 33 var randu = require( './../../../base/mt19937' ).factory; 34 var isnan = require( '@stdlib/math/base/assert/is-nan' ); 35 var gcopy = require( '@stdlib/blas/base/gcopy' ); 36 var Uint32Array = require( '@stdlib/array/uint32' ); 37 var isUint32Array = require( '@stdlib/assert/is-uint32array' ); 38 var copy = require( '@stdlib/utils/copy' ); 39 var typedarray2json = require( '@stdlib/array/to-json' ); 40 var validate = require( './validate.js' ); 41 var beta0 = require( './beta.js' ); 42 43 44 // MAIN // 45 46 /** 47 * Returns a pseudorandom number generator for generating beta distributed random numbers. 48 * 49 * @param {PositiveNumber} [alpha] - first shape parameter 50 * @param {PositiveNumber} [beta] - second shape parameter 51 * @param {Options} [options] - function options 52 * @param {PRNG} [options.prng] - pseudorandom number generator which generates uniformly distributed pseudorandom numbers 53 * @param {PRNGSeedMT19937} [options.seed] - pseudorandom number generator seed 54 * @param {PRNGStateMT19937} [options.state] - pseudorandom number generator state 55 * @param {boolean} [options.copy=true] - boolean indicating whether to copy a provided pseudorandom number generator state 56 * @throws {TypeError} `alpha` must be a positive number 57 * @throws {TypeError} `beta` must be a positive number 58 * @throws {TypeError} options argument must be an object 59 * @throws {TypeError} must provide valid options 60 * @throws {Error} must provide a valid state 61 * @returns {PRNG} pseudorandom number generator 62 * 63 * @example 64 * var beta = factory( 2.0, 1.0 ); 65 * var v = beta(); 66 * // returns <number> 67 * 68 * @example 69 * var beta = factory( 2.0, 2.0, { 70 * 'seed': 297 71 * }); 72 * var v = beta(); 73 * // returns <number> 74 */ 75 function factory() { 76 var STATE; 77 var rnorm; 78 var alpha; 79 var beta; 80 var opts; 81 var rand; 82 var prng; 83 var FLG; 84 var err; 85 86 FLG = true; 87 if ( arguments.length === 0 ) { 88 opts = { 89 'copy': false 90 }; 91 rand = randu( opts ); 92 } else if ( arguments.length === 1 ) { 93 opts = arguments[ 0 ]; 94 if ( !isObject( opts ) ) { 95 throw new TypeError( 'invalid argument. Options argument must be an object. Value: `' + opts + '`.' ); 96 } 97 if ( hasOwnProp( opts, 'copy' ) && !isBoolean( opts.copy ) ) { 98 throw new TypeError( 'invalid option. `copy` option must be a boolean. Option: `' + opts.copy + '`.' ); 99 } 100 if ( hasOwnProp( opts, 'prng' ) ) { 101 if ( !isFunction( opts.prng ) ) { 102 throw new TypeError( 'invalid option. `prng` option must be a pseudorandom number generator function. Option: `' + opts.prng + '`.' ); 103 } 104 rand = opts.prng; 105 } else { 106 if ( hasOwnProp( opts, 'state' ) && !isUint32Array( opts.state ) ) { 107 throw new TypeError( 'invalid option. `state` option must be a Uint32Array. Option: `' + opts.state + '`.' ); 108 } 109 opts = copy( opts, 1 ); 110 if ( opts.copy === false ) { 111 FLG = false; 112 } else if ( opts.state ) { 113 opts.state = gcopy( opts.state.length, opts.state, 1, new Uint32Array( opts.state.length ), 1 ); // eslint-disable-line max-len 114 } 115 opts.copy = false; 116 rand = randu( opts ); 117 } 118 } else { 119 alpha = arguments[ 0 ]; 120 beta = arguments[ 1 ]; 121 err = validate( alpha, beta ); 122 if ( err ) { 123 throw err; 124 } 125 if ( arguments.length > 2 ) { 126 opts = arguments[ 2 ]; 127 if ( !isObject( opts ) ) { 128 throw new TypeError( 'invalid argument. Options argument must be an object. Value: `' + opts + '`.' ); 129 } 130 if ( hasOwnProp( opts, 'copy' ) && !isBoolean( opts.copy ) ) { 131 throw new TypeError( 'invalid option. `copy` option must be a boolean. Option: `' + opts.copy + '`.' ); 132 } 133 if ( hasOwnProp( opts, 'prng' ) ) { 134 if ( !isFunction( opts.prng ) ) { 135 throw new TypeError( 'invalid option. `prng` option must be a pseudorandom number generator function. Option: `' + opts.prng + '`.' ); 136 } 137 rand = opts.prng; 138 } else { 139 if ( hasOwnProp( opts, 'state' ) && !isUint32Array( opts.state ) ) { 140 throw new TypeError( 'invalid option. `state` option must be a Uint32Array. Option: `' + opts.state + '`.' ); 141 } 142 opts = copy( opts, 1 ); 143 if ( opts.copy === false ) { 144 FLG = false; 145 } else if ( opts.state ) { 146 opts.state = gcopy( opts.state.length, opts.state, 1, new Uint32Array( opts.state.length ), 1 ); // eslint-disable-line max-len 147 } 148 opts.copy = false; 149 rand = randu( opts ); 150 } 151 } else { 152 opts = { 153 'copy': false 154 }; 155 rand = randu( opts ); 156 } 157 } 158 if ( opts && opts.prng ) { 159 rnorm = randn({ 160 'prng': opts.prng 161 }); 162 } else { 163 if ( opts.state ) { 164 STATE = opts.state; 165 } else { 166 STATE = rand.state; 167 rand.state = STATE; // updates the underlying PRNG to point to a shared state 168 } 169 rnorm = randn({ 170 'state': STATE, 171 'copy': false 172 }); 173 } 174 if ( alpha === void 0 ) { 175 prng = beta2; 176 } else { 177 prng = beta1; 178 } 179 setReadOnly( prng, 'NAME', 'beta' ); 180 181 // 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. 182 if ( opts && opts.prng ) { 183 setReadOnly( prng, 'seed', null ); 184 setReadOnly( prng, 'seedLength', null ); 185 setReadWriteAccessor( prng, 'state', constantFunction( null ), noop ); 186 setReadOnly( prng, 'stateLength', null ); 187 setReadOnly( prng, 'byteLength', null ); 188 setReadOnly( prng, 'toJSON', constantFunction( null ) ); 189 setReadOnly( prng, 'PRNG', rand ); 190 } else { 191 setReadOnlyAccessor( prng, 'seed', getSeed ); 192 setReadOnlyAccessor( prng, 'seedLength', getSeedLength ); 193 setReadWriteAccessor( prng, 'state', getState, setState ); 194 setReadOnlyAccessor( prng, 'stateLength', getStateLength ); 195 setReadOnlyAccessor( prng, 'byteLength', getStateSize ); 196 setReadOnly( prng, 'toJSON', toJSON ); 197 setReadOnly( prng, 'PRNG', rand ); 198 rand = rand.normalized; 199 } 200 return prng; 201 202 /** 203 * Returns the PRNG seed. 204 * 205 * @private 206 * @returns {PRNGSeedMT19937} seed 207 */ 208 function getSeed() { 209 return rand.seed; 210 } 211 212 /** 213 * Returns the PRNG seed length. 214 * 215 * @private 216 * @returns {PositiveInteger} seed length 217 */ 218 function getSeedLength() { 219 return rand.seedLength; 220 } 221 222 /** 223 * Returns the PRNG state length. 224 * 225 * @private 226 * @returns {PositiveInteger} state length 227 */ 228 function getStateLength() { 229 return rand.stateLength; 230 } 231 232 /** 233 * Returns the PRNG state size (in bytes). 234 * 235 * @private 236 * @returns {PositiveInteger} state size (in bytes) 237 */ 238 function getStateSize() { 239 return rand.byteLength; 240 } 241 242 /** 243 * Returns the current pseudorandom number generator state. 244 * 245 * @private 246 * @returns {PRNGStateMT19937} current state 247 */ 248 function getState() { 249 return rand.state; 250 } 251 252 /** 253 * Sets the pseudorandom number generator state. 254 * 255 * @private 256 * @param {PRNGStateMT19937} s - generator state 257 * @throws {TypeError} must provide a `Uint32Array` 258 * @throws {Error} must provide a valid state 259 */ 260 function setState( s ) { 261 if ( !isUint32Array( s ) ) { 262 throw new TypeError( 'invalid argument. Must provide a Uint32Array. Value: `' + s + '`.' ); 263 } 264 if ( FLG ) { 265 s = gcopy( s.length, s, 1, new Uint32Array( s.length ), 1 ); 266 } 267 rand.state = s; 268 } 269 270 /** 271 * Serializes the pseudorandom number generator as a JSON object. 272 * 273 * ## Notes 274 * 275 * - `JSON.stringify()` implicitly calls this method when stringifying a PRNG. 276 * 277 * @private 278 * @returns {Object} JSON representation 279 */ 280 function toJSON() { 281 var out = {}; 282 out.type = 'PRNG'; 283 out.name = prng.NAME; 284 out.state = typedarray2json( rand.state ); 285 if ( alpha === void 0 ) { 286 out.params = []; 287 } else { 288 out.params = [ alpha, beta ]; 289 } 290 return out; 291 } 292 293 /** 294 * Returns a random number drawn from a beta distribution with bound parameter values. 295 * 296 * @private 297 * @returns {Probability} pseudorandom number 298 * 299 * @example 300 * var v = beta1(); 301 * // returns <number> 302 */ 303 function beta1() { 304 return beta0( rand, rnorm, alpha, beta ); 305 } 306 307 /** 308 * Returns a random number drawn from a beta distribution. 309 * 310 * @private 311 * @param {PositiveNumber} alpha - first shape parameter 312 * @param {PositiveNumber} beta - second shape parameter 313 * @returns {Probability} pseudorandom number 314 * 315 * @example 316 * var v = beta2( 2.0, 3.0 ); 317 * // returns <number> 318 */ 319 function beta2( alpha, beta ) { 320 if ( 321 isnan( alpha ) || 322 isnan( beta ) || 323 alpha <= 0.0 || 324 beta <= 0.0 325 ) { 326 return NaN; 327 } 328 return beta0( rand, rnorm, alpha, beta ); 329 } 330 } 331 332 333 // EXPORTS // 334 335 module.exports = factory;