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
@@ -1,79 +0,0 @@
1
- require File.expand_path(File.join(File.dirname(__FILE__), '..', 'fathom'))
2
- module Fathom
3
- class ValueDescription
4
-
5
- class << self
6
- # Returns a simple accessor for the node, so that the value description works a little like an OpenStruct.
7
- def define_node_accessor(node, method = :node)
8
- define_method(node.name_sym) do
9
- node.send(method)
10
- end
11
- end
12
-
13
- end
14
-
15
- attr_reader :nodes, :last_process
16
-
17
- def initialize(*nodes, &block)
18
- assert_nodes(nodes)
19
- @process_block = block if block_given?
20
- end
21
-
22
- def add_node(node, method = :rand)
23
- assert_node(node, method)
24
- end
25
-
26
- def process
27
- prepare_process
28
- @process_block ? @process_block.call(@last_process) : default_process(@last_process)
29
- end
30
-
31
- protected
32
-
33
-
34
- def prepare_process
35
- @last_process = self.nodes.inject(OpenStruct.new) do |o, node|
36
- o.table[node.name_sym] = self.send(node.name_sym)
37
- o
38
- end
39
- end
40
-
41
- def default_process(obj)
42
- obj.values.inject(0.0) do |s, e|
43
- s += e
44
- end
45
- end
46
-
47
- def assert_nodes(nodes)
48
- @nodes ||= []
49
- nodes.each do |node|
50
- case node
51
- when Hash
52
- assert_node_from_hash(node)
53
- else
54
- assert_node(node)
55
- end
56
- end
57
- end
58
-
59
- def assert_node(node, method = :rand)
60
- raise ArgumentError, "Must provide a node that can respond to name" unless node.respond_to?(:name)
61
- @nodes ||= []
62
- self.class.define_node_accessor(node, method)
63
- @nodes << node
64
- end
65
-
66
- # Takes a node => :value_hash signature for the values in the hash
67
- def assert_node_from_hash(hash)
68
- hash.each do |node, value_method|
69
- assert_node(node, value_method)
70
- end
71
- end
72
- end
73
- end
74
-
75
- if __FILE__ == $0
76
- include Fathom
77
- # TODO: Is there anything you want to do to run this file on its own?
78
- # ValueDescription.new
79
- end
@@ -1,186 +0,0 @@
1
- # Taken brazenly from ActiveSupport
2
- class OptionsHash < Hash
3
- # Return a new hash with all keys converted to strings.
4
- def stringify_keys
5
- dup.stringify_keys!
6
- end
7
-
8
- # Destructively convert all keys to strings.
9
- def stringify_keys!
10
- keys.each do |key|
11
- self[key.to_s] = delete(key)
12
- end
13
- self
14
- end
15
-
16
- # Return a new hash with all keys converted to symbols, as long as
17
- # they respond to +to_sym+.
18
- def symbolize_keys
19
- dup.symbolize_keys!
20
- end
21
-
22
- # Destructively convert all keys to symbols, as long as they respond
23
- # to +to_sym+.
24
- def symbolize_keys!
25
- keys.each do |key|
26
- self[(key.to_sym rescue key) || key] = delete(key)
27
- end
28
- self
29
- end
30
-
31
- alias_method :to_options, :symbolize_keys
32
- alias_method :to_options!, :symbolize_keys!
33
-
34
- # Validate all keys in a hash match *valid keys, raising ArgumentError on a mismatch.
35
- # Note that keys are NOT treated indifferently, meaning if you use strings for keys but assert symbols
36
- # as keys, this will fail.
37
- #
38
- # ==== Examples
39
- # { :name => "Rob", :years => "28" }.assert_valid_keys(:name, :age) # => raises "ArgumentError: Unknown key(s): years"
40
- # { :name => "Rob", :age => "28" }.assert_valid_keys("name", "age") # => raises "ArgumentError: Unknown key(s): name, age"
41
- # { :name => "Rob", :age => "28" }.assert_valid_keys(:name, :age) # => passes, raises nothing
42
- def assert_valid_keys(*valid_keys)
43
- unknown_keys = keys - [valid_keys].flatten
44
- raise(ArgumentError, "Unknown key(s): #{unknown_keys.join(", ")}") unless unknown_keys.empty?
45
- end
46
-
47
- def extractable_options?
48
- true
49
- end
50
-
51
- def initialize(constructor = {})
52
- if constructor.is_a?(Hash)
53
- super()
54
- update(constructor)
55
- else
56
- super(constructor)
57
- end
58
- end
59
-
60
- def default(key = nil)
61
- if key.is_a?(Symbol) && include?(key = key.to_s)
62
- self[key]
63
- else
64
- super
65
- end
66
- end
67
-
68
- def self.new_from_hash_copying_default(hash)
69
- OptionsHash.new(hash).tap do |new_hash|
70
- new_hash.default = hash.default
71
- end
72
- end
73
-
74
- alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
75
- alias_method :regular_update, :update unless method_defined?(:regular_update)
76
-
77
- # Assigns a new value to the hash:
78
- #
79
- # hash = OptionsHash.new
80
- # hash[:key] = "value"
81
- #
82
- def []=(key, value)
83
- regular_writer(convert_key(key), convert_value(value))
84
- end
85
-
86
- # Updates the instantized hash with values from the second:
87
- #
88
- # hash_1 = OptionsHash.new
89
- # hash_1[:key] = "value"
90
- #
91
- # hash_2 = OptionsHash.new
92
- # hash_2[:key] = "New Value!"
93
- #
94
- # hash_1.update(hash_2) # => {"key"=>"New Value!"}
95
- #
96
- def update(other_hash)
97
- other_hash.each_pair { |key, value| regular_writer(convert_key(key), convert_value(value)) }
98
- self
99
- end
100
-
101
- alias_method :merge!, :update
102
-
103
- # Checks the hash for a key matching the argument passed in:
104
- #
105
- # hash = OptionsHash.new
106
- # hash["key"] = "value"
107
- # hash.key? :key # => true
108
- # hash.key? "key" # => true
109
- #
110
- def key?(key)
111
- super(convert_key(key))
112
- end
113
-
114
- alias_method :include?, :key?
115
- alias_method :has_key?, :key?
116
- alias_method :member?, :key?
117
-
118
- # Fetches the value for the specified key, same as doing hash[key]
119
- def fetch(key, *extras)
120
- super(convert_key(key), *extras)
121
- end
122
-
123
- # Returns an array of the values at the specified indices:
124
- #
125
- # hash = OptionsHash.new
126
- # hash[:a] = "x"
127
- # hash[:b] = "y"
128
- # hash.values_at("a", "b") # => ["x", "y"]
129
- #
130
- def values_at(*indices)
131
- indices.collect {|key| self[convert_key(key)]}
132
- end
133
-
134
- # Returns an exact copy of the hash.
135
- def dup
136
- OptionsHash.new(self)
137
- end
138
-
139
- # Merges the instantized and the specified hashes together, giving precedence to the values from the second hash
140
- # Does not overwrite the existing hash.
141
- def merge(hash)
142
- self.dup.update(hash)
143
- end
144
-
145
- # Performs the opposite of merge, with the keys and values from the first hash taking precedence over the second.
146
- # This overloaded definition prevents returning a regular hash, if reverse_merge is called on a OptionsHash.
147
- def reverse_merge(other_hash)
148
- super self.class.new_from_hash_copying_default(other_hash)
149
- end
150
-
151
- def reverse_merge!(other_hash)
152
- replace(reverse_merge( other_hash ))
153
- end
154
-
155
- # Removes a specified key from the hash.
156
- def delete(key)
157
- super(convert_key(key))
158
- end
159
-
160
- def stringify_keys!; self end
161
- def stringify_keys; dup end
162
- undef :symbolize_keys!
163
- def symbolize_keys; to_hash.symbolize_keys end
164
- def to_options!; self end
165
-
166
- # Convert to a Hash with String keys.
167
- def to_hash
168
- Hash.new(default).merge!(self)
169
- end
170
-
171
- protected
172
- def convert_key(key)
173
- key.kind_of?(Symbol) ? key.to_s : key
174
- end
175
-
176
- def convert_value(value)
177
- case value
178
- when Hash
179
- self.class.new_from_hash_copying_default(value)
180
- when Array
181
- value.collect { |e| e.is_a?(Hash) ? self.class.new_from_hash_copying_default(e) : e }
182
- else
183
- value
184
- end
185
- end
186
- end
@@ -1,10 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
-
3
- describe Array do
4
- it "should define rand, a way to get a random variable from an array" do
5
- a = [1,2]
6
- a.should be_include(a.rand)
7
- # 9.31322574615479e-10 chance of failing
8
- 30.times.map {a.rand}.uniq.sort.should eql([1,2])
9
- end
10
- end
@@ -1,10 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
- require 'fastercsv'
3
- require 'ext/faster_csv'
4
-
5
- describe FasterCSV do
6
- it "should have a header converter to strip values" do
7
- FasterCSV::HeaderConverters.keys.should be_include(:strip)
8
- FasterCSV::HeaderConverters[:strip].call(' this ').should eql('this')
9
- end
10
- end
@@ -1,20 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
-
3
- describe OpenStruct do
4
-
5
- before do
6
- @o = OpenStruct.new(:this => 1)
7
- end
8
-
9
- it "should expose its table" do
10
- @o.table.should eql({:this => 1})
11
- end
12
-
13
- it "should expose the keys from the table" do
14
- @o.keys.should eql([:this])
15
- end
16
-
17
- it "should expose the values from the table" do
18
- @o.values.should eql([1])
19
- end
20
- end
@@ -1,7 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
-
3
- describe String do
4
- it "should be able to constantize a string" do
5
- "Fathom".constantize.should eql(Fathom)
6
- end
7
- end
@@ -1,17 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
-
3
- include Fathom
4
-
5
- describe AgentCluster do
6
-
7
- before do
8
- @agent = double()
9
- @agents = [@agent]
10
- end
11
-
12
- it "should initialize with a set of agents" do
13
- @ac = AgentCluster.new @agent
14
- @ac.agents.should eql(@agents)
15
- end
16
-
17
- end
@@ -1,51 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
-
3
- include Fathom
4
-
5
- describe Agent do
6
-
7
- before do
8
- @pr1 = PlausibleRange.new(:min => 1, :max => 2)
9
- @pr2 = PlausibleRange.new(:min => 10, :max => 11)
10
- @da = DemoAgent.new(:field1 => @pr1, :field2 => @pr2)
11
- end
12
-
13
- it "should be have a list of properties" do
14
- DemoAgent.properties.should eql([:field1, :field2])
15
- end
16
-
17
- it "should be able to set a seed node for each property defined in the class" do
18
- @da.node_for_field1.should eql(@pr1)
19
- @da.node_for_field2.should eql(@pr2)
20
- end
21
-
22
- it "should not set a seed node for a node that doesn't have a property" do
23
- da = DemoAgent.new(:field1 => @pr1, :field2 => @pr2, :field3 => :should_not_be_found)
24
- da.should_not be_respond_to(:node_for_field3)
25
- end
26
-
27
- it "should have a state accessor for each property" do
28
- @da.should be_respond_to(:field1)
29
- @da.should be_respond_to(:field1=)
30
- @da.should be_respond_to(:field2)
31
- @da.should be_respond_to(:field2=)
32
- end
33
-
34
- it "should set the initial state of each property from the seed node, if defined" do
35
- @pr1.should_receive(:rand).and_return(1.5)
36
- @pr2.should_not_receive(:rand)
37
- @da = DemoAgent.new(:field1 => @pr1)
38
- @da.field1.should eql(1.5)
39
- @da.field2.should be_nil
40
- end
41
-
42
- it "should be able to send a hard parameter and save it in a property" do
43
- @da = DemoAgent.new(:field1 => 2)
44
- @da.field1.should eql(2)
45
- end
46
-
47
- it "should expose the callbacks, those methods starting with on_" do
48
- @da.callbacks.should eql(["on_tick"])
49
- end
50
-
51
- end
@@ -1,64 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
-
3
- include Fathom::Distributions
4
-
5
- describe DiscreteGaussian do
6
-
7
- # before do
8
- # @opts = {:mean => 0, :sd => 0.1, :confidence_interval => 0.05}
9
- # end
10
- #
11
- # it "should provide a GSL::Rng through rng" do
12
- # Gaussian.rng.should be_a(GSL::Rng)
13
- # end
14
- #
15
- # it "should be able to generate a random variable through GSL::Rng#gaussian" do
16
- # Gaussian.rng.should_receive(:gaussian).and_return(0.5)
17
- # Gaussian.rand(1)
18
- # end
19
- #
20
- # it "should be able to generate an inverse CDF" do
21
- # Gaussian.inverse_cdf(@opts).should be_close(-0.16448, 0.00001)
22
- # end
23
- #
24
- # it "should require mean as an option for an inverse CDF" do
25
- # @opts.delete(:mean)
26
- # lambda{Gaussian.inverse_cdf(@opts)}.should raise_error
27
- # end
28
- #
29
- # it "should require a standard deviation for an inverse CDF" do
30
- # @opts.delete(:sd)
31
- # lambda{Gaussian.inverse_cdf(@opts)}.should raise_error
32
- # end
33
- #
34
- # it "should take std as an alias for sd when creating an inverse CDF" do
35
- # @opts.delete(:sd)
36
- # Gaussian.inverse_cdf(@opts.merge(:std => 0.1)).should be_close(-0.16448, 0.00001)
37
- # end
38
- #
39
- # it "should take standard_deviation as an alias for sd when creating an inverse CDF" do
40
- # @opts.delete(:sd)
41
- # Gaussian.inverse_cdf(@opts.merge(:standard_deviation => 0.1)).should be_close(-0.16448, 0.00001)
42
- # end
43
- #
44
- # it "should be able to set upper to true and get Q instead of P" do
45
- # Gaussian.inverse_cdf(@opts.merge(:upper =>true)).should be_close(0.16448, 0.00001)
46
- # end
47
- #
48
- # it "should be able to take a different confidence interval" do
49
- # Gaussian.inverse_cdf(@opts.merge(:confidence_interval => 0.1)).should be_close(-0.12815, 0.00001)
50
- # end
51
- #
52
- # it "should have a lower_bound alias for inverse_cdf" do
53
- # Gaussian.lower_bound(@opts).should eql(Gaussian.inverse_cdf(@opts))
54
- # end
55
- #
56
- # it "should have an upper_bound shortcut for inverse_cdf(:upper => true, ...)" do
57
- # Gaussian.upper_bound(@opts).should eql(Gaussian.inverse_cdf(@opts.merge(:upper => true)))
58
- # end
59
- #
60
- # it "should provide interval values, an array of the lower and upper bounds" do
61
- # Gaussian.interval_values(@opts.merge(:confidence_interval => 0.9)).should eql([Gaussian.lower_bound(@opts), Gaussian.upper_bound(@opts)])
62
- # end
63
- end
64
-