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.
Files changed (57) hide show
  1. data/.autotest +10 -0
  2. data/Gemfile +10 -2
  3. data/Gemfile.lock +8 -0
  4. data/TODO.md +12 -25
  5. data/VERSION +1 -1
  6. data/lib/{fathom/ext → ext}/array.rb +0 -0
  7. data/lib/{fathom/ext → ext}/faster_csv.rb +0 -0
  8. data/lib/{fathom/ext → ext}/open_struct.rb +0 -0
  9. data/lib/{fathom/ext → ext}/string.rb +0 -0
  10. data/lib/fathom.rb +16 -13
  11. data/lib/fathom/agent.rb +8 -9
  12. data/lib/fathom/{causal_graph.rb → archive/causal_graph.rb} +0 -0
  13. data/lib/fathom/{concept.rb → archive/concept.rb} +0 -0
  14. data/lib/fathom/archive/conditional_probability_matrix.rb +3 -0
  15. data/lib/fathom/{inverter.rb → archive/inverter.rb} +0 -0
  16. data/lib/fathom/archive/node.rb +24 -1
  17. data/lib/fathom/distributions/discrete_uniform.rb +11 -32
  18. data/lib/fathom/import.rb +37 -34
  19. data/lib/fathom/import/yaml_import.rb +22 -1
  20. data/lib/fathom/knowledge_base.rb +34 -23
  21. data/lib/fathom/knowledge_base/search.rb +19 -0
  22. data/lib/fathom/node.rb +32 -1
  23. data/lib/fathom/node/belief_node.rb +121 -0
  24. data/lib/fathom/node/cpm_node.rb +100 -0
  25. data/lib/fathom/node/data_collection.rb +97 -0
  26. data/lib/fathom/{data_node.rb → node/data_node.rb} +1 -1
  27. data/lib/fathom/{value_aggregator.rb → node/decision.rb} +5 -5
  28. data/lib/fathom/node/discrete_node.rb +41 -0
  29. data/lib/fathom/node/fact.rb +24 -0
  30. data/lib/fathom/{mc_node.rb → node/mc_node.rb} +1 -1
  31. data/lib/fathom/{enforced_name.rb → node/node_extensions/enforced_name.rb} +1 -1
  32. data/lib/fathom/{numeric_methods.rb → node/node_extensions/numeric_methods.rb} +19 -1
  33. data/lib/fathom/{plausible_range.rb → node/plausible_range.rb} +1 -1
  34. data/spec/ext/array_spec.rb +10 -0
  35. data/spec/ext/faster_csv_spec.rb +10 -0
  36. data/spec/ext/open_struct_spec.rb +20 -0
  37. data/spec/ext/string_spec.rb +7 -0
  38. data/spec/fathom/import/csv_import_spec.rb +11 -9
  39. data/spec/fathom/import/yaml_import_spec.rb +27 -7
  40. data/spec/fathom/knowledge_base_spec.rb +8 -4
  41. data/spec/fathom/node/belief_node_spec.rb +180 -0
  42. data/spec/fathom/node/cpm_node_spec.rb +144 -0
  43. data/spec/fathom/node/data_collection_spec.rb +26 -0
  44. data/spec/fathom/{data_node_spec.rb → node/data_node_spec.rb} +1 -1
  45. data/spec/fathom/node/decision_spec.rb +15 -0
  46. data/spec/fathom/node/discrete_node_spec.rb +56 -0
  47. data/spec/fathom/node/fact_spec.rb +33 -0
  48. data/spec/fathom/{mc_node_spec.rb → node/mc_node_spec.rb} +1 -1
  49. data/spec/fathom/{enforced_name_spec.rb → node/node_extensions/enforced_name_spec.rb} +1 -1
  50. data/spec/fathom/{numeric_methods_spec.rb → node/node_extensions/numeric_methods_spec.rb} +53 -11
  51. data/spec/fathom/{plausible_range_spec.rb → node/plausible_range_spec.rb} +1 -1
  52. data/spec/fathom/node_spec.rb +17 -0
  53. data/spec/fathom_spec.rb +40 -0
  54. data/spec/spec_helper.rb +3 -0
  55. data/spec/support/fact.yml +11 -0
  56. metadata +57 -30
  57. data/lib/fathom/value_multiplier.rb +0 -18
