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.
- data/.autotest +7 -5
- data/.document +2 -2
- data/Gemfile +9 -10
- data/{LICENSE → LICENSE.txt} +1 -1
- data/README.md +29 -90
- data/Rakefile +34 -32
- data/VERSION +1 -1
- data/fathom.gemspec +105 -0
- data/features/fathom.feature +26 -0
- data/features/step_definitions/fathom_steps.rb +23 -0
- data/features/support/env.rb +13 -0
- data/lib/ext/array.rb +6 -2
- data/lib/ext/string.rb +86 -7
- data/lib/fathom.rb +51 -88
- data/lib/fathom/behaviors/attribute_system.rb +91 -0
- data/lib/fathom/behaviors/context_behavior.rb +28 -0
- data/lib/fathom/behaviors/plugins.rb +16 -0
- data/lib/fathom/contexts/network_population.rb +47 -0
- data/lib/fathom/contexts/network_traversal.rb +4 -0
- data/lib/fathom/data/adjacency_matrix.rb +27 -0
- data/lib/fathom/data/definition.rb +22 -0
- data/lib/fathom/data/edge.rb +58 -0
- data/lib/fathom/data/network.rb +35 -0
- data/lib/fathom/data/outcome.rb +30 -0
- data/lib/fathom/data/property.rb +31 -0
- data/lib/fathom/data/variable.rb +59 -0
- data/lib/fathom/roles/general_graph_tools.rb +87 -0
- data/lib/fathom/roles/network_builder.rb +61 -0
- data/spec/fathom/behaviors/attribute_system_spec.rb +141 -0
- data/spec/fathom/behaviors/context_behavior_spec.rb +15 -0
- data/spec/fathom/behaviors/plugins_spec.rb +80 -0
- data/spec/fathom/contexts/network_population_spec.rb +55 -0
- data/spec/fathom/contexts/network_traversal_spec.rb +11 -0
- data/spec/fathom/data/adjacency_matrix_spec.rb +42 -0
- data/spec/fathom/data/definition_spec.rb +19 -0
- data/spec/fathom/data/edge_spec.rb +77 -0
- data/spec/fathom/data/network_spec.rb +72 -0
- data/spec/fathom/data/outcome_spec.rb +17 -0
- data/spec/fathom/data/property_spec.rb +17 -0
- data/spec/fathom/data/variable_spec.rb +101 -0
- data/spec/fathom/ext/array_spec.rb +17 -0
- data/spec/fathom/ext/string_spec.rb +90 -0
- data/spec/fathom/roles/general_graph_tools_spec.rb +95 -0
- data/spec/fathom/roles/network_builder_spec.rb +90 -0
- data/spec/fathom_spec.rb +28 -49
- data/spec/spec_helper.rb +7 -11
- data/spec/support/context_behavior.rb +14 -0
- data/spec/support/custom_matchers.rb +12 -0
- data/spec/support/files.rb +8 -0
- data/spec/support/network.yml +42 -0
- metadata +133 -174
- data/.bundle/config +0 -2
- data/.gitignore +0 -6
- data/Gemfile.lock +0 -42
- data/TODO.md +0 -127
- data/autotest/discover.rb +0 -1
- data/lib/ext/faster_csv.rb +0 -1
- data/lib/ext/open_struct.rb +0 -17
- data/lib/fathom/agent.rb +0 -48
- data/lib/fathom/agent/agent_cluster.rb +0 -23
- data/lib/fathom/agent/properties.rb +0 -48
- data/lib/fathom/archive/causal_graph.rb +0 -12
- data/lib/fathom/archive/concept.rb +0 -83
- data/lib/fathom/archive/conditional_probability_matrix.rb +0 -119
- data/lib/fathom/archive/inverter.rb +0 -20
- data/lib/fathom/archive/n2.rb +0 -198
- data/lib/fathom/archive/n3.rb +0 -119
- data/lib/fathom/archive/node.rb +0 -97
- data/lib/fathom/archive/noodle.rb +0 -136
- data/lib/fathom/archive/scratch.rb +0 -45
- data/lib/fathom/distributions.rb +0 -8
- data/lib/fathom/distributions/discrete_gaussian.rb +0 -44
- data/lib/fathom/distributions/discrete_uniform.rb +0 -25
- data/lib/fathom/distributions/gaussian.rb +0 -46
- data/lib/fathom/distributions/uniform.rb +0 -35
- data/lib/fathom/import.rb +0 -85
- data/lib/fathom/import/csv_import.rb +0 -59
- data/lib/fathom/import/import_node.rb +0 -17
- data/lib/fathom/import/yaml_import.rb +0 -74
- data/lib/fathom/knowledge_base.rb +0 -46
- data/lib/fathom/knowledge_base/search.rb +0 -19
- data/lib/fathom/monte_carlo_set.rb +0 -152
- data/lib/fathom/node.rb +0 -139
- data/lib/fathom/node/belief_node.rb +0 -121
- data/lib/fathom/node/cpm_node.rb +0 -100
- data/lib/fathom/node/data_collection.rb +0 -97
- data/lib/fathom/node/data_node.rb +0 -22
- data/lib/fathom/node/decision.rb +0 -11
- data/lib/fathom/node/discrete_node.rb +0 -41
- data/lib/fathom/node/fact.rb +0 -24
- data/lib/fathom/node/mc_node.rb +0 -70
- data/lib/fathom/node/node_extensions/enforced_name.rb +0 -12
- data/lib/fathom/node/node_extensions/numeric_methods.rb +0 -68
- data/lib/fathom/node/plausible_range.rb +0 -98
- data/lib/fathom/simulation.rb +0 -59
- data/lib/fathom/simulation/tick_methods.rb +0 -25
- data/lib/fathom/simulation/tick_simulation.rb +0 -12
- data/lib/fathom/value_description.rb +0 -79
- data/lib/options_hash.rb +0 -186
- data/spec/ext/array_spec.rb +0 -10
- data/spec/ext/faster_csv_spec.rb +0 -10
- data/spec/ext/open_struct_spec.rb +0 -20
- data/spec/ext/string_spec.rb +0 -7
- data/spec/fathom/agent/agent_cluster_spec.rb +0 -17
- data/spec/fathom/agent_spec.rb +0 -51
- data/spec/fathom/distributions/discrete_gaussian_spec.rb +0 -64
- data/spec/fathom/distributions/discrete_uniform_spec.rb +0 -0
- data/spec/fathom/distributions/gaussian_spec.rb +0 -64
- data/spec/fathom/distributions/uniform_spec.rb +0 -0
- data/spec/fathom/import/csv_import_spec.rb +0 -52
- data/spec/fathom/import/import_node_spec.rb +0 -10
- data/spec/fathom/import/yaml_import_spec.rb +0 -73
- data/spec/fathom/import_spec.rb +0 -36
- data/spec/fathom/knowledge_base_spec.rb +0 -20
- data/spec/fathom/monte_carlo_set_spec.rb +0 -149
- data/spec/fathom/node/belief_node_spec.rb +0 -180
- data/spec/fathom/node/cpm_node_spec.rb +0 -144
- data/spec/fathom/node/data_collection_spec.rb +0 -26
- data/spec/fathom/node/data_node_spec.rb +0 -102
- data/spec/fathom/node/decision_spec.rb +0 -15
- data/spec/fathom/node/discrete_node_spec.rb +0 -56
- data/spec/fathom/node/fact_spec.rb +0 -33
- data/spec/fathom/node/mc_node_spec.rb +0 -66
- data/spec/fathom/node/node_extensions/enforced_name_spec.rb +0 -15
- data/spec/fathom/node/node_extensions/numeric_methods_spec.rb +0 -124
- data/spec/fathom/node/plausible_range_spec.rb +0 -151
- data/spec/fathom/node_spec.rb +0 -172
- data/spec/fathom/simulation/tick_simulation_spec.rb +0 -32
- data/spec/fathom/simulation_spec.rb +0 -24
- data/spec/fathom/value_description_spec.rb +0 -70
- data/spec/support/demo.yml +0 -17
- data/spec/support/demo_agent.rb +0 -8
- data/spec/support/dummy_numeric_node.rb +0 -8
- data/spec/support/fact.yml +0 -11
data/autotest/discover.rb
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
Autotest.add_discovery { "rspec2" }
|
data/lib/ext/faster_csv.rb
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
FasterCSV::HeaderConverters[:strip] = lambda{|h| h.strip}
|
data/lib/ext/open_struct.rb
DELETED
data/lib/fathom/agent.rb
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'fathom'))
|
|
2
|
-
class Fathom::Agent
|
|
3
|
-
|
|
4
|
-
# =================
|
|
5
|
-
# = Class Methods =
|
|
6
|
-
# =================
|
|
7
|
-
|
|
8
|
-
include Properties
|
|
9
|
-
|
|
10
|
-
def initialize(opts={})
|
|
11
|
-
self.class.define_property_states
|
|
12
|
-
assert_node_accessors(opts)
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def states
|
|
16
|
-
@states ||= self.class.properties.inject({}) do |h, state_method_name|
|
|
17
|
-
h[state_method_name] = nil
|
|
18
|
-
h
|
|
19
|
-
end
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
def callbacks
|
|
23
|
-
@callbacks ||= (self.methods - Object.methods).grep(/^on_(\w+)/)
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
protected
|
|
27
|
-
def assert_node_accessors(nodes)
|
|
28
|
-
nodes.each do |name, node|
|
|
29
|
-
next unless self.class.properties.include?(name)
|
|
30
|
-
states[name] = node.respond_to?(:rand) ? node.rand : 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
|
-
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
if __FILE__ == $0
|
|
45
|
-
include Fathom
|
|
46
|
-
# TODO: Is there anything you want to do to run this file on its own?
|
|
47
|
-
# Agent.new
|
|
48
|
-
end
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'fathom'))
|
|
2
|
-
|
|
3
|
-
=begin
|
|
4
|
-
This class is designed to hold a cluster of agents in memory. It runs the simulation locally and
|
|
5
|
-
speaks to other clusters via EventMachine. In this way, we don't need a Ruby runtime/thread/fiber
|
|
6
|
-
for each agent, just one per dozen/hundred/thousand agents, depending on what balances the
|
|
7
|
-
simulation.
|
|
8
|
-
=end
|
|
9
|
-
class Fathom::AgentCluster
|
|
10
|
-
|
|
11
|
-
attr_reader :agents
|
|
12
|
-
|
|
13
|
-
def initialize(*agents)
|
|
14
|
-
@agents = agents
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
if __FILE__ == $0
|
|
20
|
-
include Fathom
|
|
21
|
-
# TODO: Is there anything you want to do to run this file on its own?
|
|
22
|
-
# AgentCluster.new
|
|
23
|
-
end
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'fathom'))
|
|
2
|
-
module Fathom
|
|
3
|
-
module Properties
|
|
4
|
-
|
|
5
|
-
def self.included(base)
|
|
6
|
-
base.send(:extend, ClassMethods)
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
module ClassMethods
|
|
10
|
-
|
|
11
|
-
def properties
|
|
12
|
-
@properties ||= []
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def property(name, opts={})
|
|
16
|
-
self.properties << name_sym(name)
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def define_property_states
|
|
20
|
-
return true if @property_states_defined
|
|
21
|
-
self.properties.each do |state_method_name|
|
|
22
|
-
unless self.instance_methods.include?(state_method_name.to_s)
|
|
23
|
-
define_method(state_method_name) do
|
|
24
|
-
states[state_method_name]
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
state_method_writer = "#{state_method_name}=".to_sym
|
|
29
|
-
unless self.instance_methods.include?(state_method_writer.to_s)
|
|
30
|
-
define_method(state_method_writer) do |value|
|
|
31
|
-
states[state_method_name] = value
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
@property_states_defined = true
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
protected
|
|
40
|
-
def name_sym(name)
|
|
41
|
-
name.to_s.downcase.gsub(/\s+/, '_').to_sym
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'fathom'))
|
|
2
|
-
module Fathom
|
|
3
|
-
class CausalGraph
|
|
4
|
-
|
|
5
|
-
end
|
|
6
|
-
end
|
|
7
|
-
|
|
8
|
-
if __FILE__ == $0
|
|
9
|
-
include Fathom
|
|
10
|
-
# TODO: Is there anything you want to do to run this file on its own?
|
|
11
|
-
# CausalGraph.new
|
|
12
|
-
end
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
=begin
|
|
2
|
-
This is a first approach to an RDF back end for a broadly-defined data store.
|
|
3
|
-
|
|
4
|
-
I am borrowing from the SKOS ontology here to be able to define any sort of concept
|
|
5
|
-
that may assist me with my decision-making work.
|
|
6
|
-
|
|
7
|
-
TODO:
|
|
8
|
-
|
|
9
|
-
[x] Build a basic Spira modeal
|
|
10
|
-
[x] Make a SKOS commitment
|
|
11
|
-
[.] Create helper methods to find or create the concept easily (using hash syntax for field names)
|
|
12
|
-
[] Create association methods for associating the concept to other concepts (need to think about this one)
|
|
13
|
-
[] Create specific methods to define a plausible range (probably define a Spira model here too)
|
|
14
|
-
[] Create specific methods to define a ValueDescription
|
|
15
|
-
[] Create specific methods to define a MonteCarloSet
|
|
16
|
-
[] Create specific methods to define a CausalGraph
|
|
17
|
-
[] Create specific methods to define a DependencyGraph
|
|
18
|
-
[] Create specific methods to define the value of further measurement (another un-named class)
|
|
19
|
-
|
|
20
|
-
=end
|
|
21
|
-
|
|
22
|
-
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'fathom'))
|
|
23
|
-
require 'rdf'
|
|
24
|
-
require 'rdf/ntriples'
|
|
25
|
-
require 'data_objects'
|
|
26
|
-
require 'do_sqlite3'
|
|
27
|
-
require 'rdf/do'
|
|
28
|
-
require 'spira'
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
module Fathom
|
|
32
|
-
|
|
33
|
-
# Go ahead and create a generic repo for Fathom
|
|
34
|
-
def repo
|
|
35
|
-
@repo ||= RDF::DataObjects::Repository.new('sqlite3:/tmp/test.db')
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
Spira.add_repository(:default, repo)
|
|
39
|
-
|
|
40
|
-
class Concept
|
|
41
|
-
|
|
42
|
-
include Spira::Resource
|
|
43
|
-
include RDF
|
|
44
|
-
|
|
45
|
-
class << self
|
|
46
|
-
def find_or_build(name, description=nil)
|
|
47
|
-
concept = Concept.for(concept_name(name))
|
|
48
|
-
return concept if concept.exist?
|
|
49
|
-
concept.name = name
|
|
50
|
-
concept.description = description
|
|
51
|
-
concept
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
def find_or_create(name, description=nil)
|
|
55
|
-
concept = Concept.for(concept_name(name))
|
|
56
|
-
return concept if concept.exist?
|
|
57
|
-
concept.name = name
|
|
58
|
-
concept.description = description
|
|
59
|
-
concept.save!
|
|
60
|
-
concept
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
protected
|
|
64
|
-
def concept_name(name)
|
|
65
|
-
concept_name = name.downcase.gsub(/\s+/, '_')
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
base_uri "http://example.org/example/concepts"
|
|
70
|
-
|
|
71
|
-
property :name, :predicate => SKOS.prefLabel
|
|
72
|
-
property :description, :predicate => SKOS.definition
|
|
73
|
-
property :scope, :predicate => SKOS.scopeNote
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
end
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
if __FILE__ == $0
|
|
80
|
-
include Fathom
|
|
81
|
-
# TODO: Is there anything you want to do to run this file on its own?
|
|
82
|
-
# Concept.new
|
|
83
|
-
end
|
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
require 'rubygems'
|
|
2
|
-
require 'gsl'
|
|
3
|
-
|
|
4
|
-
include GSL
|
|
5
|
-
|
|
6
|
-
class NodeAccessor
|
|
7
|
-
|
|
8
|
-
attr_reader :cpm
|
|
9
|
-
|
|
10
|
-
def initialize(cpm)
|
|
11
|
-
@cpm = cpm
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def is(*labels)
|
|
15
|
-
ChildAccessor.new(cpm, *labels)
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def is_not(*labels)
|
|
19
|
-
ChildAccessor.new(cpm, *(cpm.child.labels - labels))
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
class ChildAccessor
|
|
24
|
-
|
|
25
|
-
attr_reader :cpm, :labels
|
|
26
|
-
def initialize(cpm, *labels)
|
|
27
|
-
@cpm, @labels = cpm, labels
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def given(parent_name)
|
|
31
|
-
ParentAccessor.new(cpm, labels)
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
class ParentAccessor
|
|
37
|
-
|
|
38
|
-
attr_reader :cpm, :node, :child_labels, :child_indices
|
|
39
|
-
def initialize(cpm, child_labels)
|
|
40
|
-
@cpm = cpm
|
|
41
|
-
@child_labels = child_labels
|
|
42
|
-
@node = cpm.parent
|
|
43
|
-
@child_indices = child_labels.map {|label| @cpm.child.labels.index(label)}
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def is(*labels)
|
|
47
|
-
indices = labels.map {|label| get_index(label) }
|
|
48
|
-
sum_probabilities(indices)
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
def is_not(*labels)
|
|
52
|
-
not_indices = labels.map {|label| get_index(label) }
|
|
53
|
-
indices = (0..node.labels.size).to_a - not_indices
|
|
54
|
-
sum_probabilities(indices)
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
protected
|
|
58
|
-
|
|
59
|
-
# TODO: Not right...
|
|
60
|
-
def sum_probabilities(indices)
|
|
61
|
-
first_child = child_indices.first
|
|
62
|
-
cpm.matrix[indices.first, first_child]
|
|
63
|
-
# indices.inject(0.0) do |s, i|
|
|
64
|
-
# s += cpm.matrix[i, first_child]
|
|
65
|
-
# end
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
def get_index(label)
|
|
69
|
-
node.labels.index(label)
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
class ConditionalProbabilityMatrix
|
|
76
|
-
|
|
77
|
-
class << self
|
|
78
|
-
def define_node_accessor(node, cpm)
|
|
79
|
-
define_method(node.name.to_sym) do
|
|
80
|
-
NodeAccessor.new(cpm)
|
|
81
|
-
end
|
|
82
|
-
end
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
attr_reader :parent, :child, :matrix
|
|
86
|
-
|
|
87
|
-
def initialize(parent, child)
|
|
88
|
-
@parent, @child = parent, child
|
|
89
|
-
@matrix = @parent.probabilities.col * @child.probabilities
|
|
90
|
-
assert_name_access
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
def probability(opts={})
|
|
94
|
-
child_label = opts[:child]
|
|
95
|
-
parent_label = opts[:parent]
|
|
96
|
-
raise ArgumentError, "Must provide a child and parent label. E.g., probability(:child => true, :parent => false)" unless child_label and parent_label
|
|
97
|
-
child_label_index = get_index(child, child_label)
|
|
98
|
-
parent_label_index = get_index(parent, parent_label)
|
|
99
|
-
self.matrix[parent_label_index, child_label_index]
|
|
100
|
-
end
|
|
101
|
-
alias :p :probability
|
|
102
|
-
|
|
103
|
-
def inspect
|
|
104
|
-
"ConditionalProbabilityMatrix: #{matrix.to_a.inspect}"
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
protected
|
|
108
|
-
|
|
109
|
-
def assert_name_access
|
|
110
|
-
ConditionalProbabilityMatrix.define_node_accessor(child, self)
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
def get_index(node, label)
|
|
114
|
-
node.labels.index(label)
|
|
115
|
-
end
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
class CPM < ConditionalProbabilityMatrix
|
|
119
|
-
end
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'fathom'))
|
|
2
|
-
module Fathom
|
|
3
|
-
class Invertor < Fathom::Node
|
|
4
|
-
|
|
5
|
-
def initialize(opts={})
|
|
6
|
-
super(opts)
|
|
7
|
-
@name ||= "Inverter"
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
def value
|
|
11
|
-
-1
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
if __FILE__ == $0
|
|
17
|
-
include Fathom
|
|
18
|
-
# TODO: Is there anything you want to do to run this file on its own?
|
|
19
|
-
# Invertor.new
|
|
20
|
-
end
|
data/lib/fathom/archive/n2.rb
DELETED
|
@@ -1,198 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
=begin
|
|
4
|
-
Some noodling about what a node might contain in order to describe the joint probabilities.
|
|
5
|
-
=end
|
|
6
|
-
class Node
|
|
7
|
-
|
|
8
|
-
attr_reader :variable, :parents
|
|
9
|
-
def initialize(variable, *parents)
|
|
10
|
-
@variable = Variable.infer(variable)
|
|
11
|
-
raise ArgumentError, "A valid variable cannot be implied from #{variable}" unless @variable
|
|
12
|
-
@parents = parents
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def name
|
|
16
|
-
self.variable.name
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def inspect
|
|
20
|
-
"Node: #{self.name} #{ self.parents.map{|p| p.name}.inspect }"
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
class << self
|
|
24
|
-
def infer(obj, *parents)
|
|
25
|
-
return obj if obj.is_a?(Node)
|
|
26
|
-
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
class Variable
|
|
32
|
-
|
|
33
|
-
attr_reader :values, :name, :observations, :total
|
|
34
|
-
|
|
35
|
-
def initialize(name, *values)
|
|
36
|
-
values = [true, false] if values.empty?
|
|
37
|
-
@name = name
|
|
38
|
-
@values = values
|
|
39
|
-
@observations = Array.new(@values.size, 0)
|
|
40
|
-
@total = 0
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
# You can observe anything but nothing: we record any observation but nil.
|
|
44
|
-
# If nil is set, we use the first value as the default.
|
|
45
|
-
def observe(value=nil)
|
|
46
|
-
value = self.values.first if value.nil?
|
|
47
|
-
unless self.values.include?(value)
|
|
48
|
-
self.values << value
|
|
49
|
-
self.observations << 0
|
|
50
|
-
end
|
|
51
|
-
index = self.values.index(value)
|
|
52
|
-
self.observations[index] += 1
|
|
53
|
-
@total += 1
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
# Lookup observations
|
|
57
|
-
def observed(value)
|
|
58
|
-
index = self.values.index(value)
|
|
59
|
-
return 0 unless index
|
|
60
|
-
self.observations[index]
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
def inspect
|
|
64
|
-
"Variable: #{self.name} #{self.values.inspect}"
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
class << self
|
|
68
|
-
def infer(obj, *values)
|
|
69
|
-
return obj if obj.is_a?(Variable)
|
|
70
|
-
case obj
|
|
71
|
-
when Symbol
|
|
72
|
-
Variable.new(obj, *values)
|
|
73
|
-
when String
|
|
74
|
-
Variable.new(obj.to_sym, *values)
|
|
75
|
-
else
|
|
76
|
-
nil
|
|
77
|
-
end
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
end
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
require 'rubygems'
|
|
84
|
-
require 'spec'
|
|
85
|
-
|
|
86
|
-
describe Variable do
|
|
87
|
-
|
|
88
|
-
before do
|
|
89
|
-
@v = Variable.new(:v1)
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
it "should require a name" do
|
|
93
|
-
lambda{Variable.new}.should raise_error(ArgumentError)
|
|
94
|
-
lambda{@v = Variable.new(:name)}.should_not raise_error
|
|
95
|
-
@v.name.should eql(:name)
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
it "should default to true and false as parameter values" do
|
|
99
|
-
v = Variable.new(:v)
|
|
100
|
-
v.values.should eql([true, false])
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
it "should be able to take a variables parameters" do
|
|
104
|
-
v = Variable.new :v, :red, :blue, :green
|
|
105
|
-
v.values.should eql([:red, :blue, :green])
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
it "should be able to infer a variable from a variable" do
|
|
109
|
-
v = Variable.new(:v)
|
|
110
|
-
Variable.infer(v).should eql(v)
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
it "should be able to infer a variable from a symbol" do
|
|
114
|
-
v = Variable.infer(:v)
|
|
115
|
-
v.should be_a(Variable)
|
|
116
|
-
v.name.should eql(:v)
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
it "should be able to infer a variable from a string" do
|
|
120
|
-
v = Variable.infer('v')
|
|
121
|
-
v.should be_a(Variable)
|
|
122
|
-
v.name.should eql(:v)
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
it "should be able to infer values from a list" do
|
|
126
|
-
v = Variable.infer :v, 1, 2
|
|
127
|
-
v.values.should eql([1,2])
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
it "should start with zero observations" do
|
|
131
|
-
@v.total.should eql(0)
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
it "should increment observations" do
|
|
135
|
-
@v.observe
|
|
136
|
-
@v.total.should eql(1)
|
|
137
|
-
@v.observe
|
|
138
|
-
@v.total.should eql(2)
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
it "should record observations" do
|
|
142
|
-
@v.observe(true)
|
|
143
|
-
@v.total.should eql(1)
|
|
144
|
-
@v.observed(true).should eql(1)
|
|
145
|
-
@v.observed(false).should eql(0)
|
|
146
|
-
@v.observe(false)
|
|
147
|
-
@v.total.should eql(2)
|
|
148
|
-
@v.observed(true).should eql(1)
|
|
149
|
-
@v.observed(false).should eql(1)
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
end
|
|
153
|
-
|
|
154
|
-
describe Node do
|
|
155
|
-
|
|
156
|
-
before do
|
|
157
|
-
@season = Variable.new(:season, :spring, :summer, :fall, :winter)
|
|
158
|
-
@x1 = Node.new(@season)
|
|
159
|
-
@x2 = Node.new(:rain, @x1)
|
|
160
|
-
@x3 = Node.new(:sprinkler, @x1)
|
|
161
|
-
@x4 = Node.new(:wet, @x3, @x2)
|
|
162
|
-
@x5 = Node.new(:slippery, @x4)
|
|
163
|
-
end
|
|
164
|
-
|
|
165
|
-
it "should infer a variable for the node" do
|
|
166
|
-
v = Variable.new(:v)
|
|
167
|
-
n = Node.new(v)
|
|
168
|
-
n.variable.should eql(v)
|
|
169
|
-
|
|
170
|
-
n = Node.new(:v)
|
|
171
|
-
v = n.variable
|
|
172
|
-
v.should be_a(Variable)
|
|
173
|
-
v.name.should eql(:v)
|
|
174
|
-
end
|
|
175
|
-
|
|
176
|
-
it "should raise an error when it cannot infer a variable for the node" do
|
|
177
|
-
lambda{Node.new(1)}.should raise_error(ArgumentError, /A valid variable cannot be implied from/)
|
|
178
|
-
end
|
|
179
|
-
|
|
180
|
-
it "should be able to create a node with parents" do
|
|
181
|
-
@x1.parents.should be_empty
|
|
182
|
-
@x2.parents.should eql([@x1])
|
|
183
|
-
@x3.parents.should eql([@x1])
|
|
184
|
-
@x4.parents.should eql([@x3, @x2])
|
|
185
|
-
@x5.parents.should eql([@x4])
|
|
186
|
-
end
|
|
187
|
-
|
|
188
|
-
# it "should be able to infer a node" do
|
|
189
|
-
# n = Node.infer(:v1, :v2)
|
|
190
|
-
# n.name.should eql(:v1)
|
|
191
|
-
# n.variable.name.should eql(:v1)
|
|
192
|
-
# n.variable.should be_a(Variable)
|
|
193
|
-
# n.parents.size.should eql(1)
|
|
194
|
-
# p = n.parents.first
|
|
195
|
-
# p.name.should eql(:v2)
|
|
196
|
-
# p.should be_a(Variable)
|
|
197
|
-
# end
|
|
198
|
-
end
|