fathom 0.3.0 → 0.3.1

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 (57) hide show
  1. data/.autotest +10 -0
  2. data/Gemfile +10 -2
  3. data/Gemfile.lock +8 -0
  4. data/TODO.md +12 -25
  5. data/VERSION +1 -1
  6. data/lib/{fathom/ext → ext}/array.rb +0 -0
  7. data/lib/{fathom/ext → ext}/faster_csv.rb +0 -0
  8. data/lib/{fathom/ext → ext}/open_struct.rb +0 -0
  9. data/lib/{fathom/ext → ext}/string.rb +0 -0
  10. data/lib/fathom.rb +16 -13
  11. data/lib/fathom/agent.rb +8 -9
  12. data/lib/fathom/{causal_graph.rb → archive/causal_graph.rb} +0 -0
  13. data/lib/fathom/{concept.rb → archive/concept.rb} +0 -0
  14. data/lib/fathom/archive/conditional_probability_matrix.rb +3 -0
  15. data/lib/fathom/{inverter.rb → archive/inverter.rb} +0 -0
  16. data/lib/fathom/archive/node.rb +24 -1
  17. data/lib/fathom/distributions/discrete_uniform.rb +11 -32
  18. data/lib/fathom/import.rb +37 -34
  19. data/lib/fathom/import/yaml_import.rb +22 -1
  20. data/lib/fathom/knowledge_base.rb +34 -23
  21. data/lib/fathom/knowledge_base/search.rb +19 -0
  22. data/lib/fathom/node.rb +32 -1
  23. data/lib/fathom/node/belief_node.rb +121 -0
  24. data/lib/fathom/node/cpm_node.rb +100 -0
  25. data/lib/fathom/node/data_collection.rb +97 -0
  26. data/lib/fathom/{data_node.rb → node/data_node.rb} +1 -1
  27. data/lib/fathom/{value_aggregator.rb → node/decision.rb} +5 -5
  28. data/lib/fathom/node/discrete_node.rb +41 -0
  29. data/lib/fathom/node/fact.rb +24 -0
  30. data/lib/fathom/{mc_node.rb → node/mc_node.rb} +1 -1
  31. data/lib/fathom/{enforced_name.rb → node/node_extensions/enforced_name.rb} +1 -1
  32. data/lib/fathom/{numeric_methods.rb → node/node_extensions/numeric_methods.rb} +19 -1
  33. data/lib/fathom/{plausible_range.rb → node/plausible_range.rb} +1 -1
  34. data/spec/ext/array_spec.rb +10 -0
  35. data/spec/ext/faster_csv_spec.rb +10 -0
  36. data/spec/ext/open_struct_spec.rb +20 -0
  37. data/spec/ext/string_spec.rb +7 -0
  38. data/spec/fathom/import/csv_import_spec.rb +11 -9
  39. data/spec/fathom/import/yaml_import_spec.rb +27 -7
  40. data/spec/fathom/knowledge_base_spec.rb +8 -4
  41. data/spec/fathom/node/belief_node_spec.rb +180 -0
  42. data/spec/fathom/node/cpm_node_spec.rb +144 -0
  43. data/spec/fathom/node/data_collection_spec.rb +26 -0
  44. data/spec/fathom/{data_node_spec.rb → node/data_node_spec.rb} +1 -1
  45. data/spec/fathom/node/decision_spec.rb +15 -0
  46. data/spec/fathom/node/discrete_node_spec.rb +56 -0
  47. data/spec/fathom/node/fact_spec.rb +33 -0
  48. data/spec/fathom/{mc_node_spec.rb → node/mc_node_spec.rb} +1 -1
  49. data/spec/fathom/{enforced_name_spec.rb → node/node_extensions/enforced_name_spec.rb} +1 -1
  50. data/spec/fathom/{numeric_methods_spec.rb → node/node_extensions/numeric_methods_spec.rb} +53 -11
  51. data/spec/fathom/{plausible_range_spec.rb → node/plausible_range_spec.rb} +1 -1
  52. data/spec/fathom/node_spec.rb +17 -0
  53. data/spec/fathom_spec.rb +40 -0
  54. data/spec/spec_helper.rb +3 -0
  55. data/spec/support/fact.yml +11 -0
  56. metadata +57 -30
  57. data/lib/fathom/value_multiplier.rb +0 -18