@@ -0,0 +1,144 @@
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
+
3
+ describe CPMNode do
4
+
5
+ before do
6
+ @child = BeliefNode.new(:name => :color, :values => {:red => 0.4, :green => 0.6})
7
+ @parent = BeliefNode.new(:name => :child, :values => {:asher => 0.2, :stella => 0.8})
8
+ @cpm = CPMNode.new(:child => @child, :parent => @parent)
9
+ end
10
+
11
+ it "should subclass Node" do
12
+ CPMNode.ancestors[1].should eql(Node)
13
+ end
14
+
15
+ it "should allow a BeliefNode child and parent" do
16
+ @cpm.parent.should eql(@parent)
17
+ @cpm.child.should eql(@child)
18
+ end
19
+
20
+ it "should generate a conditional probability matrix from the parent and child" do
21
+ parent_values = @cpm.parent.probabilities.col
22
+ child_values = @cpm.child.probabilities
23
+ matrix = parent_values * child_values
24
+ @cpm.values.to_a.should eql(matrix.to_a)
25
+ end
26
+
27
+ it "should alias the values with matrix" do
28
+ @cpm.values.should eql(@cpm.matrix)
29
+ end
30
+
31
+ it "should raise an error when trying to load non-BeliefNode parent or child" do
32
+ lambda{CPMNode.new(:child => :not_a_belief_node, :parent => @parent)}.should raise_error(/must be a BeliefNode/)
33
+ lambda{CPMNode.new(:child => @child, :parent => :not_a_belief_node)}.should raise_error(/must be a BeliefNode/)
34
+ end
35
+
36
+ it "should have a probability lookup" do
37
+ @cpm.should be_respond_to(:probability)
38
+ end
39
+
40
+ it "should only allow options for the parent and child names" do
41
+ lambda{@cpm.probability :not_a_link => true}.should raise_error(/unknown/i)
42
+ end
43
+
44
+ it "should be able to lookup the values we're looking for" do
45
+ # The Matrix looks like this:
46
+ # GSL::Matrix
47
+ # [ 8.000e-02 1.200e-01
48
+ # 3.200e-01 4.800e-01 ]
49
+ @cpm.probability(:color => :red, :child => :asher).should be_close(0.08, 1e-10)
50
+ @cpm.probability(:color => :red, :child => :stella).should be_close(0.32, 1e-10)
51
+ @cpm.probability(:color => :green, :child => :asher).should be_close(0.12, 1e-10)
52
+ @cpm.probability(:color => :green, :child => :stella).should be_close(0.48, 1e-10)
53
+ end
54
+
55
+ it "should look for all children values, if no child is provided" do
56
+ @cpm.probability(:child => :asher).should be_close(0.2, 1e-10)
57
+ @cpm.probability(:child => :stella).should be_close(0.8, 1e-10)
58
+ end
59
+
60
+ it "should look for all parent values, if no parent is provided" do
61
+ @cpm.probability(:color => :red).should be_close(0.4, 1e-10)
62
+ @cpm.probability(:color => :green).should be_close(0.6, 1e-10)
63
+ end
64
+
65
+ it "should provide a probability of 1 if no filter is set" do
66
+ @cpm.probability.should be_close(1.0, 1e-10)
67
+ end
68
+
69
+ it "should give a description in a hash, if description is turned on" do
70
+ @cpm.probability(:color => :red, :child => :asher, :describe => true).keys.first.should eql('P(red | asher)')
71
+ @cpm.probability(:color => :red, :child => :stella, :describe => true).keys.first.should eql('P(red | stella)')
72
+ @cpm.probability(:color => :green, :child => :asher, :describe => true).keys.first.should eql('P(green | asher)')
73
+ @cpm.probability(:color => :green, :child => :stella, :describe => true).keys.first.should eql('P(green | stella)')
74
+ @cpm.probability(:child => :asher, :describe => true).keys.first.should eql('P(red or green | asher)')
75
+ @cpm.probability(:child => :stella, :describe => true).keys.first.should eql('P(red or green | stella)')
76
+ @cpm.probability(:color => :red, :describe => true).keys.first.should eql('P(red | asher or stella)')
77
+ @cpm.probability(:color => :green, :describe => true).keys.first.should eql('P(green | asher or stella)')
78
+ @cpm.probability(:describe => true).keys.first.should eql('P(red or green | asher or stella)')
79
+ end
80
+
81
+ it "should alias p for probability" do
82
+ @cpm.p(:color => :red, :child => :asher).should be_close(0.08, 1e-10)
83
+ @cpm.p(:color => :red, :child => :stella).should be_close(0.32, 1e-10)
84
+ @cpm.p(:color => :green, :child => :asher).should be_close(0.12, 1e-10)
85
+ @cpm.p(:color => :green, :child => :stella).should be_close(0.48, 1e-10)
86
+ @cpm.p(:child => :asher).should be_close(0.2, 1e-10)
87
+ @cpm.p(:child => :stella).should be_close(0.8, 1e-10)
88
+ @cpm.p(:color => :red).should be_close(0.4, 1e-10)
89
+ @cpm.p(:color => :green).should be_close(0.6, 1e-10)
90
+ @cpm.p.should be_close(1.0, 1e-10)
91
+ end
92
+
93
+ it "should have odds for any query" do
94
+ @cpm.odds(:color => :red, :child => :asher).should be_close(0.087, 1e-3)
95
+ @cpm.odds(:color => :red, :child => :stella).should be_close(0.471, 1e-3)
96
+ @cpm.odds(:color => :green, :child => :asher).should be_close(0.136, 1e-3)
97
+ @cpm.odds(:color => :green, :child => :stella).should be_close(0.923, 1e-3)
98
+ @cpm.odds(:child => :asher).should be_close(0.25, 1e-3)
99
+ @cpm.odds(:child => :stella).should be_close(4.0, 1e-3)
100
+ @cpm.odds(:color => :red).should be_close(0.666, 1e-3)
101
+ @cpm.odds(:color => :green).should be_close(1.5, 1e-3)
102
+ @cpm.odds.should eql(1 / 0.0)
103
+ end
104
+
105
+ it "should alias o for odds" do
106
+ @cpm.o(:color => :red, :child => :asher).should be_close(0.087, 1e-3)
107
+ @cpm.o(:color => :red, :child => :stella).should be_close(0.471, 1e-3)
108
+ @cpm.o(:color => :green, :child => :asher).should be_close(0.136, 1e-3)
109
+ @cpm.o(:color => :green, :child => :stella).should be_close(0.923, 1e-3)
110
+ @cpm.o(:child => :asher).should be_close(0.25, 1e-3)
111
+ @cpm.o(:child => :stella).should be_close(4.0, 1e-3)
112
+ @cpm.o(:color => :red).should be_close(0.666, 1e-3)
113
+ @cpm.o(:color => :green).should be_close(1.5, 1e-3)
114
+ @cpm.o.should eql(1 / 0.0)
115
+ end
116
+
117
+ it "should default the name to :cpm" do
118
+ @cpm.name.should eql(:cpm)
119
+ end
120
+
121
+ it "should default the description" do
122
+ @cpm.description.should eql("Conditional Probability Matrix from child to color.")
123
+ end
124
+
125
+ it "should return a vector for likelihood" do
126
+ @cpm.likelihood(:red).should be_a(GSL::Vector)
127
+ end
128
+
129
+ it "should offer the likelihood of each parent value, given a child value" do
130
+ values = @cpm.likelihood(:red).to_a
131
+ values.sort.map {|e| (e * 100).to_i}.should eql([8, 32])
132
+
133
+ values = @cpm.likelihood(:green).to_a
134
+ values.sort.map {|e| (e * 100).to_i}.should eql([12, 48])
135
+ end
136
+
137
+ it "should offer l as an alias for likelihood" do
138
+ values = @cpm.l(:red).to_a
139
+ values.sort.map {|e| (e * 100).to_i}.should eql([8, 32])
140
+
141
+ values = @cpm.l(:green).to_a
142
+ values.sort.map {|e| (e * 100).to_i}.should eql([12, 48])
143
+ end
144
+ end
@@ -0,0 +1,26 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+
3
+ include Fathom
4
+
5
+ describe DataCollection do
6
+ it "should be a node" do
7
+ DataCollection.ancestors.should be_include(Node)
8
+ end
9
+
10
+ it "should be a DiscreteNode as well" do
11
+ DataCollection.ancestors.should be_include(DiscreteNode)
12
+ end
13
+
14
+ it "should be able to extract the discrete labels from the first discrete parent node added to it" do
15
+ pr = PlausibleRange.new(:min => 0, :max => 1)
16
+ dn = DiscreteNode.new(:labels => [1,2,3])
17
+ dc = DataCollection.new(:parents => [pr, dn])
18
+ dc.labels.should eql([1,2,3])
19
+ end
20
+
21
+ it "should enforce a name" do
22
+ dc = DataCollection.new(:labels => [1,2,3])
23
+ dc.name.should_not be_nil
24
+ end
25
+
26
+ end
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
2
 
