fathom 0.3.0 → 0.3.1

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