random_variates 0.4.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.md +1 -1
  3. data/README.md +24 -13
  4. data/lib/random_variates.rb +33 -30
  5. metadata +28 -11
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ed08b2544c35758dec93a8da948bd7c573d60d29e3051d09137d42de74ed3a2f
4
- data.tar.gz: e5cba37e231d3f408039e03e8e9490c9f9217f4c9c0c8187770aabb5895aee4d
3
+ metadata.gz: 6ae5af59fb1609e227a42bc3fe9b93f1cb0d561513526044de0f74bc95fe9040
4
+ data.tar.gz: 5a18534afb749cf8b0d829b64f1f67daec0809ee0669073872dcf477a24f4d99
5
5
  SHA512:
6
- metadata.gz: 115f11ec9c400c17b8886a87a3e533a9e8032e433756aa9567b95802a263f94c988e2ce8637a37c05bac7f4b5dcae3a965e0730acbc4b3ede1df1b4a2cfbd09a
7
- data.tar.gz: a977f035dd639ce390c019facd0b68cd883bf4cadce2db8c78ecf1d30911ded099f1f4c4a8926d6505bfd20ed542ec1523f84f8b027b776e9125293c309ed94d
6
+ metadata.gz: a22d4223b928d7013deeb938fa497b231821d28939a75521b1e30f0bea1e20e95e8bb730c0a0dc8a2a0513b9174821abe36174a068dcc1cd81fafad941620af4
7
+ data.tar.gz: b4651ab9d4a17cb250a35a819658f92b6212c39f6cac9670ea3d069ab98e2335ea5b345e9ff138c7d642741a33441d0d29faa6c5eb41c545b65845bbf0257222
data/LICENSE.md CHANGED
@@ -1,7 +1,7 @@
1
1
 
2
2
  The MIT License (MIT)
3
3
 
4
- Copyright (c) 2020 Paul J. Sanchez
4
+ Copyright (c) 2023 Paul J. Sanchez
5
5
 
6
6
  Permission is hereby granted, free of charge, to any person obtaining a copy
7
7
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- ### DESCRIPTION
1
+ #### DESCRIPTION
2
2
 
3
3
  This gem implements random variate generation for several
4
4
  common statistical distributions. Each distribution is implemented
@@ -7,22 +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
- U(0,1) generator as an `Enumerator` to use as the core source of randomness.
11
- If `rng` is not specified, it is based on Ruby's `Kernel#rand`.
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 or by using the instance as
15
- a generator in any iterable context. Since these are "infinite" generators, enumerative Ruby methods such as `.first` or `.take` will require a `.each` first to yield an iterator for lazy access. Example: `my_exp = RV::Exponential.new(rate: 3); my_exp.each.take(50)` will yield an array of fifty exponentially distributed values having rate 3.
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
- ### RELEASE NOTES
26
+ #### RELEASE NOTES
27
+
28
+ **v0.5**
20
29
 
21
- **v0.4.1**
22
- - Added ability to set rate to new value for `Poisson`.
30
+ Prior releases focused on statistical correctness and adding distributions. This
31
+ release is about speed improvements. Substantial speedups have been accomplished by:
23
32
 
24
- **v0.4**
25
- - All distribution classes have been placed in a module called `RV` to try to avoid namespace conflicts.
26
- - The `Gaussian` class has been renamed, it is now the `Normal` class.
27
- - Parameters for `Gaussian` and `BoxMuller` have been renamed. They are now `mu:` and `sigma:`.
28
- - `Exponential`, `Normal`, and `BoxMuller` distribution parameters can now be set to new values without having to instantiate an entirely new generator object.
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.
@@ -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 an enumerable
10
- # U(0,1) generator to use as the core source of randomness. If +rng+ is
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
- # Provide access to +Kernel#rand+ as an +Enumerator+.
19
- U_GENERATOR = Enumerator.new { |yielder| loop { yielder << rand } }
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 { |y| loop { y << self.next } }
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, :range
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.' if max <= 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
- @min + @range * @rng.next
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.next
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,7 +153,7 @@ module RV
148
153
  end
149
154
 
150
155
  def next
151
- -@mean * Math.log(@rng.next)
156
+ -@mean * Math.log(@rng.rand)
152
157
  end
153
158
 
154
159
  def rate=(rate)
@@ -189,10 +194,10 @@ module RV
189
194
  end
190
195
 
191
196
  def next
192
- loop do
193
- u = @rng.next
197
+ while true
198
+ u = @rng.rand
194
199
  next if u == 0.0
195
- v = BOUND * (@rng.next - 0.5)
200
+ v = BOUND * (@rng.rand - 0.5)
196
201
  x = v / u
197
202
  x_sqr = x * x
198
203
  u_sqr = u * u
@@ -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.next
247
- d = @sigma * Math.sqrt(-2.0 * Math.log(@rng.next))
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
@@ -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
- loop do
301
- loop do
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.next
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.next**(1.0 / alpha))
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.next))**@power / @rate
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
- loop do
385
- r1 = @rng.next
386
- theta = @s * (2.0 * @rng.next - 1.0) / r1
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))
@@ -420,7 +424,7 @@ module RV
420
424
  def next
421
425
  count = 0
422
426
  product = 1.0
423
- count += 1 until (product *= @rng.next) < @threshold
427
+ count += 1 until (product *= @rng.rand) < @threshold
424
428
  count
425
429
  end
426
430
  end
@@ -439,13 +443,12 @@ module RV
439
443
  def initialize(p: 0.5, rng: U_GENERATOR)
440
444
  raise 'Require 0 < p < 1.' if p <= 0 || p >= 1
441
445
 
442
- @p = p
443
446
  @log_q = Math.log(1 - p)
444
447
  @rng = rng
445
448
  end
446
449
 
447
450
  def next
448
- (Math.log(1.0 - @rng.next) / @log_q).ceil
451
+ (Math.log(1.0 - @rng.rand) / @log_q).ceil
449
452
  end
450
453
  end
451
454
 
@@ -480,8 +483,8 @@ module RV
480
483
 
481
484
  def next
482
485
  result = sum = 0
483
- loop do
484
- sum += Math.log(@rng.next) / (@n - result)
486
+ while true
487
+ sum += Math.log(@rng.rand) / (@n - result)
485
488
  break if sum < @log_q
486
489
  result += 1
487
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.1
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: 2020-05-13 00:00:00.000000000 Z
12
- dependencies: []
13
- description: Random variate generators implemented as enumerators.
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
- post_install_message:
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: '2.5'
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.1.2
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: []