3
3
  include Fathom
4
4
 
@@ -0,0 +1,15 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+
3
+ include Fathom
4
+
5
+ describe Decision do
6
+ it "should be a type of node" do
7
+ Decision.ancestors.should be_include(Node)
8
+ end
9
+
10
+ it "should not have a distribution or values" do
11
+ @d = Decision.new
12
+ @d.should_not be_respond_to(:distribution)
13
+ @d.should_not be_respond_to(:values)
14
+ end
15
+ end
@@ -0,0 +1,56 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+
3
+ include Fathom
4
+
5
+ describe DiscreteNode do
6
+
7
+ before do
8
+ @dn = DiscreteNode.new(:labels => [1,2,3])
9
+ end
10
+
11
+ it "should assign :true and :false for labels, if none are provided" do
12
+ lambda{@dn = DiscreteNode.new}.should_not raise_error
13
+ @dn.labels.should eql([:true, :false])
14
+ end
15
+
16
+ it "should create an array from a single value for its labels" do
17
+ dn = DiscreteNode.new(:labels => 1)
18
+ dn.labels.should eql([1])
19
+ end
20
+
21
+ it "should take the values and convert them to labels, if there are no labels provided" do
22
+ dn = DiscreteNode.new(:values => [1,2,3])
23
+ dn.labels.should eql([1,2,3])
24
+ end
25
+
26
+ it "should create a unique list of labels" do
27
+ dn = DiscreteNode.new(:labels => [1,1,2,3,1])
28
+ dn.labels.should eql([1,2,3])
29
+ end
30
+
31
+ it "should expose the size of the labels" do
32
+ @dn.size.should eql(3)
33
+ end
34
+
35
+ it "should alias length for size" do
36
+ @dn.length.should eql(3)
37
+ end
38
+
39
+ it "should have a default distribution of DiscreteUniform" do
40
+ @dn.distribution.should eql(Fathom::Distributions::DiscreteUniform)
41
+ end
42
+
43
+ it "should be able to produce a random variable from the discrete distribution" do
44
+ @dn = DiscreteNode.new(:labels => [1,2])
45
+ # 9.31322574615479e-10 chance of failing
46
+ 30.times.map {@dn.rand}.uniq.sort.should eql([1,2])
47
+ end
48
+
49
+ it "should offer a uniform likelihood" do
50
+ @dn.likelihood(:anything).to_a.should eql([1.0, 1.0, 1.0])
51
+ end
52
+
53
+ it "should offer an alias of l for likelihood" do
54
+ @dn.l(:anything).to_a.should eql([1.0, 1.0, 1.0])
55
+ end
56
+ end
@@ -0,0 +1,33 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+
3
+ include Fathom
4
+
5
+ describe Fact do
6
+ before do
7
+ @f = Fact.new(
8
+ :value => 2,
9
+ :name => "Some Fact",
10
+ :description => "Just a demonstration of what a good fact may look like. Facts are really about things that are certain--there are 7 days in a week, that sort of thing. Too often I was creating a PlausibleRange with the min and max to the same value."
11
+ )
12
+ end
13
+
14
+ it "should be a Node" do
15
+ Fact.ancestors.should be_include(Fathom::Node)
16
+ end
17
+
18
+ it "should take a value (rather than values)" do
19
+ @f.value.should eql(2)
20
+ end
21
+
22
+ it "should return the value for rand" do
23
+ @f.rand.should eql(2)
24
+ end
25
+
26
+ it "should have removed the distribution and values methods" do
27
+ @f.should_not be_respond_to(:values)
28
+ @f.should_not be_respond_to(:distribution)
29
+ @n = Node.new(:distribution => :standard, :values => [1,2,3])
30
+ @n.values.should eql([1,2,3])
31
+ @n.distribution.should eql(Fathom::Distributions::Gaussian)
32
+ end
33
+ end
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
2
 
