distribution 0.6.0 → 0.8.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 (117) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +7 -0
  3. data/.travis.yml +13 -0
  4. data/.yardopts +5 -0
  5. data/Gemfile +5 -0
  6. data/History.txt +24 -8
  7. data/LICENCE.md +26 -0
  8. data/README.md +155 -0
  9. data/Rakefile +15 -19
  10. data/benchmark/binomial_coefficient.rb +19 -23
  11. data/benchmark/binomial_coefficient/experiment.rb +33 -36
  12. data/benchmark/factorial_hash.rb +7 -8
  13. data/benchmark/factorial_method.rb +4 -6
  14. data/benchmark/odd.rb +6 -7
  15. data/benchmark/power.rb +11 -11
  16. data/bin/distribution +26 -26
  17. data/data/template/spec.erb +7 -6
  18. data/distribution.gemspec +25 -0
  19. data/lib/distribution.rb +79 -124
  20. data/lib/distribution/beta.rb +6 -8
  21. data/lib/distribution/beta/gsl.rb +14 -9
  22. data/lib/distribution/beta/java.rb +1 -1
  23. data/lib/distribution/beta/ruby.rb +41 -7
  24. data/lib/distribution/binomial.rb +10 -11
  25. data/lib/distribution/binomial/gsl.rb +6 -5
  26. data/lib/distribution/binomial/java.rb +1 -1
  27. data/lib/distribution/binomial/ruby.rb +22 -15
  28. data/lib/distribution/bivariatenormal.rb +4 -5
  29. data/lib/distribution/bivariatenormal/gsl.rb +2 -2
  30. data/lib/distribution/bivariatenormal/java.rb +1 -1
  31. data/lib/distribution/bivariatenormal/ruby.rb +245 -254
  32. data/lib/distribution/chisquare.rb +8 -10
  33. data/lib/distribution/chisquare/gsl.rb +24 -19
  34. data/lib/distribution/chisquare/java.rb +1 -1
  35. data/lib/distribution/chisquare/ruby.rb +60 -55
  36. data/lib/distribution/chisquare/statistics2.rb +16 -13
  37. data/lib/distribution/distributable.rb +40 -0
  38. data/lib/distribution/exponential.rb +4 -5
  39. data/lib/distribution/exponential/gsl.rb +13 -9
  40. data/lib/distribution/exponential/ruby.rb +17 -11
  41. data/lib/distribution/f.rb +10 -11
  42. data/lib/distribution/f/gsl.rb +26 -22
  43. data/lib/distribution/f/java.rb +1 -1
  44. data/lib/distribution/f/ruby.rb +104 -105
  45. data/lib/distribution/f/statistics2.rb +22 -19
  46. data/lib/distribution/gamma.rb +5 -7
  47. data/lib/distribution/gamma/gsl.rb +13 -9
  48. data/lib/distribution/gamma/java.rb +1 -1
  49. data/lib/distribution/gamma/ruby.rb +5 -11
  50. data/lib/distribution/hypergeometric.rb +5 -8
  51. data/lib/distribution/hypergeometric/gsl.rb +5 -6
  52. data/lib/distribution/hypergeometric/java.rb +1 -1
  53. data/lib/distribution/hypergeometric/ruby.rb +34 -35
  54. data/lib/distribution/logistic.rb +6 -9
  55. data/lib/distribution/logistic/ruby.rb +14 -9
  56. data/lib/distribution/lognormal.rb +37 -0
  57. data/lib/distribution/lognormal/gsl.rb +21 -0
  58. data/lib/distribution/lognormal/ruby.rb +16 -0
  59. data/lib/distribution/math_extension.rb +187 -231
  60. data/lib/distribution/math_extension/chebyshev_series.rb +281 -272
  61. data/lib/distribution/math_extension/erfc.rb +28 -31
  62. data/lib/distribution/math_extension/exponential_integral.rb +17 -17
  63. data/lib/distribution/math_extension/gammastar.rb +19 -20
  64. data/lib/distribution/math_extension/gsl_utilities.rb +12 -12
  65. data/lib/distribution/math_extension/incomplete_beta.rb +52 -61
  66. data/lib/distribution/math_extension/incomplete_gamma.rb +166 -168
  67. data/lib/distribution/math_extension/log_utilities.rb +20 -22
  68. data/lib/distribution/normal.rb +11 -13
  69. data/lib/distribution/normal/gsl.rb +13 -10
  70. data/lib/distribution/normal/java.rb +30 -1
  71. data/lib/distribution/normal/ruby.rb +69 -59
  72. data/lib/distribution/normal/statistics2.rb +5 -2
  73. data/lib/distribution/normalmultivariate.rb +64 -64
  74. data/lib/distribution/poisson.rb +12 -14
  75. data/lib/distribution/poisson/gsl.rb +7 -7
  76. data/lib/distribution/poisson/java.rb +26 -0
  77. data/lib/distribution/poisson/ruby.rb +38 -9
  78. data/lib/distribution/shorthand.rb +17 -0
  79. data/lib/distribution/t.rb +16 -16
  80. data/lib/distribution/t/gsl.rb +27 -24
  81. data/lib/distribution/t/java.rb +1 -1
  82. data/lib/distribution/t/ruby.rb +99 -100
  83. data/lib/distribution/t/statistics2.rb +19 -19
  84. data/lib/distribution/uniform.rb +26 -0
  85. data/lib/distribution/uniform/gsl.rb +36 -0
  86. data/lib/distribution/uniform/ruby.rb +91 -0
  87. data/lib/distribution/version.rb +3 -0
  88. data/lib/distribution/weibull.rb +10 -0
  89. data/lib/distribution/weibull/gsl.rb +21 -0
  90. data/lib/distribution/weibull/ruby.rb +34 -0
  91. data/spec/beta_spec.rb +48 -50
  92. data/spec/binomial_spec.rb +80 -84
  93. data/spec/bivariatenormal_spec.rb +28 -35
  94. data/spec/chisquare_spec.rb +49 -52
  95. data/spec/distribution_spec.rb +11 -11
  96. data/spec/exponential_spec.rb +48 -39
  97. data/spec/f_spec.rb +73 -71
  98. data/spec/gamma_spec.rb +50 -53
  99. data/spec/hypergeometric_spec.rb +63 -69
  100. data/spec/logistic_spec.rb +31 -37
  101. data/spec/lognormal_spec.rb +54 -0
  102. data/spec/math_extension_spec.rb +192 -209
  103. data/spec/normal_spec.rb +80 -73
  104. data/spec/poisson_spec.rb +78 -36
  105. data/spec/shorthand_spec.rb +19 -22
  106. data/spec/spec_helper.rb +31 -6
  107. data/spec/t_spec.rb +63 -77
  108. data/spec/uniform_spec.rb +154 -0
  109. data/spec/weibull_spec.rb +17 -0
  110. data/vendor/java/commons-math-2.2.jar +0 -0
  111. metadata +91 -111
  112. data.tar.gz.sig +0 -0
  113. data/.autotest +0 -23
  114. data/.gemtest +0 -0
  115. data/Manifest.txt +0 -95
  116. data/README.txt +0 -100
  117. metadata.gz.sig +0 -0
