fathom 0.3.7 → 0.5.0

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 (134) hide show
  1. data/.autotest +7 -5
  2. data/.document +2 -2
  3. data/Gemfile +9 -10
  4. data/{LICENSE → LICENSE.txt} +1 -1
  5. data/README.md +29 -90
  6. data/Rakefile +34 -32
  7. data/VERSION +1 -1
  8. data/fathom.gemspec +105 -0
  9. data/features/fathom.feature +26 -0
  10. data/features/step_definitions/fathom_steps.rb +23 -0
  11. data/features/support/env.rb +13 -0
  12. data/lib/ext/array.rb +6 -2
  13. data/lib/ext/string.rb +86 -7
  14. data/lib/fathom.rb +51 -88
  15. data/lib/fathom/behaviors/attribute_system.rb +91 -0
  16. data/lib/fathom/behaviors/context_behavior.rb +28 -0
  17. data/lib/fathom/behaviors/plugins.rb +16 -0
  18. data/lib/fathom/contexts/network_population.rb +47 -0
  19. data/lib/fathom/contexts/network_traversal.rb +4 -0
  20. data/lib/fathom/data/adjacency_matrix.rb +27 -0
  21. data/lib/fathom/data/definition.rb +22 -0
  22. data/lib/fathom/data/edge.rb +58 -0
  23. data/lib/fathom/data/network.rb +35 -0
  24. data/lib/fathom/data/outcome.rb +30 -0
  25. data/lib/fathom/data/property.rb +31 -0
  26. data/lib/fathom/data/variable.rb +59 -0
  27. data/lib/fathom/roles/general_graph_tools.rb +87 -0
  28. data/lib/fathom/roles/network_builder.rb +61 -0
  29. data/spec/fathom/behaviors/attribute_system_spec.rb +141 -0
  30. data/spec/fathom/behaviors/context_behavior_spec.rb +15 -0
  31. data/spec/fathom/behaviors/plugins_spec.rb +80 -0
  32. data/spec/fathom/contexts/network_population_spec.rb +55 -0
  33. data/spec/fathom/contexts/network_traversal_spec.rb +11 -0
  34. data/spec/fathom/data/adjacency_matrix_spec.rb +42 -0
  35. data/spec/fathom/data/definition_spec.rb +19 -0
  36. data/spec/fathom/data/edge_spec.rb +77 -0
  37. data/spec/fathom/data/network_spec.rb +72 -0
  38. data/spec/fathom/data/outcome_spec.rb +17 -0
  39. data/spec/fathom/data/property_spec.rb +17 -0
  40. data/spec/fathom/data/variable_spec.rb +101 -0
  41. data/spec/fathom/ext/array_spec.rb +17 -0
  42. data/spec/fathom/ext/string_spec.rb +90 -0
  43. data/spec/fathom/roles/general_graph_tools_spec.rb +95 -0
  44. data/spec/fathom/roles/network_builder_spec.rb +90 -0
  45. data/spec/fathom_spec.rb +28 -49
  46. data/spec/spec_helper.rb +7 -11
  47. data/spec/support/context_behavior.rb +14 -0
  48. data/spec/support/custom_matchers.rb +12 -0
  49. data/spec/support/files.rb +8 -0
  50. data/spec/support/network.yml +42 -0
  51. metadata +133 -174
  52. data/.bundle/config +0 -2
  53. data/.gitignore +0 -6
  54. data/Gemfile.lock +0 -42
  55. data/TODO.md +0 -127
  56. data/autotest/discover.rb +0 -1
  57. data/lib/ext/faster_csv.rb +0 -1
  58. data/lib/ext/open_struct.rb +0 -17
  59. data/lib/fathom/agent.rb +0 -48
  60. data/lib/fathom/agent/agent_cluster.rb +0 -23
  61. data/lib/fathom/agent/properties.rb +0 -48
  62. data/lib/fathom/archive/causal_graph.rb +0 -12
  63. data/lib/fathom/archive/concept.rb +0 -83
  64. data/lib/fathom/archive/conditional_probability_matrix.rb +0 -119
  65. data/lib/fathom/archive/inverter.rb +0 -20
  66. data/lib/fathom/archive/n2.rb +0 -198
  67. data/lib/fathom/archive/n3.rb +0 -119
  68. data/lib/fathom/archive/node.rb +0 -97
  69. data/lib/fathom/archive/noodle.rb +0 -136
  70. data/lib/fathom/archive/scratch.rb +0 -45
  71. data/lib/fathom/distributions.rb +0 -8
  72. data/lib/fathom/distributions/discrete_gaussian.rb +0 -44
  73. data/lib/fathom/distributions/discrete_uniform.rb +0 -25
  74. data/lib/fathom/distributions/gaussian.rb +0 -46
  75. data/lib/fathom/distributions/uniform.rb +0 -35
  76. data/lib/fathom/import.rb +0 -85
  77. data/lib/fathom/import/csv_import.rb +0 -59
  78. data/lib/fathom/import/import_node.rb +0 -17
  79. data/lib/fathom/import/yaml_import.rb +0 -74
  80. data/lib/fathom/knowledge_base.rb +0 -46
  81. data/lib/fathom/knowledge_base/search.rb +0 -19
  82. data/lib/fathom/monte_carlo_set.rb +0 -152
  83. data/lib/fathom/node.rb +0 -139
  84. data/lib/fathom/node/belief_node.rb +0 -121
  85. data/lib/fathom/node/cpm_node.rb +0 -100
  86. data/lib/fathom/node/data_collection.rb +0 -97
  87. data/lib/fathom/node/data_node.rb +0 -22
  88. data/lib/fathom/node/decision.rb +0 -11
  89. data/lib/fathom/node/discrete_node.rb +0 -41
  90. data/lib/fathom/node/fact.rb +0 -24
  91. data/lib/fathom/node/mc_node.rb +0 -70
  92. data/lib/fathom/node/node_extensions/enforced_name.rb +0 -12
  93. data/lib/fathom/node/node_extensions/numeric_methods.rb +0 -68
  94. data/lib/fathom/node/plausible_range.rb +0 -98
  95. data/lib/fathom/simulation.rb +0 -59
  96. data/lib/fathom/simulation/tick_methods.rb +0 -25
  97. data/lib/fathom/simulation/tick_simulation.rb +0 -12
  98. data/lib/fathom/value_description.rb +0 -79
  99. data/lib/options_hash.rb +0 -186
  100. data/spec/ext/array_spec.rb +0 -10
  101. data/spec/ext/faster_csv_spec.rb +0 -10
  102. data/spec/ext/open_struct_spec.rb +0 -20
  103. data/spec/ext/string_spec.rb +0 -7
  104. data/spec/fathom/agent/agent_cluster_spec.rb +0 -17
  105. data/spec/fathom/agent_spec.rb +0 -51
  106. data/spec/fathom/distributions/discrete_gaussian_spec.rb +0 -64
  107. data/spec/fathom/distributions/discrete_uniform_spec.rb +0 -0
  108. data/spec/fathom/distributions/gaussian_spec.rb +0 -64
  109. data/spec/fathom/distributions/uniform_spec.rb +0 -0
  110. data/spec/fathom/import/csv_import_spec.rb +0 -52
  111. data/spec/fathom/import/import_node_spec.rb +0 -10
  112. data/spec/fathom/import/yaml_import_spec.rb +0 -73
  113. data/spec/fathom/import_spec.rb +0 -36
  114. data/spec/fathom/knowledge_base_spec.rb +0 -20
  115. data/spec/fathom/monte_carlo_set_spec.rb +0 -149
  116. data/spec/fathom/node/belief_node_spec.rb +0 -180
  117. data/spec/fathom/node/cpm_node_spec.rb +0 -144
  118. data/spec/fathom/node/data_collection_spec.rb +0 -26
  119. data/spec/fathom/node/data_node_spec.rb +0 -102
  120. data/spec/fathom/node/decision_spec.rb +0 -15
  121. data/spec/fathom/node/discrete_node_spec.rb +0 -56
  122. data/spec/fathom/node/fact_spec.rb +0 -33
  123. data/spec/fathom/node/mc_node_spec.rb +0 -66
  124. data/spec/fathom/node/node_extensions/enforced_name_spec.rb +0 -15
  125. data/spec/fathom/node/node_extensions/numeric_methods_spec.rb +0 -124
  126. data/spec/fathom/node/plausible_range_spec.rb +0 -151
  127. data/spec/fathom/node_spec.rb +0 -172
  128. data/spec/fathom/simulation/tick_simulation_spec.rb +0 -32
  129. data/spec/fathom/simulation_spec.rb +0 -24
  130. data/spec/fathom/value_description_spec.rb +0 -70
  131. data/spec/support/demo.yml +0 -17
  132. data/spec/support/demo_agent.rb +0 -8
  133. data/spec/support/dummy_numeric_node.rb +0 -8
  134. data/spec/support/fact.yml +0 -11
