random_variates 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE.md +1 -1
- data/README.md +25 -11
- data/lib/random_variates.rb +46 -36
- metadata +28 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6ae5af59fb1609e227a42bc3fe9b93f1cb0d561513526044de0f74bc95fe9040
|
4
|
+
data.tar.gz: 5a18534afb749cf8b0d829b64f1f67daec0809ee0669073872dcf477a24f4d99
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a22d4223b928d7013deeb938fa497b231821d28939a75521b1e30f0bea1e20e95e8bb730c0a0dc8a2a0513b9174821abe36174a068dcc1cd81fafad941620af4
|
7
|
+
data.tar.gz: b4651ab9d4a17cb250a35a819658f92b6212c39f6cac9670ea3d069ab98e2335ea5b345e9ff138c7d642741a33441d0d29faa6c5eb41c545b65845bbf0257222
|
data/LICENSE.md
CHANGED
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
#### DESCRIPTION
|
2
2
|
|
3
3
|
This gem implements random variate generation for several
|
4
4
|
common statistical distributions. Each distribution is implemented
|
@@ -7,19 +7,33 @@ instances of the class using the constructor to specify the
|
|
7
7
|
parameterization. All constructors use named parameters for clarity,
|
8
8
|
so the order of parameters does not matter. All random variate classes
|
9
9
|
provide an optional argument `rng`, with which the user can specify a
|
10
|
-
|
11
|
-
|
10
|
+
pseudo-random number generator that has a `rand` method that provides
|
11
|
+
U(0,1) values to use as the core source of randomness.
|
12
|
+
If `rng` is not specified, it defaults to an instance of
|
13
|
+
class `Xoroshiro::Random`.
|
12
14
|
|
13
15
|
Once a random variate class has been instantiated, values can either be
|
14
|
-
generated on demand using the `next` method
|
15
|
-
|
16
|
+
generated on demand using the `next` method (preferred for speed) or by
|
17
|
+
creating an `Enumerator` for use in any iterable context. For example,
|
18
|
+
the following will create an array of fifty exponentially distributed
|
19
|
+
values having rate 3.
|
20
|
+
|
21
|
+
my_exp = RV::Exponential.new(rate: 3)
|
22
|
+
my_exp.each.take(50)
|
16
23
|
|
17
24
|
---
|
18
25
|
|
19
|
-
|
26
|
+
#### RELEASE NOTES
|
27
|
+
|
28
|
+
**v0.5**
|
29
|
+
|
30
|
+
Prior releases focused on statistical correctness and adding distributions. This
|
31
|
+
release is about speed improvements. Substantial speedups have been accomplished by:
|
20
32
|
|
21
|
-
|
22
|
-
-
|
23
|
-
|
24
|
-
-
|
25
|
-
|
33
|
+
- replacing the prior U(0,1) `Enumerator` architecture with direct calls to the PRNG;
|
34
|
+
- replacing `loop do` with the measurably faster `while true` in
|
35
|
+
acceptance/rejection based algorithms; and
|
36
|
+
- replacing ruby's built-in `Random` with `Xoroshiro::Random` as the default
|
37
|
+
PRNG. Xoshiro256** is faster than ruby's MT19937 implementation, and passes
|
38
|
+
all tests in the TestU01 suite. See [xoshiro / xoroshiro generators and the
|
39
|
+
PRNG shootout](https://prng.di.unimi.it) for more information.
|
data/lib/random_variates.rb
CHANGED
@@ -1,13 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'xoroshiro'
|
4
|
+
|
3
5
|
# This library implements random variate generation for several
|
4
6
|
# common statistical distributions. Each distribution is implemented
|
5
7
|
# in its own class, and different parameterizations are created as
|
6
8
|
# instances of the class using the constructor to specify the
|
7
9
|
# parameterization. All constructors use named parameters for clarity,
|
8
10
|
# so the order of parameters does not matter. All RV classes provide an
|
9
|
-
# optional argument +rng+, with which the user can specify
|
10
|
-
#
|
11
|
+
# optional argument +rng+, with which the user can specify a U(0,1)
|
12
|
+
# PRNG object to use as the core source of randomness. If +rng+ is
|
11
13
|
# not specified, it defaults to +Kernel#rand+.
|
12
14
|
#
|
13
15
|
# Once a random variate class has been instantiated, values can either be
|
@@ -15,8 +17,8 @@
|
|
15
17
|
# a generator in any iterable context.
|
16
18
|
|
17
19
|
module RV
|
18
|
-
#
|
19
|
-
U_GENERATOR =
|
20
|
+
# Set default PRNG.
|
21
|
+
U_GENERATOR = Xoroshiro::Random.new
|
20
22
|
|
21
23
|
# The +RV_Generator+ module provides a common core of methods to make
|
22
24
|
# all of the RV classes iterable.
|
@@ -26,7 +28,11 @@ module RV
|
|
26
28
|
end
|
27
29
|
|
28
30
|
def each
|
29
|
-
Enumerator.new
|
31
|
+
Enumerator.new do |y|
|
32
|
+
while true
|
33
|
+
y << self.next
|
34
|
+
end
|
35
|
+
end
|
30
36
|
end
|
31
37
|
end
|
32
38
|
|
@@ -40,18 +46,17 @@ module RV
|
|
40
46
|
class Uniform
|
41
47
|
include RV_Generator
|
42
48
|
|
43
|
-
attr_reader :min, :max
|
49
|
+
attr_reader :min, :max
|
44
50
|
|
45
51
|
def initialize(min: 0.0, max: 1.0, rng: U_GENERATOR)
|
46
|
-
raise 'Max must be greater than min.'
|
52
|
+
raise 'Max must be greater than min.' unless max > min
|
47
53
|
@min = min
|
48
54
|
@max = max
|
49
|
-
@range = max - min
|
50
55
|
@rng = rng
|
51
56
|
end
|
52
57
|
|
53
58
|
def next
|
54
|
-
@
|
59
|
+
@rng.rand(@min..@max)
|
55
60
|
end
|
56
61
|
end
|
57
62
|
|
@@ -110,7 +115,7 @@ module RV
|
|
110
115
|
end
|
111
116
|
|
112
117
|
def next
|
113
|
-
u = @rng.
|
118
|
+
u = @rng.rand
|
114
119
|
u < @crossover_p ?
|
115
120
|
@min + Math.sqrt(@range * (@mode - @min) * u) :
|
116
121
|
@max - Math.sqrt(@range * (@max - @mode) * (1.0 - u))
|
@@ -148,18 +153,18 @@ module RV
|
|
148
153
|
end
|
149
154
|
|
150
155
|
def next
|
151
|
-
-@mean * Math.log(@rng.
|
156
|
+
-@mean * Math.log(@rng.rand)
|
152
157
|
end
|
153
158
|
|
154
159
|
def rate=(rate)
|
155
|
-
raise 'Rate must be a number.' unless rate.is_a?
|
160
|
+
raise 'Rate must be a number.' unless rate.is_a? Numeric
|
156
161
|
raise 'Rate must be positive.' if rate <= 0
|
157
162
|
@mean = 1.0 / rate
|
158
163
|
@rate = rate
|
159
164
|
end
|
160
165
|
|
161
166
|
def mean=(mean)
|
162
|
-
raise 'Mean must be a number.' unless mean.is_a?
|
167
|
+
raise 'Mean must be a number.' unless mean.is_a? Numeric
|
163
168
|
raise 'Mean must be positive.' if mean <= 0
|
164
169
|
@mean = mean
|
165
170
|
@rate = 1.0 / mean
|
@@ -189,10 +194,10 @@ module RV
|
|
189
194
|
end
|
190
195
|
|
191
196
|
def next
|
192
|
-
|
193
|
-
u = @rng.
|
197
|
+
while true
|
198
|
+
u = @rng.rand
|
194
199
|
next if u == 0.0
|
195
|
-
v = BOUND * (@rng.
|
200
|
+
v = BOUND * (@rng.rand - 0.5)
|
196
201
|
x = v / u
|
197
202
|
x_sqr = x * x
|
198
203
|
u_sqr = u * u
|
@@ -207,12 +212,12 @@ module RV
|
|
207
212
|
end
|
208
213
|
|
209
214
|
def sigma=(sigma)
|
210
|
-
raise 'sigma must be a number.' unless sigma.is_a?
|
215
|
+
raise 'sigma must be a number.' unless sigma.is_a? Numeric
|
211
216
|
@sigma = sigma
|
212
217
|
end
|
213
218
|
|
214
219
|
def mu=(mu)
|
215
|
-
raise 'mu must be a number.' unless mu.is_a?
|
220
|
+
raise 'mu must be a number.' unless mu.is_a? Numeric
|
216
221
|
@mu = mu
|
217
222
|
end
|
218
223
|
end
|
@@ -238,13 +243,12 @@ module RV
|
|
238
243
|
@sigma = sigma
|
239
244
|
@rng = rng
|
240
245
|
@need_new_pair = false
|
241
|
-
# @next_norm = 0.0
|
242
246
|
end
|
243
247
|
|
244
248
|
def next
|
245
249
|
if @need_new_pair ^= true
|
246
|
-
theta = TWO_PI * @rng.
|
247
|
-
d = @sigma * Math.sqrt(-2.0 * Math.log(@rng.
|
250
|
+
theta = TWO_PI * @rng.rand
|
251
|
+
d = @sigma * Math.sqrt(-2.0 * Math.log(@rng.rand))
|
248
252
|
@next_norm = @mu + d * Math.sin(theta)
|
249
253
|
@mu + d * Math.cos(theta)
|
250
254
|
else
|
@@ -253,12 +257,12 @@ module RV
|
|
253
257
|
end
|
254
258
|
|
255
259
|
def sigma=(sigma)
|
256
|
-
raise 'sigma must be a number.' unless sigma.is_a?
|
260
|
+
raise 'sigma must be a number.' unless sigma.is_a? Numeric
|
257
261
|
@sigma = sigma
|
258
262
|
end
|
259
263
|
|
260
264
|
def mu=(mu)
|
261
|
-
raise 'mu must be a number.' unless mu.is_a?
|
265
|
+
raise 'mu must be a number.' unless mu.is_a? Numeric
|
262
266
|
@mu = mu
|
263
267
|
end
|
264
268
|
end
|
@@ -297,21 +301,21 @@ module RV
|
|
297
301
|
z = v = 0.0
|
298
302
|
d = alpha - 1.0 / 3.0
|
299
303
|
c = (1.0 / 3.0) / Math.sqrt(d)
|
300
|
-
|
301
|
-
|
304
|
+
while true
|
305
|
+
while true
|
302
306
|
z = @std_normal.next
|
303
307
|
v = 1.0 + c * z
|
304
308
|
break if v > 0
|
305
309
|
end
|
306
310
|
z2 = z * z
|
307
311
|
v = v * v * v
|
308
|
-
u = @rng.
|
312
|
+
u = @rng.rand
|
309
313
|
break if u < 1.0 - 0.0331 * z2 * z2
|
310
314
|
break if Math.log(u) < (0.5 * z2 + d * (1.0 - v + Math.log(v)))
|
311
315
|
end
|
312
316
|
d * v * beta
|
313
317
|
else
|
314
|
-
__gen__(alpha + 1.0, beta) * (@rng.
|
318
|
+
__gen__(alpha + 1.0, beta) * (@rng.rand**(1.0 / alpha))
|
315
319
|
end
|
316
320
|
end
|
317
321
|
end
|
@@ -338,7 +342,7 @@ module RV
|
|
338
342
|
end
|
339
343
|
|
340
344
|
def next
|
341
|
-
(-Math.log(@rng.
|
345
|
+
(-Math.log(@rng.rand))**@power / @rate
|
342
346
|
end
|
343
347
|
end
|
344
348
|
|
@@ -381,9 +385,9 @@ module RV
|
|
381
385
|
end
|
382
386
|
|
383
387
|
def next
|
384
|
-
|
385
|
-
r1 = @rng.
|
386
|
-
theta = @s * (2.0 * @rng.
|
388
|
+
while true
|
389
|
+
r1 = @rng.rand
|
390
|
+
theta = @s * (2.0 * @rng.rand - 1.0) / r1
|
387
391
|
next if theta.abs > Math::PI
|
388
392
|
return theta if (0.25 * @kappa * theta * theta < 1.0 - r1) ||
|
389
393
|
(0.5 * @kappa * (Math.cos(theta) - 1.0) >= Math.log(r1))
|
@@ -410,10 +414,17 @@ module RV
|
|
410
414
|
@rng = rng
|
411
415
|
end
|
412
416
|
|
417
|
+
def rate=(rate)
|
418
|
+
raise 'Rate must be a number.' unless rate.is_a? Numeric
|
419
|
+
raise 'Rate must be positive.' if rate <= 0
|
420
|
+
@rate = rate
|
421
|
+
@threshold = Math.exp(-rate)
|
422
|
+
end
|
423
|
+
|
413
424
|
def next
|
414
425
|
count = 0
|
415
426
|
product = 1.0
|
416
|
-
count += 1 until (product *= @rng.
|
427
|
+
count += 1 until (product *= @rng.rand) < @threshold
|
417
428
|
count
|
418
429
|
end
|
419
430
|
end
|
@@ -432,13 +443,12 @@ module RV
|
|
432
443
|
def initialize(p: 0.5, rng: U_GENERATOR)
|
433
444
|
raise 'Require 0 < p < 1.' if p <= 0 || p >= 1
|
434
445
|
|
435
|
-
@p = p
|
436
446
|
@log_q = Math.log(1 - p)
|
437
447
|
@rng = rng
|
438
448
|
end
|
439
449
|
|
440
450
|
def next
|
441
|
-
(Math.log(1.0 - @rng.
|
451
|
+
(Math.log(1.0 - @rng.rand) / @log_q).ceil
|
442
452
|
end
|
443
453
|
end
|
444
454
|
|
@@ -473,8 +483,8 @@ module RV
|
|
473
483
|
|
474
484
|
def next
|
475
485
|
result = sum = 0
|
476
|
-
|
477
|
-
sum += Math.log(@rng.
|
486
|
+
while true
|
487
|
+
sum += Math.log(@rng.rand) / (@n - result)
|
478
488
|
break if sum < @log_q
|
479
489
|
result += 1
|
480
490
|
end
|
metadata
CHANGED
@@ -1,16 +1,30 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: random_variates
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Paul J Sanchez
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
12
|
-
dependencies:
|
13
|
-
|
11
|
+
date: 2023-05-25 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: xoroshiro
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.2'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.2'
|
27
|
+
description: Random variate generators implemented with Enumerator.
|
14
28
|
email: pjs@alum.mit.edu
|
15
29
|
executables: []
|
16
30
|
extensions: []
|
@@ -22,8 +36,11 @@ files:
|
|
22
36
|
homepage: https://bitbucket.org/paul_j_sanchez/random_variates.git
|
23
37
|
licenses:
|
24
38
|
- MIT
|
25
|
-
metadata:
|
26
|
-
|
39
|
+
metadata:
|
40
|
+
rubygems_mfa_required: 'true'
|
41
|
+
homepage_uri: https://bitbucket.org/paul_j_sanchez/random_variates.git
|
42
|
+
documentation_uri: https://rubydoc.info/gems/random_variates
|
43
|
+
post_install_message:
|
27
44
|
rdoc_options: []
|
28
45
|
require_paths:
|
29
46
|
- lib
|
@@ -31,15 +48,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
31
48
|
requirements:
|
32
49
|
- - ">="
|
33
50
|
- !ruby/object:Gem::Version
|
34
|
-
version: '
|
51
|
+
version: '3.0'
|
35
52
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
36
53
|
requirements:
|
37
54
|
- - ">="
|
38
55
|
- !ruby/object:Gem::Version
|
39
56
|
version: '0'
|
40
57
|
requirements: []
|
41
|
-
rubygems_version: 3.
|
42
|
-
signing_key:
|
58
|
+
rubygems_version: 3.4.13
|
59
|
+
signing_key:
|
43
60
|
specification_version: 4
|
44
|
-
summary: Random variate generator classes.
|
61
|
+
summary: Random variate generator classes for popular distributions.
|
45
62
|
test_files: []
|