@@ -5,22 +5,21 @@ require 'distribution/beta/java'
5
5
 
6
6
  module Distribution
7
7
  # From Wikipedia:
8
- # In probability theory and statistics, the beta distribution
9
- # is a family of continuous probability distributions defined
10
- # on the interval (0, 1) parameterized by two positive shape
11
- # parameters, typically denoted by alpha and beta.
8
+ # In probability theory and statistics, the beta distribution
9
+ # is a family of continuous probability distributions defined
10
+ # on the interval (0, 1) parameterized by two positive shape
11
+ # parameters, typically denoted by alpha and beta.
12
12
  # This module calculate cdf and inverse cdf for Beta Distribution.
13
13
  #
14
14
  module Beta
15
15
  extend Distributable
16
- SHORTHAND='beta'
16
+ SHORTHAND = 'beta'
17
17
  create_distribution_methods
18
18
 
19
19
  ##
20
20
  # :singleton-method: pdf(x,a,b)
21
21
  # Returns PDF of of Beta distribution with parameters a and b
22
22
 
23
-
24
23
  ##
25
24
  # :singleton-method: cdf(x,a,b)
26
25
  # Returns the integral of Beta distribution with parameters a and b
@@ -28,7 +27,6 @@ module Distribution
28
27
  ##
29
28
  # :singleton-method: p_value(qn,a,b)
30
29
  # Return the quantile of the corresponding integral +qn+
31
- # on a beta distribution's cdf with parameters a and b
32
-
30
+ # on a beta distribution's cdf with parameters a and b
33
31
  end
34
32
  end
@@ -2,22 +2,27 @@ module Distribution
2
2
  module Beta
3
3
  module GSL_
4
4
  class << self
5
- def pdf(x,a,b)
6
- GSL::Ran::beta_pdf(x.to_f, a.to_f, b.to_f)
7
- end
8
- # Return the P-value of the corresponding integral with
9
- # k degrees of freedom
10
- def p_value(pr,a,b)
11
- GSL::Cdf::beta_Pinv(pr.to_f, a.to_f, b.to_f)
5
+ # PDF.
6
+ def pdf(x, a, b)
7
+ GSL::Ran.beta_pdf(x.to_f, a.to_f, b.to_f)
12
8
  end
9
+
13
10
  # Beta cumulative distribution function (cdf).
14
11
  #
15
12
  # Returns the integral of Beta distribution
16
13
  # with parameters +a+ and +b+ over [0, x]
17
14
  #
18
- def cdf(x,a,b)
19
- GSL::Cdf::beta_P(x.to_f, a.to_f, b.to_f)
15
+ def cdf(x, a, b)
16
+ GSL::Cdf.beta_P(x.to_f, a.to_f, b.to_f)
17
+ end
18
+
19
+ # Return the P-value of the corresponding integral with
20
+ # k degrees of freedom
21
+ def quantile(pr, a, b)
22
+ GSL::Cdf.beta_Pinv(pr.to_f, a.to_f, b.to_f)
20
23
  end
24
+
25
+ alias_method :p_value, :quantile
21
26
  end
22
27
  end
23
28
  end
@@ -6,4 +6,4 @@ module Distribution
6
6
  end
7
7
  end
8
8
  end
9
- end
9
+ end
@@ -3,7 +3,6 @@ module Distribution
3
3
  module Beta
4
4
  module Ruby_
5
5
  class << self
6
-
7
6
  include Math
8
7
  # Beta distribution probability density function
9
8
  #
@@ -13,29 +12,64 @@ module Distribution
13
12
  #
14
13
  # == References
15
14
  # * http://www.gnu.org/s/gsl/manual/html_node/The-Gamma-Distribution.html
16
- def pdf(x,a,b)
15
+ def pdf(x, a, b)
17
16
  return 0 if x < 0 || x > 1
18
17
 
19
- gab = Math.lgamma(a+b).first
18
+ gab = Math.lgamma(a + b).first
20
19
  ga = Math.lgamma(a).first
21
20
  gb = Math.lgamma(b).first
22
21
 
23
22
  if x == 0.0 || x == 1.0