@@ -0,0 +1,4 @@
1
+ module Fathom
2
+ class NetworkTraversal
3
+ end
4
+ end
@@ -0,0 +1,27 @@
1
+ module Fathom
2
+ class AdjacencyMatrix
3
+
4
+ include Enumerable
5
+
6
+ def initialize(default=0)
7
+ @default = default
8
+ @store = Hash.new(default)
9
+ end
10
+
11
+ def [](parent, child)
12
+ @store[[parent, child]]
13
+ end
14
+
15
+ def []=(parent, child, value)
16
+ if value == @default
17
+ @store.delete([parent, child]) # preserves a little memory
18
+ else
19
+ @store[[parent, child]] = value
20
+ end
21
+ end
22
+
23
+ def each
24
+ @store.each {|k, v| yield(k,v)}
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,22 @@
1
+ module Fathom
2
+ class Definition
3
+
4
+ # ============
5
+ # = Behavior =
6
+ # ============
7
+ extend Plugins
8
+ plugin AttributeSystem
9
+
10
+ # ==============
11
+ # = Attributes =
12
+ # ==============
13
+ attribute :for
14
+ attribute :given
15
+ attribute :table
16
+
17
+ def initialize(attrs={})
18
+ @attributes = attrs
19
+ end
20
+
21
+ end # Definition
22
+ end # Fathom
@@ -0,0 +1,58 @@
1
+ module Fathom
2
+ class Edge
3
+
4
+ # =============
5
+ # Class Methods
6
+ # =============
7
+ class << self
8
+ def infer(obj, optional_child=nil)
9
+ return new(:parent => Variable.infer(obj), :child => Variable.infer(optional_child)) if optional_child
10
+
11
+ case obj
12
+ when Edge
13
+ obj
14
+ when Array
15
+ new(:parent => Variable.infer(obj[0]), :child => Variable.infer(obj[1]))
16
+ when Hash
17
+ new(obj)
18
+ end
19
+ end
20
+ end
21
+
22
+ # ============
23
+ # = Behavior =
24
+ # ============
25
+ extend Plugins
26
+ plugin AttributeSystem
27
+
28
+ # ==============
29
+ # = Attributes =
30
+ # ==============
31
+ attribute :is_directed, true
32
+ attribute :parent
33
+ attribute :child
34
+
35
+ def initialize(attributes={})
36
+ attributes[:parent] = Variable.infer(attributes[:parent])
37
+ attributes[:child] = Variable.infer(attributes[:child])
38
+ @attributes = attributes
39
+ end
40
+
41
+ # ============================================================
42
+ # = Override setters on parent and child to ensure Variables =
43
+ # ============================================================
44
+ def parent=(obj)
45
+ send(self.class.attributes_proxy)[:parent] = Variable.infer(obj)
46
+ end
47
+
48
+ def child=(obj)
49
+ send(self.class.attributes_proxy)[:child] = Variable.infer(obj)
50
+ end
51
+
52
+ def eql(other)
53
+ return false unless other.is_a?(Edge)
54
+ self.parent == other.parent and self.child == other.child
55
+ end
56
+ alias :== :eql
57
+ end
58
+ end
@@ -0,0 +1,35 @@
1
+ module Fathom
2
+ class Network
3
+
4
+ # ============
5
+ # = Behavior =
6
+ # ============
7
+ extend Plugins
8
+ include Enumerable
9
+ plugin AttributeSystem
10
+
11
+ # ==============
12
+ # = Attributes =
13
+ # ==============
14
+ attribute :name
15
+ attribute :variables, []
16
+ attribute :properties, []
17
+ attribute :edges, []
18
+ attribute :definitions, []
19
+
20
+ def initialize(attrs={})
21
+ @attributes = attrs
22
+ end
23
+
24
+ # ====================
25
+ # = Instance Methods =
26
+ # ====================
27
+ def each(&block)
28
+ variables.each do |variable|
29
+ yield variable
30
+ end
31
+ end
32
+ alias :each_variable :each
33
+
34
+ end
35
+ end
@@ -0,0 +1,30 @@
1
+ module Fathom
2
+ class Outcome
3
+
4
+ # =================
5
+ # = Class Methods =
6
+ # =================
7
+ class << self
8
+ def infer(obj)
9
+ new(:value => obj)
10
+ end
11
+ end
12
+
13
+ # ============
14
+ # = Behavior =
15
+ # ============
16
+ extend Plugins
17
+ plugin AttributeSystem
18
+
19
+ # ==============
20
+ # = Attributes =
21
+ # ==============
22
+ attribute :value
23
+
24
+ def initialize(attrs={})
25
+ @attributes = attrs
26
+ end
27
+
28
+ end # Outcome
29
+ end # Fathom
30
+
@@ -0,0 +1,31 @@
1
+ module Fathom
2
+ class Property
3
+
4
+ # =================
5
+ # = Class Methods =
6
+ # =================
7
+ class << self
8
+ def infer(obj)
9
+ new(:value => obj)
10
+ end
11
+ end
12
+
13
+ # ============
14
+ # = Behavior =
15
+ # ============
16
+ extend Plugins
17
+ plugin AttributeSystem
18
+
19
+ # ==============
20
+ # = Attributes =
21
+ # ==============
22
+ attribute :value
23
+
24
+ def initialize(attrs={})
25
+ @attributes = attrs
26
+ end
27
+
28
+ end # Property
29
+ end # Fathom
30
+
31
+
@@ -0,0 +1,59 @@
1
+ module Fathom
2
+ class Variable
3
+
4
+ # =================
5
+ # = Class Methods =
6
+ # =================
7
+ class << self
8
+ def infer(obj)
9
+ case obj
10
+ when Variable
11
+ obj
12
+ when Hash
13
+ new(obj)
14
+ when Symbol, String
15
+ new(:name => obj)
16
+ end
17
+ end
18
+ end
19
+
20
+ # ============
21
+ # = Behavior =
22
+ # ============
23
+ extend Plugins
24
+ plugin AttributeSystem
25
+
26
+ # ==============
27
+ # = Attributes =
28
+ # ==============
29
+ attribute :id
30
+ attribute :name
31
+ attribute :properties
32
+ attribute :outcomes, [true, false]
33
+ attribute :prior_odds
34
+
35
+ def initialize(attrs={})
36
+ attrs[:id] ||= UUID.generate
37
+ @attributes = attrs
38
+ self.prior_odds ||= uniform_set.dup
39
+ end
40
+
41
+ def underscored_name
42
+ name.to_s.gsub(/\W/, "_").underscore
43
+ end
44
+
45
+ def eql(other)
46
+ return false unless other.is_a?(Variable)
47
+ self.name == other.name
48
+ end
49
+ alias :== :eql
50
+
51
+ protected
52
+ def uniform_set
53
+ size = self.outcomes.size
54
+ Array.new(size, 1/size.to_f)
55
+ end
56
+
57
+ end # class Data
58
+
59
+ end
@@ -0,0 +1,87 @@
1
+ module Fathom
2
+ module GeneralGraphTools
3
+
4
+ def adjacency_matrix
5
+ @adjacency_matrix ||= AdjacencyMatrix.new
6
+ end
7
+
8
+ def add_edge(parent, child)
9
+ self.variables |= [parent, child]
10
+ self.adjacency_matrix[parent, child] = 1
11
+ end
12
+
13
+ def remove_edge(parent, child)
14
+ return false unless self.variables.include?(parent) and self.variables.include?(child)
15
+ self.adjacency_matrix[parent, child] = 0
16
+ end
17
+
18
+ def at(parent, child)
19
+ self.adjacency_matrix[parent, child]
20
+ end
21
+ alias :[] :at
22
+
23
+ def edge?(parent, child)
24
+ return false unless ([parent, child] - self.variables).empty?
25
+ self.adjacency_matrix[parent, child] == 1
26
+ end
27
+
28
+ # Implemented as an out-degree
29
+ def degree(node)
30
+ found = self.adjacency_matrix.select do |(parent, child), value|
31
+ parent == node
32
+ end
33
+ found.size
34
+ end
35
+
36
+ def each_vertex
37
+ self.variables.each {|v| yield v}
38
+ end
39
+
40
+ def each_edge
41
+ self.adjacency_matrix.each {|(parent, child), value| yield parent, child}
42
+ end
43
+
44
+ # Find whether the graph is connected, based on notes here:
45
+ # http://www.ics.uci.edu/~eppstein/161/960201.html
46
+ # This assumes that all variables in the network have been added
47
+ # to the adjacency matrix.
48
+ def connected?
49
+ x = self.variables[0] # Arbitrary first node
50
+ reachable_vertices = [x]
51
+ vertices_to_explore = [x]
52
+
53
+ while !vertices_to_explore.empty?
54
+ y = vertices_to_explore.shift
55
+ self.each_edge do |parent, child|
56
+ unless reachable_vertices.include?(child)
57
+ vertices_to_explore << child
58
+ reachable_vertices << child
59
+ end
60
+ end
61
+ end
62
+
63
+ reachable_vertices.size < self.variables.size ? false : true
64
+
65
+ end
66
+
67
+ def euler_circuit?
68
+ return false if !connected?
69
+ self.each_vertex do |v|
70
+ return false if degree(v) % 2 != 0
71
+ end
72
+ true
73
+ end
74
+
75
+ def euler_path?
76
+ return false if !connected?
77
+ odd=0
78
+ self.each_vertex do |v|
79
+ if degree(v) % 2 == 1
80
+ odd += 1
81
+ end
82
+ end
83
+ odd <= 2
84
+ end
85
+
86
+ end
87
+ end
@@ -0,0 +1,61 @@
1
+ module Fathom
2
+
3
+ # This is the role necessary for importing nodes, shaping graphs, adding edges, that sort of thing.
4
+ module NetworkBuilder
5
+ def from_hash(hash)
6
+ hash = serialize_hash(hash)
7
+ attributes.merge!(hash)
8
+ end
9
+
10
+ protected
11
+
12
+ def serialize_hash(hash)
13
+ hash.inject({}) do |h, (key, value)|
14
+
15
+ value = [value] if attribute_should_be_an_array(key) and not value.is_a?(Array)
16
+
17
+ if klass = serialization_class_for(key)
18
+ value = infer_serialized_array(value, klass)
19
+ end
20
+
21
+ h[key] = value
22
+
23
+ h
24
+ end
25
+ end
26
+
27
+ def serialization_class_for(attribute)
28
+ serialization_map[attribute]
29
+ end
30
+
31
+ def serialization_map
32
+ @serialization_map ||= {
33
+ definitions: Definition,
34
+ edges: Edge,
35
+ properties: Property,
36
+ variables: Variable
37
+ }
38
+ end
39
+
40
+ def attribute_should_be_an_array(attribute)
41
+ array_conversion_attributes.include?(attribute)
42
+ end
43
+
44
+ def array_conversion_attributes
45
+ @array_conversion_map ||= [:properties, :variables, :edges, :definitions]
46
+ end
47
+
48
+ # Uses the Class.infer, if available. Otherwise initializes the value passed in.
49
+ def infer_serialized_array(array, klass)
50
+ array.map do |e|
51
+ if klass == e.class
52
+ e
53
+ elsif klass.respond_to?(:infer)
54
+ klass.infer(e)
55
+ else
56
+ klass.new(e)
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,141 @@
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
+
3
+ include Fathom
4
+
5
+ describe AttributeSystem do
6
+
7
+ context "when checking the default nature of a class that has plugged in AttributeSystem" do
8
+
9
+ before do
10
+ @class = Class.new do
11
+ extend Plugins
12
+ plugin AttributeSystem
13
+ end
14
+ end
15
+
16
+ it "should have an attributes getter" do
17
+ obj = @class.new
18
+ obj.should respond_to(:attributes)
19
+ end
20
+
21
+ it "should default the attributes to a Hash" do
22
+ obj = @class.new
23
+ obj.attributes.should eql({})
24
+ end
25
+ end
26
+
27
+ context "when proxying to another object" do
28
+
29
+ before do
30
+ @class = Class.new do
31
+ extend Plugins
32
+ plugin AttributeSystem
33
+ end
34
+ end
35
+
36
+ it "should default the attributes_proxy to :attributes" do
37
+ @class.attributes_proxy.should eql(:attributes)
38
+ end
39
+
40
+ it "should be able to set_attributes_proxy" do
41
+ @class.set_attributes_proxy :some_method
42
+ @class.attributes_proxy.should eql(:some_method)
43
+ end
44
+ end
45
+
46
+ context "when defining an attribute" do
47
+
48
+ before do
49
+ @class = Class.new do
50
+ extend Plugins
51
+ plugin AttributeSystem
52
+ end
53
+ end
54
+
55
+ it "should have an attribute method" do
56
+ @class.should respond_to(:attribute)
57
+ end
58
+
59
+ it "should be able to take an attribute name and have a setter and getter on that" do
60
+ @class.attribute(:name)
61
+ obj = @class.new
62
+ obj.name = :name
63
+ obj.name.should eql(:name)
64
+ end
65
+
66
+ it "should be able to read from the attributes object" do
67
+ @class = Class.new do
68
+ extend Plugins
69
+ plugin AttributeSystem
70
+ attribute :name
71
+ def initialize
72
+ @attributes = {:name => :value}
73
+ end
74
+ end
75
+ obj = @class.new
76
+ obj.name.should eql(:value)
77
+ end
78
+
79
+ it "should follow the proxied attributes, if it's been changed" do
80
+ @class = Class.new do
81
+ extend Plugins
82
+ plugin AttributeSystem
83
+
84
+ attribute :name
85
+ set_attributes_proxy :alternate_attributes
86
+
87
+ def alternate_attributes
88
+ {:name => :value}
89
+ end
90
+ end
91
+ obj = @class.new
92
+ obj.name.should eql(:value)
93
+ end
94
+
95
+ it "should be able to set an attribute with a default" do
96
+ @class.attribute(:name, :default)
97
+ obj = @class.new
98
+ obj.name.should eql(:default)
99
+ end
100
+
101
+ it "should not use the default if the attribute has been set" do
102
+ @class.attribute(:name, :default)
103
+ obj = @class.new
104
+ obj.name.should eql(:default)
105
+ obj.name = :changed
106
+ obj.name.should eql(:changed)
107
+ end
108
+
109
+ it "should not use the default if the underlying attribute has been set" do
110
+ @class = Class.new do
111
+ extend Plugins
112
+ plugin AttributeSystem
113
+ attribute :name, :default
114
+
115
+ def initialize(attrs={})
116
+ @attributes = attrs
117
+ end
118
+ end
119
+
120
+ obj = @class.new :name => :changed
121
+ obj.name.should eql(:changed)
122
+ end
123
+
124
+
125
+ it "should not use the default if the underlying attribute has been set" do
126
+ @class = Class.new do
127
+ extend Plugins
128
+ plugin AttributeSystem
129
+ attribute :name, :default
130
+
131
+ def initialize(attrs={})
132
+ @attributes = attrs
133
+ end
134
+ end
135
+
136
+ obj = @class.new :name => nil
137
+ obj.name.should be_nil
138
+ end
139
+ end
140
+
141
+ end