3
3
  include Fathom
4
4
 
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper')
2
2
 
3
3
  include Fathom::Distributions
4
4
 
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper')
2
2
 
3
3
  include Fathom
4
4
 
@@ -11,11 +11,11 @@ describe NumericMethods do
11
11
  end
12
12
 
13
13
  before do
14
- @n = DummyNumericNode.new(@opts)
14
+ @n = Distributions::DummyNumericNode.new(@opts)
15
15
  end
16
16
 
17
17
  it "should respond to a rand method" do
18
- @n = DummyNumericNode.new
18
+ @n = Distributions::DummyNumericNode.new
19
19
  @n.should be_respond_to(:rand)
20
20
  end
21
21
 
@@ -33,7 +33,7 @@ describe NumericMethods do
33
33
  end
34
34
 
35
35
  it "should not raise an error when looking for a standard deviation and there is no vector" do
36
- @n = DummyNumericNode.new
36
+ @n = Distributions::DummyNumericNode.new
37
37
  @n.vector.should be_nil
38
38
  @n.sd.should be_nil
39
39
  end
@@ -43,17 +43,17 @@ describe NumericMethods do
43
43
  end
44
44
 
45
45
  it "should return nil for mean if it doesn't have a vector" do
46
- @n = DummyNumericNode.new
46
+ @n = Distributions::DummyNumericNode.new
47
47
  @n.mean.should be_nil