24
- Math.exp(gab - ga - gb) * x**(a-1) * (1-x)**(b-1)
23
+ Math.exp(gab - ga - gb) * x**(a - 1) * (1 - x)**(b - 1)
25
24
  else
26
- Math.exp(gab - ga - gb + Math.log(x)*(a-1) + Math::Log.log1p(-x)*(b-1))
25
+ Math.exp(gab - ga - gb + Math.log(x) * (a - 1) + Math::Log.log1p(-x) * (b - 1))
27
26
  end
28
27
  end
29
28
 
30
29
  # Gamma cumulative distribution function
31
30
  # Translated from GSL-1.9: cdf/beta.c gsl_cdf_beta_P
32
- def cdf(x,a,b)
31
+ def cdf(x, a, b)
33
32
  return 0.0 if x <= 0.0
34
33
  return 1.0 if x >= 1.0
35
- Math::IncompleteBeta.axpy(1.0, 0.0, a,b,x)
34
+ Math::IncompleteBeta.axpy(1.0, 0.0, a, b, x)
36
35
  end
37
36
 
37
+ # Inverse of the beta distribution function
38
+ def quantile(p, a, b, rmin = 0, rmax = 1)
39
+ fail 'a <= 0' if a <= 0
40
+ fail 'b <= 0' if b <= 0
41
+ fail 'rmin == rmax' if rmin == rmax
42
+ fail 'p <= 0' if p <= 0
43
+ fail 'p > 1' if p > 1
44
+
45
+ precision = 8.88e-016
46
+ max_iterations = 256
47
+
48
+ ga = 0
49
+ gb = 2
50
+
51
+ i = 1
52
+ while ((gb - ga) > precision) && (i < max_iterations)
53
+ guess = (ga + gb) / 2.0
54
+ result = cdf(guess, a, b)
55
+
56
+ if (result == p) || (result == 0)
57
+ gb = ga
58
+ elsif result > p
59
+ gb = guess
60
+ else
61
+ ga = guess
62
+ end
63
+
64
+ fail 'No value' if i == max_iterations
65
+
66
+ i += 1
67
+ end
68
+
69
+ rmin + guess * (rmax - rmin)
70
+ end
38
71
 
72
+ alias_method :p_value, :quantile
39
73
  end
40
74
  end
41
75
  end
@@ -2,27 +2,26 @@ require 'distribution/binomial/ruby'
2
2
  require 'distribution/binomial/gsl'
3
3
  require 'distribution/binomial/java'
4
4
  module Distribution
5
-
6
5
  # Calculate statisticals for Binomial Distribution.
7
6
  module Binomial
8
- SHORTHAND = 'bino'
9
-
7
+ SHORTHAND = 'bino'
8
+
10
9
  extend Distributable
11
10
  create_distribution_methods
12
-
11
+
13
12
  ##
14
13
  # :singleton-method: pdf(x,k)
15
- # Returns the integral of T distribution
16
- # with +k+ degrees of freedom over [0, +x+]
14
+ # Returns the integral of T distribution
15
+ # with +k+ degrees of freedom over [0, +x+]
17
16
 
18
17
  ##
19
18
  # :singleton-method: p_value(qn, k)
20
- # Return the P-value of the corresponding integral +qn+ with
21
- # +k+ degrees of freedom
22
-
19
+ # Return the P-value of the corresponding integral +qn+ with
20
+ # +k+ degrees of freedom
21
+
23
22
  ##
24
23
  # :singleton-method: cdf(x,k)
25
- # Returns the integral of T distribution
26
- # with +k+ degrees of freedom over [0, +x+]
24
+ # Returns the integral of T distribution
25
+ # with +k+ degrees of freedom over [0, +x+]
27
26
  end
28
27
  end
@@ -2,13 +2,14 @@ module Distribution
2
2
  module Binomial
3
3
  module GSL_
4
4
  class << self
5
- def pdf(k,n,prob)
6
- GSL::Ran.binomial_pdf(k,prob,n)
5
+ def pdf(k, n, prob)
6
+ GSL::Ran.binomial_pdf(k, prob, n)
7
7
  end
8
- def cdf(k,n,prob)
9
- GSL::Cdf.binomial_P(k,prob,n)
8
+
9
+ def cdf(k, n, prob)
10
+ GSL::Cdf.binomial_P(k, prob, n)
10
11
  end
11
12
  end
12
13
  end
13
14
  end
14
- end
15
+ end
@@ -6,4 +6,4 @@ module Distribution
6
6
  end
7
7
  end
8
8
  end
9
- end
9
+ end
@@ -2,28 +2,35 @@ module Distribution
2
2
  module Binomial
3
3
  module Ruby_
4
4
  class << self
5
- def pdf(k,n,pr)
6
- raise "k>n" if k>n
7
- Math.binomial_coefficient(n,k)*(pr**k)*(1-pr)**(n-k)
5
+ def pdf(k, n, pr)
6
+ fail 'k>n' if k > n
7
+ Math.binomial_coefficient(n, k) * (pr**k) * (1 - pr)**(n - k)
8
8
  end
9
- # TODO: Use exact_regularized_beta for
9
+
10
+ alias_method :exact_pdf, :pdf
11
+
12
+ # TODO: Use exact_regularized_beta for
10
13
  # small values and regularized_beta for bigger ones.
11
- def cdf(k,n,pr)
12
- #(0..x.floor).inject(0) {|ac,i| ac+pdf(i,n,pr)}
13
- Math.regularized_beta(1-pr,n - k,k+1)
14
+ def cdf(k, n, pr)
15
+ # (0..x.floor).inject(0) {|ac,i| ac+pdf(i,n,pr)}
16
+ Math.regularized_beta(1 - pr, n - k, k + 1)
14
17
  end
