Class: Sbn::Net
- Defined in:
- lib/sbn/net.rb,
lib/sbn/formats.rb,
lib/sbn/learning.rb,
lib/sbn/inference.rb
Constant Summary collapse
- MCMC_DEFAULT_SAMPLE_COUNT =
2000
Instance Attribute Summary collapse
-
#name ⇒ Object
readonly
Returns the value of attribute name.
-
#variables ⇒ Object
readonly
Returns the value of attribute variables.
Class Method Summary collapse
-
.from_xmlbif(source) ⇒ Object
Reconstitute a saved network.
Instance Method Summary collapse
- #==(obj) ⇒ Object
- #===(obj) ⇒ Object
- #add_sample_point(evidence) ⇒ Object
- #add_variable(variable) ⇒ Object
- #eql?(obj) ⇒ Boolean
-
#initialize(name = '') ⇒ Net
constructor
A new instance of Net.
-
#learn(data) ⇒ Object
Expects data to be an array of hashes containing complete sets of evidence for all variables in the network.
-
#query_variable(varname, callback = nil) ⇒ Object
possible state for the specified variable, based on previously-supplied evidence, using the Markov Chain Monte Carlo algorithm.
- #set_evidence(event) ⇒ Object
- #set_probabilities_from_sample_points! ⇒ Object
-
#symbolize_evidence(evidence) ⇒ Object
:nodoc:.
-
#to_xmlbif ⇒ Object
Returns a string containing a representation of the network in XMLBIF format.
Constructor Details
#initialize(name = '') ⇒ Net
Returns a new instance of Net.
5 6 7 8 9 10 11 |
# File 'lib/sbn/net.rb', line 5 def initialize(name = '') @@net_count ||= 0 @@net_count += 1 @name = (name.empty? ? "net_#{@@net_count}" : name.to_underscore_sym) @variables = {} @evidence = {} end |
Instance Attribute Details
#name ⇒ Object (readonly)
Returns the value of attribute name.
3 4 5 |
# File 'lib/sbn/net.rb', line 3 def name @name end |
#variables ⇒ Object (readonly)
Returns the value of attribute variables.
3 4 5 |
# File 'lib/sbn/net.rb', line 3 def variables @variables end |
Class Method Details
.from_xmlbif(source) ⇒ Object
Reconstitute a saved network.
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 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
# File 'lib/sbn/formats.rb', line 47 def self.from_xmlbif(source) # convert tags to lower case source.gsub!(/(<.*?>)/, '\\1'.downcase) doc = XmlSimple.xml_in(source) netname = doc['network'].first['name'].first # find net name returnval = Net.new(netname) # find variables count = 0 variables = {} variable_elements = doc['network'].first['variable'].each do |var| varname = var['name'].first.to_sym properties = var['property'] vartype = nil manager_name = nil text_to_match = "" = {} thresholds = [] properties.each do |prop| key, val = prop.split('=').map {|e| e.strip } vartype = val if key == 'SbnVariableType' manager_name = val if key == 'ManagerVariableName' text_to_match = eval(val) if key == 'TextToMatch' [key.to_sym] = val.to_i if key =~ /stdev_state_count/ thresholds = val.map {|e| e.to_f } if key == 'StateThresholds' end states = var['outcome'] table = [] doc['network'].first['definition'].each do |defn| if defn['for'].first.to_sym == varname table = defn['table'].first.split.map {|prob| prob.to_f } end end count += 1 variables[varname] = case vartype when "Sbn::StringVariable" then StringVariable.new(returnval, varname) when "Sbn::NumericVariable" then NumericVariable.new(returnval, varname, table, thresholds, ) when "Sbn::Variable" then Variable.new(returnval, varname, table, states) when "Sbn::StringCovariable" then StringCovariable.new(returnval, manager_name, text_to_match, table) end end # find relationships between variables # connect covariables to their managers variable_elements = doc['network'].first['variable'].each do |var| varname = var['name'].first.to_sym properties = var['property'] vartype = nil covars = nil parents = nil properties.each do |prop| key, val = prop.split('=').map {|e| e.strip } covars = val.split(',').map {|e| e.strip.to_sym } if key == 'Covariables' parents = val.split(',').map {|e| e.strip.to_sym } if key == 'Parents' vartype = val if key == 'SbnVariableType' end if vartype == "Sbn::StringVariable" parents.each {|p| variables[varname].add_parent(variables[p]) } if parents covars.each {|covar| variables[varname].add_covariable(variables[covar]) } if covars end end # connect all other variables to their parents doc['network'].first['definition'].each do |defn| varname = defn['for'].first.to_sym parents = defn['given'] parents.each {|p| variables[varname].add_parent(variables[p.to_sym]) } if parents end returnval end |
Instance Method Details
#==(obj) ⇒ Object
13 |
# File 'lib/sbn/net.rb', line 13 def ==(obj); test_equal(obj); end |
#===(obj) ⇒ Object
15 |
# File 'lib/sbn/net.rb', line 15 def ===(obj); test_equal(obj); end |
#add_sample_point(evidence) ⇒ Object
116 117 118 119 |
# File 'lib/sbn/learning.rb', line 116 def add_sample_point(evidence) evidence = symbolize_evidence(evidence) @variables.keys.each {|key| @variables[key].add_sample_point(evidence) } end |
#add_variable(variable) ⇒ Object
17 18 19 20 21 22 23 |
# File 'lib/sbn/net.rb', line 17 def add_variable(variable) name = variable.name if @variables.has_key? name raise "Variable of name #{name} has already been added to this net" end @variables[name] = variable end |
#eql?(obj) ⇒ Boolean
14 |
# File 'lib/sbn/net.rb', line 14 def eql?(obj); test_equal(obj); end |
#learn(data) ⇒ Object
Expects data to be an array of hashes containing complete sets of evidence for all variables in the network. Constructs probability tables for each variable based on the data.
111 112 113 114 |
# File 'lib/sbn/learning.rb', line 111 def learn(data) data.each {|evidence| add_sample_point(evidence) } set_probabilities_from_sample_points! end |
#query_variable(varname, callback = nil) ⇒ Object
possible state for the specified variable, based on previously-supplied evidence, using the Markov Chain Monte Carlo algorithm. The MCMC algorithm generates each event by making a random change to the preceding event. The next state is generated by randomly sampling a value for one of the nonevidence variables Xi, conditioned on the current values of the variables in the Markov blanket of Xi. MCMC basically wanders randomly around the state space–the space of possible complete assignments–flipping one variable at a time, but keeping the evidence variables fixed. The sampling process works because it settles into a “dynamic equilibrium” in which the long-run fraction of time spent in each state is proportional to its posterior probability.
Optionally accepts a block that receives a number between 0 and 1 indicating the percentage of completion.
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/sbn/inference.rb', line 20 def query_variable(varname, callback = nil) # keep track of number of times a state has been observed state_frequencies = {} varname = varname.to_underscore_sym states = @variables[varname].states states.each {|s| state_frequencies[s] ||= 0 } e = generate_random_event relevant_evidence = e.reject {|key, val| @variables[key].set_in_evidence?(@evidence) } MCMC_DEFAULT_SAMPLE_COUNT.times do |n| state = e[varname] state_frequencies[state] += 1 relevant_evidence.each do |vname, vstate| e[vname] = @variables[vname].get_random_state_with_markov_blanket(e) end yield(n / MCMC_DEFAULT_SAMPLE_COUNT.to_f) if block_given? end # normalize results magnitude = 0 returnval = {} state_frequencies.each_value {|count| magnitude += count } state_frequencies.each {|state, count| returnval[state] = count / magnitude.to_f } returnval end |
#set_evidence(event) ⇒ Object
35 36 37 |
# File 'lib/sbn/net.rb', line 35 def set_evidence(event) @evidence = symbolize_evidence(event) end |
#set_probabilities_from_sample_points! ⇒ Object
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
# File 'lib/sbn/learning.rb', line 121 def set_probabilities_from_sample_points! # we must first conduct learning on parents then their children unlearned_variables = @variables.keys count = 0 size = @variables.size.to_f until unlearned_variables.empty? learnable_variables = @variables.reject do |name, var| reject = false var.parents.each {|p| reject = true if unlearned_variables.include?(p.name) } reject end learnable_variables.keys.each do |key| @variables[key].set_probabilities_from_sample_points! count += 1 unlearned_variables.delete(key) end end end |
#symbolize_evidence(evidence) ⇒ Object
:nodoc:
25 26 27 28 29 30 31 32 33 |
# File 'lib/sbn/net.rb', line 25 def symbolize_evidence(evidence) # :nodoc: newevidence = {} evidence.each do |key, val| key = key.to_underscore_sym raise "Invalid variable name #{key}." unless @variables.has_key?(key) newevidence[key] = @variables[key].transform_evidence_value(val) end newevidence end |
#to_xmlbif ⇒ Object
Returns a string containing a representation of the network in XMLBIF format. www.cs.cmu.edu/afs/cs/user/fgcozman/www/Research/InterchangeFormat
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
# File 'lib/sbn/formats.rb', line 8 def to_xmlbif xml = Builder::XmlMarkup.new(:indent => 2) xml.instruct! xml.comment! <<-EOS Bayesian network in XMLBIF v0.3 (BayesNet Interchange Format) Produced by SBN (Simple Bayesian Network library) Output created #{Time.now} EOS xml.text! "\n" xml.comment! "DTD for the XMLBIF 0.3 format" xml.declare! :DOCTYPE, :bif do xml.declare! :ELEMENT, :bif, :"(network)*" xml.declare! :ATTLIST, :bif, :version, :CDATA, :"#REQUIRED" xml.declare! :ELEMENT, :"network (name, (property | variable | definition)*)" xml.declare! :ELEMENT, :name, :"(#PCDATA)" xml.declare! :ELEMENT, :"variable (name, (outcome | property)*)" xml.declare! :ATTLIST, :"variable type (nature | decision | utility) \"nature\"" xml.declare! :ELEMENT, :outcome, :"(#PCDATA)" xml.declare! :ELEMENT, :definition, :"(for | given | table | property)*" xml.declare! :ELEMENT, :for, :"(#PCDATA)" xml.declare! :ELEMENT, :given, :"(#PCDATA)" xml.declare! :ELEMENT, :table, :"(#PCDATA)" xml.declare! :ELEMENT, :property, :"(#PCDATA)" end xml.bif :version => 0.3 do xml.network do xml.name(@name.to_s) xml.text! "\n" xml.comment! "Variables" @variables.each {|name, variable| variable.to_xmlbif_variable(xml) } xml.text! "\n" xml.comment! "Probability distributions" @variables.each {|name, variable| variable.to_xmlbif_definition(xml) } end end end |