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 +0,0 @@
1
- Autotest.add_discovery { "rspec2" }
@@ -1 +0,0 @@
1
- FasterCSV::HeaderConverters[:strip] = lambda{|h| h.strip}
@@ -1,17 +0,0 @@
1
- require 'ostruct'
2
-
3
- # Fix a few bugs in OpenStruct
4
- class OpenStruct
5
- def table
6
- @table
7
- end
8
-
9
- def values
10
- @table.values
11
- end
12
-
13
- def keys
14
- @table.keys
15
- end
16
- end
17
-
@@ -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
@@ -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