15
- def exact_cdf(k,n,pr)
16
- (0..k).inject(0) {|ac,i| ac+pdf(i,n,pr)}
18
+
19
+ def exact_cdf(k, n, pr)
20
+ out = (0..k).inject(0) { |ac, i| ac + pdf(i, n, pr) }
21
+ out = 1 if out > 1.0
22
+ out
17
23
  end
18
- def p_value(prob,n,pr)
19
- ac=0
24
+
25
+ def quantile(prob, n, pr)
26
+ ac = 0
20
27
  (0..n).each do |i|
21
- ac+=pdf(i,n,pr)
22
- return i if prob<=ac
28
+ ac += pdf(i, n, pr)
29
+ return i if prob <= ac
23
30
  end
24
31
  end
25
-
26
- alias :exact_pdf :pdf
32
+
33
+ alias_method :p_value, :quantile
27
34
  end
28
35
  end
29
36
  end
@@ -7,21 +7,20 @@ module Distribution
7
7
  # Pdf if easy to calculate, but CDF is not trivial. Several papers
8
8
  # describe methods to calculate the integral.
9
9
  module BivariateNormal
10
- SHORTHAND='bnor'
10
+ SHORTHAND = 'bnor'
11
11
 
12
12
  extend Distributable
13
13
  create_distribution_methods
14
-
14
+
15
15
  ##
16
16
  # :singleton-method: pdf(k,n,prob)
17
17
  # Probability density function for exactly +k+ successes in +n+ trials
18
18
  # with success probability +prob+
19
- #
20
-
19
+ #
20
+
21
21
  ##
22
22
  # :singleton-method: cdf(k,n,prob)
23
23
  # Cumulative density function for +k+ or less successes in +n+ trials
24
24
  # with success probability +prob+
25
-
26
25
  end
27
26
  end
@@ -2,8 +2,8 @@ module Distribution
2
2
  module BivariateNormal
3
3
  module GSL_
4
4
  class <<self
5
- def pdf(x,y,rho,s1=1.0,s2=1.0)
6
- GSL::Ran::bivariate_gaussian_pdf(x, y, s1,s2,rho)
5
+ def pdf(x, y, rho, s1 = 1.0, s2 = 1.0)
6
+ GSL::Ran.bivariate_gaussian_pdf(x, y, s1, s2, rho)
7
7
  end
8
8
  end
9
9
  end
@@ -6,4 +6,4 @@ module Distribution
6
6
  end
7
7
  end
8
8
  end
9
- end
9
+ end
@@ -1,281 +1,272 @@
1
1
  module Distribution
2
- #
2
+ #
3
3
  # Ruby version implements three methods on this module:
4
4
  # * Genz:: Used by default, with improvement to calculate p on rho > 0.95
5
5
  # * Hull:: Port from a C++ code
6
6
  # * Jantaravareerat:: Iterative (slow and buggy)
7
- #
8
-
7
+ #
8
+
9
9
  module BivariateNormal
10
10
  module Ruby_