48
48
  end
49
49
 
50
50
  it "should provide rand, if it has a vector" do
51
- Gaussian.should_receive(:rand).and_return(0.5)
51
+ Distributions::Gaussian.should_receive(:rand).and_return(0.5)
52
52
  @n.rand
53
53
  end
54
54
 
55
55
  it "should return nil for rand if it doesn't have a vector" do
56
- @n = DummyNumericNode.new
56
+ @n = Distributions::DummyNumericNode.new
57
57
  @n.mean.should be_nil
58
58
  end
59
59
 
@@ -65,18 +65,60 @@ describe NumericMethods do
65
65
  end
66
66
 
67
67
  it "should pass the mean and standard deviation to the distribution when calculating an inverse_cdf" do
68
- @n.inverse_cdf.should eql(Gaussian.inverse_cdf(:mean => @n.mean, :sd => @n.sd))
68
+ @n.inverse_cdf.should eql(Distributions::Gaussian.inverse_cdf(:mean => @n.mean, :sd => @n.sd))
69
69
  end
70
70
 
71
71
  it "should pass the mean and standard deviation to the distribution when calculating an lower_bound" do
72
- @n.lower_bound.should eql(Gaussian.lower_bound(:mean => @n.mean, :sd => @n.sd))
72
+ @n.lower_bound.should eql(Distributions::Gaussian.lower_bound(:mean => @n.mean, :sd => @n.sd))
73
73
  end
74
74
 
75
75
  it "should pass the mean and standard deviation to the distribution when calculating an upper_bound" do
76
- @n.upper_bound.should eql(Gaussian.upper_bound(:mean => @n.mean, :sd => @n.sd))
76
+ @n.upper_bound.should eql(Distributions::Gaussian.upper_bound(:mean => @n.mean, :sd => @n.sd))
77
77
  end
78
78
 
79
79
  it "should pass the mean and standard deviation to the distribution when calculating an interval_values" do
80
- @n.interval_values.should eql(Gaussian.interval_values(:mean => @n.mean, :sd => @n.sd))
80
+ @n.interval_values.should eql(Distributions::Gaussian.interval_values(:mean => @n.mean, :sd => @n.sd))
81
81
  end