@@ -0,0 +1,10 @@
1
+ # require 'autotest/fsevent'
2
+ # require 'autotest/growl'
3
+
4
+ Autotest.add_hook :initialize do |autotest|
5
+ # Keeps me from calling something document_spec.rb
6
+ # %w{.git .svn .hg .DS_Store ._* vendor tmp log doc}.each do |exception|
7
+ %w{.git .svn .hg .DS_Store ._* vendor tmp log}.each do |exception|
8
+ autotest.add_exception(exception)
9
+ end
10
+ end
data/Gemfile CHANGED
@@ -1,6 +1,14 @@
1
1
  source 'http://rubygems.org'
2
2
 
3
- gem "rspec"
4
- gem "ruby-debug"
5
3
  gem "fastercsv"
6
4
  gem 'uuid'
5
+ # gem 'spira'
6
+ # gem 'rdf'
7
+ gem 'sparql-algebra'
8
+
9
+ group :test do
10
+ gem "ruby-debug"
11
+ gem "rspec"
12
+ gem "ZenTest"
13
+ end
14
+
@@ -1,11 +1,15 @@
1
1
  GEM
2
2
  remote: http://rubygems.org/
3
3
  specs:
4
+ ZenTest (4.4.0)
5
+ addressable (2.2.2)
4
6
  columnize (0.3.1)
5
7
  diff-lcs (1.1.2)
6
8
  fastercsv (1.5.3)
7
9
  linecache (0.43)
8
10
  macaddr (1.0.0)
11
+ rdf (0.3.0.pre)
12
+ addressable (>= 2.2)
9
13
  rspec (2.0.1)
10
14
  rspec-core (~> 2.0.1)
11
15
  rspec-expectations (~> 2.0.1)
@@ -21,6 +25,8 @@ GEM
21
25
  ruby-debug-base (~> 0.10.3.0)
22
26
  ruby-debug-base (0.10.3)
23
27
  linecache (>= 0.3)
28
+ sparql-algebra (0.0.1)
29
+ rdf (= 0.3.0.pre)
24
30
  uuid (2.3.1)
25
31
  macaddr (~> 1.0)
26
32
 
@@ -28,7 +34,9 @@ PLATFORMS
28
34
  ruby
29
35
 
30
36
  DEPENDENCIES
37
+ ZenTest
31
38
  fastercsv
32
39
  rspec
33
40
  ruby-debug
41
+ sparql-algebra
34
42
  uuid
data/TODO.md CHANGED
@@ -1,30 +1,7 @@
1
1
  TODO
2
2
  ====
3
3
 
4
- Reorganizing (0.2.5)
5
- ------------
6
-
7
- I've just made some big refactoring steps regarding the organization of the system and the distributions. To make sure we're there:
8
-
9
- * Go back and test the 4 distributions I decided on
10
- * Finish the discrete ideas, adding size to the node and automatically using that for stats
11
- * Create the idea of a labeled, multinomial node
12
- * Add SQLite3 for in-memory set operations for a labeled, multinomial node
13
- * Make sure we are not defining methods on all objects in a class when they should only be set for a single object.
14
-
15
- Also, the general organization of the system could be broken down better:
16
-
17
- * agent
18
- * distributions
19
- * node
20
- * import
21
- * causal_graph
22
- * belief_network
23
- * knowledge_base
24
- * apophenia
25
- * simulation
26
-
27
- Belief Networks (0.3)
4
+ Belief Networks (0.3.1)
28
5
  ---------------