11
- class << self
12
- SIDE=0.1 # :nodoc:
13
- LIMIT=5 # :nodoc:
14
- # Return the partial derivative of cdf over x, with y and rho constant
15
- # Reference:
16
- # * Tallis, 1962, p.346, cited by Olsson, 1979
17
- def partial_derivative_cdf_x(x,y,rho)
18
- Distribution::Normal.pdf(x) * Distribution::Normal.cdf((y-rho*x).quo( Math::sqrt( 1 - rho**2 )))
19
- end
20
- alias :pd_cdf_x :partial_derivative_cdf_x
21
- # Probability density function for a given x, y and rho value.
22
- #
23
- # Source: http://en.wikipedia.org/wiki/Multivariate_normal_distribution
24
- def pdf(x,y, rho, s1=1.0, s2=1.0)
25
- 1.quo(2 * Math::PI * s1 * s2 * Math::sqrt( 1 - rho**2 )) * (Math::exp(-(1.quo(2*(1-rho**2))) *
26
- ((x**2.quo(s1)) + (y**2.quo(s2)) - (2*rho*x*y).quo(s1*s2))))
27
- end
28
-
29
- def f(x,y,aprime,bprime,rho)
30
- r=aprime*(2*x-aprime)+bprime*(2*y-bprime)+2*rho*(x-aprime)*(y-bprime)
31
- Math::exp(r)
32
- end
33
-
34
- # CDF for a given x, y and rho value.
35
- # Uses Genz algorithm (cdf_genz method).
36
- #
37
- def cdf(a,b,rho)
38
- cdf_genz(a,b,rho)
39
- end
40
-
41
- def sgn(x)
42
- if(x>=0)
43
- 1
44
- else
45
- -1
46
- end
47
- end
48
-
49
- # Normal cumulative distribution function (cdf) for a given x, y and rho.
50
- # Based on Hull (1993, cited by Arne, 2003)
51
- #
52
- # References:
53
- # * Arne, B.(2003). Financial Numerical Recipes in C ++. Available on http://finance.bi.no/~bernt/gcc_prog/recipes/recipes/node23.html
54
- def cdf_hull(a,b,rho)
55
- #puts "a:#{a} - b:#{b} - rho:#{rho}"
56
- if (a<=0 and b<=0 and rho<=0)
57
- # puts "ruta 1"
58
- aprime=a.quo(Math::sqrt(2.0*(1.0-rho**2)))
59
- bprime=b.quo(Math::sqrt(2.0*(1.0-rho**2)))
60
- aa=[0.3253030, 0.4211071, 0.1334425, 0.006374323]
61
- bb=[0.1337764, 0.6243247, 1.3425378, 2.2626645]
62
- sum=0
63
- 4.times do |i|
64
- 4.times do |j|
65
- sum+=aa[i]*aa[j] * f(bb[i], bb[j], aprime, bprime,rho)
66
- end
67
- end
68
- sum=sum*(Math::sqrt(1.0-rho**2).quo(Math::PI))
69
- return sum
70
- elsif(a*b*rho<=0.0)
71
-
72
- #puts "ruta 2"
73
- if(a<=0 and b>=0 and rho>=0)
74
- return Distribution::Normal.cdf(a) - cdf(a,-b,-rho)
75
- elsif (a>=0.0 and b<=0.0 and rho>=0)
76
- return Distribution::Normal.cdf(b) - cdf(-a,b,-rho)
77
- elsif (a>=0.0 and b>=0.0 and rho<=0)
78
- return Distribution::Normal.cdf(a) + Distribution::Normal.cdf(b) - 1.0 + cdf(-a,-b,rho)
11
+ class << self
12
+ SIDE = 0.1 # :nodoc:
13
+ LIMIT = 5 # :nodoc:
14
+ # Return the partial derivative of cdf over x, with y and rho constant
15
+ # Reference:
16
+ # * Tallis, 1962, p.346, cited by Olsson, 1979
17
+ def partial_derivative_cdf_x(x, y, rho)
18
+ Distribution::Normal.pdf(x) * Distribution::Normal.cdf((y - rho * x).quo(Math.sqrt(1 - rho**2)))
19
+ end
20
+ alias_method :pd_cdf_x, :partial_derivative_cdf_x
21
+ # Probability density function for a given x, y and rho value.
22
+ #
23
+ # Source: http://en.wikipedia.org/wiki/Multivariate_normal_distribution
24
+ def pdf(x, y, rho, s1 = 1.0, s2 = 1.0)
25
+ 1.quo(2 * Math::PI * s1 * s2 * Math.sqrt(1 - rho**2)) * (Math.exp(-(1.quo(2 * (1 - rho**2))) *
26
+ ((x**2.quo(s1)) + (y**2.quo(s2)) - (2 * rho * x * y).quo(s1 * s2))))
27
+ end
28
+
29
+ def f(x, y, aprime, bprime, rho)
30
+ r = aprime * (2 * x - aprime) + bprime * (2 * y - bprime) + 2 * rho * (x - aprime) * (y - bprime)
31
+ Math.exp(r)
32
+ end
33
+
34
+ # CDF for a given x, y and rho value.
35
+ # Uses Genz algorithm (cdf_genz method).
36
+ #
37
+ def cdf(a, b, rho)
38
+ cdf_genz(a, b, rho)
39
+ end
40
+
41
+ def sgn(x)
42
+ if (x >= 0)
43
+ 1
44
+ else
45
+ -1
79
46
  end
80
- elsif (a*b*rho>=0.0)
81
- #puts "ruta 3"
82
- denum=Math::sqrt(a**2 - 2*rho*a*b + b**2)
83
- rho1=((rho*a-b)*sgn(a)).quo(denum)
84
- rho2=((rho*b-a)*sgn(b)).quo(denum)
85
- delta=(1.0-sgn(a)*sgn(b)).quo(4)
86
- #puts "#{rho1} - #{rho2}"
87
- return cdf(a, 0.0, rho1) + cdf(b, 0.0, rho2) - delta
88
47
  end
89
- raise "Should'nt be here! #{a} - #{b} #{rho}"
90
- end
91
-
92
- # CDF. Iterative method by Jantaravareerat (n/d)
93
- #
94
- # Reference:
95
- # * Jantaravareerat, M. & Thomopoulos, N. (n/d). Tables for standard bivariate normal distribution
96
-
97
- def cdf_jantaravareerat(x,y,rho,s1=1,s2=1)
98
- # Special cases
99
- return 1 if x>LIMIT and y>LIMIT
100
- return 0 if x<-LIMIT or y<-LIMIT
101
- return Distribution::Normal.cdf(y) if x>LIMIT
102
- return Distribution::Normal.cdf(x) if y>LIMIT
103
-
104
- #puts "x:#{x} - y:#{y}"
105
- x=-LIMIT if x<-LIMIT
106
- x=LIMIT if x>LIMIT
107
- y=-LIMIT if y<-LIMIT
108
- y=LIMIT if y>LIMIT
109
48
 
