fathom 0.3.7 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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