82
+
83
+ it "should have a coefficient_of_variation" do
84
+ @n.coefficient_of_variation.should eql(@n.sd / @n.mean)
85
+ end
86
+
87
+ it "should have a cov alias for coefficient_of_variation" do
88
+ @n.cov.should eql(@n.coefficient_of_variation)
89
+ end
90
+
91
+ it "should have a summary hash" do
92
+ @n.should be_respond_to(:summary)
93
+ @n.summary.should be_a(Hash)
94
+ end
95
+
96
+ it "should have a mean in the summary" do
97
+ @n.summary[:mean].should eql(@n.mean)
98
+ end
99
+
100
+ it "should have a standard deviation in the summary" do
101
+ @n.summary[:standard_deviation].should eql(@n.standard_deviation)
102
+ end
103
+
104
+ it "should have a min in the summary" do
105
+ @n.summary[:min].should eql(@n.vector.min)
106
+ end
107
+
108
+ it "should have a max in the summary" do
109
+ @n.summary[:max].should eql(@n.vector.max)
110
+ end
111
+
112
+ it "should have a lower_bound in the summary" do
113
+ @n.summary[:lower_bound].should eql(@n.lower_bound)
114
+ end
115
+
116
+ it "should have an upper_bound in the summary" do
117
+ @n.summary[:upper_bound].should eql(@n.upper_bound)
118
+ end
119
+
120
+ it "should have a coefficient_of_variation in the summary" do
121
+ @n.summary[:coefficient_of_variation].should eql(@n.sd / @n.mean)
122
+ end
123
+
82
124
  end
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
2
 
3
3
  include Fathom
4
4
 
@@ -151,5 +151,22 @@ describe Node do
151
151
  n2 = Node.new :name => 'n2', :parent => n1
152
152
  n1.n2.should eql(n2)
153
153
  end
154
+
155
+ # it "should have introduced the Spira infrastructure by now" do
156
+ # Object.should be_const_defined(:Spira)
157
+ # end
158
+
159
+ # it "should have defined the spira repository" do
160
+ # Spira.repositories[:default].should be_a(RDF::Repository)
161
+ # end
162
+
163
+ # This really changes the interface for everything. So, either I re-purpose a node, or something...
164
+ # Next steps: read the Spira source and figure out what's easier to do: replace the functionality
165
+ # or change my interfaces. (btw, 9 issues, only.
166
+ # Mostly just tied to creating the node accessors (n1.n2 type thing))
167
+
168
+ # it "should have Spira included" do
169
+ # Node.included_modules.should be_include(Spira::Resource)
170
+ # end
154
171
 
155
172
  end
@@ -14,3 +14,43 @@ describe "Fathom" do
14
14
  Fathom.kb.should eql(Fathom.knowledge_base)
15
15
  end
16
16
  end
17
+
18
+
19
+ =begin
20
+ OK, so I'm thinking about how this works....
21
+ A knowledge base is something I work from. I could create a default knowledge base. I won't be able to save it to anywhere permenant, but possibly I want to just be able to start working for a while, have things link up, then decide if I've created anything of value...
22
+
23
+ Or, I could always use an explicit knowledge base.
24
+
25
+ Frankly, the way I thought I would use it would be:
26
+
27
+ home_budget = Fathom::KnowledgeBase.find_or_create(:home_budget)
28
+ home_budget.new(:plausible_range, :hard_min => 0, :min => 500, :max => 750, :name => "Referral Income", :type => :revenue)
29
+ revenue = home_budget.find(...)
30
+ mc_revenue = home_budget.new(:mc_node, :values => revenue, :type => :revenue, :name => "Combined Income", :description => "A collection of income from our investments, job, and miscellaneous projects.") do |s|
31
+ :combined_income => s.values.inject(0.0) do |sum, e|
32
+ sum += e
33
+ end
34
+ end
35
+ home_budget.new(:svm_node, :values => mc_revenue.process, :type => :revenue)
36
+
37
+ mc_daily_expenses = home_budget.new(:mc_node,
38
+ :values => {:expenses => home_budget.find(...), :dates => Fathom::KnowledgeBase.find(:dates)},
39
+ :type => :expenses,
40
+ :name => "Combined Expenses",
41
+ :description => "All of the household expenses on a monthly basis"
42
+ ) do |s|
43
+ cycle ||= (1..365).cycle
44
+ day = s.values.dates.day_in_year(cycle.next)
45
+ ...
46
+ end
47
+
48
+ create a mini belief network about whether the budget is appropriate here...
49
+
50
+
51
+
52
+ For this demo, I picked up a project I had been working on. I added a Monte Carlo analysis that
53
+ could always be re-run, in case the inputs change. I don't yet have an automated way to keep up with the
54
+ changes to the inputs on that, but I do on the belief nodes.
55
+
56
+ =end