29
6
 
30
7
  To get these delivered, I need to revisit the edge logic, to make sure it's easy to extend each edge with an object.
@@ -35,11 +12,21 @@ Then:
35
12
  * Network propagation
36
13
  * Network testing (polytree)
37
14
 
38
- Agent Based Modeling (0.3.1)
15
+ Reorganizing (0.3.2)
16
+ ------------
17
+
18
+ I've just made some big refactoring steps regarding the organization of the system and the distributions. To make sure we're there:
19
+
20
+ * Fix the inheritance structure
21
+ * Go back and test the 4 distributions I decided on
22
+ * Add SQLite3 for in-memory set operations for a labeled, multinomial node
23
+
24
+ Agent Based Modeling (0.3.3)
39
25
  --------------------
40
26
 
41
27
  * Add parameter-passing standards for callbacks
42
28
  * Add EventMachine and async capabilities (Including the cluster idea)
29
+ * Make sure the scope of dynamic methods defined are correct (class, object, singleton) (on properties and simulation)
43
30
 
44
31
  Knowledge Base (0.4)
45
32
  --------------
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.0
1
+ 0.3.1
File without changes
File without changes
File without changes
File without changes
@@ -21,24 +21,27 @@ module Fathom
21
21
 
22
22
  # Autoload classes and modules so that we only load as much of the library as we're using.
23
23
  # This allows us to have a fairly large library without taking up a lot of memory unless we need it.
24
- autoload :Inverter, "inverter"
25
24
  autoload :Node, "node"
26
- autoload :PlausibleRange, "plausible_range"
25
+ autoload :BeliefNode, "node/belief_node"
26
+ autoload :DataCollection, "node/data_collection"
27
+ autoload :DataNode, "node/data_node"
28
+ autoload :DiscreteNode, "node/discrete_node"
29
+ autoload :MCNode, "node/mc_node"
30
+ autoload :PlausibleRange, "node/plausible_range"
31
+ autoload :Fact, "node/fact"
32
+ autoload :Decision, "node/decision"
33
+ autoload :CPMNode, 'node/cpm_node'
34
+
27
35
  autoload :ValueDescription, "value_description"
28
- autoload :ValueAggregator, "value_aggregator"
29
- autoload :ValueMultiplier, "value_multiplier"
30
36
  autoload :MonteCarloSet, "monte_carlo_set"
31
- autoload :MCNode, "mc_node"
32
- autoload :CausalGraph, "causal_graph"
33
- autoload :DataNode, "data_node"
34
37
  autoload :KnowledgeBase, "knowledge_base"
35
38
 
36
39
  autoload :Import, "import"
37
40
  autoload :ImportNode, "import/import_node"
38
41
  autoload :YAMLImport, 'import/yaml_import'
39
42
  autoload :CSVImport, 'import/csv_import'
40
- autoload :RDFImport, 'import/rdf_import'
41
- autoload :SQLiteImport, 'import/sqlite_import'
43
+ # autoload :RDFImport, 'import/rdf_import'
44
+ # autoload :SQLiteImport, 'import/sqlite_import'
42
45
 
43
46
  autoload :Simulation, 'simulation'
44
47
  autoload :TickMethods, 'simulation/tick_methods'
@@ -48,10 +51,10 @@ module Fathom
48
51
  autoload :Properties, 'agent/properties'
49
52
  autoload :AgentCluster, 'agent/agent_cluster'
50
53
 
51
- autoload :NumericMethods, 'numeric_methods'
52
- autoload :EnforcedName, 'enforced_name'
54
+ autoload :EnforcedName, 'node/node_extensions/enforced_name'
55
+ autoload :NumericMethods, 'node/node_extensions/numeric_methods'
53
56
 
54
- autoload :Distributions, 'distributions'
57
+ require 'distributions'
55
58
  module Distributions
56
59
  autoload :Gaussian, 'distributions/gaussian'
