statistical 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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