fathom 0.3.0 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.autotest +10 -0
- data/Gemfile +10 -2
- data/Gemfile.lock +8 -0
- data/TODO.md +12 -25
- data/VERSION +1 -1
- data/lib/{fathom/ext → ext}/array.rb +0 -0
- data/lib/{fathom/ext → ext}/faster_csv.rb +0 -0
- data/lib/{fathom/ext → ext}/open_struct.rb +0 -0
- data/lib/{fathom/ext → ext}/string.rb +0 -0
- data/lib/fathom.rb +16 -13
- data/lib/fathom/agent.rb +8 -9
- data/lib/fathom/{causal_graph.rb → archive/causal_graph.rb} +0 -0
- data/lib/fathom/{concept.rb → archive/concept.rb} +0 -0
- data/lib/fathom/archive/conditional_probability_matrix.rb +3 -0
- data/lib/fathom/{inverter.rb → archive/inverter.rb} +0 -0
- data/lib/fathom/archive/node.rb +24 -1
- data/lib/fathom/distributions/discrete_uniform.rb +11 -32
- data/lib/fathom/import.rb +37 -34
- data/lib/fathom/import/yaml_import.rb +22 -1
- data/lib/fathom/knowledge_base.rb +34 -23
- data/lib/fathom/knowledge_base/search.rb +19 -0
- data/lib/fathom/node.rb +32 -1
- data/lib/fathom/node/belief_node.rb +121 -0
- data/lib/fathom/node/cpm_node.rb +100 -0
- data/lib/fathom/node/data_collection.rb +97 -0
- data/lib/fathom/{data_node.rb → node/data_node.rb} +1 -1
- data/lib/fathom/{value_aggregator.rb → node/decision.rb} +5 -5
- data/lib/fathom/node/discrete_node.rb +41 -0
- data/lib/fathom/node/fact.rb +24 -0
- data/lib/fathom/{mc_node.rb → node/mc_node.rb} +1 -1
- data/lib/fathom/{enforced_name.rb → node/node_extensions/enforced_name.rb} +1 -1
- data/lib/fathom/{numeric_methods.rb → node/node_extensions/numeric_methods.rb} +19 -1
- data/lib/fathom/{plausible_range.rb → node/plausible_range.rb} +1 -1
- data/spec/ext/array_spec.rb +10 -0
- data/spec/ext/faster_csv_spec.rb +10 -0
- data/spec/ext/open_struct_spec.rb +20 -0
- data/spec/ext/string_spec.rb +7 -0
- data/spec/fathom/import/csv_import_spec.rb +11 -9
- data/spec/fathom/import/yaml_import_spec.rb +27 -7
- data/spec/fathom/knowledge_base_spec.rb +8 -4
- data/spec/fathom/node/belief_node_spec.rb +180 -0
- data/spec/fathom/node/cpm_node_spec.rb +144 -0
- data/spec/fathom/node/data_collection_spec.rb +26 -0
- data/spec/fathom/{data_node_spec.rb → node/data_node_spec.rb} +1 -1
- data/spec/fathom/node/decision_spec.rb +15 -0
- data/spec/fathom/node/discrete_node_spec.rb +56 -0
- data/spec/fathom/node/fact_spec.rb +33 -0
- data/spec/fathom/{mc_node_spec.rb → node/mc_node_spec.rb} +1 -1
- data/spec/fathom/{enforced_name_spec.rb → node/node_extensions/enforced_name_spec.rb} +1 -1
- data/spec/fathom/{numeric_methods_spec.rb → node/node_extensions/numeric_methods_spec.rb} +53 -11
- data/spec/fathom/{plausible_range_spec.rb → node/plausible_range_spec.rb} +1 -1
- data/spec/fathom/node_spec.rb +17 -0
- data/spec/fathom_spec.rb +40 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/support/fact.yml +11 -0
- metadata +57 -30
- data/lib/fathom/value_multiplier.rb +0 -18
@@ -1,11 +1,11 @@
|
|
1
|
-
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'fathom'))
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'fathom'))
|
2
|
+
class Fathom::Decision < Node
|
3
|
+
undef_method :values
|
4
|
+
undef_method :distribution
|
5
5
|
end
|
6
6
|
|
7
7
|
if __FILE__ == $0
|
8
8
|
include Fathom
|
9
9
|
# TODO: Is there anything you want to do to run this file on its own?
|
10
|
-
#
|
10
|
+
# Decision.new
|
11
11
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'fathom'))
|
2
|
+
class Fathom::DiscreteNode < Node
|
3
|
+
attr_reader :labels
|
4
|
+
|
5
|
+
def initialize(opts={})
|
6
|
+
opts[:distribution] ||= :discrete_uniform
|
7
|
+
super(opts)
|
8
|
+
assert_labels(opts)
|
9
|
+
end
|
10
|
+
|
11
|
+
def size
|
12
|
+
@size ||= self.labels.length
|
13
|
+
end
|
14
|
+
alias :length :size
|
15
|
+
|
16
|
+
def rand
|
17
|
+
self.labels[self.distribution.rand(self.size)]
|
18
|
+
end
|
19
|
+
|
20
|
+
# This makes it easier to interface to a belief node
|
21
|
+
def likelihood(ignored_value)
|
22
|
+
GSL::Vector.ary_to_gv(Array.new(self.size, 1))
|
23
|
+
end
|
24
|
+
alias :l :likelihood
|
25
|
+
|
26
|
+
protected
|
27
|
+
def assert_labels(opts)
|
28
|
+
@labels = opts[:labels]
|
29
|
+
@labels ||= self.values
|
30
|
+
@labels ||= [:true, :false]
|
31
|
+
@labels = Array[@labels] unless @labels.is_a?(Array)
|
32
|
+
@labels.uniq!
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
if __FILE__ == $0
|
38
|
+
include Fathom
|
39
|
+
# TODO: Is there anything you want to do to run this file on its own?
|
40
|
+
# DiscreteNode.new
|
41
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'fathom'))
|
2
|
+
class Fathom::Fact < Node
|
3
|
+
|
4
|
+
attr_reader :value
|
5
|
+
|
6
|
+
def initialize(opts={})
|
7
|
+
symbolize_keys!(opts)
|
8
|
+
@value = opts[:value]
|
9
|
+
@value ||= opts[:values]
|
10
|
+
super(opts)
|
11
|
+
end
|
12
|
+
|
13
|
+
alias :rand :value
|
14
|
+
|
15
|
+
undef_method :values
|
16
|
+
undef_method :distribution
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
if __FILE__ == $0
|
21
|
+
include Fathom
|
22
|
+
# TODO: Is there anything you want to do to run this file on its own?
|
23
|
+
# Fact.new
|
24
|
+
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'fathom'))
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', '..', 'fathom'))
|
2
2
|
module Fathom
|
3
3
|
module NumericMethods
|
4
4
|
def initialize(opts={})
|
@@ -46,5 +46,23 @@ module Fathom
|
|
46
46
|
distribution.interval_values(opts.merge(:mean => mean, :sd => sd))
|
47
47
|
end
|
48
48
|
|
49
|
+
def coefficient_of_variation
|
50
|
+
return nil unless vector
|
51
|
+
vector.sd / vector.mean
|
52
|
+
end
|
53
|
+
alias :cov :coefficient_of_variation
|
54
|
+
|
55
|
+
def summary
|
56
|
+
{
|
57
|
+
:mean => mean,
|
58
|
+
:standard_deviation => standard_deviation,
|
59
|
+
:min => vector ? vector.min : nil,
|
60
|
+
:max => vector ? vector.max : nil,
|
61
|
+
:lower_bound => lower_bound,
|
62
|
+
:upper_bound => upper_bound,
|
63
|
+
:coefficient_of_variation => coefficient_of_variation
|
64
|
+
}
|
65
|
+
end
|
66
|
+
|
49
67
|
end
|
50
68
|
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe Array do
|
4
|
+
it "should define rand, a way to get a random variable from an array" do
|
5
|
+
a = [1,2]
|
6
|
+
a.should be_include(a.rand)
|
7
|
+
# 9.31322574615479e-10 chance of failing
|
8
|
+
30.times.map {a.rand}.uniq.sort.should eql([1,2])
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
require 'fastercsv'
|
3
|
+
require 'ext/faster_csv'
|
4
|
+
|
5
|
+
describe FasterCSV do
|
6
|
+
it "should have a header converter to strip values" do
|
7
|
+
FasterCSV::HeaderConverters.keys.should be_include(:strip)
|
8
|
+
FasterCSV::HeaderConverters[:strip].call(' this ').should eql('this')
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe OpenStruct do
|
4
|
+
|
5
|
+
before do
|
6
|
+
@o = OpenStruct.new(:this => 1)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should expose its table" do
|
10
|
+
@o.table.should eql({:this => 1})
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should expose the keys from the table" do
|
14
|
+
@o.keys.should eql([:this])
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should expose the values from the table" do
|
18
|
+
@o.values.should eql([1])
|
19
|
+
end
|
20
|
+
end
|
@@ -37,14 +37,16 @@ describe CSVImport do
|
|
37
37
|
@result.that.values.should eql([3,6,9])
|
38
38
|
end
|
39
39
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
40
|
+
# KB Override
|
41
|
+
# it "should store the imported values in the knowledge base" do
|
42
|
+
# Fathom.knowledge_base[:this].should be_a(DataNode)
|
43
|
+
# Fathom.kb[:this].values.should eql([1,4,7])
|
44
|
+
# end
|
44
45
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
46
|
+
# KB Override
|
47
|
+
# it "should import from the class level" do
|
48
|
+
# CSVImport.import(@opts)
|
49
|
+
# Fathom.knowledge_base[:this].should be_a(DataNode)
|
50
|
+
# Fathom.kb[:this].values.should eql([1,4,7])
|
51
|
+
# end
|
50
52
|
end
|
@@ -38,16 +38,36 @@ describe YAMLImport do
|
|
38
38
|
@result.co2_readings.should be_a(DataNode)
|
39
39
|
@result.co2_readings.values.should eql([10,20,30])
|
40
40
|
end
|
41
|
+
|
42
|
+
# KB Override
|
43
|
+
# it "should store the imported values in the knowledge base" do
|
44
|
+
# Fathom.knowledge_base['CO2 Emissions'].should be_a(PlausibleRange)
|
45
|
+
# Fathom.kb['CO2 Emissions'].min.should eql(1_000_000)
|
46
|
+
# end
|
47
|
+
|
48
|
+
# KB Override
|
49
|
+
# it "should import from the class level" do
|
50
|
+
# YAMLImport.import(@opts)
|
51
|
+
# Fathom.knowledge_base['CO2 Emissions'].should be_a(PlausibleRange)
|
52
|
+
# Fathom.kb['CO2 Emissions'].min.should eql(1_000_000)
|
53
|
+
# end
|
41
54
|
|
42
|
-
it "should
|
43
|
-
|
44
|
-
|
55
|
+
it "should not complain if there's an empty YAML file" do
|
56
|
+
filename = '/tmp/empty_yaml_test.yml'
|
57
|
+
File.open(filename, 'w') {|f| f.puts ''}
|
58
|
+
lambda{YAMLImport.import(:content => filename)}.should_not raise_error
|
59
|
+
`rm -rf #{filename}`
|
45
60
|
end
|
46
61
|
|
47
|
-
it "should
|
48
|
-
|
49
|
-
|
50
|
-
|
62
|
+
it "should create a Fact if it is an object named fact and has a name and value function" do
|
63
|
+
filename = File.expand_path(File.dirname(__FILE__) + "/../../support/fact.yml")
|
64
|
+
yaml = open(filename).read
|
65
|
+
opts = {:content => yaml}
|
66
|
+
yi = YAMLImport.new(opts)
|
67
|
+
result = yi.import
|
68
|
+
result.one.value.should eql(1)
|
69
|
+
result.two.value.should eql(2)
|
70
|
+
result.three.value.should eql(3)
|
51
71
|
end
|
52
72
|
|
53
73
|
end
|
@@ -8,9 +8,13 @@ describe KnowledgeBase do
|
|
8
8
|
@kb = KnowledgeBase.new
|
9
9
|
end
|
10
10
|
|
11
|
-
it "should
|
12
|
-
|
13
|
-
@kb[:new_node] = @dn
|
14
|
-
@kb[:new_node].should eql(@dn)
|
11
|
+
it "should have a find accessor" do
|
12
|
+
KnowledgeBase.should respond_to(:find)
|
15
13
|
end
|
14
|
+
|
15
|
+
# it "should be able to add a node" do
|
16
|
+
# @dn = DataNode.new(:name => :new_node, :values => [1,2,3])
|
17
|
+
# @kb[:new_node] = @dn
|
18
|
+
# @kb[:new_node].should eql(@dn)
|
19
|
+
# end
|
16
20
|
end
|
@@ -0,0 +1,180 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
2
|
+
|
3
|
+
include Fathom
|
4
|
+
|
5
|
+
describe BeliefNode do
|
6
|
+
|
7
|
+
before do
|
8
|
+
@bn = BeliefNode.new(:labels => [:red, :green, :blue])
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should be a Node" do
|
12
|
+
BeliefNode.ancestors.should be_include(Node)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should be a DiscreteNode as well" do
|
16
|
+
BeliefNode.ancestors.should be_include(DiscreteNode)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should initialize with a uniform probability distribution" do
|
20
|
+
@bn.probabilities.should ==(GSL::Vector[1.0/3, 1.0/3, 1.0/3])
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should allow a pre-defined probability distribution" do
|
24
|
+
@bn = BeliefNode.new(:labels => [:red, :green, :blue], :probabilities => [0.1, 0.2, 0.7])
|
25
|
+
@bn.probabilities.should ==(GSL::Vector[0.1, 0.2, 0.7])
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should raise an error if the supplied probability distribution is of the wrong size" do
|
29
|
+
lambda{BeliefNode.new(:labels => [:red, :green, :blue], :probabilities => [0.1, 0.2, 0.7, 0.1])}.should raise_error(/Probabilities must be 3/)
|
30
|
+
lambda{BeliefNode.new(:labels => [:red, :green, :blue], :probabilities => [0.1, 0.2])}.should raise_error(/Probabilities must be 3/)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should normalize even the supplied probabilities" do
|
34
|
+
@bn = BeliefNode.new(:labels => [:red, :green, :blue], :probabilities => [1, 2, 7])
|
35
|
+
@bn.probabilities.should ==(GSL::Vector[0.1, 0.2, 0.7])
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should initialize with a uniform likelihood vector" do
|
39
|
+
@bn.likelihoods.should ==(GSL::Vector[1,1,1])
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should allow pre-defined likelihoods to be supplied" do
|
43
|
+
@bn = BeliefNode.new(:labels => [:red, :green, :blue], :likelihoods => [1,1,2])
|
44
|
+
@bn.likelihoods.should ==(GSL::Vector[1,1,2])
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should raise an error if the supplied likelihoods are the wrong size" do
|
48
|
+
lambda{BeliefNode.new(:labels => [:red, :green, :blue], :likelihoods => [1,1,2,1])}.should raise_error(/Likelihoods must be 3/)
|
49
|
+
lambda{BeliefNode.new(:labels => [:red, :green, :blue], :likelihoods => [1,1])}.should raise_error(/Likelihoods must be 3/)
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should establish a precision threshold at 1.0e-05" do
|
53
|
+
@bn.precision_threshold.should eql(0.00001)
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should allow the precision_threshold as a parameter" do
|
57
|
+
@bn = BeliefNode.new(:labels => [:red, :green, :blue], :precision_threshold => 0.01)
|
58
|
+
@bn.precision_threshold.should eql(0.01)
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should allow a initialization from a values hash" do
|
62
|
+
@bn = BeliefNode.new(:values => {:red => 0.1, :green => 0.2, :yellow => 0.3, :blue => 0.4})
|
63
|
+
@bn.probabilities.to_a.sort.should eql([0.1, 0.2, 0.3, 0.4])
|
64
|
+
@bn.labels.map(&:to_s).sort.should eql(%w(blue green red yellow))
|
65
|
+
end
|
66
|
+
|
67
|
+
context "CPMNodes" do
|
68
|
+
before do
|
69
|
+
@b1 = BeliefNode.new(:name => :person, :values => {:david => 0.4, :adina => 0.6})
|
70
|
+
@b2 = BeliefNode.new(:name => :movie_preference, :values => {:drama => 0.5, :comedy => 0.4, :romance => 0.1})
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should automatically create an intermediary cpm when adding a child" do
|
74
|
+
@b1.add_child(@b2)
|
75
|
+
@b1.children.first.should be_a(CPMNode)
|
76
|
+
@b1.children.first.child.should eql(@b2)
|
77
|
+
@b2.parents.first.should be_a(CPMNode)
|
78
|
+
@b2.parents.first.parent.should eql(@b1)
|
79
|
+
@cpm = @b1.children.first
|
80
|
+
@cpm.should eql(@b2.parents.first)
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should create an accessor for the child, rather than the cpm" do
|
84
|
+
@b1.add_child(@b2)
|
85
|
+
@b1.should_not be_respond_to(:cpm)
|
86
|
+
@b1.should respond_to(:movie_preference)
|
87
|
+
@b1.movie_preference.should eql(@b2)
|
88
|
+
end
|
89
|
+
|
90
|
+
it "should create a cpm_for_* accessor for the cpm" do
|
91
|
+
@b1.add_child(@b2)
|
92
|
+
@b1.should respond_to(:cpm_for_movie_preference)
|
93
|
+
@b1.cpm_for_movie_preference.should eql(@b1.children.first)
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should create a cpm_for_* accessor for the child" do
|
97
|
+
@b1.add_child(@b2)
|
98
|
+
@b2.should respond_to(:cpm_for_person)
|
99
|
+
@b2.cpm_for_person.should eql(@b2.parents.first)
|
100
|
+
@b2.cpm_for_person.should eql(@b1.children.first)
|
101
|
+
end
|
102
|
+
|
103
|
+
it "should automatically create an intermediary cpm when adding a parent" do
|
104
|
+
@b2.add_parent(@b1)
|
105
|
+
@b1.children.first.should be_a(CPMNode)
|
106
|
+
@b1.children.first.child.should eql(@b2)
|
107
|
+
@b2.parents.first.should be_a(CPMNode)
|
108
|
+
@b2.parents.first.parent.should eql(@b1)
|
109
|
+
@cpm = @b1.children.first
|
110
|
+
@cpm.should eql(@b2.parents.first)
|
111
|
+
end
|
112
|
+
|
113
|
+
it "should create an accessor for the parent, rather than the cpm" do
|
114
|
+
@b2.add_parent(@b1)
|
115
|
+
@b2.should_not be_respond_to(:cpm)
|
116
|
+
@b2.should respond_to(:person)
|
117
|
+
@b2.person.should eql(@b1)
|
118
|
+
end
|
119
|
+
|
120
|
+
it "should create a cpm_for_* accessor for the cpm" do
|
121
|
+
@b2.add_parent(@b1)
|
122
|
+
@b2.should respond_to(:cpm_for_person)
|
123
|
+
@b2.cpm_for_person.should eql(@b2.parents.first)
|
124
|
+
end
|
125
|
+
|
126
|
+
it "should create an inspect string that reflects the grandchild, rather than the cpm" do
|
127
|
+
@b1.add_child(@b2)
|
128
|
+
@b1.inspect.should eql("Fathom::BeliefNode: person, children:, [\"movie_preference (Fathom::BeliefNode)\", \"movie_preference (Fathom::BeliefNode)\"], parents: , []")
|
129
|
+
end
|
130
|
+
|
131
|
+
it "should create an inspect string that reflects the grandparent, rather than the cpm" do
|
132
|
+
@b2.add_parent(@b1)
|
133
|
+
@b2.inspect.should eql("Fathom::BeliefNode: movie_preference, children:, [], parents: , [\"person (Fathom::BeliefNode)\", \"person (Fathom::BeliefNode)\"]")
|
134
|
+
end
|
135
|
+
|
136
|
+
it "should create a cpm_for_* accessor for the parent" do
|
137
|
+
@b2.add_parent(@b1)
|
138
|
+
@b1.should respond_to(:cpm_for_movie_preference)
|
139
|
+
@b1.cpm_for_movie_preference.should eql(@b1.children.first)
|
140
|
+
@b1.cpm_for_movie_preference.should eql(@b2.parents.first)
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
144
|
+
|
145
|
+
context "Belief and Propagation" do
|
146
|
+
|
147
|
+
before do
|
148
|
+
|
149
|
+
@season = BeliefNode.new(:name => :season, :labels => [:winter, :spring, :summer, :fall])
|
150
|
+
@sprinkler = BeliefNode.new(:name => :sprinkler)
|
151
|
+
@rain = BeliefNode.new(:name => :rain)
|
152
|
+
@wet = BeliefNode.new(:name => :wet)
|
153
|
+
@slippery = BeliefNode.new(:name => :slippery)
|
154
|
+
|
155
|
+
@season.add_child(@sprinkler)
|
156
|
+
@sprinkler.add_child(@wet)
|
157
|
+
@season.add_child(@rain)
|
158
|
+
@rain.add_child(@wet)
|
159
|
+
@wet.add_child(@slippery)
|
160
|
+
|
161
|
+
end
|
162
|
+
|
163
|
+
it "should provide the likelihood for a particular value" do
|
164
|
+
@sprinkler.should be_respond_to(:likelihood)
|
165
|
+
output = @sprinkler.likelihood(:true)
|
166
|
+
output.should be_a(OpenStruct)
|
167
|
+
# @sprinkler.parents.map(&:name_sym).each {|key| output.table.keys.should be_include(key)}
|
168
|
+
# output.
|
169
|
+
# output.table.keys.should eql([:matrix, :parents])
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
# What's next:
|
174
|
+
# Deal with multiple parents and the matrix that comes in
|
175
|
+
# Propagate the likelihoods and probabilities up and down
|
176
|
+
# Calculate the belief
|
177
|
+
# Update the information directly on the node and propagate
|
178
|
+
# Receive an update from a child or parent and propagate
|
179
|
+
# Adhere to the precision_threshold when propagating
|
180
|
+
end
|