57
60
  autoload :Uniform, 'distributions/uniform'
@@ -66,4 +69,4 @@ module Fathom
66
69
  end
67
70
 
68
71
  # Temporary
69
- include Fathom
72
+ # include Fathom
@@ -4,14 +4,6 @@ class Fathom::Agent
4
4
  # =================
5
5
  # = Class Methods =
6
6
  # =================
7
- class << self
8
- def define_node_accessor(name, node)
9
- method_name = ("node_for_" + name.to_s.downcase.gsub(/\s+/, '_')).to_sym
10
- define_method(method_name) do
11
- node
12
- end
13
- end
14
- end
15
7
 
16
8
  include Properties
17
9
 
@@ -36,7 +28,14 @@ class Fathom::Agent
36
28
  nodes.each do |name, node|
37
29
  next unless self.class.properties.include?(name)
38
30
  states[name] = node.respond_to?(:rand) ? node.rand : node
39
- self.class.define_node_accessor(name, node)
31
+
32
+ method_name = ("node_for_" + name.to_s.downcase.gsub(/\s+/, '_')).to_sym
33
+ (class << self; self; end).module_eval do
34
+ define_method method_name do
35
+ node
36
+ end
37
+ end
38
+
40
39
  end
41
40
  end
42
41
 
@@ -113,4 +113,7 @@ class ConditionalProbabilityMatrix
113
113
  def get_index(node, label)
114
114
  node.labels.index(label)
115
115
  end
116
+ end
117
+
118
+ class CPM < ConditionalProbabilityMatrix
116
119
  end
@@ -23,7 +23,10 @@ class Node
23
23
  end
24
24
 
25
25
  def inspect
26
- "Node: #{self.labels.inspect} #{self.probabilities.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}"
27
30
  end
28
31
 
29
32
  def belief
@@ -34,7 +37,27 @@ class Node
34
37
  alias :l :likelihood
35
38
  alias :p :probabilities
36
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
+
37
56
  protected
57
+
58
+ def index_for(label)
59
+ labels.index(label)
60
+ end
38
61
 
39
62
  def assert_likelihood
40
63
  @likelihood = Vector.ary_to_gv(Array.new(@probabilities.size, 1))
@@ -6,40 +6,19 @@ class Fathom::Distributions::DiscreteUniform
6
6
  @rng ||= GSL::Rng.alloc(GSL::Rng::MT19937_1999, Kernel.rand(100_000))
7
7
  end
8
8
 
9
- def rand
10
- (rng.ugaussian / 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.ugaussian_Pinv(confidence_interval) : GSL::Cdf.ugaussian_Qinv(confidence_interval)
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.ugaussian_Qinv((1 - confidence_interval) / 2) * 2
9
+ # Bounded from 0 to size - 1
10
+ def rand(size)
11
+ value = get_rand(size)
12
+ while value < 0 or value >= size
13
+ value = get_rand(size)
14
+ end
15
+ value
41
16
  end
42
17
 
18
+ protected
19
+ def get_rand(size)
20
+ (rng.ugaussian / size).floor + 1
21
+ end
43
22
 
44
23
  end
45
24
  end
@@ -33,50 +33,53 @@ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'fathom'))
33
33
  This way data from spreadsheets or YAML files can easily be added to the knowledge base.
34
34
 
35
35
  =end
36
- class Fathom::Import
36
+
37
+ module Fathom
38
+ class Import
37
39
 
38
- class << self
39
- def import(opts={})
40
- importer = new(opts)
41
- importer.import
40
+ class << self
41
+ def import(opts={})
42
+ importer = new(opts)
43
+ importer.import
44
+ end
42
45
  end
43
- end
44
46
 
45
- attr_reader :content, :options, :import_node
47
+ attr_reader :content, :options, :import_node
46
48
 
