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
data/.autotest
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
# require 'autotest/fsevent'
|
2
|
+
# require 'autotest/growl'
|
3
|
+
|
4
|
+
Autotest.add_hook :initialize do |autotest|
|
5
|
+
# Keeps me from calling something document_spec.rb
|
6
|
+
# %w{.git .svn .hg .DS_Store ._* vendor tmp log doc}.each do |exception|
|
7
|
+
%w{.git .svn .hg .DS_Store ._* vendor tmp log}.each do |exception|
|
8
|
+
autotest.add_exception(exception)
|
9
|
+
end
|
10
|
+
end
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,11 +1,15 @@
|
|
1
1
|
GEM
|
2
2
|
remote: http://rubygems.org/
|
3
3
|
specs:
|
4
|
+
ZenTest (4.4.0)
|
5
|
+
addressable (2.2.2)
|
4
6
|
columnize (0.3.1)
|
5
7
|
diff-lcs (1.1.2)
|
6
8
|
fastercsv (1.5.3)
|
7
9
|
linecache (0.43)
|
8
10
|
macaddr (1.0.0)
|
11
|
+
rdf (0.3.0.pre)
|
12
|
+
addressable (>= 2.2)
|
9
13
|
rspec (2.0.1)
|
10
14
|
rspec-core (~> 2.0.1)
|
11
15
|
rspec-expectations (~> 2.0.1)
|
@@ -21,6 +25,8 @@ GEM
|
|
21
25
|
ruby-debug-base (~> 0.10.3.0)
|
22
26
|
ruby-debug-base (0.10.3)
|
23
27
|
linecache (>= 0.3)
|
28
|
+
sparql-algebra (0.0.1)
|
29
|
+
rdf (= 0.3.0.pre)
|
24
30
|
uuid (2.3.1)
|
25
31
|
macaddr (~> 1.0)
|
26
32
|
|
@@ -28,7 +34,9 @@ PLATFORMS
|
|
28
34
|
ruby
|
29
35
|
|
30
36
|
DEPENDENCIES
|
37
|
+
ZenTest
|
31
38
|
fastercsv
|
32
39
|
rspec
|
33
40
|
ruby-debug
|
41
|
+
sparql-algebra
|
34
42
|
uuid
|
data/TODO.md
CHANGED
@@ -1,30 +1,7 @@
|
|
1
1
|
TODO
|
2
2
|
====
|
3
3
|
|
4
|
-
|
5
|
-
------------
|
6
|
-
|
7
|
-
I've just made some big refactoring steps regarding the organization of the system and the distributions. To make sure we're there:
|
8
|
-
|
9
|
-
* Go back and test the 4 distributions I decided on
|
10
|
-
* Finish the discrete ideas, adding size to the node and automatically using that for stats
|
11
|
-
* Create the idea of a labeled, multinomial node
|
12
|
-
* Add SQLite3 for in-memory set operations for a labeled, multinomial node
|
13
|
-
* Make sure we are not defining methods on all objects in a class when they should only be set for a single object.
|
14
|
-
|
15
|
-
Also, the general organization of the system could be broken down better:
|
16
|
-
|
17
|
-
* agent
|
18
|
-
* distributions
|
19
|
-
* node
|
20
|
-
* import
|
21
|
-
* causal_graph
|
22
|
-
* belief_network
|
23
|
-
* knowledge_base
|
24
|
-
* apophenia
|
25
|
-
* simulation
|
26
|
-
|
27
|
-
Belief Networks (0.3)
|
4
|
+
Belief Networks (0.3.1)
|
28
5
|
---------------
|
29
6
|
|
30
7
|
To get these delivered, I need to revisit the edge logic, to make sure it's easy to extend each edge with an object.
|
@@ -35,11 +12,21 @@ Then:
|
|
35
12
|
* Network propagation
|
36
13
|
* Network testing (polytree)
|
37
14
|
|
38
|
-
|
15
|
+
Reorganizing (0.3.2)
|
16
|
+
------------
|
17
|
+
|
18
|
+
I've just made some big refactoring steps regarding the organization of the system and the distributions. To make sure we're there:
|
19
|
+
|
20
|
+
* Fix the inheritance structure
|
21
|
+
* Go back and test the 4 distributions I decided on
|
22
|
+
* Add SQLite3 for in-memory set operations for a labeled, multinomial node
|
23
|
+
|
24
|
+
Agent Based Modeling (0.3.3)
|
39
25
|
--------------------
|
40
26
|
|
41
27
|
* Add parameter-passing standards for callbacks
|
42
28
|
* Add EventMachine and async capabilities (Including the cluster idea)
|
29
|
+
* Make sure the scope of dynamic methods defined are correct (class, object, singleton) (on properties and simulation)
|
43
30
|
|
44
31
|
Knowledge Base (0.4)
|
45
32
|
--------------
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.3.
|
1
|
+
0.3.1
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
data/lib/fathom.rb
CHANGED
@@ -21,24 +21,27 @@ module Fathom
|
|
21
21
|
|
22
22
|
# Autoload classes and modules so that we only load as much of the library as we're using.
|
23
23
|
# This allows us to have a fairly large library without taking up a lot of memory unless we need it.
|
24
|
-
autoload :Inverter, "inverter"
|
25
24
|
autoload :Node, "node"
|
26
|
-
autoload :
|
25
|
+
autoload :BeliefNode, "node/belief_node"
|
26
|
+
autoload :DataCollection, "node/data_collection"
|
27
|
+
autoload :DataNode, "node/data_node"
|
28
|
+
autoload :DiscreteNode, "node/discrete_node"
|
29
|
+
autoload :MCNode, "node/mc_node"
|
30
|
+
autoload :PlausibleRange, "node/plausible_range"
|
31
|
+
autoload :Fact, "node/fact"
|
32
|
+
autoload :Decision, "node/decision"
|
33
|
+
autoload :CPMNode, 'node/cpm_node'
|
34
|
+
|
27
35
|
autoload :ValueDescription, "value_description"
|
28
|
-
autoload :ValueAggregator, "value_aggregator"
|
29
|
-
autoload :ValueMultiplier, "value_multiplier"
|
30
36
|
autoload :MonteCarloSet, "monte_carlo_set"
|
31
|
-
autoload :MCNode, "mc_node"
|
32
|
-
autoload :CausalGraph, "causal_graph"
|
33
|
-
autoload :DataNode, "data_node"
|
34
37
|
autoload :KnowledgeBase, "knowledge_base"
|
35
38
|
|
36
39
|
autoload :Import, "import"
|
37
40
|
autoload :ImportNode, "import/import_node"
|
38
41
|
autoload :YAMLImport, 'import/yaml_import'
|
39
42
|
autoload :CSVImport, 'import/csv_import'
|
40
|
-
autoload :RDFImport, 'import/rdf_import'
|
41
|
-
autoload :SQLiteImport, 'import/sqlite_import'
|
43
|
+
# autoload :RDFImport, 'import/rdf_import'
|
44
|
+
# autoload :SQLiteImport, 'import/sqlite_import'
|
42
45
|
|
43
46
|
autoload :Simulation, 'simulation'
|
44
47
|
autoload :TickMethods, 'simulation/tick_methods'
|
@@ -48,10 +51,10 @@ module Fathom
|
|
48
51
|
autoload :Properties, 'agent/properties'
|
49
52
|
autoload :AgentCluster, 'agent/agent_cluster'
|
50
53
|
|
51
|
-
autoload :
|
52
|
-
autoload :
|
54
|
+
autoload :EnforcedName, 'node/node_extensions/enforced_name'
|
55
|
+
autoload :NumericMethods, 'node/node_extensions/numeric_methods'
|
53
56
|
|
54
|
-
|
57
|
+
require 'distributions'
|
55
58
|
module Distributions
|
56
59
|
autoload :Gaussian, 'distributions/gaussian'
|
57
60
|
autoload :Uniform, 'distributions/uniform'
|
@@ -66,4 +69,4 @@ module Fathom
|
|
66
69
|
end
|
67
70
|
|
68
71
|
# Temporary
|
69
|
-
include Fathom
|
72
|
+
# include Fathom
|
data/lib/fathom/agent.rb
CHANGED
@@ -4,14 +4,6 @@ class Fathom::Agent
|
|
4
4
|
# =================
|
5
5
|
# = Class Methods =
|
6
6
|
# =================
|
7
|
-
class << self
|
8
|
-
def define_node_accessor(name, node)
|
9
|
-
method_name = ("node_for_" + name.to_s.downcase.gsub(/\s+/, '_')).to_sym
|
10
|
-
define_method(method_name) do
|
11
|
-
node
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
15
7
|
|
16
8
|
include Properties
|
17
9
|
|
@@ -36,7 +28,14 @@ class Fathom::Agent
|
|
36
28
|
nodes.each do |name, node|
|
37
29
|
next unless self.class.properties.include?(name)
|
38
30
|
states[name] = node.respond_to?(:rand) ? node.rand : node
|
39
|
-
|
31
|
+
|
32
|
+
method_name = ("node_for_" + name.to_s.downcase.gsub(/\s+/, '_')).to_sym
|
33
|
+
(class << self; self; end).module_eval do
|
34
|
+
define_method method_name do
|
35
|
+
node
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
40
39
|
end
|
41
40
|
end
|
42
41
|
|
File without changes
|
File without changes
|
File without changes
|
data/lib/fathom/archive/node.rb
CHANGED
@@ -23,7 +23,10 @@ class Node
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def inspect
|
26
|
-
|
26
|
+
matched_array = []
|
27
|
+
self.labels.each_with_index {|e, i| matched_array << e; matched_array << self.probabilities[i]}
|
28
|
+
"Node: #{self.name.to_s} #{self.labels.inspect} #{self.probabilities.to_a.inspect}"
|
29
|
+
"Node: #{self.name.to_s} #{matched_array.inspect}"
|
27
30
|
end
|
28
31
|
|
29
32
|
def belief
|
@@ -34,7 +37,27 @@ class Node
|
|
34
37
|
alias :l :likelihood
|
35
38
|
alias :p :probabilities
|
36
39
|
|
40
|
+
def probability(label)
|
41
|
+
probabilities[index_for(label)]
|
42
|
+
end
|
43
|
+
|
44
|
+
def inverse_probability(label)
|
45
|
+
1 - probability(label)
|
46
|
+
end
|
47
|
+
|
48
|
+
def odds(label)
|
49
|
+
probability(label) / inverse_probability(label)
|
50
|
+
end
|
51
|
+
|
52
|
+
def to_a
|
53
|
+
self.probabilities.to_a
|
54
|
+
end
|
55
|
+
|
37
56
|
protected
|
57
|
+
|
58
|
+
def index_for(label)
|
59
|
+
labels.index(label)
|
60
|
+
end
|
38
61
|
|
39
62
|
def assert_likelihood
|
40
63
|
@likelihood = Vector.ary_to_gv(Array.new(@probabilities.size, 1))
|
@@ -6,40 +6,19 @@ class Fathom::Distributions::DiscreteUniform
|
|
6
6
|
@rng ||= GSL::Rng.alloc(GSL::Rng::MT19937_1999, Kernel.rand(100_000))
|
7
7
|
end
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
sd ||= opts[:std]
|
17
|
-
sd ||= opts[:standard_deviation]
|
18
|
-
lower = opts.fetch(:lower, true)
|
19
|
-
lower = false if opts[:upper]
|
20
|
-
confidence_interval = opts.fetch(:confidence_interval, 0.05)
|
21
|
-
value = lower ? GSL::Cdf.ugaussian_Pinv(confidence_interval) : GSL::Cdf.ugaussian_Qinv(confidence_interval)
|
22
|
-
value + mean
|
23
|
-
end
|
24
|
-
alias :lower_bound :inverse_cdf
|
25
|
-
|
26
|
-
def upper_bound(opts={})
|
27
|
-
inverse_cdf(opts.merge(:lower => false))
|
28
|
-
end
|
29
|
-
|
30
|
-
def interval_values(opts={})
|
31
|
-
confidence_interval = opts.fetch(:confidence_interval, 0.9)
|
32
|
-
bound = (1 - confidence_interval) / 2.0
|
33
|
-
[lower_bound(opts.merge(:confidence_interval => bound)), upper_bound(opts.merge(:confidence_interval => bound))]
|
34
|
-
end
|
35
|
-
|
36
|
-
# If only I had the background to explain what this is....
|
37
|
-
# I want to know how many standard deviations are expressed by the confidence interval
|
38
|
-
# I can then divide the range by this number to get the standard deviation
|
39
|
-
def standard_deviations_under(confidence_interval)
|
40
|
-
GSL::Cdf.ugaussian_Qinv((1 - confidence_interval) / 2) * 2
|
9
|
+
# Bounded from 0 to size - 1
|
10
|
+
def rand(size)
|
11
|
+
value = get_rand(size)
|
12
|
+
while value < 0 or value >= size
|
13
|
+
value = get_rand(size)
|
14
|
+
end
|
15
|
+
value
|
41
16
|
end
|
42
17
|
|
18
|
+
protected
|
19
|
+
def get_rand(size)
|
20
|
+
(rng.ugaussian / size).floor + 1
|
21
|
+
end
|
43
22
|
|
44
23
|
end
|
45
24
|
end
|
data/lib/fathom/import.rb
CHANGED
@@ -33,50 +33,53 @@ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'fathom'))
|
|
33
33
|
This way data from spreadsheets or YAML files can easily be added to the knowledge base.
|
34
34
|
|
35
35
|
=end
|
36
|
-
|
36
|
+
|
37
|
+
module Fathom
|
38
|
+
class Import
|
37
39
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
40
|
+
class << self
|
41
|
+
def import(opts={})
|
42
|
+
importer = new(opts)
|
43
|
+
importer.import
|
44
|
+
end
|
42
45
|
end
|
43
|
-
end
|
44
46
|
|
45
|
-
|
47
|
+
attr_reader :content, :options, :import_node
|
46
48
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
49
|
+
def initialize(opts={})
|
50
|
+
@options = OptionsHash.new(opts)
|
51
|
+
@content = @options[:content]
|
52
|
+
@import_node = ImportNode.new(opts)
|
53
|
+
end
|
52
54
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
55
|
+
def import
|
56
|
+
import_methods.each do |method|
|
57
|
+
klass, initialization_data = self.send(method.to_sym)
|
58
|
+
initialization_data.each do |values|
|
59
|
+
extract_nodes(klass, values)
|
60
|
+
end
|
58
61
|
end
|
62
|
+
self.import_node
|
59
63
|
end
|
60
|
-
self.import_node
|
61
|
-
end
|
62
64
|
|
63
|
-
|
65
|
+
protected
|
64
66
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
67
|
+
# Just blankly try to initiate a node. If it fails, silently fail for now.
|
68
|
+
def extract_nodes(klass, values)
|
69
|
+
begin
|
70
|
+
node = klass.new(values)
|
71
|
+
if node
|
72
|
+
self.import_node.add_child(node)
|
73
|
+
Fathom.knowledge_base[node.name] = node
|
74
|
+
end
|
75
|
+
rescue
|
76
|
+
nil
|
72
77
|
end
|
73
|
-
rescue
|
74
|
-
nil
|
75
78
|
end
|
76
|
-
end
|
77
79
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
80
|
+
def import_methods
|
81
|
+
(self.methods - self.class.superclass.instance_methods).map {|m| m if m =~ /import_\w+/}.compact
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
82
85
|
end
|
@@ -2,7 +2,7 @@ require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'fathom')
|
|
2
2
|
require 'open-uri'
|
3
3
|
require 'yaml'
|
4
4
|
|
5
|
-
class Fathom::YAMLImport < Import
|
5
|
+
class Fathom::YAMLImport < Fathom::Import
|
6
6
|
def import_plausible_ranges
|
7
7
|
assert_yaml_content
|
8
8
|
plausible_ranges = extract_plausible_ranges
|
@@ -15,6 +15,12 @@ class Fathom::YAMLImport < Import
|
|
15
15
|
[DataNode, data_nodes]
|
16
16
|
end
|
17
17
|
|
18
|
+
def import_facts
|
19
|
+
assert_yaml_content
|
20
|
+
facts = extract_facts
|
21
|
+
[Fact, facts]
|
22
|
+
end
|
23
|
+
|
18
24
|
protected
|
19
25
|
def assert_yaml_content
|
20
26
|
return @yaml_content if @yaml_content
|
@@ -27,7 +33,21 @@ class Fathom::YAMLImport < Import
|
|
27
33
|
@yaml_content = YAML.load(file_contents)
|
28
34
|
end
|
29
35
|
|
36
|
+
def extract_facts
|
37
|
+
return [] unless @yaml_content
|
38
|
+
output = []
|
39
|
+
@yaml_content.each do |e|
|
40
|
+
next unless e.is_a?(Array)
|
41
|
+
next unless e.first.to_s.match(/fact/i)
|
42
|
+
obj = e[1]
|
43
|
+
next unless obj.is_a?(Hash)
|
44
|
+
output << obj
|
45
|
+
end
|
46
|
+
output
|
47
|
+
end
|
48
|
+
|
30
49
|
def extract_plausible_ranges
|
50
|
+
return [] unless @yaml_content
|
31
51
|
@yaml_content.inject([]) do |list, array|
|
32
52
|
name, value = array.first, array.last
|
33
53
|
if value.is_a?(Hash)
|
@@ -40,6 +60,7 @@ class Fathom::YAMLImport < Import
|
|
40
60
|
end
|
41
61
|
|
42
62
|
def extract_data_nodes
|
63
|
+
return [] unless @yaml_content
|
43
64
|
@yaml_content.inject([]) do |list, array|
|
44
65
|
name, value = array.first, array.last
|
45
66
|
list << {:name => name, :values => value} if value.is_a?(Array)
|