fathom 0.3.0 → 0.3.1
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.
- 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
|