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,119 +0,0 @@
1
- class Array
2
- def expand(array)
3
- return array.map{|e| [e]} if self.empty?
4
- array.inject([]) do |list, other_e|
5
- self.each do |e|
6
- list << [e,other_e].flatten
7
- end
8
- list
9
- end
10
- end
11
- end
12
-
13
- class Variable
14
-
15
- attr_reader :values, :name, :observations, :total
16
-
17
- def initialize(name, *values)
18
- values = [true, false] if values.empty?
19
- @name = name
20
- @values = values
21
- @observations = Array.new(@values.size, 0)
22
- @total = 0
23
- end
24
-
25
- # You can observe anything but nothing: we record any observation but nil.
26
- # If nil is set, we use the first value as the default.
27
- def observe(value=nil)
28
- value = self.values.first if value.nil?
29
- unless self.values.include?(value)
30
- self.values << value
31
- self.observations << 0
32
- end
33
- index = self.values.index(value)
34
- self.observations[index] += 1
35
- @total += 1
36
- end
37
-
38
- # Lookup observations
39
- def observed(value)
40
- index = self.values.index(value)
41
- return 0 unless index
42
- self.observations[index]
43
- end
44
-
45
- def inspect
46
- "Variable: #{self.name} #{self.values.inspect}"
47
- end
48
-
49
- class << self
50
- def infer(obj, *values)
51
- return obj if obj.is_a?(Variable)
52
- case obj
53
- when Symbol
54
- Variable.new(obj, *values)
55
- when String
56
- Variable.new(obj.to_sym, *values)
57
- else
58
- nil
59
- end
60
- end
61
-
62
- end
63
- end
64
-
65
- class Table
66
-
67
- attr_reader :columns, :variables, :legend
68
- def initialize(*variables)
69
- @columns = variables.map {|v| v.name}
70
- @variables = variables
71
- @legend = @variables.inject([]) do |list, e|
72
- list = list.expand(e)
73
- end
74
- end
75
-
76
- protected
77
- def lookup()
78
- end
79
- end
80
-
81
- class Node
82
- def initialize(name, *parents)
83
- end
84
-
85
- # Take an array, array of arrays, dictionary, hash, or OpenStruct.
86
- # Anything but an array can add a new parent to observe.
87
- def observe(values)
88
- end
89
- end
90
-
91
- require 'rubygems'
92
- require 'spec'
93
-
94
- =begin
95
- variables = [[:spring, :summer, :fall, :winter], [:true, :false], [:true, :false], [:true, :false]]
96
-
97
- @all = []
98
- (0...variables.size).each do |i|
99
- @all << variables.inject([]) do |list, v|
100
- list << (0...v.size).map do |j|
101
- variables[i][j]
102
- end
103
- end
104
- end
105
-
106
- @all
107
-
108
-
109
- v1 = [:spring, :summer, :fall, :winter]
110
- v2 = [:true, :false]
111
- a = [v1, v2]
112
- b = []
113
- a.each do |e|
114
- b << e
115
- end
116
-
117
-
118
-
119
- =end
@@ -1,97 +0,0 @@
1
- require 'rubygems'
2
- require 'gsl'
3
-
4
- include GSL
5
-
6
- class Node
7
-
8
- attr_reader :name, :labels, :probabilities, :likelihood
9
-
10
- def initialize(*args)
11
- @name = args.shift
12
- raise ArgumentError, "Must provide a node name" unless self.name
13
- if args.empty?
14
- extract_from_array([:true, :false])
15
- elsif args.length == 1 and args.first.is_a?(Hash)
16
- extract_from_hash(args.first)
17
- elsif args.length == 1 and args.first.is_a?(Array)
18
- extract_from_array(args.first)
19
- else
20
- extract_from_array(args)
21
- end
22
- assert_likelihood
23
- end
24
-
25
- def inspect
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}"
30
- end
31
-
32
- def belief
33
- probabilities * likelihood
34
- end
35
-
36
- alias :b :belief
37
- alias :l :likelihood
38
- alias :p :probabilities
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
-
56
- protected
57
-
58
- def index_for(label)
59
- labels.index(label)
60
- end
61
-
62
- def assert_likelihood
63
- @likelihood = Vector.ary_to_gv(Array.new(@probabilities.size, 1))
64
- end
65
-
66
- def extract_from_array(array)
67
- @labels = array
68
- @probabilities = Vector.ary_to_gv(uniform_distribution(array.size))
69
- end
70
-
71
- def uniform_distribution(n)
72
- Array.new(n, 1/n.to_f)
73
- end
74
-
75
- def extract_from_hash(hash)
76
- @labels, probabilities = [], []
77
- hash.each do |k, v|
78
- @labels << k
79
- probabilities << v
80
- end
81
- @probabilities = Vector.ary_to_gv(probabilities)
82
- normalize_probabilities!
83
- end
84
-
85
- # I don't like GSL::Vector#normalize!, it's not accurate, or has a different
86
- # idea of what a normalized vector looks like.
87
- def normalize_probabilities!
88
- sum = 0.0
89
- @probabilities.each do |value|
90
- sum += value
91
- end
92
- @probabilities.map! do |value|
93
- value.to_f / sum
94
- end
95
- end
96
-
97
- end
@@ -1,136 +0,0 @@
1
- =begin
2
- I want to noodle around a bit with basic probabilities, odds, that sort of thing. Here's the example:
3
-
4
- Upon being awakened by the sound of a burglar alary, what is your degree of belief that a burglary attempt took place?
5
-
6
- Supporting information:
7
-
8
- * There is a 95% chance that an attempted burglary will trigger the alarm system, P(alarm|burglary) = 95%
9
- * There is a 1% chance that the alarms will be triggered by non-burglary attempts, p(alarm|no burglary) = 1%
10
- * There is a 1/10_000 chance of a particular home being burglarized, generally, P(burglary) = 10^-4
11
-
12
- O(burglary|alarm) = L(alarm|burglary)O(burglary)
13
-
14
- P(burglary|alarm) = O(burglary|alarm) / 1 + O(burglary|alarm)
15
-
16
- =end
17
-
18
- require 'mathn'
19
-
20
- def likelihood(effect, cause)
21
- effect / cause
22
- end
23
- alias :l :likelihood
24
-
25
- def odds(effect, cause=nil)
26
- cause ? ( likelihood(cause, effect) * odds(effect) ) : (effect / (1 - effect))
27
- end
28
- alias :o :odds
29
-
30
- # The same as the odds above, but only in the case of a single hypothesis
31
- def prior_odds(e)
32
- e / ( 1 - e )
33
- end
34
-
35
- def likelihood_ratio(e, h)
36
- p(e,h) / p(e, 1 - h)
37
- end
38
-
39
- def probability(e, h)
40
- (e * h) / h
41
- end
42
-
43
- def product_rule(*e)
44
- end
45
- alias :p :probability
46
-
47
- class Array
48
-
49
- def givens
50
- @givens ||= {}
51
- end
52
-
53
- def give(sym, array=Array.new(self.size, 1/self.size))
54
- self.givens[sym] = array
55
- end
56
-
57
- def given(sym, index)
58
- self.givens[sym][index]
59
- end
60
- end
61
-
62
- =begin
63
- Since Rational is the automatic choice for probabilistic data,
64
- and since I don't want to override how mathn infers numbers,
65
- I am adding some baggage to Rational:
66
-
67
- * It still reduces to the LCD
68
- * It keeps track of all events,
69
- so that I can keep a new event proportional to old ones
70
- * It has an add_event (add) which takes a true or false value
71
- true values, records that a condition was found
72
-
73
- This really only works for binary data, but this is a noodle file.
74
-
75
- =end
76
- class Rational < Numeric
77
- class << self
78
- alias :orig_reduce :reduce
79
- def reduce(num, den=1)
80
- val = orig_reduce(num, den)
81
- val.total_events = den
82
- val.positive_events = num
83
- val
84
- end
85
- end
86
-
87
- def total_events(val=nil)
88
- @total_events ||= 0
89
- @total_events = val if val
90
- @total_events
91
- end
92
- alias :events :total_events
93
- alias :total :total_events
94
-
95
- def total_events=(val)
96
- total_events(val)
97
- end
98
-
99
- def positive_events(val=nil)
100
- @positive_events ||= 0
101
- @positive_events = val if val
102
- @positive_events
103
- end
104
- alias :positive :positive_events
105
-
106
- def positive_events=(val)
107
- positive_events(val)
108
- end
109
-
110
- # Uses the Rational constructor to calculate the lowest common denominator
111
- def add_event(positive=true)
112
- num = positive ? self.positive_events + 1 : self.positive_events
113
- den = self.total_events + 1
114
- other = Rational(num, den)
115
- @numerator, @denominator, @positive_events, @total_events = other.numerator, other.denominator, num, den
116
- self
117
- end
118
- alias :add :add_event
119
- end
120
-
121
-
122
- class A
123
- class << self
124
- include GSL
125
-
126
- def r
127
- @r ||= Rng.alloc
128
- end
129
-
130
- def pdf
131
- r = self.r.gaussian
132
- pdf = Ran.gaussian_pdf(r)
133
- puts r, pdf
134
- end
135
- end
136
- end
@@ -1,45 +0,0 @@
1
- require 'node'
2
- require 'conditional_probability_matrix'
3
-
4
- class A
5
- class << self
6
- # def will_pay
7
- # @will_pay ||= Node.new :true, :false
8
- # end
9
- #
10
- # def has_money
11
- # @has_money ||= Node.new :plenty => 0.1, :some => 0.8, :little => 0.1
12
- # end
13
- #
14
- # def ones
15
- # Matrix.ones(will_pay.values.length, has_money.values.length)
16
- # end
17
- #
18
- # def parents
19
- # will_pay.values.col * has_money.values
20
- # # will_pay.each do |value|
21
- # #
22
- # # end
23
- # end
24
- #
25
- # def cpm
26
- # ConditionalProbabilityMatrix.new(will_pay, has_money)
27
- # end
28
-
29
- def killer_identity
30
- @killer_identity ||= Node.new(:killer_identity, :jack => 0.8, :joe => 0.1, :jeff => 0.1)
31
- end
32
- alias :k :killer_identity
33
- alias :x :killer_identity
34
-
35
- def fingerprint_information
36
- @fingerprint_information ||= Node.new(:fingerprint_information, :jack => 2/3.0, :joe => 1/6.0, :jeff => 1/6.0)
37
- end
38
- alias :f :fingerprint_information
39
- alias :y :fingerprint_information
40
-
41
- def cpm
42
- @cpm ||= ConditionalProbabilityMatrix.new(x, y)
43
- end
44
- end
45
- end
@@ -1,8 +0,0 @@
1
- require File.expand_path(File.join(File.dirname(__FILE__), '..', 'fathom'))
2
- module Fathom
3
- module Distributions
4
- module SharedMethods
5
- # TODO: Put helper methods here for sharing some of the distribution functionality
6
- end
7
- end
8
- end
@@ -1,44 +0,0 @@
1
- require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'fathom'))
2
- class Fathom::Distributions::DiscreteGaussian
3
- extend Fathom::Distributions::SharedMethods
4
- class << self
5
- def rng
6
- @rng ||= GSL::Rng.alloc(GSL::Rng::MT19937_1999, Kernel.rand(100_000))
7
- end
8
-
9
- def rand(sd)
10
- (rng.gaussian(sd) / size).floor + 1
11
- end
12
-
13
- def inverse_cdf(opts={})
14
- mean = opts[:mean]
15
- sd = opts[:sd]
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.gaussian_Pinv(confidence_interval, sd) : GSL::Cdf.gaussian_Qinv(confidence_interval, sd)
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.gaussian_Qinv((1 - confidence_interval) / 2) * 2
41
- end
42
- end
43
- end
44
-