110
- x_squares=((LIMIT+x) / SIDE).to_i
111
- y_squares=((LIMIT+y) / SIDE).to_i
112
- sum=0
113
- x_squares.times do |i|
114
- y_squares.times do |j|
115
- z1=-LIMIT+(i+1)*SIDE
116
- z2=-LIMIT+(j+1)*SIDE
117
- #puts " #{z1}-#{z2}"
118
- h=(pdf(z1,z2,rho,s1,s2)+pdf(z1-SIDE,z2,rho,s1,s2)+pdf(z1,z2-SIDE,rho,s1,s2) + pdf(z1-SIDE,z2-SIDE,rho,s1,s2)).quo(4)
119
- sum+= (SIDE**2)*h # area
49
+ # Normal cumulative distribution function (cdf) for a given x, y and rho.
50
+ # Based on Hull (1993, cited by Arne, 2003)
51
+ #
52
+ # References:
53
+ # * Arne, B.(2003). Financial Numerical Recipes in C ++. Available on http://finance.bi.no/~bernt/gcc_prog/recipes/recipes/node23.html
54
+ def cdf_hull(a, b, rho)
55
+ # puts "a:#{a} - b:#{b} - rho:#{rho}"
56
+ if a <= 0 && b <= 0 && rho <= 0
57
+ # puts "ruta 1"
58
+ aprime = a.quo(Math.sqrt(2.0 * (1.0 - rho**2)))
59
+ bprime = b.quo(Math.sqrt(2.0 * (1.0 - rho**2)))
60
+ aa = [0.3253030, 0.4211071, 0.1334425, 0.006374323]
61
+ bb = [0.1337764, 0.6243247, 1.3425378, 2.2626645]
62
+ sum = 0
63
+ 4.times do |i|
64
+ 4.times do |j|
65
+ sum += aa[i] * aa[j] * f(bb[i], bb[j], aprime, bprime, rho)
66
+ end
67
+ end
68
+ sum *= (Math.sqrt(1.0 - rho**2).quo(Math::PI))
69
+ return sum
70
+ elsif (a * b * rho <= 0.0)
71
+
72
+ # puts "ruta 2"
73
+ if a <= 0 && b >= 0 && rho >= 0
74
+ return Distribution::Normal.cdf(a) - cdf(a, -b, -rho)
75
+ elsif a >= 0.0 && b <= 0.0 && rho >= 0
76
+ return Distribution::Normal.cdf(b) - cdf(-a, b, -rho)
77
+ elsif a >= 0.0 && b >= 0.0 && rho <= 0
78
+ return Distribution::Normal.cdf(a) + Distribution::Normal.cdf(b) - 1.0 + cdf(-a, -b, rho)
79
+ end
80
+ elsif (a * b * rho >= 0.0)
81
+ # puts "ruta 3"
82
+ denum = Math.sqrt(a**2 - 2 * rho * a * b + b**2)
83
+ rho1 = ((rho * a - b) * sgn(a)).quo(denum)
84
+ rho2 = ((rho * b - a) * sgn(b)).quo(denum)
85
+ delta = (1.0 - sgn(a) * sgn(b)).quo(4)
86
+ # puts "#{rho1} - #{rho2}"
87
+ return cdf(a, 0.0, rho1) + cdf(b, 0.0, rho2) - delta
120
88
  end
89
+ fail "Should'nt be here! #{a} - #{b} #{rho}"
121
90
  end
122
- sum
123
- end
124
- # Normal cumulative distribution function (cdf) for a given x, y and rho.
125
- # Ported from Fortran code by Alan Genz
126
- #
127
- # Original documentation
128
- # DOUBLE PRECISION FUNCTION BVND( DH, DK, R )
129
- # A function for computing bivariate normal probabilities.
130
- #
131
- # Alan Genz
132
- # Department of Mathematics
133
- # Washington State University
134
- # Pullman, WA 99164-3113
135
- # Email : alangenz_AT_wsu.edu
136
- #
137
- # This function is based on the method described by
138
- # Drezner, Z and G.O. Wesolowsky, (1989),
139
- # On the computation of the bivariate normal integral,
140
- # Journal of Statist. Comput. Simul. 35, pp. 101-107,
141
- # with major modifications for double precision, and for |R| close to 1.
142
- #
143
- # Original location:
144
- # * http://www.math.wsu.edu/faculty/genz/software/fort77/tvpack.f
145
- def cdf_genz(x,y,rho)
146
- dh=-x
147
- dk=-y
148
- r=rho
149
- twopi = 6.283185307179586
150
-
151
- w=11.times.collect {[nil]*4};
152
- x=11.times.collect {[nil]*4}
153
-
154
- data=[
155
- 0.1713244923791705E+00, -0.9324695142031522E+00,
156
- 0.3607615730481384E+00, -0.6612093864662647E+00,
157
- 0.4679139345726904E+00, -0.2386191860831970E+00]
158
-
159
- (1..3).each {|i|
160
- w[i][1]=data[(i-1)*2]
161
- x[i][1]=data[(i-1)*2+1]
162
-
163
- }
164
- data=[
165
- 0.4717533638651177E-01,-0.9815606342467191E+00,
166
- 0.1069393259953183E+00,-0.9041172563704750E+00,
167
- 0.1600783285433464E+00,-0.7699026741943050E+00,
168
- 0.2031674267230659E+00,-0.5873179542866171E+00,
169
- 0.2334925365383547E+00,-0.3678314989981802E+00,
170
- 0.2491470458134029E+00,-0.1252334085114692E+00]
171
- (1..6).each {|i|
172
- w[i][2]=data[(i-1)*2]
173
- x[i][2]=data[(i-1)*2+1]
174
91
 
175
-
176
- }
177
- data=[
178
- 0.1761400713915212E-01,-0.9931285991850949E+00,
179
- 0.4060142980038694E-01,-0.9639719272779138E+00,
180
- 0.6267204833410906E-01,-0.9122344282513259E+00,
181
- 0.8327674157670475E-01,-0.8391169718222188E+00,
182
- 0.1019301198172404E+00,-0.7463319064601508E+00,
183
- 0.1181945319615184E+00,-0.6360536807265150E+00,
184
- 0.1316886384491766E+00,-0.5108670019508271E+00,
185
- 0.1420961093183821E+00,-0.3737060887154196E+00,
186
- 0.1491729864726037E+00,-0.2277858511416451E+00,
187
- 0.1527533871307259E+00,-0.7652652113349733E-01]
188
-
189
- (1..10).each {|i|
190
- w[i][3]=data[(i-1)*2]
191
- x[i][3]=data[(i-1)*2+1]
92
+ # CDF. Iterative method by Jantaravareerat (n/d)
93
+ #
94
+ # Reference:
95
+ # * Jantaravareerat, M. & Thomopoulos, N. (n/d). Tables for standard bivariate normal distribution
192
96
 
193
-
194
- }
195
-
196
-
197
- if ( r.abs < 0.3 )
198
- ng = 1
199
- lg = 3
200
- elsif ( r.abs < 0.75 )
201
- ng = 2
202
- lg = 6
203
- else
204
- ng = 3
205
- lg = 10
97
+ def cdf_jantaravareerat(x, y, rho, s1 = 1, s2 = 1)
98
+ # Special cases
99
+ return 1 if x > LIMIT && y > LIMIT
100
+ return 0 if x < -LIMIT || y < -LIMIT
101
+ return Distribution::Normal.cdf(y) if x > LIMIT
102
+ return Distribution::Normal.cdf(x) if y > LIMIT
103
+
104
+ # puts "x:#{x} - y:#{y}"
105
+ x = -LIMIT if x < -LIMIT
106
+ x = LIMIT if x > LIMIT
107
+ y = -LIMIT if y < -LIMIT
108
+ y = LIMIT if y > LIMIT
109
+
110
+ x_squares = ((LIMIT + x) / SIDE).to_i
111
+ y_squares = ((LIMIT + y) / SIDE).to_i
112
+ sum = 0
113
+ x_squares.times do |i|
114
+ y_squares.times do |j|
115
+ z1 = -LIMIT + (i + 1) * SIDE
116
+ z2 = -LIMIT + (j + 1) * SIDE
117
+ # puts " #{z1}-#{z2}"
118
+ h = (pdf(z1, z2, rho, s1, s2) + pdf(z1 - SIDE, z2, rho, s1, s2) + pdf(z1, z2 - SIDE, rho, s1, s2) + pdf(z1 - SIDE, z2 - SIDE, rho, s1, s2)).quo(4)
119
+ sum += (SIDE**2) * h # area
120
+ end
121
+ end
122
+ sum
206
123
  end
