Class: Sbn::Variable
Direct Known Subclasses
Constant Summary collapse
- NEGLIGIBLE_PROBABILITY =
0.0001
Instance Attribute Summary collapse
-
#children ⇒ Object
readonly
Returns the value of attribute children.
-
#name ⇒ Object
readonly
Returns the value of attribute name.
-
#parents ⇒ Object
readonly
Returns the value of attribute parents.
-
#probability_table ⇒ Object
readonly
Returns the value of attribute probability_table.
-
#states ⇒ Object
readonly
Returns the value of attribute states.
Instance Method Summary collapse
- #==(obj) ⇒ Object
- #===(obj) ⇒ Object
- #add_child(variable) ⇒ Object
-
#add_child_no_recurse(variable) ⇒ Object
:nodoc:.
- #add_parent(variable) ⇒ Object
-
#add_parent_no_recurse(variable) ⇒ Object
:nodoc:.
- #add_sample_point(evidence) ⇒ Object
-
#can_be_evaluated?(evidence) ⇒ Boolean
A variable can’t be evaluated unless its parents have been observed.
- #eql?(obj) ⇒ Boolean
-
#evaluate_marginal(state, event) ⇒ Object
:nodoc:.
-
#evidence_name ⇒ Object
:nodoc:.
-
#generate_probability_table ⇒ Object
:nodoc:.
-
#get_observed_state(evidence) ⇒ Object
:nodoc:.
-
#get_random_state(event = {}) ⇒ Object
In order to draw uniformly from the probabilty space, we can’t just pick a random state.
-
#get_random_state_with_markov_blanket(event) ⇒ Object
similar to get_random_state() except it evaluates a variable’s markov blanket in addition to the variable itself.
-
#initialize(net, name = '', probabilities = [0.5, 0.5], states = [:true, :false]) ⇒ Variable
constructor
A new instance of Variable.
-
#is_complete_evidence?(evidence) {|varnames| ... } ⇒ Boolean
:nodoc:.
-
#set_in_evidence?(evidence) ⇒ Boolean
:nodoc:.
- #set_probabilities(probs) ⇒ Object
- #set_probabilities_from_sample_points! ⇒ Object
- #set_probability(probability, event) ⇒ Object
- #set_states(states) ⇒ Object
- #to_s ⇒ Object
- #to_xmlbif_definition(xml) ⇒ Object
- #to_xmlbif_variable(xml) ⇒ Object
-
#transform_evidence_value(val) ⇒ Object
:nodoc:.
Constructor Details
#initialize(net, name = '', probabilities = [0.5, 0.5], states = [:true, :false]) ⇒ Variable
Returns a new instance of Variable.
5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
# File 'lib/sbn/variable.rb', line 5 def initialize(net, name = '', probabilities = [0.5, 0.5], states = [:true, :false]) @net = net @@variable_count ||= 0 @@variable_count += 1 name = "variable_#{@@variable_count}" if name.is_a? String and name.empty? @name = name.to_underscore_sym @children = [] @parents = [] @states = [] @state_frequencies = {} # used for storing sample points set_states(states) set_probabilities(probabilities) net.add_variable(self) end |
Instance Attribute Details
#children ⇒ Object (readonly)
Returns the value of attribute children.
3 4 5 |
# File 'lib/sbn/variable.rb', line 3 def children @children end |
#name ⇒ Object (readonly)
Returns the value of attribute name.
3 4 5 |
# File 'lib/sbn/variable.rb', line 3 def name @name end |
#parents ⇒ Object (readonly)
Returns the value of attribute parents.
3 4 5 |
# File 'lib/sbn/variable.rb', line 3 def parents @parents end |
#probability_table ⇒ Object (readonly)
Returns the value of attribute probability_table.
3 4 5 |
# File 'lib/sbn/variable.rb', line 3 def probability_table @probability_table end |
#states ⇒ Object (readonly)
Returns the value of attribute states.
3 4 5 |
# File 'lib/sbn/variable.rb', line 3 def states @states end |
Instance Method Details
#==(obj) ⇒ Object
20 |
# File 'lib/sbn/variable.rb', line 20 def ==(obj); test_equal(obj); end |
#===(obj) ⇒ Object
22 |
# File 'lib/sbn/variable.rb', line 22 def ===(obj); test_equal(obj); end |
#add_child(variable) ⇒ Object
65 66 67 68 |
# File 'lib/sbn/variable.rb', line 65 def add_child(variable) add_child_no_recurse(variable) variable.add_parent_no_recurse(self) end |
#add_child_no_recurse(variable) ⇒ Object
:nodoc:
84 85 86 87 88 89 90 91 92 |
# File 'lib/sbn/variable.rb', line 84 def add_child_no_recurse(variable) # :nodoc: return if variable == self or @children.include?(variable) if variable.is_a?(StringVariable) @children.concat variable.covariables else @children << variable end variable.generate_probability_table end |
#add_parent(variable) ⇒ Object
70 71 72 73 |
# File 'lib/sbn/variable.rb', line 70 def add_parent(variable) add_parent_no_recurse(variable) variable.add_child_no_recurse(self) end |
#add_parent_no_recurse(variable) ⇒ Object
:nodoc:
94 95 96 97 98 99 100 101 102 |
# File 'lib/sbn/variable.rb', line 94 def add_parent_no_recurse(variable) # :nodoc: return if variable == self or @parents.include?(variable) if variable.is_a?(StringVariable) @parents.concat variable.covariables else @parents << variable end generate_probability_table end |
#add_sample_point(evidence) ⇒ Object
23 24 25 26 27 28 29 30 31 32 |
# File 'lib/sbn/learning.rb', line 23 def add_sample_point(evidence) # reject incomplete evidence sets raise "Incomplete sample points" unless is_complete_evidence?(evidence) # Because string variables add new variables to the net during learning, # the process of determining state frequencies has to be deferred until # the end. For now, we'll just store the evidence and use it later. @sample_points ||= [] @sample_points << evidence end |
#can_be_evaluated?(evidence) ⇒ Boolean
A variable can’t be evaluated unless its parents have been observed
114 115 116 117 118 |
# File 'lib/sbn/variable.rb', line 114 def can_be_evaluated?(evidence) # :nodoc: returnval = true parents.each {|p| returnval = false unless p.set_in_evidence?(evidence) } returnval end |
#eql?(obj) ⇒ Boolean
21 |
# File 'lib/sbn/variable.rb', line 21 def eql?(obj); test_equal(obj); end |
#evaluate_marginal(state, event) ⇒ Object
:nodoc:
146 147 148 149 150 151 152 |
# File 'lib/sbn/variable.rb', line 146 def evaluate_marginal(state, event) # :nodoc: temp_probs = @probability_table.dup remove_irrelevant_states(temp_probs, state, event) sum = 0.0 temp_probs.each {|e| sum += e[1] } sum end |
#evidence_name ⇒ Object
:nodoc:
80 81 82 |
# File 'lib/sbn/variable.rb', line 80 def evidence_name # :nodoc: @name end |
#generate_probability_table ⇒ Object
:nodoc:
137 138 139 140 141 142 143 144 |
# File 'lib/sbn/variable.rb', line 137 def generate_probability_table # :nodoc: @probab @probability_table = nil if @probabilities and @probabilities.size == state_combinations.size probs = @probabilities.dup @probability_table = state_combinations.collect {|e| [e, probs.shift] } end end |
#get_observed_state(evidence) ⇒ Object
:nodoc:
108 109 110 |
# File 'lib/sbn/variable.rb', line 108 def get_observed_state(evidence) # :nodoc: evidence[@name] end |
#get_random_state(event = {}) ⇒ Object
In order to draw uniformly from the probabilty space, we can’t just pick a random state. Instead we generate a random number between zero and one and iterate through the states until the cumulative sum of their probabilities exceeds our random number.
124 125 126 |
# File 'lib/sbn/variable.rb', line 124 def get_random_state(event = {}) # :nodoc: seek_state {|s| evaluate_marginal(s, event) } end |
#get_random_state_with_markov_blanket(event) ⇒ Object
similar to get_random_state() except it evaluates a variable’s markov blanket in addition to the variable itself.
130 131 132 133 134 135 |
# File 'lib/sbn/variable.rb', line 130 def get_random_state_with_markov_blanket(event) # :nodoc: evaluations = [] @states.each {|s| evaluations << evaluate_markov_blanket(s, event) } evaluations.normalize! seek_state {|s| evaluations.shift } end |
#is_complete_evidence?(evidence) {|varnames| ... } ⇒ Boolean
:nodoc:
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
# File 'lib/sbn/learning.rb', line 5 def is_complete_evidence?(evidence) # :nodoc: varnames = [evidence_name.to_s] @parents.each {|p| varnames << p.name.to_s } yield(varnames) if block_given? # ignore covariables when determining whether evidence is complete or not varnames.map! do |n| n = n.split('_').first if n =~ /covar/ n end varnames.uniq! varnames.sort! keys = evidence.keys.map {|k| k.to_s } keys.sort! varnames & keys == varnames end |
#set_in_evidence?(evidence) ⇒ Boolean
:nodoc:
104 105 106 |
# File 'lib/sbn/variable.rb', line 104 def set_in_evidence?(evidence) # :nodoc: evidence.has_key?(evidence_name) end |
#set_probabilities(probs) ⇒ Object
75 76 77 78 |
# File 'lib/sbn/variable.rb', line 75 def set_probabilities(probs) @probabilities = probs generate_probability_table end |
#set_probabilities_from_sample_points! ⇒ Object
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
# File 'lib/sbn/learning.rb', line 34 def set_probabilities_from_sample_points! return unless @sample_points accumulate_state_frequencies # find the sums for each parent combination so we # know how to normalize their associated states sums = {} state_combinations.each do |comb| parent_comb = comb.dup # remove state for this node so that all # that is left is the parent combination parent_comb.pop @state_frequencies[comb] ||= 0 sums[parent_comb] ||= 0 sums[parent_comb] += @state_frequencies[comb] end probabilities = [] count_of_zero_prob_states = count_of_nonzero_prob_states = {} last_state = @states.first state_combinations.each do |comb| state = comb.last parent_comb = comb.dup parent_comb.pop prob = @state_frequencies[comb] / sums[parent_comb].to_f probabilities << (prob == 0.0 ? NEGLIGIBLE_PROBABILITY : prob) # Keep track of how many of this node's states were # empty for this particular parent combination, so that # we can pad them with tiny numbers later. Otherwise, # some exact inference algorithms will fail. if prob == 0.0 count_of_zero_prob_states[parent_comb] ||= 0 count_of_zero_prob_states[parent_comb] += 1 else count_of_nonzero_prob_states[parent_comb] ||= 0 count_of_nonzero_prob_states[parent_comb] += 1 end end # pad the zero probabilities count = 0 state_combinations.each do |comb| state = comb.last parent_comb = comb.dup parent_comb.pop amount_to_subtract = count_of_zero_prob_states[parent_comb] * NEGLIGIBLE_PROBABILITY / count_of_nonzero_prob_states[parent_comb].to_f p = probabilities[count] p = (p > NEGLIGIBLE_PROBABILITY ? p - amount_to_subtract : p) probabilities[count] = p count += 1 end # assign new probabilities set_probabilities(probabilities) end |
#set_probability(probability, event) ⇒ Object
52 53 54 55 56 57 58 59 60 61 62 63 |
# File 'lib/sbn/variable.rb', line 52 def set_probability(probability, event) event = @net.symbolize_evidence(event) unless can_be_evaluated?(event) raise "A valid state was not supplied for variable #{@name} and all its parents in call to set_probability()" end combination_for_this_event = [] @parents.each {|p| combination_for_this_event << event[p.name] } combination_for_this_event << event[@name] index = state_combinations.index(combination_for_this_event) @probabilities[index] = probability generate_probability_table end |
#set_states(states) ⇒ Object
46 47 48 49 50 |
# File 'lib/sbn/variable.rb', line 46 def set_states(states) states.symbolize_values! @states = states generate_probability_table end |
#to_s ⇒ Object
24 25 26 |
# File 'lib/sbn/variable.rb', line 24 def to_s @name.to_s end |
#to_xmlbif_definition(xml) ⇒ Object
37 38 39 40 41 42 43 44 |
# File 'lib/sbn/variable.rb', line 37 def to_xmlbif_definition(xml) xml.definition do xml.for(@name.to_s) @parents.each {|p| xml.given(p.name.to_s) } xml.table(@probability_table.transpose.last.join(' ')) yield(xml) if block_given? end end |
#to_xmlbif_variable(xml) ⇒ Object
28 29 30 31 32 33 34 35 |
# File 'lib/sbn/variable.rb', line 28 def to_xmlbif_variable(xml) xml.variable(:type => "nature") do xml.name(@name.to_s) @states.each {|s| xml.outcome(s.to_s) } xml.property("SbnVariableType = #{self.class.to_s}") yield(xml) if block_given? end end |
#transform_evidence_value(val) ⇒ Object
:nodoc:
154 155 156 |
# File 'lib/sbn/variable.rb', line 154 def transform_evidence_value(val) # :nodoc: val.to_underscore_sym end |