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
@@ -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
|
@@ -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__) + '
|
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
|
data/spec/fathom/node_spec.rb
CHANGED
@@ -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
|
data/spec/fathom_spec.rb
CHANGED
@@ -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
|