random_variates 0.4.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []