statistical 0.1.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.
@@ -0,0 +1,132 @@
1
+ module Statistical
2
+ # This class models a mathematical domain by basing it on Ruby's Range
3
+ # Does not allow for enumeration of the domain unless. The primary addition
4
+ # here is the addtion of an exclusion list which allows us to exclude specific
5
+ # points and ranges from within the domain.
6
+ #
7
+ # @note If the exclusion list contains points outside of the base range these
8
+ # points are not validated. The user is expect to supply valid input, since
9
+ # this is a helper class
10
+ # @note All instances of this class are returned frozen
11
+ #
12
+ # @author Vaibhav Yenamandra
13
+ #
14
+ # @attr_reader :exclusions The exclusion list of points and ranges to not
15
+ # be included in the domain. The list must be homogenous
16
+ class Domain < Range
17
+ include Comparable
18
+
19
+ attr_reader :start, :finish, :exclusions, :domain_type
20
+
21
+ TYPES = [
22
+ :left_open,
23
+ :right_open,
24
+ :full_open,
25
+ :closed
26
+ ].freeze
27
+
28
+ # Creates a new domain instance which can be one of the following types
29
+ # :left_open, :right_open, :full_open, :closed
30
+ # An exclusion list is also maintained to capture undesired points, ranges
31
+ #
32
+ # @param [Fixnum, Bignum] start number where the range starts
33
+ # @param [Fixnum, Bignum] finish number where the range ends
34
+ # @param [Symbol] domain_type kind of domain to represent
35
+ # @param exclusions homogenous list of exclusions
36
+ def initialize(start, finish, domain_type, *exclusions)
37
+ exclusions ||= []
38
+ exclude_end = false
39
+
40
+ case domain_type
41
+ when :left_open
42
+ @exclusions = [start, exclusions].flatten
43
+ exclude_end = false
44
+ when :right_open
45
+ @exclusions = [exclusions, finish].flatten
46
+ exclude_end = true
47
+ when :full_open
48
+ @exclusions = [start, exclusions, finish].flatten
49
+ exclude_end = true
50
+ when :closed
51
+ @exclusions = [exclusions].flatten
52
+ exclude_end = false
53
+ else
54
+ raise ArgumentError,
55
+ "Invalid domain type, must be one of #{DOMAIN_TYPES}"
56
+ end
57
+ @start = start
58
+ @finish = finish
59
+ @domain_type = domain_type
60
+
61
+ super(@start, @finish, exclude_end)
62
+ end
63
+
64
+ # Returns a frozen new instance, overrides Range::new
65
+ # @return [Domain] a new instance of the Domain class
66
+ def new(*args)
67
+ super(*args).freeze
68
+ end
69
+
70
+ # Returns a frozen new instance
71
+ # @see #new
72
+ # @return [Domain] a new instance of the Domain class
73
+ def self.[](*args)
74
+ new(*args)
75
+ end
76
+
77
+ # Find if a point value is part of the instance's exclusion list
78
+ #
79
+ # @param val The numeric value to test for exclusion
80
+ # @return [Boolean] if the value is excluded from the current domain
81
+ def exclude?(val)
82
+ has_val = false
83
+ @exclusions.each do |e|
84
+ case e
85
+ when Fixnum, Bignum, Float
86
+ has_val = has_val || (e == val)
87
+ when Range
88
+ has_val ||= e.include?(val)
89
+ end
90
+ end
91
+ return has_val
92
+ end
93
+
94
+ # Find if a point value is part of the domain
95
+ #
96
+ # @param val The value to test for inclusion
97
+ # @return [Boolean] if the value is included in the current domain
98
+ def include?(val)
99
+ super(val) && !exclude?(val)
100
+ end
101
+
102
+ # Serialize the instance
103
+ #
104
+ # @see #to_s
105
+ # @return [String] string representation of the Domain object
106
+ def inspect
107
+ return "[#{super}] - #{@exclusions}" unless @exclusions.empty?
108
+ return super
109
+ end
110
+
111
+ # Compares Domain objects with Real numeric types (Fixnum, Bignum, Float)
112
+ #
113
+ # @param other the point to compare
114
+ # @return -1 if the value lies to the left of the domain
115
+ # 1 if the value lies to the right of domain
116
+ # 0 if the value is included in the range
117
+ # nil if the two are not deemed comparable
118
+ def <=>(other)
119
+ case other
120
+ when Fixnum, Bignum, Float
121
+ return -1 if other <= @start && !include?(other)
122
+ return 1 if other >= @finish && !include?(other)
123
+ return 0 if include?(other)
124
+ else
125
+ # Not comparable
126
+ return nil
127
+ end
128
+ end
129
+
130
+ alias :to_s :inspect
131
+ end
132
+ end
@@ -0,0 +1,37 @@
1
+ require 'statistical/rng/uniform'
2
+ require 'statistical/rng/uniform_discrete'
3
+ require 'statistical/rng/two_point'
4
+ require 'statistical/rng/bernoulli'
5
+ require 'statistical/rng/exponential'
6
+ require 'statistical/rng/laplace'
7
+ require 'statistical/rng/weibull'
8
+
9
+ module Statistical
10
+ # Factory module to create instances of the various classes
11
+ # nested under itself
12
+ module Rng
13
+ # @private
14
+ # No need to document this
15
+ # Dynamically add constants when called
16
+ def self.const_missing(cname)
17
+ const_set(cname, make_classmap) if cname == :RNG_TYPES
18
+ end
19
+
20
+ # Creates a new instance of the give type if the type was found.
21
+ #
22
+ # @raise ArgumentError If the give type parameter was not found
23
+ def self.create(type = :uniform, *args, &block)
24
+ raise ArgumentError unless RNG_TYPES.include?(type)
25
+ RNG_TYPES[type].new(*args, &block)
26
+ end
27
+
28
+ def self.make_classmap
29
+ rng_klasses = constants.select { |k| const_get(k).is_a?(Class)}
30
+ keylist = rng_klasses.map { |k| k.to_s.snakecase.to_sym}
31
+ klasses = rng_klasses.map { |k| const_get(k)}
32
+ return Hash[keylist.zip(klasses)].freeze
33
+ end
34
+
35
+ private_class_method :make_classmap
36
+ end
37
+ end
@@ -0,0 +1,29 @@
1
+ require 'statistical/distribution/bernoulli'
2
+ require 'statistical/rng/two_point'
3
+
4
+ module Statistical
5
+ module Rng
6
+ # This class models a bernoulli trial using the TwoPoint distribution as
7
+ # as base distribution / trial system.
8
+ class Bernoulli < TwoPoint
9
+ # Companion RNG class for the Bernoulli distribution. Requires a
10
+ # distrbution object of the same type. Defaults to standard bernoulli
11
+ # if arguments are unspecified
12
+ #
13
+ # @author Vaibhav Yenamandra
14
+ #
15
+ # @attr_reader [Float] p Probability of success state
16
+ # @attr_reader [Float] q Probability of failure state
17
+ # @attr_reader [Hash] states Possible states that the RNG can take up
18
+ # @attr_reader [Random] generator The PRNG being used for randomness
19
+ def initialize(dobj = nil, seed = Random.new_seed)
20
+ unless dobj.nil? || dobj.is_a?(Statistical::Distribution::Bernoulli)
21
+ raise TypeError,
22
+ "Expected Distribution object or nil, found #{dobj.class}"
23
+ end
24
+ dobj = Statistical::Distribution::Bernoulli.new if dobj.nil?
25
+ super(dobj, seed)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,56 @@
1
+ require 'statistical/distribution/exponential'
2
+ require 'statistical/distribution/uniform'
3
+ require 'statistical/rng/uniform'
4
+
5
+ module Statistical
6
+ module Rng
7
+ # Companion RNG class for the exponential distribution. Requires a
8
+ # distrbution object of the corresponding distribution
9
+ #
10
+ # @author Vaibhav Yenamandra
11
+ #
12
+ # @attr_reader [Numeric] rate Rate parameter of the exponential distribution
13
+ # @attr_reader [Numeric] upper The upper bound of the uniform distribution
14
+ class Exponential
15
+ attr_reader :rate, :generator
16
+
17
+ def initialize(dobj = nil, seed = Random.new_seed)
18
+ unless dobj.nil? || dobj.is_a?(Statistical::Distribution::Exponential)
19
+ raise TypeError,
20
+ "Expected Distribution object or nil, found #{dobj.class}"
21
+ end
22
+ dobj = Statistical::Distribution::Exponential.new if dobj.nil?
23
+ @generator = Random.new(seed)
24
+ @rate = dobj.rate
25
+ @sdist = dobj
26
+ end
27
+
28
+ # Return the next random number from the sequence
29
+ #
30
+ # @return [Numeric] next random number in the sequence
31
+ def rand
32
+ return @sdist.quantile(@generator.rand)
33
+ end
34
+
35
+ # Compare against another rng to see if they are the same
36
+ #
37
+ # @return true if and only if, source distributions are the same and the
38
+ # prng has the same initial state
39
+ def eql?(other)
40
+ return other.is_a?(self.class) &&
41
+ @generator == other.generator &&
42
+ @rate = other.rate
43
+ end
44
+
45
+ # Return the type of the source distribution
46
+ #
47
+ # @return source distribution's type
48
+ def type
49
+ @sdist.class
50
+ end
51
+
52
+ alias :== :eql?
53
+ private :eql?
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,57 @@
1
+ require 'statistical/distribution/laplace'
2
+
3
+ module Statistical
4
+ module Rng
5
+ # Companion RNG class for the continuous uniform distribution. Requires a
6
+ # distrbution object of the corresponding distribution
7
+ #
8
+ # @author Vaibhav Yenamandra
9
+ #
10
+ # @attr_reader [Numeric] scale Scale parameter of the Laplace distribution.
11
+ # @attr_reader [Numeric] location Location parameter to determine where the
12
+ # distribution is centered / where the mean lies at
13
+ class Laplace
14
+ attr_reader :generator, :scale, :location
15
+
16
+ def initialize(dobj = nil, seed = Random.new_seed)
17
+ unless dobj.nil? || dobj.is_a?(Statistical::Distribution::Laplace)
18
+ raise TypeError,
19
+ "Expected Distribution object or nil, found #{dobj.class}"
20
+ end
21
+ dobj = Statistical::Distribution::Laplace.new if dobj.nil?
22
+ @generator = Random.new(seed)
23
+ @scale = dobj.scale
24
+ @location = dobj.location
25
+ @sdist = dobj
26
+ end
27
+
28
+ # Return the next random number from the sequence
29
+ #
30
+ # @return next random number in the sequence
31
+ def rand
32
+ return @sdist.quantile(@generator.rand)
33
+ end
34
+
35
+ # Compare against another rng to see if they are the same
36
+ #
37
+ # @return true if and only if, source distributions are the same and the
38
+ # prng has the same initial state
39
+ def eql?(other)
40
+ return other.is_a?(self.class) &&
41
+ other.generator == @generator &&
42
+ other.location == @location &&
43
+ other.scale == @scale
44
+ end
45
+
46
+ # Return the type of the source distribution
47
+ #
48
+ # @return source distribution's type
49
+ def type
50
+ @sdist.class
51
+ end
52
+
53
+ alias :== :eql?
54
+ private :eql?
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,70 @@
1
+ require 'statistical/distribution/two_point'
2
+ require 'statistical/distribution/uniform'
3
+
4
+ module Statistical
5
+ module Rng
6
+ # Random number generator to model the two point distribution used for
7
+ # working with problem where the state space has only two points with
8
+ # distinct probabilities
9
+ class TwoPoint
10
+ attr_reader :generator, :p, :q, :states
11
+
12
+ # Companion RNG class for the two-point distribution. Requires a
13
+ # distrbution object of the corresponding type. Defaults to the standard
14
+ # bernoulli if no arguments are given
15
+ #
16
+ # @author Vaibhav Yenamandra
17
+ #
18
+ # @attr_reader [Float] p Probability of success state
19
+ # @attr_reader [Float] q Probability of failure state
20
+ # @attr_reader [Hash] states Possible states that the RNG can take up
21
+ # @attr_reader [Object] generator The PRNG being used for randomness
22
+ def initialize(dobj = nil, seed = Random.new_seed)
23
+ unless dobj.nil? || dobj.is_a?(Statistical::Distribution::TwoPoint)
24
+ raise TypeError,
25
+ "Expected Distribution object or nil, found #{dobj.class}"
26
+ end
27
+ dobj = Statistical::Distribution::TwoPoint.new if dobj.nil?
28
+ @generator = Random.new(seed)
29
+ @sdist = dobj
30
+ @p = dobj.p
31
+ @q = dobj.q
32
+ @states = dobj.states
33
+ end
34
+
35
+ # Return the next random number from the sequence following the given
36
+ # distribution
37
+ #
38
+ # @return next random number in the sequence
39
+ def rand
40
+ return @sdist.quantile(@generator.rand)
41
+ end
42
+
43
+ # Compare against another rng to see if they are the same
44
+ #
45
+ # @return [Boolean] true if and only if, source distributions are the same
46
+ # and the prng has the same initial state
47
+ def eql?(other)
48
+ return other.is_a?(self.class) &&
49
+ @p = other.p &&
50
+ @states == other.states &&
51
+ @generator == other.generator
52
+ end
53
+
54
+ # Return the type of the source distribution
55
+ #
56
+ # @return source distribution's type
57
+ def type
58
+ @sdist.class
59
+ end
60
+
61
+ # The support set over which the distribution exists
62
+ def support
63
+ @sdist.support
64
+ end
65
+
66
+ alias :== :eql?
67
+ private :eql?
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,62 @@
1
+ require 'statistical/distribution/uniform'
2
+
3
+ module Statistical
4
+ module Rng
5
+ # Companion RNG class for the continuous uniform distribution. Requires a
6
+ # distrbution object of the corresponding distribution
7
+ #
8
+ # @author Vaibhav Yenamandra
9
+ #
10
+ # @attr_reader [Numeric] lower The lower bound of the uniform distribution.
11
+ # @attr_reader [Numeric] upper The upper bound of the uniform distribution.
12
+ class Uniform
13
+ attr_reader :lower, :upper, :generator
14
+
15
+ def initialize(dobj = nil, seed = Random.new_seed)
16
+ unless dobj.nil? || dobj.is_a?(Statistical::Distribution::Uniform)
17
+ raise TypeError,
18
+ "Expected Distribution object or nil, found #{dobj.class}"
19
+ end
20
+ dobj = Statistical::Distribution::Uniform.new if dobj.nil?
21
+ @generator = Random.new(seed)
22
+ @lower = dobj.lower
23
+ @upper = dobj.upper
24
+ @sdist = dobj
25
+ end
26
+
27
+ # Return the next random number from the sequence
28
+ #
29
+ # @author Vaibhav Yenamandra
30
+ #
31
+ # @return next random number in the sequence
32
+ def rand
33
+ @lower + @generator.rand * (@upper - @lower)
34
+ end
35
+
36
+ # Compare against another rng to see if they are the same
37
+ #
38
+ # @author Vaibhav Yenamandra
39
+ #
40
+ # @return [Boolean] true if and only if, source distributions are the
41
+ # same and the prng has the same initial state
42
+ def eql?(other)
43
+ return other.is_a?(self.class) &&
44
+ @lower == other.lower &&
45
+ @upper == other.upper &&
46
+ @generator == other.generator
47
+ end
48
+
49
+ # Return the type of the source distribution
50
+ #
51
+ # @author Vaibhav Yenamandra
52
+ #
53
+ # @return [Statistical::Distribution::Uniform] source distribution's type
54
+ def type
55
+ @sdist.class
56
+ end
57
+
58
+ alias :== :eql?
59
+ private :eql?
60
+ end
61
+ end
62
+ end