47
- def initialize(opts={})
48
- @options = OptionsHash.new(opts)
49
- @content = @options[:content]
50
- @import_node = ImportNode.new(opts)
51
- end
49
+ def initialize(opts={})
50
+ @options = OptionsHash.new(opts)
51
+ @content = @options[:content]
52
+ @import_node = ImportNode.new(opts)
53
+ end
52
54
 
53
- def import
54
- import_methods.each do |method|
55
- klass, initialization_data = self.send(method.to_sym)
56
- initialization_data.each do |values|
57
- extract_nodes(klass, values)
55
+ def import
56
+ import_methods.each do |method|
57
+ klass, initialization_data = self.send(method.to_sym)
58
+ initialization_data.each do |values|
59
+ extract_nodes(klass, values)
60
+ end
58
61
  end
62
+ self.import_node
59
63
  end
60
- self.import_node
61
- end
62
64
 
63
- protected
65
+ protected
64
66
 
65
- # Just blankly try to initiate a node. If it fails, silently fail for now.
66
- def extract_nodes(klass, values)
67
- begin
68
- node = klass.new(values)
69
- if node
70
- self.import_node.add_child(node)
71
- Fathom.knowledge_base[node.name] = node
67
+ # Just blankly try to initiate a node. If it fails, silently fail for now.
68
+ def extract_nodes(klass, values)
69
+ begin
70
+ node = klass.new(values)
71
+ if node
72
+ self.import_node.add_child(node)
73
+ Fathom.knowledge_base[node.name] = node
74
+ end
75
+ rescue
76
+ nil
72
77
  end
73
- rescue
74
- nil
75
78
  end
76
- end
77
79
 
78
- def import_methods
79
- (self.methods - self.class.superclass.instance_methods).map {|m| m if m =~ /import_\w+/}.compact
80
- end
81
-
80
+ def import_methods
81
+ (self.methods - self.class.superclass.instance_methods).map {|m| m if m =~ /import_\w+/}.compact
82
+ end
83
+
84
+ end
82
85
  end
@@ -2,7 +2,7 @@ require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'fathom')
2
2
  require 'open-uri'
3
3
  require 'yaml'
4
4
 
5
- class Fathom::YAMLImport < Import
5
+ class Fathom::YAMLImport < Fathom::Import
6
6
  def import_plausible_ranges
7
7
  assert_yaml_content
8
8
  plausible_ranges = extract_plausible_ranges
@@ -15,6 +15,12 @@ class Fathom::YAMLImport < Import
15
15
  [DataNode, data_nodes]
16
16
  end
17
17
 
18
+ def import_facts
19
+ assert_yaml_content
20
+ facts = extract_facts
21
+ [Fact, facts]
22
+ end
23
+
18
24
  protected
19
25
  def assert_yaml_content
20
26
  return @yaml_content if @yaml_content
@@ -27,7 +33,21 @@ class Fathom::YAMLImport < Import
27
33
  @yaml_content = YAML.load(file_contents)
28
34
  end
29
35
 
36
+ def extract_facts
37
+ return [] unless @yaml_content
38
+ output = []
39
+ @yaml_content.each do |e|
40
+ next unless e.is_a?(Array)
41
+ next unless e.first.to_s.match(/fact/i)
42
+ obj = e[1]
43
+ next unless obj.is_a?(Hash)
44
+ output << obj
45
+ end
46
+ output
47
+ end
48
+
30
49
  def extract_plausible_ranges
50
+ return [] unless @yaml_content
31
51
  @yaml_content.inject([]) do |list, array|
32
52
  name, value = array.first, array.last
33
53
  if value.is_a?(Hash)
@@ -40,6 +60,7 @@ class Fathom::YAMLImport < Import
40
60
  end
41
61
 
42
62
  def extract_data_nodes
63
+ return [] unless @yaml_content
43
64
  @yaml_content.inject([]) do |list, array|
44
65
  name, value = array.first, array.last
45
66
  list << {:name => name, :values => value} if value.is_a?(Array)