207
-
208
-
209
- h = dh
210
- k = dk
211
- hk = h*k
212
- bvn = 0
213
- if ( r.abs < 0.925 )
214
- if ( r.abs > 0 )
215
- hs = ( h*h + k*k ).quo(2)
216
- asr = Math::asin(r)
217
- (1..lg).each do |i|
218
- [-1,1].each do |is|
219
- sn = Math::sin(asr*(is* x[i][ng]+1).quo(2) )
220
- bvn = bvn + w[i][ng] * Math::exp( ( sn*hk-hs ).quo( 1-sn*sn ) )
221
- end # do
222
- end # do
223
- bvn = bvn*asr.quo( 2*twopi )
224
- end # if
225
- bvn = bvn + Distribution::Normal.cdf(-h) * Distribution::Normal.cdf(-k)
226
-
227
-
228
- else # r.abs
229
- if ( r < 0 )
230
- k = -k
231
- hk = -hk
124
+ # Normal cumulative distribution function (cdf) for a given x, y and rho.
125
+ # Ported from Fortran code by Alan Genz
126
+ #
127
+ # Original documentation
128
+ # DOUBLE PRECISION FUNCTION BVND( DH, DK, R )
129
+ # A function for computing bivariate normal probabilities.
130
+ #
131
+ # Alan Genz
132
+ # Department of Mathematics
133
+ # Washington State University
134
+ # Pullman, WA 99164-3113
135
+ # Email : alangenz_AT_wsu.edu
136
+ #
137
+ # This function is based on the method described by
138
+ # Drezner, Z and G.O. Wesolowsky, (1989),
139
+ # On the computation of the bivariate normal integral,
140
+ # Journal of Statist. Comput. Simul. 35, pp. 101-107,
141
+ # with major modifications for double precision, and for |R| close to 1.
142
+ #
143
+ # Original location:
144
+ # * http://www.math.wsu.edu/faculty/genz/software/fort77/tvpack.f
145
+ def cdf_genz(x, y, rho)
146
+ dh = -x
147
+ dk = -y
148
+ r = rho
149
+ twopi = 6.283185307179586
150
+
151
+ w = 11.times.collect { [nil] * 4 }
152
+ x = 11.times.collect { [nil] * 4 }
153
+
154
+ data = [
155
+ 0.1713244923791705E+00, -0.9324695142031522E+00,
156
+ 0.3607615730481384E+00, -0.6612093864662647E+00,
157
+ 0.4679139345726904E+00, -0.2386191860831970E+00]
158
+
159
+ (1..3).each do|i|
160
+ w[i][1] = data[(i - 1) * 2]
161
+ x[i][1] = data[(i - 1) * 2 + 1]
232
162
  end
