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
@@ -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
-