erv 0.0.2 → 0.2.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.
- checksums.yaml +4 -4
- data/README.md +28 -56
- data/Rakefile +8 -52
- data/TODO +2 -0
- data/erv.gemspec +5 -12
- data/lib/erv/constant_distribution.rb +13 -2
- data/lib/erv/distribution.rb +8 -8
- data/lib/erv/exponential_distribution.rb +16 -18
- data/lib/erv/gaussian_distribution.rb +22 -18
- data/lib/erv/mixture_distribution.rb +81 -0
- data/lib/erv/random_variable.rb +4 -3
- data/lib/erv/uniform_distribution.rb +8 -16
- data/lib/erv/version.rb +1 -1
- data/lib/erv.rb +0 -8
- data/{spec/erv/constant_distribution_spec.rb → test/erv/constant_distribution_test.rb} +4 -6
- data/test/erv/distribution_test.rb +17 -0
- data/test/erv/mixture_distribution_test.rb +55 -0
- data/test/erv/random_variable_test.rb +30 -0
- data/test/test_helper.rb +6 -0
- metadata +51 -30
- data/lib/erv/rng.rb +0 -29
- data/spec/spec_helper.rb +0 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4df7f1fe74e08a93b0011dbce05db2862db6f742
|
4
|
+
data.tar.gz: bef0cb26a91b1353971c3d2f1de8cc071832b60b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 85bc816dcaf649c8410138ddcb3496d449c701e9f847d842139c59c8e17a5f0e97b857ca362845c7365138afe0b524902bbdfd1eb060aa4151b03246582b1298
|
7
|
+
data.tar.gz: 4308c0e5862acb574b6adb771998e95459f19dc929749716f55118ac068246f319be8bcd3a801ee36d6aaa2a812a6c94ed08f41efcb7568773624c4a0a1c95b5
|
data/README.md
CHANGED
@@ -10,31 +10,13 @@ variables with a given probability distribution (gaussian, uniform, etc.) and
|
|
10
10
|
to sample from them. ruby-erv was built from code that I extracted out of
|
11
11
|
several scientific software I wrote for my research projects.
|
12
12
|
|
13
|
-
ruby-erv is designed to work on both YARV/MRI and JRuby. I have not tested it
|
14
|
-
on Rubinius yet.
|
15
|
-
|
16
13
|
|
17
14
|
## Installation
|
18
15
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
```ruby
|
23
|
-
gem 'erv', git: 'https://github.com/mtortonesi/ruby-erv.git'
|
24
|
-
```
|
25
|
-
|
26
|
-
in your Gemfile and run:
|
27
|
-
|
28
|
-
bundle install
|
29
|
-
|
30
|
-
If using JRuby, you'll also need to run:
|
31
|
-
|
32
|
-
rake get_latest_commons_math_snapshot
|
16
|
+
You can get the stable version of ruby-erv by installing the erv gem from
|
17
|
+
RubyGems:
|
33
18
|
|
34
|
-
|
35
|
-
[implementation notes below](#implementation-notes) for more information. (Note
|
36
|
-
that the rake-based installation of Apache Commons Math 3.3-SNAPSHOT requires
|
37
|
-
[nokogiri](http://nokogiri.org/).)
|
19
|
+
gem install erv
|
38
20
|
|
39
21
|
|
40
22
|
## Examples
|
@@ -45,49 +27,39 @@ variables using ruby-erv, and how to sample from them:
|
|
45
27
|
```ruby
|
46
28
|
require 'erv'
|
47
29
|
|
30
|
+
# Gaussian random variable with mean 10 and standard deviation 2
|
48
31
|
gaussian_rv = ERV::RandomVariable.new(distribution: :gaussian,
|
49
32
|
mean: 10, sd: 2)
|
50
33
|
s1 = gaussian_rv.sample
|
34
|
+
|
35
|
+
# Geometric random variable with probability of success 0.3
|
36
|
+
geometric_rv = ERV::RandomVariable.new(distribution: :geometric,
|
37
|
+
probability_of_success: 0.3)
|
38
|
+
s2 = geometric_rv.sample
|
39
|
+
```
|
40
|
+
|
41
|
+
Starting from version 0.2.0, ruby-erv also supports mixture models. Here is a
|
42
|
+
simple example of how to create a random variable with an exponential mixture
|
43
|
+
distribution:
|
44
|
+
|
45
|
+
```ruby
|
46
|
+
require 'erv'
|
47
|
+
|
48
|
+
emd = ERV::MixtureDistribution.new([ { distribution: :exponential, rate: 1.0, weight: 100.0 },
|
49
|
+
{ distribution: :exponential, rate: 2.0, weight: 200.0 },
|
50
|
+
{ distribution: :exponential, rate: 3.0, weight: 300.0 } ])
|
51
|
+
s3 = emd.sample
|
51
52
|
```
|
52
53
|
|
53
54
|
|
54
55
|
## Implementation notes
|
55
56
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
In JRuby, ruby-erv leverages the [Apache Commons
|
64
|
-
Math](http://commons.apache.org/proper/commons-math/) library to access the
|
65
|
-
same random number generation functions. Unfortunately, at the time of this
|
66
|
-
writing the Apache Commons Math maintainers have not shipped the 3.3 release
|
67
|
-
yet. Commons Math 3.3 should include support for geometric distribution (that I
|
68
|
-
need for several of my projects), thanks to [a
|
69
|
-
patch](https://issues.apache.org/jira/browse/MATH-973) that I submitted and
|
70
|
-
that was merged some time ago. So, for the moment ruby-env builds on top of the
|
71
|
-
3.3-SNAPSHOT version of Apache Commons Math.
|
72
|
-
|
73
|
-
To facilitate the installation of ruby-env under JRuby, I have decided to
|
74
|
-
bundle the jar archive of Apache Commons Math 3.3-SNAPSHOT in (the jars
|
75
|
-
directory of) the ruby-env gem package. This is a rather dirty but not uncommon
|
76
|
-
approach, as many other gems (including the awesome
|
77
|
-
[Nokogiri](https://github.com/sparklemotion/nokogiri/tree/master/lib)) bundle
|
78
|
-
external jar dependencies in their JRuby version. However, in future I might
|
79
|
-
decide to switch to a more powerful, Maven-based automated installation of
|
80
|
-
Apache Commons Math 3.3-SNAPSHOT. For instance, based on Christian Meier's
|
81
|
-
[jar-dependencies](https://github.com/mkristian/jar-dependencies).
|
82
|
-
|
83
|
-
|
84
|
-
## Acknowledgment
|
85
|
-
|
86
|
-
I would like to thank [Christian Meier](https://github.com/mkristian) for his
|
87
|
-
very valuable suggestions on how to package this gem for JRuby. If you're
|
88
|
-
interested in building serious applications for JRuby, I strongly recommend you
|
89
|
-
to check out Christian's [jbundler](https://github.com/mkristian/jbundler) and
|
90
|
-
[jar-dependencies](https://github.com/mkristian/jar-dependencies).
|
57
|
+
Starting from version 0.2.0, ruby-erv makes use of the standard (Pseudo) Random
|
58
|
+
Number Generator provided by Ruby VMs, which is a variant of the
|
59
|
+
Mersenne-Twister algorithm modified to have a period of 2<sup>19937</sup>-1.
|
60
|
+
The randomness provided by Ruby's PRNG, whose adoption significantly improved
|
61
|
+
ruby-erv's portability to different Ruby VMs (MRI, JRuby, etc.), should be more
|
62
|
+
than enough for most purposes.
|
91
63
|
|
92
64
|
|
93
65
|
## License
|
data/Rakefile
CHANGED
@@ -1,55 +1,11 @@
|
|
1
|
-
|
1
|
+
require 'bundler/gem_tasks'
|
2
2
|
|
3
|
-
|
4
|
-
require 'bundler/gem_helper'
|
5
|
-
|
6
|
-
require 'nokogiri'
|
7
|
-
require 'open-uri'
|
8
|
-
|
9
|
-
# setup absolute path for jars directory
|
10
|
-
JAR_DIR = File.expand_path(File.join(File.dirname(__FILE__), 'jars'))
|
11
|
-
|
12
|
-
directory JAR_DIR
|
13
|
-
|
14
|
-
desc 'Get latest snapshot of Apache Commons Math 3.3'
|
15
|
-
task :get_latest_commons_math_snapshot => [ JAR_DIR ] do
|
16
|
-
# base URL of the apache commons math 3 snapshot repository
|
17
|
-
BASE_URL = 'http://repository.apache.org/content/groups/snapshots/org/apache/commons/commons-math3/3.3-SNAPSHOT/'
|
18
|
-
|
19
|
-
# retrieve timestamp and build number of latest snapshot
|
20
|
-
puts 'Retrieving metadata information on latest Apache Commons Math 3.3 snapshot.'
|
21
|
-
doc = Nokogiri::HTML(open(BASE_URL + 'maven-metadata.xml'))
|
22
|
-
timestamp = doc.xpath('//timestamp/text()')
|
23
|
-
buildnumber = doc.xpath('//buildnumber/text()')
|
24
|
-
|
25
|
-
# get archive name of latest snapshot
|
26
|
-
filename = "commons-math3-3.3-#{timestamp}-#{buildnumber}.jar"
|
27
|
-
|
28
|
-
# get destination file name
|
29
|
-
jar_path = File.join(JAR_DIR, filename)
|
30
|
-
|
31
|
-
# don't download if file already exists
|
32
|
-
if File.exists?(jar_path)
|
33
|
-
puts 'Archive already present in jars directory.'
|
34
|
-
else
|
35
|
-
puts 'Retrieving latest Apache Commons Math 3.3 snapshot.'
|
36
|
-
file_url = BASE_URL + filename
|
37
|
-
File.write(jar_path, open(file_url).read)
|
38
|
-
end
|
39
|
-
|
40
|
-
# clean JAR_DIR of every file except filename
|
41
|
-
puts 'Removing obsolete archives from jars directory.'
|
42
|
-
FileUtils.rm(Dir.glob("#{JAR_DIR}/*").reject!{|file| file == jar_path })
|
43
|
-
end
|
44
|
-
|
45
|
-
desc "Build erv-#{ERV::VERSION}-java.gem into the pkg directory."
|
46
|
-
task :build => [ :get_latest_commons_math_snapshot ] do
|
47
|
-
Bundler::GemHelper.instance = Bundler::GemHelper.new
|
48
|
-
Bundler::GemHelper.instance.build_gem
|
49
|
-
end
|
50
|
-
|
51
|
-
else # RUBY_PLATFORM !~ /java/
|
52
|
-
|
53
|
-
require 'bundler/gem_tasks'
|
3
|
+
require 'rake/testtask'
|
54
4
|
|
5
|
+
Rake::TestTask.new do |t|
|
6
|
+
t.libs << 'test'
|
7
|
+
t.test_files = Dir.glob('test/**/*_test.rb').sort
|
8
|
+
t.verbose = true
|
55
9
|
end
|
10
|
+
|
11
|
+
task :default => :test
|
data/erv.gemspec
CHANGED
@@ -18,16 +18,9 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ['lib']
|
20
20
|
|
21
|
-
spec.add_development_dependency 'bundler', '~> 1.
|
22
|
-
spec.add_development_dependency 'rake'
|
23
|
-
spec.add_development_dependency '
|
24
|
-
|
25
|
-
|
26
|
-
# we need to release a JRuby-specific gem
|
27
|
-
spec.platform = 'java'
|
28
|
-
# include Apache Commons Math 3.3 jar archive
|
29
|
-
spec.files.concat(Dir['jars/*.jar'])
|
30
|
-
else
|
31
|
-
spec.add_dependency 'gsl-nmatrix'
|
32
|
-
end
|
21
|
+
spec.add_development_dependency 'bundler', '~> 1.14'
|
22
|
+
spec.add_development_dependency 'rake', '~> 12.0'
|
23
|
+
spec.add_development_dependency 'minitest', '~> 5.10'
|
24
|
+
spec.add_development_dependency 'minitest-reporters', '~> 1.1'
|
25
|
+
spec.add_development_dependency 'minitest-spec-context', '~> 0.0.3'
|
33
26
|
end
|
@@ -7,8 +7,19 @@ module ERV
|
|
7
7
|
super(opts)
|
8
8
|
|
9
9
|
raise ArgumentError unless opts[:value]
|
10
|
-
val = opts[:value].to_f
|
11
|
-
|
10
|
+
@val = opts[:value].to_f
|
11
|
+
end
|
12
|
+
|
13
|
+
def mean
|
14
|
+
@val
|
15
|
+
end
|
16
|
+
|
17
|
+
def variance
|
18
|
+
0.0
|
19
|
+
end
|
20
|
+
|
21
|
+
def sample
|
22
|
+
@val
|
12
23
|
end
|
13
24
|
end
|
14
25
|
|
data/lib/erv/distribution.rb
CHANGED
@@ -1,15 +1,15 @@
|
|
1
|
-
require 'erv/rng'
|
2
|
-
|
3
1
|
module ERV
|
4
2
|
|
5
3
|
class Distribution
|
6
4
|
def initialize(opts)
|
7
|
-
#
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
5
|
+
# use provided RNG or create a new one
|
6
|
+
if opts[:rng]
|
7
|
+
@rng = opts[:rng]
|
8
|
+
elsif opts[:seed]
|
9
|
+
@rng = Random.new(opts[:seed])
|
10
|
+
else
|
11
|
+
@rng = Random.new
|
12
|
+
end
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
@@ -1,30 +1,28 @@
|
|
1
|
-
if RUBY_PLATFORM == 'java'
|
2
|
-
require 'java'
|
3
|
-
JExponentialDistribution = org.apache.commons.math3.distribution.ExponentialDistribution
|
4
|
-
end
|
5
|
-
|
6
1
|
require 'erv/distribution'
|
2
|
+
require 'erv/support/try'
|
7
3
|
|
8
4
|
|
9
5
|
module ERV
|
10
6
|
|
11
7
|
class ExponentialDistribution < Distribution
|
8
|
+
attr_reader :mean, :variance
|
9
|
+
|
12
10
|
def initialize(opts)
|
13
11
|
super(opts)
|
14
12
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
13
|
+
@rate = opts[:rate].try(:to_f)
|
14
|
+
raise ArgumentError unless @rate and @rate > 0.0
|
15
|
+
|
16
|
+
@mean = 1 / @rate
|
17
|
+
@variance = @mean ** 2
|
18
|
+
end
|
19
|
+
|
20
|
+
def sample
|
21
|
+
# starting from a random variable X ~ U(0,1), which is provided by the
|
22
|
+
# RNG, we can obtain a random variable Y ~ Exp(\lambda), with mean = 1 /
|
23
|
+
# \lambda, through the transformation: Y = - (1 / \lambda) ln X. see
|
24
|
+
# [GROESE11], section 4.2.3.
|
25
|
+
- @mean * Math.log(@rng.rand)
|
28
26
|
end
|
29
27
|
end
|
30
28
|
|
@@ -1,8 +1,3 @@
|
|
1
|
-
if RUBY_PLATFORM == 'java'
|
2
|
-
require 'java'
|
3
|
-
java_import org.apache.commons.math3.distribution.NormalDistribution
|
4
|
-
end
|
5
|
-
|
6
1
|
require 'erv/distribution'
|
7
2
|
|
8
3
|
|
@@ -13,19 +8,28 @@ module ERV
|
|
13
8
|
super(opts)
|
14
9
|
|
15
10
|
raise ArgumentError unless opts[:mean] and opts[:sd]
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
11
|
+
@mu = opts[:mean].to_f
|
12
|
+
@sigma = opts[:sd].to_f
|
13
|
+
end
|
14
|
+
|
15
|
+
def sample
|
16
|
+
# use box-muller algorithm (see [GROESE11], section 4.2.11, algorithm
|
17
|
+
# 4.47) to obtain x ~ N(0,1).
|
18
|
+
u_1 = @rng.rand
|
19
|
+
u_2 = @rng.rand
|
20
|
+
x = Math.sqrt(-2.0 * Math.log(u_1)) * Math.cos(2.0 * Math.PI * u_2)
|
21
|
+
|
22
|
+
# use location-scale transformation to obtain a N(\mu, \sigma^2)
|
23
|
+
# distribution. see [GROESE11], section 3.1.2.2.
|
24
|
+
@mu + @sigma * x
|
25
|
+
end
|
26
|
+
|
27
|
+
def mean
|
28
|
+
@mu
|
29
|
+
end
|
30
|
+
|
31
|
+
def variance
|
32
|
+
@sigma ** 2
|
29
33
|
end
|
30
34
|
end
|
31
35
|
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'erv/constant_distribution'
|
2
|
+
require 'erv/discrete_uniform_distribution'
|
3
|
+
require 'erv/exponential_distribution'
|
4
|
+
require 'erv/gamma_distribution'
|
5
|
+
require 'erv/gaussian_distribution'
|
6
|
+
require 'erv/general_pareto_distribution'
|
7
|
+
require 'erv/geometric_distribution'
|
8
|
+
require 'erv/uniform_distribution'
|
9
|
+
|
10
|
+
module ERV
|
11
|
+
|
12
|
+
class MixtureDistribution
|
13
|
+
def initialize(confs, opts={})
|
14
|
+
raise ArgumentError, "Please, provide at least 2 distributions!" unless confs.length >= 2
|
15
|
+
|
16
|
+
@mixture = []
|
17
|
+
@weight_sum = 0.0
|
18
|
+
while dist_conf = confs.shift
|
19
|
+
# get weight ...
|
20
|
+
weight = dist_conf.delete(:weight).to_f
|
21
|
+
|
22
|
+
# ... and keep track of it
|
23
|
+
@weight_sum += weight
|
24
|
+
|
25
|
+
# get distribution name
|
26
|
+
dist_name = dist_conf.delete(:distribution).to_s
|
27
|
+
|
28
|
+
# get class name that corresponds to the requested distribution
|
29
|
+
klass_name = dist_name.split('_').push('distribution').map(&:capitalize).join
|
30
|
+
|
31
|
+
# create distribution object
|
32
|
+
distribution = ERV.const_get(klass_name).new(dist_conf)
|
33
|
+
|
34
|
+
# add distribution to mixture
|
35
|
+
@mixture << { weight: weight, distribution: distribution }
|
36
|
+
end
|
37
|
+
|
38
|
+
seed = opts[:seed]
|
39
|
+
@mixture_sampler = (seed ? Random.new(seed) : Random.new)
|
40
|
+
end
|
41
|
+
|
42
|
+
def sample
|
43
|
+
x = @mixture_sampler.rand
|
44
|
+
|
45
|
+
# find index of distribution we are supposed to sample from
|
46
|
+
i = 0
|
47
|
+
while x > (@mixture[i][:weight] / @weight_sum)
|
48
|
+
x -= (@mixture[i][:weight] / @weight_sum)
|
49
|
+
i += 1
|
50
|
+
end
|
51
|
+
|
52
|
+
# return sample
|
53
|
+
@mixture[i][:distribution].sample
|
54
|
+
end
|
55
|
+
|
56
|
+
def mean
|
57
|
+
@mean ||= calculate_mean
|
58
|
+
end
|
59
|
+
|
60
|
+
def variance
|
61
|
+
@variance ||= calculate_variance
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def calculate_mean
|
67
|
+
@mixture.inject(0.0) do |s,x|
|
68
|
+
s += (x[:weight] / @weight_sum * x[:distribution].mean)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def calculate_variance
|
73
|
+
@mixture.inject(0.0) do |s,x|
|
74
|
+
s += (x[:weight] / @weight_sum *
|
75
|
+
((x[:distribution].mean - self.mean) ** 2 +
|
76
|
+
x[:distribution].variance))
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
data/lib/erv/random_variable.rb
CHANGED
@@ -18,6 +18,7 @@ module ERV
|
|
18
18
|
|
19
19
|
# get class name that corresponds to the requested distribution
|
20
20
|
klass_name = dist_name.split('_').push('distribution').map(&:capitalize).join
|
21
|
+
|
21
22
|
# create distribution object
|
22
23
|
@dist = ERV.const_get(klass_name).new(opts)
|
23
24
|
end
|
@@ -30,9 +31,9 @@ module ERV
|
|
30
31
|
|
31
32
|
class SequentialRandomVariable
|
32
33
|
def initialize(args)
|
33
|
-
|
34
|
-
@most_recent =
|
35
|
-
@var = RandomVariable.new(args)
|
34
|
+
first = args[:first_value]
|
35
|
+
@most_recent = first.nil? ? 0.0 : first
|
36
|
+
@var = RandomVariable.new(args.reject{|k,v| k == :first_value })
|
36
37
|
end
|
37
38
|
|
38
39
|
def next
|
@@ -1,8 +1,3 @@
|
|
1
|
-
if RUBY_PLATFORM == 'java'
|
2
|
-
require 'java'
|
3
|
-
java_import org.apache.commons.math3.distribution.UniformRealDistribution
|
4
|
-
end
|
5
|
-
|
6
1
|
require 'erv/distribution'
|
7
2
|
require 'erv/support/try'
|
8
3
|
|
@@ -15,18 +10,15 @@ module ERV
|
|
15
10
|
|
16
11
|
raise ArgumentError unless opts[:max_value]
|
17
12
|
max = opts[:max_value].to_f
|
18
|
-
min = opts[:min_value].try(:to_f) || 0.0
|
13
|
+
@min = opts[:min_value].try(:to_f) || 0.0
|
14
|
+
@range = max - @min
|
15
|
+
end
|
19
16
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
@func = Proc.new { d.sample }
|
26
|
-
else
|
27
|
-
# setup sampling function
|
28
|
-
@func = Proc.new { @rng.flat(min, max) }
|
29
|
-
end
|
17
|
+
def sample
|
18
|
+
# starting from a random variable X ~ U(0,1), which is provided by the
|
19
|
+
# RNG, we can obtain a random variable Y ~ U(a,b) through location-scale
|
20
|
+
# transformation: Y = a + (b-a) X. see [GROESE11], section 3.1.2.2.
|
21
|
+
@min + @range * @rng.rand
|
30
22
|
end
|
31
23
|
end
|
32
24
|
|
data/lib/erv/version.rb
CHANGED
data/lib/erv.rb
CHANGED
@@ -1,10 +1,2 @@
|
|
1
|
-
# Load Apache Commons Math 3.3 jar if we're using JRuby
|
2
|
-
if RUBY_PLATFORM =~ /java/
|
3
|
-
JARS_DIR = File.expand_path(File.join(File.dirname(__FILE__), '..', 'jars'))
|
4
|
-
Dir["#{JARS_DIR}/*.jar"].each do |jar|
|
5
|
-
$CLASSPATH << jar unless $CLASSPATH.include?(jar)
|
6
|
-
end
|
7
|
-
end
|
8
|
-
|
9
1
|
require 'erv/version'
|
10
2
|
require 'erv/random_variable'
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require '
|
1
|
+
require 'test_helper'
|
2
2
|
|
3
3
|
require 'erv/constant_distribution'
|
4
4
|
|
@@ -7,21 +7,19 @@ describe ERV::ConstantDistribution do
|
|
7
7
|
it 'should require a reference value' do
|
8
8
|
lambda do
|
9
9
|
ERV::ConstantDistribution.new
|
10
|
-
end.
|
10
|
+
end.must_raise ArgumentError
|
11
11
|
end
|
12
12
|
|
13
13
|
context 'reference value' do
|
14
14
|
|
15
15
|
it 'should be accepted at initialization time' do
|
16
|
-
|
17
|
-
ERV::ConstantDistribution.new(value: 10)
|
18
|
-
end.should_not raise_error
|
16
|
+
ERV::ConstantDistribution.new(value: 10)
|
19
17
|
end
|
20
18
|
|
21
19
|
it 'should match the value returned by sampling' do
|
22
20
|
val = rand(100)
|
23
21
|
crv = ERV::ConstantDistribution.new(value: val)
|
24
|
-
crv.sample.
|
22
|
+
crv.sample.must_equal val
|
25
23
|
end
|
26
24
|
|
27
25
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
require 'erv/distribution'
|
4
|
+
|
5
|
+
describe ERV::Distribution do
|
6
|
+
|
7
|
+
context 'when explicitly given an RNG' do
|
8
|
+
|
9
|
+
it 'should use the given RNG' do
|
10
|
+
rng = Random.new
|
11
|
+
d = ERV::Distribution.new(rng: rng)
|
12
|
+
d.instance_variable_get(:@rng).must_equal rng
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
require 'erv/mixture_distribution'
|
4
|
+
|
5
|
+
describe ERV::MixtureDistribution do
|
6
|
+
it 'should require at least two distributions' do
|
7
|
+
lambda do
|
8
|
+
ERV::MixtureDistribution.new([ { distribution: :exponential, rate: 1.0, weight: 0.5 } ])
|
9
|
+
end.must_raise ArgumentError
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'should keep track of distribution weights (for normalization)' do
|
13
|
+
# create a mixture distribution with unnormalized weights
|
14
|
+
uw_md = ERV::MixtureDistribution.new([ { distribution: :exponential, rate: 1.0, weight: 100.0 },
|
15
|
+
{ distribution: :exponential, rate: 2.0, weight: 200.0 },
|
16
|
+
{ distribution: :exponential, rate: 3.0, weight: 300.0 } ])
|
17
|
+
uw_md.instance_variable_get("@weight_sum").must_equal 600.0
|
18
|
+
end
|
19
|
+
|
20
|
+
let :md do
|
21
|
+
ERV::MixtureDistribution.new([ { distribution: :exponential, rate: 1.0, weight: 0.3 },
|
22
|
+
{ distribution: :exponential, rate: 2.0, weight: 0.2 },
|
23
|
+
{ distribution: :exponential, rate: 3.0, weight: 0.5 } ])
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'sampling' do
|
27
|
+
|
28
|
+
it 'should allow sampling from the mixture' do
|
29
|
+
md.sample
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'moments' do
|
35
|
+
|
36
|
+
let :expected_mean do
|
37
|
+
0.3 * 1/1.0 + 0.2 * 1/2.0 + 0.5 * 1/3.0
|
38
|
+
end
|
39
|
+
|
40
|
+
let :expected_variance do
|
41
|
+
0.3 * (1/1.0 - expected_mean) ** 2 + (1/1.0) ** 2 +
|
42
|
+
0.2 * (1/2.0 - expected_mean) ** 2 + (1/2.0) ** 2 +
|
43
|
+
0.5 * (1/3.0 - expected_mean) ** 2 + (1/3.0) ** 2
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'should correctly calculate the mean of the mixture' do
|
47
|
+
md.mean.must_equal expected_mean
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should correctly calculate the variance of the mixture' do
|
51
|
+
md.mean.must_equal expected_mean
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
require 'erv/random_variable'
|
4
|
+
|
5
|
+
describe ERV::SequentialRandomVariable do
|
6
|
+
|
7
|
+
it 'should require the :first_value parameter' do
|
8
|
+
lambda do
|
9
|
+
ERV::SequentialRandomVariable.new
|
10
|
+
end.must_raise ArgumentError
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'should consider starting value' do
|
14
|
+
first = 1.0
|
15
|
+
srv = ERV::SequentialRandomVariable.new(first_value: first, distribution: :exponential, rate: 2.0)
|
16
|
+
srv.next.must_be :>, first
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should consider previous sample' do
|
20
|
+
first = 1.0
|
21
|
+
srv = ERV::SequentialRandomVariable.new(first_value: first, distribution: :exponential, rate: 2.0)
|
22
|
+
previous_sample = srv.next
|
23
|
+
10.times do
|
24
|
+
new_sample = srv.next
|
25
|
+
new_sample.must_be :>, previous_sample
|
26
|
+
previous_sample = new_sample
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
CHANGED
@@ -1,72 +1,86 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: erv
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mauro Tortonesi
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-01-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - ~>
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '1.
|
19
|
+
version: '1.14'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - ~>
|
24
|
+
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '1.
|
26
|
+
version: '1.14'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rake
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- -
|
31
|
+
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '0'
|
33
|
+
version: '12.0'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- -
|
38
|
+
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '0'
|
40
|
+
version: '12.0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: minitest
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- -
|
45
|
+
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
47
|
+
version: '5.10'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- -
|
52
|
+
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
54
|
+
version: '5.10'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
56
|
+
name: minitest-reporters
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- -
|
59
|
+
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: '
|
62
|
-
type: :
|
61
|
+
version: '1.1'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.1'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: minitest-spec-context
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 0.0.3
|
76
|
+
type: :development
|
63
77
|
prerelease: false
|
64
78
|
version_requirements: !ruby/object:Gem::Requirement
|
65
79
|
requirements:
|
66
|
-
- -
|
80
|
+
- - "~>"
|
67
81
|
- !ruby/object:Gem::Version
|
68
|
-
version:
|
69
|
-
description: erv-0.0
|
82
|
+
version: 0.0.3
|
83
|
+
description: erv-0.2.0
|
70
84
|
email:
|
71
85
|
- mauro.tortonesi@unife.it
|
72
86
|
executables: []
|
@@ -77,6 +91,7 @@ files:
|
|
77
91
|
- LICENSE
|
78
92
|
- README.md
|
79
93
|
- Rakefile
|
94
|
+
- TODO
|
80
95
|
- erv.gemspec
|
81
96
|
- lib/erv.rb
|
82
97
|
- lib/erv/constant_distribution.rb
|
@@ -87,13 +102,16 @@ files:
|
|
87
102
|
- lib/erv/gaussian_distribution.rb
|
88
103
|
- lib/erv/general_pareto_distribution.rb
|
89
104
|
- lib/erv/geometric_distribution.rb
|
105
|
+
- lib/erv/mixture_distribution.rb
|
90
106
|
- lib/erv/random_variable.rb
|
91
|
-
- lib/erv/rng.rb
|
92
107
|
- lib/erv/support/try.rb
|
93
108
|
- lib/erv/uniform_distribution.rb
|
94
109
|
- lib/erv/version.rb
|
95
|
-
-
|
96
|
-
-
|
110
|
+
- test/erv/constant_distribution_test.rb
|
111
|
+
- test/erv/distribution_test.rb
|
112
|
+
- test/erv/mixture_distribution_test.rb
|
113
|
+
- test/erv/random_variable_test.rb
|
114
|
+
- test/test_helper.rb
|
97
115
|
homepage: https://github.com/mtortonesi/ruby-erv
|
98
116
|
licenses:
|
99
117
|
- MIT
|
@@ -104,20 +122,23 @@ require_paths:
|
|
104
122
|
- lib
|
105
123
|
required_ruby_version: !ruby/object:Gem::Requirement
|
106
124
|
requirements:
|
107
|
-
- -
|
125
|
+
- - ">="
|
108
126
|
- !ruby/object:Gem::Version
|
109
127
|
version: '0'
|
110
128
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
111
129
|
requirements:
|
112
|
-
- -
|
130
|
+
- - ">="
|
113
131
|
- !ruby/object:Gem::Version
|
114
132
|
version: '0'
|
115
133
|
requirements: []
|
116
134
|
rubyforge_project:
|
117
|
-
rubygems_version: 2.
|
135
|
+
rubygems_version: 2.6.8
|
118
136
|
signing_key:
|
119
137
|
specification_version: 4
|
120
138
|
summary: Easy/elegant Ruby library providing support for random variable generation
|
121
139
|
test_files:
|
122
|
-
-
|
123
|
-
-
|
140
|
+
- test/erv/constant_distribution_test.rb
|
141
|
+
- test/erv/distribution_test.rb
|
142
|
+
- test/erv/mixture_distribution_test.rb
|
143
|
+
- test/erv/random_variable_test.rb
|
144
|
+
- test/test_helper.rb
|
data/lib/erv/rng.rb
DELETED
@@ -1,29 +0,0 @@
|
|
1
|
-
if RUBY_PLATFORM == 'java'
|
2
|
-
require 'java'
|
3
|
-
java_import org.apache.commons.math3.random.MersenneTwister
|
4
|
-
else
|
5
|
-
require 'gsl'
|
6
|
-
end
|
7
|
-
|
8
|
-
|
9
|
-
module ERV
|
10
|
-
|
11
|
-
class RNG
|
12
|
-
def self.make_rng(seed = nil)
|
13
|
-
# if not explicitly provided, seed is taken from the (lower quality)
|
14
|
-
# pseudo-random Kernel::rand generator
|
15
|
-
if RUBY_PLATFORM == 'java'
|
16
|
-
# this is a somewhat ugly workaround in order to avoid ambibuities in
|
17
|
-
# the constructor method that will be called (we basically explicit the
|
18
|
-
# signature of the constructor that we want to use)
|
19
|
-
constructor = org.apache.commons.math3.random.MersenneTwister.java_class.constructor(Java::long)
|
20
|
-
rng = constructor.new_instance(seed || Kernel::rand(2**31 - 1))
|
21
|
-
else
|
22
|
-
rng = seed ?
|
23
|
-
GSL::Rng.alloc(GSL::Rng::MT19937, seed) :
|
24
|
-
GSL::Rng.alloc(GSL::Rng::MT19937, Kernel::rand(2**31 - 1))
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
end
|
data/spec/spec_helper.rb
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
# This file was generated by the `rspec --init` command. Conventionally, all
|
2
|
-
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
3
|
-
# Require this file using `require "spec_helper"` to ensure that it is only
|
4
|
-
# loaded once.
|
5
|
-
|
6
|
-
# Required to setup classpath for Apache Commons Math 3.3-SNAPSHOT in JRuby
|
7
|
-
require 'erv'
|
8
|
-
|
9
|
-
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
10
|
-
RSpec.configure do |config|
|
11
|
-
config.treat_symbols_as_metadata_keys_with_true_values = true
|
12
|
-
config.run_all_when_everything_filtered = true
|
13
|
-
config.filter_run :focus
|
14
|
-
|
15
|
-
# Run specs in random order to surface order dependencies. If you find an
|
16
|
-
# order dependency and want to debug it, you can fix the order by providing
|
17
|
-
# the seed, which is printed after each run.
|
18
|
-
# --seed 1234
|
19
|
-
config.order = 'random'
|
20
|
-
end
|