233
-
234
- if ( r.abs < 1 )
235
- as = ( 1 - r )*( 1 + r )
236
- a = Math::sqrt(as)
237
- bs = ( h - k )**2
238
- c = ( 4 - hk ).quo(8)
239
- d = ( 12 - hk ).quo(16)
240
- asr = -( bs.quo(as) + hk ).quo(2)
241
- if ( asr > -100 )
242
- bvn = a*Math::exp(asr) * ( 1 - c*( bs - as )*( 1 - d*bs.quo(5) ).quo(3) + c*d*as*as.quo(5) )
243
- end
244
- if ( -hk < 100 )
245
- b = Math::sqrt(bs)
246
- bvn = bvn - Math::exp( -hk.quo(2) ) * Math::sqrt(twopi)*Distribution::Normal.cdf(-b.quo(a))*b *
247
- ( 1 - c*bs*( 1 - d*bs.quo(5) ).quo(3) )
163
+ data = [
164
+ 0.4717533638651177E-01, -0.9815606342467191E+00,
165
+ 0.1069393259953183E+00, -0.9041172563704750E+00,
166
+ 0.1600783285433464E+00, -0.7699026741943050E+00,
167
+ 0.2031674267230659E+00, -0.5873179542866171E+00,
168
+ 0.2334925365383547E+00, -0.3678314989981802E+00,
169
+ 0.2491470458134029E+00, -0.1252334085114692E+00]
170
+ (1..6).each do|i|
171
+ w[i][2] = data[(i - 1) * 2]
172
+ x[i][2] = data[(i - 1) * 2 + 1]
173
+ end
174
+ data = [
175
+ 0.1761400713915212E-01, -0.9931285991850949E+00,
176
+ 0.4060142980038694E-01, -0.9639719272779138E+00,
177
+ 0.6267204833410906E-01, -0.9122344282513259E+00,
178
+ 0.8327674157670475E-01, -0.8391169718222188E+00,
179
+ 0.1019301198172404E+00, -0.7463319064601508E+00,
180
+ 0.1181945319615184E+00, -0.6360536807265150E+00,
181
+ 0.1316886384491766E+00, -0.5108670019508271E+00,
182
+ 0.1420961093183821E+00, -0.3737060887154196E+00,
183
+ 0.1491729864726037E+00, -0.2277858511416451E+00,
184
+ 0.1527533871307259E+00, -0.7652652113349733E-01]
185
+
186
+ (1..10).each do|i|
187
+ w[i][3] = data[(i - 1) * 2]
188
+ x[i][3] = data[(i - 1) * 2 + 1]
189
+ end
190
+
191
+ if r.abs < 0.3
192
+ ng = 1
193
+ lg = 3
194
+ elsif r.abs < 0.75
195
+ ng = 2
196
+ lg = 6
197
+ else
198
+ ng = 3
199
+ lg = 10
200
+ end
201
+
202
+ h = dh
203
+ k = dk
204
+ hk = h * k
205
+ bvn = 0
206
+ if r.abs < 0.925
207
+ if r.abs > 0
208
+ hs = (h * h + k * k).quo(2)
209
+ asr = Math.asin(r)
210
+ (1..lg).each do |i|
211
+ [-1, 1].each do |is|
212
+ sn = Math.sin(asr * (is * x[i][ng] + 1).quo(2))
213
+ bvn += w[i][ng] * Math.exp((sn * hk - hs).quo(1 - sn * sn))
214
+ end # do
215
+ end # do
216
+ bvn *= asr.quo(2 * twopi)
217
+ end # if
218
+ bvn += Distribution::Normal.cdf(-h) * Distribution::Normal.cdf(-k)
219
+
220
+ else # r.abs
221
+ if r < 0
222
+ k = -k
223
+ hk = -hk
248
224
  end
249
-
250
-
251
- a = a.quo(2)
252
- (1..lg).each do |i|
253
- [-1,1].each do |is|
254
- xs = (a*( is*x[i][ng] + 1 ) )**2
255
- rs = Math::sqrt( 1 - xs )
256
- asr = -( bs/xs + hk ).quo(2)
257
- if ( asr > -100 )
258
- bvn = bvn + a*w[i][ng] * Math::exp( asr ) *
259
- ( Math::exp( -hk*( 1 - rs ).quo(2*( 1 + rs ) ) ) .quo(rs) - ( 1 + c*xs*( 1 + d*xs ) ) )
225
+
226
+ if r.abs < 1
227
+ as = (1 - r) * (1 + r)
228
+ a = Math.sqrt(as)
229
+ bs = (h - k)**2
230
+ c = (4 - hk).quo(8)
231
+ d = (12 - hk).quo(16)
232
+ asr = -(bs.quo(as) + hk).quo(2)
233
+ if asr > -100
234
+ bvn = a * Math.exp(asr) * (1 - c * (bs - as) * (1 - d * bs.quo(5)).quo(3) + c * d * as * as.quo(5))
235
+ end
236
+ if -hk < 100
237
+ b = Math.sqrt(bs)
238
+ bvn -= Math.exp(-hk.quo(2)) * Math.sqrt(twopi) * Distribution::Normal.cdf(-b.quo(a)) * b *
239
+ (1 - c * bs * (1 - d * bs.quo(5)).quo(3))
240
+ end
241
+
242
+ a = a.quo(2)
243
+ (1..lg).each do |i|
244
+ [-1, 1].each do |is|
245
+ xs = (a * (is * x[i][ng] + 1))**2
246
+ rs = Math.sqrt(1 - xs)
247
+ asr = -(bs / xs + hk).quo(2)
248
+ if asr > -100
249
+ bvn += a * w[i][ng] * Math.exp(asr) *
250
+ (Math.exp(-hk * (1 - rs).quo(2 * (1 + rs))) .quo(rs) - (1 + c * xs * (1 + d * xs)))
251
+ end
260
252
  end
261
253
  end
254
+ bvn = -bvn / twopi
262
255
  end
263
- bvn = -bvn/twopi
264
- end
265
-
266
- if ( r > 0 )
267
- bvn = bvn + Distribution::Normal.cdf(-[h,k].max)
268
- else
269
- bvn = -bvn
270
- if ( k > h )
271
- bvn = bvn + Distribution::Normal.cdf(k) - Distribution::Normal.cdf(h)
256
+
257
+ if r > 0
258
+ bvn += Distribution::Normal.cdf(-[h, k].max)
259
+ else
260
+ bvn = -bvn
261
+ if k > h
262
+ bvn = bvn + Distribution::Normal.cdf(k) - Distribution::Normal.cdf(h)
263
+ end
272
264
  end
273
265
  end
266
+ bvn
274
267
  end
275
- bvn
268
+ private :f, :sgn
276
269
  end
277
- private :f, :sgn
278
- end
279
270
  end
280
271
  end
281
272
  end